<commit_before>
use std::fmt;
use std::io::Read;  // Trait providing read_to_string()
use std::env;

use hyper;
use serde;
use serde_json;

use BuildQuery;
use Groups;

pub const API_VERSION: u16 = 3;

#[derive(Debug)]
pub struct Pagination {
    pub page: u16,
    pub per_page: u16,
}

pub struct GitLab {
    scheme: String,
    domain: String,
    port: u16,
    private_token: String,
    client: hyper::Client,
    pagination: Pagination,
}

// Explicitly implement Debug trait for GitLab so we can hide the token.
impl fmt::Debug for GitLab {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f,
               "GitLab {{ scheme: {}, domain: {}, port: {}, private_token: XXXXXXXXXXXXXXXXXXXX, \
                client: {:?}, pagination: {:?} }}",
               self.scheme,
               self.domain,
               self.port,
               self.client,
               self.pagination)
    }
}

impl GitLab {
    pub fn new(scheme: &str, domain: &str, port: u16, private_token: &str) -> GitLab {
        GitLab {
            scheme: scheme.to_string(),
            domain: domain.to_string(),
            port: port,
            private_token: private_token.to_string(),
            client: match env::var("HTTP_PROXY") {
                Ok(proxy) => {
                    let proxy: Vec<&str> = proxy.trim_left_matches("http://").split(':').collect();
                    let hostname = proxy[0].to_string();
                    let port = proxy[1];

                    hyper::Client::with_http_proxy(hostname, port.parse().unwrap())
                }
                Err(_) => hyper::Client::new(),
            },
            pagination: Pagination {
                page: 1,
                per_page: 20,
            },
        }
    }

    pub fn new_http(domain: &str, private_token: &str) -> GitLab {
        GitLab::new("http", domain, 80, private_token)
    }

    pub fn new_https(domain: &str, private_token: &str) -> GitLab {
        GitLab::new("https", domain, 443, private_token)
    }

    /// Build a URL used to access GitLab instance, including some parameters.
    ///
    /// # Examples
    ///
    /// Example from GitLab: https://docs.gitlab.com/ce/api/#basic-usage
    ///
    /// ```
    /// use gitlab_api::GitLab;
    ///
    /// let expected_url = "https://gitlab.example.com:\
    ///                     443/api/v3/groups?\
    ///                     order_by=path&private_token=XXXXXXXXXXXXX&page=1&per_page=20";
    ///
    /// let gl = GitLab::new_https("gitlab.example.com", "XXXXXXXXXXXXX");
    ///
    /// assert_eq!(gl.build_url("groups?order_by=path"), expected_url);
    /// ```
    pub fn build_url(&self, query: &str) -> String {
        let mut params_splitter = "?";
        if query.find("?").is_some() {
            params_splitter = "&";
        }
        format!("{}://{}:{}/api/v{}/{}{}private_token={}&page={}&per_page={}",
                self.scheme,
                self.domain,
                self.port,
                API_VERSION,
                query,
                params_splitter,
                self.private_token,
                self.pagination.page,
                self.pagination.per_page)
    }

    pub fn attempt_connection(&self) -> Result<hyper::client::Response, hyper::Error> {
        let url = self.build_url("version");
        // Close connections after each GET.
        self.client.get(&url).header(hyper::header::Connection::close()).send()
    }

    pub fn set_pagination(&mut self, pagination: Pagination) {
        self.pagination = pagination;
    }

    pub fn get<T>(&self, query: &str) -> Result<T, serde_json::Error>
        where T: serde::Deserialize
    {
        let url = self.build_url(&query);
        info!("url: {:?}", url);
        let mut res: hyper::client::Response = self.client
            .get(&url)
            .header(hyper::header::Connection::close())
            .send()
            .unwrap();

        let mut body = String::new();
        res.read_to_string(&mut body).unwrap();
        debug!("body: {:?}", body);

        // FIXME: Properly handle the error. Will require defining our own errors...
        assert_eq!(res.status, hyper::status::StatusCode::Ok);

        serde_json::from_str(&body.as_str())
    }

    // pub fn version(&self) -> Result<Version, serde_json::Error> {
    //     self.get("version")
    // }
    //
    // pub fn groups(&self) -> Result<Groups, serde_json::Error> {
    //     self.get("groups")
    // }
    //
    // pub fn projects(&self) -> Result<Projects, serde_json::Error> {
    //     self.get("projects")
    // }

    pub fn groups(&mut self, listing: ::groups::Listing) -> Result<Groups, serde_json::Error> {
        let query = listing.build_query();
        self.get(&query)
    }

    pub fn owned_groups(&mut self) -> Result<Groups, serde_json::Error> {
        let query = ::groups::owned_groups::Listing::new().build_query();
        info!("query: {:?}", query);
        self.get(&query)
    }
}

// #[cfg(test)]
// mod tests {
//
// #[test]
// fn it_works() {
// }
// }
//
<commit_msg>Refactored params_splitter<commit_after>
use std::fmt;
use std::io::Read;  // Trait providing read_to_string()
use std::env;

use hyper;
use serde;
use serde_json;

use BuildQuery;
use Groups;

pub const API_VERSION: u16 = 3;

#[derive(Debug)]
pub struct Pagination {
    pub page: u16,
    pub per_page: u16,
}

pub struct GitLab {
    scheme: String,
    domain: String,
    port: u16,
    private_token: String,
    client: hyper::Client,
    pagination: Pagination,
}

// Explicitly implement Debug trait for GitLab so we can hide the token.
impl fmt::Debug for GitLab {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f,
               "GitLab {{ scheme: {}, domain: {}, port: {}, private_token: XXXXXXXXXXXXXXXXXXXX, \
                client: {:?}, pagination: {:?} }}",
               self.scheme,
               self.domain,
               self.port,
               self.client,
               self.pagination)
    }
}

impl GitLab {
    pub fn new(scheme: &str, domain: &str, port: u16, private_token: &str) -> GitLab {
        GitLab {
            scheme: scheme.to_string(),
            domain: domain.to_string(),
            port: port,
            private_token: private_token.to_string(),
            client: match env::var("HTTP_PROXY") {
                Ok(proxy) => {
                    let proxy: Vec<&str> = proxy.trim_left_matches("http://").split(':').collect();
                    let hostname = proxy[0].to_string();
                    let port = proxy[1];

                    hyper::Client::with_http_proxy(hostname, port.parse().unwrap())
                }
                Err(_) => hyper::Client::new(),
            },
            pagination: Pagination {
                page: 1,
                per_page: 20,
            },
        }
    }

    pub fn new_http(domain: &str, private_token: &str) -> GitLab {
        GitLab::new("http", domain, 80, private_token)
    }

    pub fn new_https(domain: &str, private_token: &str) -> GitLab {
        GitLab::new("https", domain, 443, private_token)
    }

    /// Build a URL used to access GitLab instance, including some parameters.
    ///
    /// # Examples
    ///
    /// Example from GitLab: https://docs.gitlab.com/ce/api/#basic-usage
    ///
    /// ```
    /// use gitlab_api::GitLab;
    ///
    /// let expected_url = "https://gitlab.example.com:\
    ///                     443/api/v3/groups?\
    ///                     order_by=path&private_token=XXXXXXXXXXXXX&page=1&per_page=20";
    ///
    /// let gl = GitLab::new_https("gitlab.example.com", "XXXXXXXXXXXXX");
    ///
    /// assert_eq!(gl.build_url("groups?order_by=path"), expected_url);
    /// ```
    pub fn build_url(&self, query: &str) -> String {
        let params_splitter = if query.find("?").is_some() { "&" } else { "?" };
        format!("{}://{}:{}/api/v{}/{}{}private_token={}&page={}&per_page={}",
                self.scheme,
                self.domain,
                self.port,
                API_VERSION,
                query,
                params_splitter,
                self.private_token,
                self.pagination.page,
                self.pagination.per_page)
    }

    pub fn attempt_connection(&self) -> Result<hyper::client::Response, hyper::Error> {
        let url = self.build_url("version");
        // Close connections after each GET.
        self.client.get(&url).header(hyper::header::Connection::close()).send()
    }

    pub fn set_pagination(&mut self, pagination: Pagination) {
        self.pagination = pagination;
    }

    pub fn get<T>(&self, query: &str) -> Result<T, serde_json::Error>
        where T: serde::Deserialize
    {
        let url = self.build_url(&query);
        info!("url: {:?}", url);
        let mut res: hyper::client::Response = self.client
            .get(&url)
            .header(hyper::header::Connection::close())
            .send()
            .unwrap();

        let mut body = String::new();
        res.read_to_string(&mut body).unwrap();
        debug!("body: {:?}", body);

        // FIXME: Properly handle the error. Will require defining our own errors...
        assert_eq!(res.status, hyper::status::StatusCode::Ok);

        serde_json::from_str(&body.as_str())
    }

    // pub fn version(&self) -> Result<Version, serde_json::Error> {
    //     self.get("version")
    // }
    //
    // pub fn groups(&self) -> Result<Groups, serde_json::Error> {
    //     self.get("groups")
    // }
    //
    // pub fn projects(&self) -> Result<Projects, serde_json::Error> {
    //     self.get("projects")
    // }

    pub fn groups(&mut self, listing: ::groups::Listing) -> Result<Groups, serde_json::Error> {
        let query = listing.build_query();
        self.get(&query)
    }

    pub fn owned_groups(&mut self) -> Result<Groups, serde_json::Error> {
        let query = ::groups::owned_groups::Listing::new().build_query();
        info!("query: {:?}", query);
        self.get(&query)
    }
}

// #[cfg(test)]
// mod tests {
//
// #[test]
// fn it_works() {
// }
// }
//
<|endoftext|>


<commit_before>use regex::Regex;
use std::collections::HashSet;
use std::fmt;
use std::fmt::Write as _;
use std::fs;
use std::path::Path;
use std::path::PathBuf;
use std::path::MAIN_SEPARATOR;
use std::str;
use tempfile::NamedTempFile;

use protobuf::descriptor::{
    DescriptorProto, EnumDescriptorProto, FileDescriptorProto, FileDescriptorSet,
};
use protobuf::RepeatedField;
use protobuf_test_common::build::copy_tests_v2_v3;
use protobuf_test_common::build::glob_simple;
use std::process::Command;
use std::process::Stdio;

fn to_paths(iter: impl IntoIterator<Item = impl Into<String>>) -> Vec<PathBuf> {
    iter.into_iter()
        .map(|item| item.into().replace(MAIN_SEPARATOR, "/").into())
        .collect()
}

#[derive(Default, Debug)]
struct TestStats {
    passed: u32,
    passed_marked_skip: u32,
    skipped: u32,
    failed: u32,
}

fn normalize_generated_file(contents: &str) -> String {
    let parsed_by = Regex::new("^// \\.proto file is parsed by.*").unwrap();

    let mut r = String::new();
    let mut inside_descriptor_data = false;
    for line in contents.lines() {
        let line = if inside_descriptor_data {
            if line == "\";" {
                inside_descriptor_data = false;
                line.to_owned()
            } else {
                continue;
            }
        } else if line.starts_with("static file_descriptor_proto_data") {
            inside_descriptor_data = true;
            line.to_owned()
        } else {
            parsed_by.replace(line, "").into_owned()
        };

        writeln!(&mut r, "{}", line).unwrap();
    }

    // sanity check
    assert!(!inside_descriptor_data);

    r
}

fn normalize_generated_file_in_place(path: &Path) {
    let contents = fs::read_to_string(path).unwrap();
    let contents = normalize_generated_file(&contents);
    fs::write(path, &contents).unwrap();
}

fn print_diff(dir: &Path, a: &Path, b: &Path) {
    if cfg!(windows) {
        // likely we don't have `diff` command on Windows
        return;
    }

    // sanity check
    let mut a_full = dir.to_path_buf();
    a_full.push(a);
    assert!(a_full.exists());
    let mut b_full = dir.to_path_buf();
    b_full.push(b);
    assert!(b_full.exists());

    // Not using difference crate because it's slow for large files
    let output = Command::new("diff")
        .current_dir(dir)
        .arg("-u")
        .arg(a)
        .arg(b)
        .stdin(Stdio::null())
        .stdout(Stdio::piped())
        .stderr(Stdio::piped())
        .output()
        .unwrap();

    print!("{}", str::from_utf8(&output.stdout).unwrap());
    print!("{}", str::from_utf8(&output.stderr).unwrap());
}

fn protoc_descriptor_set(includes: &[PathBuf], inputs: &[PathBuf]) -> FileDescriptorSet {
    let mut temp_file = NamedTempFile::new().unwrap();
    protoc::Protoc::from_env_path()
        .descriptor_set_out_args()
        .out(temp_file.path())
        .includes(includes)
        .inputs(inputs)
        .write_descriptor_set()
        .unwrap();
    protobuf::parse_from_reader(&mut temp_file).unwrap()
}

// TODO: expose this utility from protobuf-codegen-pure crate.
fn pure_descriptor_set(includes: &[PathBuf], inputs: &[PathBuf]) -> FileDescriptorSet {
    let mut codegen = protobuf_codegen_pure::parse_and_typecheck(includes, inputs).unwrap();
    let relative_paths: HashSet<_> = codegen
        .relative_paths
        .iter()
        .map(|path| path.to_string_lossy())
        .collect();
    codegen
        .file_descriptors
        .retain(|fd| relative_paths.contains(fd.get_name()));
    let mut fds = FileDescriptorSet::new();
    fds.file = RepeatedField::from_vec(codegen.file_descriptors);
    fds
}

fn normalize_descriptor_set(fds: &mut FileDescriptorSet) {
    for desc in fds.file.iter_mut() {
        normalize_file_descriptor(desc)
    }
}

fn normalize_file_descriptor(desc: &mut FileDescriptorProto) {
    if !desc.has_syntax() {
        desc.set_syntax("proto2".into())
    }
    for desc in &mut *desc.message_type {
        normalize_descriptor(desc)
    }
    for desc in desc.enum_type.iter_mut() {
        normalize_enum_descriptor(desc)
    }

    // TODO: don't clear options.
    for desc in &mut *desc.extension {
        desc.options.clear();
    }
    desc.options.clear();

    // TODO: don't clear services.
    desc.service.clear();
}

fn normalize_enum_descriptor(desc: &mut EnumDescriptorProto) {
    desc.options.mut_or_default();

    for value in desc.value.iter_mut() {
        value.options.mut_or_default();
    }
}

fn normalize_descriptor(desc: &mut DescriptorProto) {
    for desc in desc.nested_type.iter_mut() {
        normalize_descriptor(desc);
    }
    for desc in desc.enum_type.iter_mut() {
        normalize_enum_descriptor(desc);
    }

    // TODO: don't clear options.
    desc.options.clear();
    for field in desc.field.iter_mut() {
        field.options.mut_or_default();
    }
}

fn pretty_message<M: protobuf::Message>(message: &M) -> String {
    // TODO: pull this handy utility into the protobuf crate.
    struct FormatMessage<'a, M>(&'a M);

    impl<M: protobuf::Message> fmt::Display for FormatMessage<'_, M> {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            protobuf::text_format::fmt(self.0, f)
        }
    }

    format!("{:#}", FormatMessage(message))
}

fn test_diff_in<F>(root: &str, s: &str, include: &str, should_skip: F)
where
    F: Fn(&str) -> bool,
{
    let mut stats = TestStats {
        passed: 0,
        passed_marked_skip: 0,
        skipped: 0,
        failed: 0,
    };

    let include_full = format!("{}/{}", root, include);
    let s_full = format!("{}/{}", root, s);

    let inputs_glob = format!("{}/*.proto", s_full);
    let inputs = to_paths(glob_simple(&inputs_glob));
    assert!(!inputs.is_empty(), "glob is empty: {}", inputs_glob);
    let includes = to_paths(vec![include_full.as_str(), "../proto"]);

    let temp_dir = tempfile::Builder::new()
        .prefix("protobuf-codegen-identical-test")
        .tempdir()
        .unwrap();

    let protoc_dir = format!("{}/protoc", temp_dir.path().to_str().unwrap());
    let pure_dir = format!("{}/pure", temp_dir.path().to_str().unwrap());

    fs::create_dir(&protoc_dir).unwrap();
    fs::create_dir(&pure_dir).unwrap();

    protoc_rust::Codegen::new()
        .inputs(&inputs)
        .includes(&includes)
        .out_dir(&protoc_dir)
        .run()
        .unwrap();

    protobuf_codegen_pure::Codegen::new()
        .inputs(&inputs)
        .includes(&includes)
        .out_dir(&pure_dir)
        .run()
        .unwrap();

    for input in &inputs {
        let label = input.strip_prefix(root).unwrap().to_str().unwrap();
        let proto_name = input.file_name().unwrap().to_str().unwrap();
        let rs_name = protobuf_codegen::proto_name_to_rs(proto_name);
        let protoc_rs = format!("{}/{}", protoc_dir, rs_name);
        let pure_rs = format!("{}/{}", pure_dir, rs_name);

        normalize_generated_file_in_place(Path::new(&protoc_rs));
        normalize_generated_file_in_place(Path::new(&pure_rs));

        let protoc_rs_contents =
            fs::read_to_string(&protoc_rs).expect(&format!("while reading {}", protoc_rs));
        let pure_rs_contents =
            fs::read_to_string(&pure_rs).expect(&format!("while reading {}", pure_rs));

        let mut protoc_descriptors = protoc_descriptor_set(&includes, &inputs);
        let mut pure_descriptors = pure_descriptor_set(&includes, &inputs);
        normalize_descriptor_set(&mut protoc_descriptors);
        normalize_descriptor_set(&mut pure_descriptors);

        let skip = should_skip(input.to_str().unwrap());
        if protoc_rs_contents == pure_rs_contents && protoc_descriptors == pure_descriptors {
            if !skip {
                stats.passed += 1;
                println!("{}: PASSED", label);
            } else {
                stats.passed_marked_skip += 1;
                println!("{}: PASSED BUT MARKED SKIP", label);
            }
        } else {
            if skip {
                stats.skipped += 1;
                println!("{} SKIPPED", label);
            } else {
                stats.failed += 1;
                println!("{} FAILED", label);
            }

            print_diff(
                temp_dir.path(),
                Path::new(&protoc_rs).strip_prefix(temp_dir.path()).unwrap(),
                Path::new(&pure_rs).strip_prefix(temp_dir.path()).unwrap(),
            );

            fs::write(
                format!("{}/{}.descriptors", protoc_dir, proto_name),
                pretty_message(&protoc_descriptors),
            )
            .unwrap();
            fs::write(
                format!("{}/{}.descriptors", pure_dir, proto_name),
                pretty_message(&pure_descriptors),
            )
            .unwrap();
            print_diff(
                temp_dir.path(),
                Path::new(&format!("protoc/{}.descriptors", proto_name)),
                Path::new(&format!("pure/{}.descriptors", proto_name)),
            );
        }
    }

    println!("{:?}", stats);
    assert!(
        stats.passed != 0 || s == "src/google/protobuf",
        "sanity check"
    );
    assert!(stats.failed == 0, "at least one test failed");
}

fn should_skip_with_marker(path: &str) -> bool {
    fs::read_to_string(path)
        .unwrap()
        .contains("@skip-codegen-identical-test")
}

#[test]
fn common_v2() {
    test_diff_in(
        "../protobuf-test",
        "src/common/v2",
        "src/common/v2",
        should_skip_with_marker,
    );
}

#[test]
fn common_v3() {
    let common_v3_root = tempfile::Builder::new()
        .prefix("common-v3")
        .tempdir()
        .unwrap();
    fs::create_dir_all(format!(
        "{}/src/common/v3",
        common_v3_root.path().to_str().unwrap()
    ))
    .unwrap();

    copy_tests_v2_v3(
        "../protobuf-test/src/common/v2",
        &format!("{}/src/common/v3", common_v3_root.path().to_str().unwrap()),
    );

    test_diff_in(
        common_v3_root.path().to_str().unwrap(),
        "src/common/v3",
        "src/common/v3",
        should_skip_with_marker,
    );
}

#[test]
fn v2() {
    test_diff_in(
        "../protobuf-test",
        "src/v2",
        "src/v2",
        should_skip_with_marker,
    );
}

#[test]
fn v3() {
    test_diff_in(
        "../protobuf-test",
        "src/v3",
        "src/v3",
        should_skip_with_marker,
    );
}

#[test]
fn interop() {
    test_diff_in(
        "../protobuf-test",
        "../interop/cxx",
        "../interop/cxx",
        should_skip_with_marker,
    );
}

#[test]
fn google() {
    test_diff_in("../protobuf-test", "src/google/protobuf", "src", |_| true);
}
<commit_msg>Document descriptor comparison is not exactly correct<commit_after>use regex::Regex;
use std::collections::HashSet;
use std::fmt;
use std::fmt::Write as _;
use std::fs;
use std::path::Path;
use std::path::PathBuf;
use std::path::MAIN_SEPARATOR;
use std::str;
use tempfile::NamedTempFile;

use protobuf::descriptor::{
    DescriptorProto, EnumDescriptorProto, FileDescriptorProto, FileDescriptorSet,
};
use protobuf::RepeatedField;
use protobuf_test_common::build::copy_tests_v2_v3;
use protobuf_test_common::build::glob_simple;
use std::process::Command;
use std::process::Stdio;

fn to_paths(iter: impl IntoIterator<Item = impl Into<String>>) -> Vec<PathBuf> {
    iter.into_iter()
        .map(|item| item.into().replace(MAIN_SEPARATOR, "/").into())
        .collect()
}

#[derive(Default, Debug)]
struct TestStats {
    passed: u32,
    passed_marked_skip: u32,
    skipped: u32,
    failed: u32,
}

fn normalize_generated_file(contents: &str) -> String {
    let parsed_by = Regex::new("^// \\.proto file is parsed by.*").unwrap();

    let mut r = String::new();
    let mut inside_descriptor_data = false;
    for line in contents.lines() {
        let line = if inside_descriptor_data {
            if line == "\";" {
                inside_descriptor_data = false;
                line.to_owned()
            } else {
                continue;
            }
        } else if line.starts_with("static file_descriptor_proto_data") {
            inside_descriptor_data = true;
            line.to_owned()
        } else {
            parsed_by.replace(line, "").into_owned()
        };

        writeln!(&mut r, "{}", line).unwrap();
    }

    // sanity check
    assert!(!inside_descriptor_data);

    r
}

fn normalize_generated_file_in_place(path: &Path) {
    let contents = fs::read_to_string(path).unwrap();
    let contents = normalize_generated_file(&contents);
    fs::write(path, &contents).unwrap();
}

fn print_diff(dir: &Path, a: &Path, b: &Path) {
    if cfg!(windows) {
        // likely we don't have `diff` command on Windows
        return;
    }

    // sanity check
    let mut a_full = dir.to_path_buf();
    a_full.push(a);
    assert!(a_full.exists());
    let mut b_full = dir.to_path_buf();
    b_full.push(b);
    assert!(b_full.exists());

    // Not using difference crate because it's slow for large files
    let output = Command::new("diff")
        .current_dir(dir)
        .arg("-u")
        .arg(a)
        .arg(b)
        .stdin(Stdio::null())
        .stdout(Stdio::piped())
        .stderr(Stdio::piped())
        .output()
        .unwrap();

    print!("{}", str::from_utf8(&output.stdout).unwrap());
    print!("{}", str::from_utf8(&output.stderr).unwrap());
}

fn protoc_descriptor_set(includes: &[PathBuf], inputs: &[PathBuf]) -> FileDescriptorSet {
    let mut temp_file = NamedTempFile::new().unwrap();
    protoc::Protoc::from_env_path()
        .descriptor_set_out_args()
        .out(temp_file.path())
        .includes(includes)
        .inputs(inputs)
        .write_descriptor_set()
        .unwrap();
    protobuf::parse_from_reader(&mut temp_file).unwrap()
}

// TODO: expose this utility from protobuf-codegen-pure crate.
fn pure_descriptor_set(includes: &[PathBuf], inputs: &[PathBuf]) -> FileDescriptorSet {
    let mut codegen = protobuf_codegen_pure::parse_and_typecheck(includes, inputs).unwrap();
    let relative_paths: HashSet<_> = codegen
        .relative_paths
        .iter()
        .map(|path| path.to_string_lossy())
        .collect();
    codegen
        .file_descriptors
        .retain(|fd| relative_paths.contains(fd.get_name()));
    let mut fds = FileDescriptorSet::new();
    fds.file = RepeatedField::from_vec(codegen.file_descriptors);
    fds
}

fn normalize_descriptor_set(fds: &mut FileDescriptorSet) {
    for desc in fds.file.iter_mut() {
        normalize_file_descriptor(desc)
    }
}

fn normalize_file_descriptor(desc: &mut FileDescriptorProto) {
    if !desc.has_syntax() {
        desc.set_syntax("proto2".into())
    }
    for desc in &mut *desc.message_type {
        normalize_descriptor(desc)
    }
    for desc in desc.enum_type.iter_mut() {
        normalize_enum_descriptor(desc)
    }

    // TODO: don't clear options.
    for desc in &mut *desc.extension {
        desc.options.clear();
    }
    desc.options.clear();

    // TODO: don't clear services.
    desc.service.clear();
}

fn normalize_enum_descriptor(desc: &mut EnumDescriptorProto) {
    desc.options.mut_or_default();

    for value in desc.value.iter_mut() {
        value.options.mut_or_default();
    }
}

fn normalize_descriptor(desc: &mut DescriptorProto) {
    for desc in desc.nested_type.iter_mut() {
        normalize_descriptor(desc);
    }
    for desc in desc.enum_type.iter_mut() {
        normalize_enum_descriptor(desc);
    }

    // TODO: don't clear options.
    desc.options.clear();
    for field in desc.field.iter_mut() {
        field.options.mut_or_default();
    }
}

fn pretty_message<M: protobuf::Message>(message: &M) -> String {
    // TODO: pull this handy utility into the protobuf crate.
    struct FormatMessage<'a, M>(&'a M);

    impl<M: protobuf::Message> fmt::Display for FormatMessage<'_, M> {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            protobuf::text_format::fmt(self.0, f)
        }
    }

    format!("{:#}", FormatMessage(message))
}

fn test_diff_in<F>(root: &str, s: &str, include: &str, should_skip: F)
where
    F: Fn(&str) -> bool,
{
    let mut stats = TestStats {
        passed: 0,
        passed_marked_skip: 0,
        skipped: 0,
        failed: 0,
    };

    let include_full = format!("{}/{}", root, include);
    let s_full = format!("{}/{}", root, s);

    let inputs_glob = format!("{}/*.proto", s_full);
    let inputs = to_paths(glob_simple(&inputs_glob));
    assert!(!inputs.is_empty(), "glob is empty: {}", inputs_glob);
    let includes = to_paths(vec![include_full.as_str(), "../proto"]);

    let temp_dir = tempfile::Builder::new()
        .prefix("protobuf-codegen-identical-test")
        .tempdir()
        .unwrap();

    let protoc_dir = format!("{}/protoc", temp_dir.path().to_str().unwrap());
    let pure_dir = format!("{}/pure", temp_dir.path().to_str().unwrap());

    fs::create_dir(&protoc_dir).unwrap();
    fs::create_dir(&pure_dir).unwrap();

    protoc_rust::Codegen::new()
        .inputs(&inputs)
        .includes(&includes)
        .out_dir(&protoc_dir)
        .run()
        .unwrap();

    protobuf_codegen_pure::Codegen::new()
        .inputs(&inputs)
        .includes(&includes)
        .out_dir(&pure_dir)
        .run()
        .unwrap();

    let mut protoc_descriptors = protoc_descriptor_set(&includes, &inputs);
    let mut pure_descriptors = pure_descriptor_set(&includes, &inputs);
    normalize_descriptor_set(&mut protoc_descriptors);
    normalize_descriptor_set(&mut pure_descriptors);

    for input in &inputs {
        let label = input.strip_prefix(root).unwrap().to_str().unwrap();
        let proto_name = input.file_name().unwrap().to_str().unwrap();
        let rs_name = protobuf_codegen::proto_name_to_rs(proto_name);
        let protoc_rs = format!("{}/{}", protoc_dir, rs_name);
        let pure_rs = format!("{}/{}", pure_dir, rs_name);

        normalize_generated_file_in_place(Path::new(&protoc_rs));
        normalize_generated_file_in_place(Path::new(&pure_rs));

        let protoc_rs_contents =
            fs::read_to_string(&protoc_rs).expect(&format!("while reading {}", protoc_rs));
        let pure_rs_contents =
            fs::read_to_string(&pure_rs).expect(&format!("while reading {}", pure_rs));

        let skip = should_skip(input.to_str().unwrap());
        // TODO: compare per-file descriptors, not full descriptors
        if protoc_rs_contents == pure_rs_contents && protoc_descriptors == pure_descriptors {
            if !skip {
                stats.passed += 1;
                println!("{}: PASSED", label);
            } else {
                stats.passed_marked_skip += 1;
                println!("{}: PASSED BUT MARKED SKIP", label);
            }
        } else {
            if skip {
                stats.skipped += 1;
                println!("{} SKIPPED", label);
            } else {
                stats.failed += 1;
                println!("{} FAILED", label);
            }

            print_diff(
                temp_dir.path(),
                Path::new(&protoc_rs).strip_prefix(temp_dir.path()).unwrap(),
                Path::new(&pure_rs).strip_prefix(temp_dir.path()).unwrap(),
            );

            fs::write(
                format!("{}/{}.descriptors", protoc_dir, proto_name),
                pretty_message(&protoc_descriptors),
            )
            .unwrap();
            fs::write(
                format!("{}/{}.descriptors", pure_dir, proto_name),
                pretty_message(&pure_descriptors),
            )
            .unwrap();
            print_diff(
                temp_dir.path(),
                Path::new(&format!("protoc/{}.descriptors", proto_name)),
                Path::new(&format!("pure/{}.descriptors", proto_name)),
            );
        }
    }

    println!("{:?}", stats);
    assert!(
        stats.passed != 0 || s == "src/google/protobuf",
        "sanity check"
    );
    assert!(stats.failed == 0, "at least one test failed");
}

fn should_skip_with_marker(path: &str) -> bool {
    fs::read_to_string(path)
        .unwrap()
        .contains("@skip-codegen-identical-test")
}

#[test]
fn common_v2() {
    test_diff_in(
        "../protobuf-test",
        "src/common/v2",
        "src/common/v2",
        should_skip_with_marker,
    );
}

#[test]
fn common_v3() {
    let common_v3_root = tempfile::Builder::new()
        .prefix("common-v3")
        .tempdir()
        .unwrap();
    fs::create_dir_all(format!(
        "{}/src/common/v3",
        common_v3_root.path().to_str().unwrap()
    ))
    .unwrap();

    copy_tests_v2_v3(
        "../protobuf-test/src/common/v2",
        &format!("{}/src/common/v3", common_v3_root.path().to_str().unwrap()),
    );

    test_diff_in(
        common_v3_root.path().to_str().unwrap(),
        "src/common/v3",
        "src/common/v3",
        should_skip_with_marker,
    );
}

#[test]
fn v2() {
    test_diff_in(
        "../protobuf-test",
        "src/v2",
        "src/v2",
        should_skip_with_marker,
    );
}

#[test]
fn v3() {
    test_diff_in(
        "../protobuf-test",
        "src/v3",
        "src/v3",
        should_skip_with_marker,
    );
}

#[test]
fn interop() {
    test_diff_in(
        "../protobuf-test",
        "../interop/cxx",
        "../interop/cxx",
        should_skip_with_marker,
    );
}

#[test]
fn google() {
    test_diff_in("../protobuf-test", "src/google/protobuf", "src", |_| true);
}
<|endoftext|>


<commit_before>use crate::syntax::{derive, Enum, Struct, Trait};
use proc_macro2::{Ident, Span, TokenStream};
use quote::{quote, quote_spanned, ToTokens};

pub use crate::syntax::derive::*;

pub fn expand_struct(strct: &Struct, actual_derives: &mut Option<TokenStream>) -> TokenStream {
    let mut expanded = TokenStream::new();
    let mut traits = Vec::new();

    for derive in &strct.derives {
        let span = derive.span;
        match derive.what {
            Trait::Copy => expanded.extend(struct_copy(strct, span)),
            Trait::Clone => expanded.extend(struct_clone(strct, span)),
            Trait::Debug => expanded.extend(struct_debug(strct, span)),
            Trait::Default => expanded.extend(struct_default(strct, span)),
            Trait::Eq => traits.push(quote_spanned!(span=> ::std::cmp::Eq)),
            Trait::ExternType => unreachable!(),
            Trait::Hash => traits.push(quote_spanned!(span=> ::std::hash::Hash)),
            Trait::Ord => expanded.extend(struct_ord(strct, span)),
            Trait::PartialEq => traits.push(quote_spanned!(span=> ::std::cmp::PartialEq)),
            Trait::PartialOrd => expanded.extend(struct_partial_ord(strct, span)),
        }
    }

    if traits.is_empty() {
        *actual_derives = None;
    } else {
        *actual_derives = Some(quote!(#[derive(#(#traits),*)]));
    }

    expanded
}

pub fn expand_enum(enm: &Enum, actual_derives: &mut Option<TokenStream>) -> TokenStream {
    let mut expanded = TokenStream::new();
    let mut traits = Vec::new();
    let mut has_copy = false;
    let mut has_clone = false;
    let mut has_eq = false;
    let mut has_partial_eq = false;

    for derive in &enm.derives {
        let span = derive.span;
        match derive.what {
            Trait::Copy => {
                expanded.extend(enum_copy(enm, span));
                has_copy = true;
            }
            Trait::Clone => {
                expanded.extend(enum_clone(enm, span));
                has_clone = true;
            }
            Trait::Debug => expanded.extend(enum_debug(enm, span)),
            Trait::Default => unreachable!(),
            Trait::Eq => {
                traits.push(quote_spanned!(span=> ::std::cmp::Eq));
                has_eq = true;
            }
            Trait::ExternType => unreachable!(),
            Trait::Hash => traits.push(quote_spanned!(span=> ::std::hash::Hash)),
            Trait::Ord => expanded.extend(enum_ord(enm, span)),
            Trait::PartialEq => {
                traits.push(quote_spanned!(span=> ::std::cmp::PartialEq));
                has_partial_eq = true;
            }
            Trait::PartialOrd => expanded.extend(enum_partial_ord(enm, span)),
        }
    }

    let span = enm.name.rust.span();
    if !has_copy {
        expanded.extend(enum_copy(enm, span));
    }
    if !has_clone {
        expanded.extend(enum_clone(enm, span));
    }
    if !has_eq {
        // Required to be derived in order for the enum's "variants" to be
        // usable in patterns.
        traits.push(quote!(::std::cmp::Eq));
    }
    if !has_partial_eq {
        traits.push(quote!(::std::cmp::PartialEq));
    }

    *actual_derives = Some(quote!(#[derive(#(#traits),*)]));

    expanded
}

fn struct_copy(strct: &Struct, span: Span) -> TokenStream {
    let ident = &strct.name.rust;
    let generics = &strct.generics;

    quote_spanned! {span=>
        impl #generics ::std::marker::Copy for #ident #generics {}
    }
}

fn struct_clone(strct: &Struct, span: Span) -> TokenStream {
    let ident = &strct.name.rust;
    let generics = &strct.generics;

    let body = if derive::contains(&strct.derives, Trait::Copy) {
        quote!(*self)
    } else {
        let fields = strct.fields.iter().map(|field| &field.name.rust);
        let values = strct.fields.iter().map(|field| {
            let ident = &field.name.rust;
            let ty = field.ty.to_token_stream();
            let span = ty.into_iter().last().unwrap().span();
            quote_spanned!(span=> &self.#ident)
        });
        quote_spanned!(span=> #ident {
            #(#fields: ::std::clone::Clone::clone(#values),)*
        })
    };

    quote_spanned! {span=>
        impl #generics ::std::clone::Clone for #ident #generics {
            fn clone(&self) -> Self {
                #body
            }
        }
    }
}

fn struct_debug(strct: &Struct, span: Span) -> TokenStream {
    let ident = &strct.name.rust;
    let generics = &strct.generics;
    let struct_name = ident.to_string();
    let fields = strct.fields.iter().map(|field| &field.name.rust);
    let field_names = fields.clone().map(Ident::to_string);

    quote_spanned! {span=>
        impl #generics ::std::fmt::Debug for #ident #generics {
            fn fmt(&self, formatter: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
                formatter.debug_struct(#struct_name)
                    #(.field(#field_names, &self.#fields))*
                    .finish()
            }
        }
    }
}

fn struct_default(strct: &Struct, span: Span) -> TokenStream {
    let ident = &strct.name.rust;
    let generics = &strct.generics;
    let fields = strct.fields.iter().map(|field| &field.name.rust);

    quote_spanned! {span=>
        impl #generics ::std::default::Default for #ident #generics {
            fn default() -> Self {
                #ident {
                    #(
                        #fields: ::std::default::Default::default(),
                    )*
                }
            }
        }
    }
}

fn struct_ord(strct: &Struct, span: Span) -> TokenStream {
    let ident = &strct.name.rust;
    let generics = &strct.generics;
    let fields = strct.fields.iter().map(|field| &field.name.rust);

    quote_spanned! {span=>
        impl #generics ::std::cmp::Ord for #ident #generics {
            fn cmp(&self, other: &Self) -> ::std::cmp::Ordering {
                #(
                    match ::std::cmp::Ord::cmp(&self.#fields, &other.#fields) {
                        ::std::cmp::Ordering::Equal => {}
                        ordering => return ordering,
                    }
                )*
                ::std::cmp::Ordering::Equal
            }
        }
    }
}

fn struct_partial_ord(strct: &Struct, span: Span) -> TokenStream {
    let ident = &strct.name.rust;
    let generics = &strct.generics;

    let body = if derive::contains(&strct.derives, Trait::Ord) {
        quote! {
            ::std::option::Option::Some(::std::cmp::Ord::cmp(self, other))
        }
    } else {
        let fields = strct.fields.iter().map(|field| &field.name.rust);
        quote! {
            #(
                match ::std::cmp::PartialOrd::partial_cmp(&self.#fields, &other.#fields) {
                    ::std::option::Option::Some(::std::cmp::Ordering::Equal) => {}
                    ordering => return ordering,
                }
            )*
            ::std::option::Option::Some(::std::cmp::Ordering::Equal)
        }
    };

    quote_spanned! {span=>
        impl #generics ::std::cmp::PartialOrd for #ident #generics {
            fn partial_cmp(&self, other: &Self) -> ::std::option::Option<::std::cmp::Ordering> {
                #body
            }
        }
    }
}

fn enum_copy(enm: &Enum, span: Span) -> TokenStream {
    let ident = &enm.name.rust;

    quote_spanned! {span=>
        impl ::std::marker::Copy for #ident {}
    }
}

fn enum_clone(enm: &Enum, span: Span) -> TokenStream {
    let ident = &enm.name.rust;

    quote_spanned! {span=>
        impl ::std::clone::Clone for #ident {
            fn clone(&self) -> Self {
                *self
            }
        }
    }
}

fn enum_debug(enm: &Enum, span: Span) -> TokenStream {
    let ident = &enm.name.rust;
    let variants = enm.variants.iter().map(|variant| {
        let variant = &variant.name.rust;
        let name = variant.to_string();
        quote_spanned! {span=>
            #ident::#variant => formatter.write_str(#name),
        }
    });
    let fallback = format!("{}({{}})", ident);

    quote_spanned! {span=>
        impl ::std::fmt::Debug for #ident {
            fn fmt(&self, formatter: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
                match *self {
                    #(#variants)*
                    _ => ::std::write!(formatter, #fallback, self.repr),
                }
            }
        }
    }
}

fn enum_ord(enm: &Enum, span: Span) -> TokenStream {
    let ident = &enm.name.rust;

    quote_spanned! {span=>
        impl ::std::cmp::Ord for #ident {
            fn cmp(&self, other: &Self) -> ::std::cmp::Ordering {
                ::std::cmp::Ord::cmp(&self.repr, &other.repr)
            }
        }
    }
}

fn enum_partial_ord(enm: &Enum, span: Span) -> TokenStream {
    let ident = &enm.name.rust;

    quote_spanned! {span=>
        impl ::std::cmp::PartialOrd for #ident {
            fn partial_cmp(&self, other: &Self) -> ::std::option::Option<::std::cmp::Ordering> {
                ::std::cmp::PartialOrd::partial_cmp(&self.repr, &other.repr)
            }
        }
    }
}
<commit_msg>Ignore derivable_impls lint in generated code<commit_after>use crate::syntax::{derive, Enum, Struct, Trait};
use proc_macro2::{Ident, Span, TokenStream};
use quote::{quote, quote_spanned, ToTokens};

pub use crate::syntax::derive::*;

pub fn expand_struct(strct: &Struct, actual_derives: &mut Option<TokenStream>) -> TokenStream {
    let mut expanded = TokenStream::new();
    let mut traits = Vec::new();

    for derive in &strct.derives {
        let span = derive.span;
        match derive.what {
            Trait::Copy => expanded.extend(struct_copy(strct, span)),
            Trait::Clone => expanded.extend(struct_clone(strct, span)),
            Trait::Debug => expanded.extend(struct_debug(strct, span)),
            Trait::Default => expanded.extend(struct_default(strct, span)),
            Trait::Eq => traits.push(quote_spanned!(span=> ::std::cmp::Eq)),
            Trait::ExternType => unreachable!(),
            Trait::Hash => traits.push(quote_spanned!(span=> ::std::hash::Hash)),
            Trait::Ord => expanded.extend(struct_ord(strct, span)),
            Trait::PartialEq => traits.push(quote_spanned!(span=> ::std::cmp::PartialEq)),
            Trait::PartialOrd => expanded.extend(struct_partial_ord(strct, span)),
        }
    }

    if traits.is_empty() {
        *actual_derives = None;
    } else {
        *actual_derives = Some(quote!(#[derive(#(#traits),*)]));
    }

    expanded
}

pub fn expand_enum(enm: &Enum, actual_derives: &mut Option<TokenStream>) -> TokenStream {
    let mut expanded = TokenStream::new();
    let mut traits = Vec::new();
    let mut has_copy = false;
    let mut has_clone = false;
    let mut has_eq = false;
    let mut has_partial_eq = false;

    for derive in &enm.derives {
        let span = derive.span;
        match derive.what {
            Trait::Copy => {
                expanded.extend(enum_copy(enm, span));
                has_copy = true;
            }
            Trait::Clone => {
                expanded.extend(enum_clone(enm, span));
                has_clone = true;
            }
            Trait::Debug => expanded.extend(enum_debug(enm, span)),
            Trait::Default => unreachable!(),
            Trait::Eq => {
                traits.push(quote_spanned!(span=> ::std::cmp::Eq));
                has_eq = true;
            }
            Trait::ExternType => unreachable!(),
            Trait::Hash => traits.push(quote_spanned!(span=> ::std::hash::Hash)),
            Trait::Ord => expanded.extend(enum_ord(enm, span)),
            Trait::PartialEq => {
                traits.push(quote_spanned!(span=> ::std::cmp::PartialEq));
                has_partial_eq = true;
            }
            Trait::PartialOrd => expanded.extend(enum_partial_ord(enm, span)),
        }
    }

    let span = enm.name.rust.span();
    if !has_copy {
        expanded.extend(enum_copy(enm, span));
    }
    if !has_clone {
        expanded.extend(enum_clone(enm, span));
    }
    if !has_eq {
        // Required to be derived in order for the enum's "variants" to be
        // usable in patterns.
        traits.push(quote!(::std::cmp::Eq));
    }
    if !has_partial_eq {
        traits.push(quote!(::std::cmp::PartialEq));
    }

    *actual_derives = Some(quote!(#[derive(#(#traits),*)]));

    expanded
}

fn struct_copy(strct: &Struct, span: Span) -> TokenStream {
    let ident = &strct.name.rust;
    let generics = &strct.generics;

    quote_spanned! {span=>
        impl #generics ::std::marker::Copy for #ident #generics {}
    }
}

fn struct_clone(strct: &Struct, span: Span) -> TokenStream {
    let ident = &strct.name.rust;
    let generics = &strct.generics;

    let body = if derive::contains(&strct.derives, Trait::Copy) {
        quote!(*self)
    } else {
        let fields = strct.fields.iter().map(|field| &field.name.rust);
        let values = strct.fields.iter().map(|field| {
            let ident = &field.name.rust;
            let ty = field.ty.to_token_stream();
            let span = ty.into_iter().last().unwrap().span();
            quote_spanned!(span=> &self.#ident)
        });
        quote_spanned!(span=> #ident {
            #(#fields: ::std::clone::Clone::clone(#values),)*
        })
    };

    quote_spanned! {span=>
        impl #generics ::std::clone::Clone for #ident #generics {
            fn clone(&self) -> Self {
                #body
            }
        }
    }
}

fn struct_debug(strct: &Struct, span: Span) -> TokenStream {
    let ident = &strct.name.rust;
    let generics = &strct.generics;
    let struct_name = ident.to_string();
    let fields = strct.fields.iter().map(|field| &field.name.rust);
    let field_names = fields.clone().map(Ident::to_string);

    quote_spanned! {span=>
        impl #generics ::std::fmt::Debug for #ident #generics {
            fn fmt(&self, formatter: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
                formatter.debug_struct(#struct_name)
                    #(.field(#field_names, &self.#fields))*
                    .finish()
            }
        }
    }
}

fn struct_default(strct: &Struct, span: Span) -> TokenStream {
    let ident = &strct.name.rust;
    let generics = &strct.generics;
    let fields = strct.fields.iter().map(|field| &field.name.rust);

    quote_spanned! {span=>
        #[allow(clippy::derivable_impls)] // different spans than the derived impl
        impl #generics ::std::default::Default for #ident #generics {
            fn default() -> Self {
                #ident {
                    #(
                        #fields: ::std::default::Default::default(),
                    )*
                }
            }
        }
    }
}

fn struct_ord(strct: &Struct, span: Span) -> TokenStream {
    let ident = &strct.name.rust;
    let generics = &strct.generics;
    let fields = strct.fields.iter().map(|field| &field.name.rust);

    quote_spanned! {span=>
        impl #generics ::std::cmp::Ord for #ident #generics {
            fn cmp(&self, other: &Self) -> ::std::cmp::Ordering {
                #(
                    match ::std::cmp::Ord::cmp(&self.#fields, &other.#fields) {
                        ::std::cmp::Ordering::Equal => {}
                        ordering => return ordering,
                    }
                )*
                ::std::cmp::Ordering::Equal
            }
        }
    }
}

fn struct_partial_ord(strct: &Struct, span: Span) -> TokenStream {
    let ident = &strct.name.rust;
    let generics = &strct.generics;

    let body = if derive::contains(&strct.derives, Trait::Ord) {
        quote! {
            ::std::option::Option::Some(::std::cmp::Ord::cmp(self, other))
        }
    } else {
        let fields = strct.fields.iter().map(|field| &field.name.rust);
        quote! {
            #(
                match ::std::cmp::PartialOrd::partial_cmp(&self.#fields, &other.#fields) {
                    ::std::option::Option::Some(::std::cmp::Ordering::Equal) => {}
                    ordering => return ordering,
                }
            )*
            ::std::option::Option::Some(::std::cmp::Ordering::Equal)
        }
    };

    quote_spanned! {span=>
        impl #generics ::std::cmp::PartialOrd for #ident #generics {
            fn partial_cmp(&self, other: &Self) -> ::std::option::Option<::std::cmp::Ordering> {
                #body
            }
        }
    }
}

fn enum_copy(enm: &Enum, span: Span) -> TokenStream {
    let ident = &enm.name.rust;

    quote_spanned! {span=>
        impl ::std::marker::Copy for #ident {}
    }
}

fn enum_clone(enm: &Enum, span: Span) -> TokenStream {
    let ident = &enm.name.rust;

    quote_spanned! {span=>
        impl ::std::clone::Clone for #ident {
            fn clone(&self) -> Self {
                *self
            }
        }
    }
}

fn enum_debug(enm: &Enum, span: Span) -> TokenStream {
    let ident = &enm.name.rust;
    let variants = enm.variants.iter().map(|variant| {
        let variant = &variant.name.rust;
        let name = variant.to_string();
        quote_spanned! {span=>
            #ident::#variant => formatter.write_str(#name),
        }
    });
    let fallback = format!("{}({{}})", ident);

    quote_spanned! {span=>
        impl ::std::fmt::Debug for #ident {
            fn fmt(&self, formatter: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
                match *self {
                    #(#variants)*
                    _ => ::std::write!(formatter, #fallback, self.repr),
                }
            }
        }
    }
}

fn enum_ord(enm: &Enum, span: Span) -> TokenStream {
    let ident = &enm.name.rust;

    quote_spanned! {span=>
        impl ::std::cmp::Ord for #ident {
            fn cmp(&self, other: &Self) -> ::std::cmp::Ordering {
                ::std::cmp::Ord::cmp(&self.repr, &other.repr)
            }
        }
    }
}

fn enum_partial_ord(enm: &Enum, span: Span) -> TokenStream {
    let ident = &enm.name.rust;

    quote_spanned! {span=>
        impl ::std::cmp::PartialOrd for #ident {
            fn partial_cmp(&self, other: &Self) -> ::std::option::Option<::std::cmp::Ordering> {
                ::std::cmp::PartialOrd::partial_cmp(&self.repr, &other.repr)
            }
        }
    }
}
<|endoftext|>


<commit_before>extern crate bindgen;
extern crate pkg_config;
extern crate gcc;

use std::env;
use std::ffi::OsString;
use std::fs;
use std::path::{Path, PathBuf};
use std::process::Command;

// stolen from: https://github.com/alexcrichton/backtrace-rs/blob/master/backtrace-sys/build.rs
macro_rules! t {
    ($e:expr) => (match $e{
        Ok(e) => e,
        Err(e) => panic!("{} failed with {}", stringify!($e), e),
    })
}

// stolen from: https://github.com/alexcrichton/backtrace-rs/blob/master/backtrace-sys/build.rs
fn try_tool(compiler: &gcc::Tool, cc: &str, compiler_suffix: &str, tool_suffix: &str)
            -> Option<PathBuf> {
    if !cc.ends_with(compiler_suffix) {
        return None
    }
    let cc = cc.replace(compiler_suffix, tool_suffix);
    let candidate = compiler.path().parent().unwrap().join(cc);
    if Command::new(&candidate).output().is_ok() {
        Some(candidate)
    } else {
        None
    }
}
// stolen from: https://github.com/alexcrichton/backtrace-rs/blob/master/backtrace-sys/build.rs, too
fn find_tool(compiler: &gcc::Tool, cc: &str, tool: &str) -> PathBuf {
    // Allow overrides via env var
    if let Some(s) = env::var_os(tool.to_uppercase()) {
        return s.into()
    }
    let tool_suffix = format!("-{}", tool);
    try_tool(compiler, cc, "-gcc", &tool_suffix)
        .or_else(|| try_tool(compiler, cc, "-clang", &tool_suffix))
        .or_else(|| try_tool(compiler, cc, "-cc", &tool_suffix))
        .unwrap_or_else(|| PathBuf::from(tool))
}

const LIBMODBUS_DIR: &'static str = "libmodbus";

fn main() {
    let dst = env::var("OUT_DIR").unwrap();
    let target = env::var("TARGET").unwrap();
    let host = env::var("HOST").unwrap();

    let build_dir = Path::new(LIBMODBUS_DIR);
    let prefix    = Path::new(&dst).join("libmodbus-root");
    let include   = Path::new(&prefix).join("include")
                                      .join("modbus");

    // if `pkg-config` is present and the libmodbus headers are found
    // we use `pkg-config` to find the include_path and call bindgen with it.
    //
    if let Ok(library) = pkg_config::find_library("libmodbus") {
        if let Some(include) = library.include_paths.get(0) {
            run_bindgen(&include);
        }

        return
    }

    // pkg-config is not found. We build libmodbus from source (source are in a git submodule)
    // and run bindgen with that folder as include path set.
    //

    // If autogen.sh is not present, initalize git submodules
    if !Path::new("libmodbus/autogen.sh").exists() {
        run_command("", Command::new("git").args(&["submodule", "update", "--init"]));
    }

    let _ = fs::remove_dir_all(env::var("OUT_DIR").unwrap());
    t!(fs::create_dir_all(env::var("OUT_DIR").unwrap()));

    let cfg = gcc::Config::new();
    let compiler = cfg.get_compiler();
    let cc = compiler.path().file_name().unwrap().to_str().unwrap();
    let mut flags = OsString::new();
    for (i, flag) in compiler.args().iter().enumerate() {
        if i > 0 {
            flags.push(" ");
        }
        flags.push(flag);
    }

    // Generate configure, run configure, make, make install
    run_command("Generating configure",
        Command::new("autoreconf")
            .arg("--install")
            .arg("--symlink")
            .arg("--force")
            .current_dir(&build_dir));

    run_command("Configuring libmodbus",
        Command::new("./configure")
            .arg("--prefix")
            .arg(&prefix)
            .env("CC", compiler.path())
            .env("CFLAGS", flags)
            .arg("--with-pic")
            .arg("--disable-multilib")
            .arg("--disable-shared")
            .arg("--disable-host-shared")
            .arg(format!("--target={}", target))
            .arg(format!("--host={}", host))
            .current_dir(&build_dir));

    run_command("Building libmodbus",
        Command::new("make")
            .arg("install")
            .current_dir(&build_dir));

    println!("cargo:rustc-link-lib=modbus");
    println!("cargo:rustc-link-search=native={}/libmodbus-root/lib", dst);

    run_bindgen(&include);
}

fn run_bindgen(include: &PathBuf) {
    let include_path = format!("-I{}", include.display());

    // The bindgen::Builder is the main entry point
    // to bindgen, and lets you build up options for
    // the resulting bindings.
    let bindings = bindgen::Builder::default()
        // Do not generate unstable Rust code that
        // requires a nightly rustc and enabling
        // unstable features.
        .no_unstable_rust()
        // The input header we would like to generate
        // bindings for.
        .header("wrapper.h")
        .clang_arg(include_path)
        // Finish the builder and generate the bindings.
        .generate()
        // Unwrap the Result and panic on failure.
        .expect("Unable to generate bindings");

    // Write the bindings to the $OUT_DIR/bindings.rs file.
    let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
    bindings
        .write_to_file(out_path.join("bindings.rs"))
        .expect("Couldn't write bindings!");
}

// Helper which run a given command, and check it's success
fn run_command(which: &'static str, cmd: &mut Command) {
    assert!(cmd.status().expect(which).success(), which);
}
<commit_msg>Remove some unneeded configure params and make lib static for cargo<commit_after>extern crate bindgen;
extern crate pkg_config;
extern crate gcc;

use std::env;
use std::ffi::OsString;
use std::fs;
use std::path::{Path, PathBuf};
use std::process::Command;

// stolen from: https://github.com/alexcrichton/backtrace-rs/blob/master/backtrace-sys/build.rs
macro_rules! t {
    ($e:expr) => (match $e{
        Ok(e) => e,
        Err(e) => panic!("{} failed with {}", stringify!($e), e),
    })
}

// stolen from: https://github.com/alexcrichton/backtrace-rs/blob/master/backtrace-sys/build.rs
fn try_tool(compiler: &gcc::Tool, cc: &str, compiler_suffix: &str, tool_suffix: &str)
            -> Option<PathBuf> {
    if !cc.ends_with(compiler_suffix) {
        return None
    }
    let cc = cc.replace(compiler_suffix, tool_suffix);
    let candidate = compiler.path().parent().unwrap().join(cc);
    if Command::new(&candidate).output().is_ok() {
        Some(candidate)
    } else {
        None
    }
}
// stolen from: https://github.com/alexcrichton/backtrace-rs/blob/master/backtrace-sys/build.rs, too
fn find_tool(compiler: &gcc::Tool, cc: &str, tool: &str) -> PathBuf {
    // Allow overrides via env var
    if let Some(s) = env::var_os(tool.to_uppercase()) {
        return s.into()
    }
    let tool_suffix = format!("-{}", tool);
    try_tool(compiler, cc, "-gcc", &tool_suffix)
        .or_else(|| try_tool(compiler, cc, "-clang", &tool_suffix))
        .or_else(|| try_tool(compiler, cc, "-cc", &tool_suffix))
        .unwrap_or_else(|| PathBuf::from(tool))
}

const LIBMODBUS_DIR: &'static str = "libmodbus";

fn main() {
    let dst = env::var("OUT_DIR").unwrap();
    let target = env::var("TARGET").unwrap();
    let host = env::var("HOST").unwrap();

    let build_dir = Path::new(LIBMODBUS_DIR);
    let prefix    = Path::new(&dst).join("libmodbus-root");
    let include   = Path::new(&prefix).join("include")
                                      .join("modbus");

    // if `pkg-config` is present and the libmodbus headers are found
    // we use `pkg-config` to find the include_path and call bindgen with it.
    //
    if let Ok(library) = pkg_config::find_library("libmodbus") {
        if let Some(include) = library.include_paths.get(0) {
            run_bindgen(&include);
        }

        return
    }

    // pkg-config is not found. We build libmodbus from source (source are in a git submodule)
    // and run bindgen with that folder as include path set.
    //

    // If autogen.sh is not present, initalize git submodules
    if !Path::new("libmodbus/autogen.sh").exists() {
        run_command("", Command::new("git").args(&["submodule", "update", "--init"]));
    }

    let _ = fs::remove_dir_all(env::var("OUT_DIR").unwrap());
    t!(fs::create_dir_all(env::var("OUT_DIR").unwrap()));

    let cfg = gcc::Config::new();
    let compiler = cfg.get_compiler();
    let cc = compiler.path().file_name().unwrap().to_str().unwrap();
    let mut flags = OsString::new();
    for (i, flag) in compiler.args().iter().enumerate() {
        if i > 0 {
            flags.push(" ");
        }
        flags.push(flag);
    }

    // Generate configure, run configure, make, make install
    run_command("Generating configure",
        Command::new("autoreconf")
            .arg("--install")
            .arg("--symlink")
            .arg("--force")
            .current_dir(&build_dir));

    run_command("Configuring libmodbus",
        Command::new("./configure")
            .arg("--prefix")
            .arg(&prefix)
            .env("CC", compiler.path())
            .env("CFLAGS", flags)
            .arg("--with-pic")
            .arg("--disable-shared")
            .arg(format!("--target={}", target))
            .arg(format!("--host={}", host))
            .current_dir(&build_dir));

    run_command("Building libmodbus",
        Command::new("make")
            .arg("install")
            .current_dir(&build_dir));

    println!("cargo:rustc-link-lib=static=modbus");
    println!("cargo:rustc-link-search=native={}/libmodbus-root/lib", dst);

    run_bindgen(&include);
}

fn run_bindgen(include: &PathBuf) {
    let include_path = format!("-I{}", include.display());

    // The bindgen::Builder is the main entry point
    // to bindgen, and lets you build up options for
    // the resulting bindings.
    let bindings = bindgen::Builder::default()
        // Do not generate unstable Rust code that
        // requires a nightly rustc and enabling
        // unstable features.
        .no_unstable_rust()
        // The input header we would like to generate
        // bindings for.
        .header("wrapper.h")
        .clang_arg(include_path)
        // Finish the builder and generate the bindings.
        .generate()
        // Unwrap the Result and panic on failure.
        .expect("Unable to generate bindings");

    // Write the bindings to the $OUT_DIR/bindings.rs file.
    let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
    bindings
        .write_to_file(out_path.join("bindings.rs"))
        .expect("Couldn't write bindings!");
}

// Helper which run a given command, and check it's success
fn run_command(which: &'static str, cmd: &mut Command) {
    assert!(cmd.status().expect(which).success(), which);
}
<|endoftext|>


<commit_before>use std::sync::Arc;
use std::cmp;

use na::Dot;

use ::Point2f;
use bsdf;
use spectrum::Spectrum;
use interaction::SurfaceInteraction;
use light::{Light, is_delta_light};
use ray::Ray;
use sampler::Sampler;
use sampling::power_heuristic;
use scene::Scene;

mod whitted;
mod directlighting;
mod ao;
mod normal;

pub use self::whitted::Whitted;
pub use self::directlighting::DirectLightingIntegrator;
pub use self::ao::AmbientOcclusion;
pub use self::normal::Normal;

pub trait SamplerIntegrator {
    fn li(&self, scene: &Scene, ray: &mut Ray, sampler: &mut Sampler, depth: u32) -> Spectrum;

    fn specular_reflection(&self,
                           ray: &mut Ray,
                           isect: &SurfaceInteraction,
                           scene: &Scene,
                           bsdf: &bsdf::BSDF,
                           sampler: &mut Sampler,
                           depth: u32)
                           -> Spectrum {
        let flags = bsdf::BSDF_REFLECTION | bsdf::BSDF_SPECULAR;
        let (f, wi, pdf, bsdf_type) = bsdf.sample_f(&isect.wo, &sampler.get_2d(), flags);
        let ns = isect.shading.n;
        if !f.is_black() && pdf != 0.0 && wi.dot(&ns) != 0.0 {
            let mut r = ray.spawn(isect.p, wi);
            let refl = self.li(scene, &mut r, sampler, depth + 1);
            f * refl * wi.dot(&ns).abs() / pdf
        } else {
            Spectrum::black()
        }
    }

    fn specular_transmission(&self,
                             ray: &mut Ray,
                             isect: &SurfaceInteraction,
                             scene: &Scene,
                             bsdf: &bsdf::BSDF,
                             sampler: &mut Sampler,
                             depth: u32)
                             -> Spectrum {
        let flags = bsdf::BSDF_TRANSMISSION | bsdf::BSDF_SPECULAR;
        // TODO use sampler.get_2d()
        let (f, wi, pdf, bsdf_type) = bsdf.sample_f(&isect.wo, &sampler.get_2d(), flags);
        let ns = isect.shading.n;
        if !f.is_black() && pdf != 0.0 && wi.dot(&ns) != 0.0 {
            let mut r = ray.spawn(isect.p, wi);
            let refr = self.li(scene, &mut r, sampler, depth + 1);
            f * refr * wi.dot(&ns).abs() / pdf
        } else {
            Spectrum::black()
        }
    }
}

pub fn uniform_sample_one_light(it: &SurfaceInteraction,
                                scene: &Scene,
                                sampler: &mut Sampler)
                                -> Spectrum {
    let n_lights = scene.lights.len();
    if n_lights == 0 {
        Spectrum::black()
    } else {
        // Randomly chose a light to sample
        let s = sampler.get_1d();
        let num_light = cmp::min(n_lights - 1, (s * n_lights as f32) as usize);
        debug!("sampler.get_1d()={}, n_lights={}, num_light={}, num_light as usize={}",
               s,
               n_lights,
               s * n_lights as f32,
               num_light);
        let light = &scene.lights[num_light];
        let u_light = sampler.get_2d();
        let u_scattering = sampler.get_2d();
        estimate_direct(it, &u_scattering, light, &u_light, scene, sampler) * n_lights as f32
    }
}

pub fn estimate_direct(it: &SurfaceInteraction,
                       u_scattering: &Point2f,
                       light: &Arc<Light + Send + Sync>,
                       u_light: &Point2f,
                       scene: &Scene,
                       sampler: &mut Sampler)
                       -> Spectrum {
    let specular = false;

    let bsdf_flags = if specular {
        bsdf::BxDFType::all()
    } else {
        bsdf::BxDFType::all() & !bsdf::BSDF_SPECULAR
    };
    let mut ld = Spectrum::black();
    // Sample light with multiple importance sampling
    let bsdf = it.bsdf.as_ref().expect("There should be a BSDF set at this point!");
    let (mut li, wi, light_pdf, vis) = light.sample_li(it, &u_light);
    if light_pdf > 0.0 && !li.is_black() {
        // Compute BSDF for light sample
        let f = bsdf.f(&it.wo, &wi, bsdf_flags) * wi.dot(&it.shading.n).abs();
        let scattering_pdf = bsdf.pdf(&it.wo, &wi, bsdf_flags);
        if !f.is_black() {
            if !vis.unoccluded(scene) {
                li = Spectrum::black();
            }
            // Add light's contribution to reflected radiance
            if !li.is_black() {
                if is_delta_light(light.flags()) {
                    ld += f * li / light_pdf;
                } else {
                    let weight = power_heuristic(1, light_pdf, 1, scattering_pdf);
                    ld += f * li * weight / light_pdf;
                }
            }
        }
        // TODO compute phase function for medium interaction when supported
    }
    // Sample BSDF with multiple importance sampling
    if !is_delta_light(light.flags()) {
        let (mut f, wi, scattering_pdf, sampled_type) =
            bsdf.sample_f(&it.wo, u_scattering, bsdf_flags);
        f = f * wi.dot(&it.shading.n).abs();
        let sampled_specular = sampled_type.contains(bsdf::BSDF_SPECULAR);
        // TODO compute medium interaction when supported
        if !f.is_black() && scattering_pdf > 0.0 {
            // Account for light contribution along sampled direction wi
            let mut weight = 1.0;
            if !sampled_specular {
                let light_pdf = light.pdf_li(it, &wi);
                if light_pdf == 0.0 {
                    return ld;
                }
                weight = power_heuristic(1, scattering_pdf, 1, light_pdf);
            }

            // Find intersection and compute transmittance
            let mut ray = it.spawn_ray(&wi);
            let li = match scene.intersect(&mut ray) {
                Some(light_isect) => {
                    // Add light contribution from material sampling
                    if let Some(area_light) = light_isect.primitive.and_then(|p| p.area_light()) {
                        if area_light.id() == light.id() {
                            light_isect.le(&(-wi))
                        } else {
                            Spectrum::black()
                        }
                    } else {
                        Spectrum::black()
                    }
                }
                None => light.le(&ray),
            };
            if !li.is_black() {
                ld += f * li * weight / scattering_pdf;
            }
        }
    }

    ld
}
<commit_msg>Use proper way to spawn ray<commit_after>use std::sync::Arc;
use std::cmp;

use na::Dot;

use ::Point2f;
use bsdf;
use spectrum::Spectrum;
use interaction::SurfaceInteraction;
use light::{Light, is_delta_light};
use ray::Ray;
use sampler::Sampler;
use sampling::power_heuristic;
use scene::Scene;

mod whitted;
mod directlighting;
mod ao;
mod normal;

pub use self::whitted::Whitted;
pub use self::directlighting::DirectLightingIntegrator;
pub use self::ao::AmbientOcclusion;
pub use self::normal::Normal;

pub trait SamplerIntegrator {
    fn li(&self, scene: &Scene, ray: &mut Ray, sampler: &mut Sampler, depth: u32) -> Spectrum;

    fn specular_reflection(&self,
                           ray: &mut Ray,
                           isect: &SurfaceInteraction,
                           scene: &Scene,
                           bsdf: &bsdf::BSDF,
                           sampler: &mut Sampler,
                           depth: u32)
                           -> Spectrum {
        let flags = bsdf::BSDF_REFLECTION | bsdf::BSDF_SPECULAR;
        let (f, wi, pdf, bsdf_type) = bsdf.sample_f(&isect.wo, &sampler.get_2d(), flags);
        let ns = isect.shading.n;
        if !f.is_black() && pdf != 0.0 && wi.dot(&ns) != 0.0 {
            let mut r = isect.spawn_ray(&wi);
            let refl = self.li(scene, &mut r, sampler, depth + 1);
            f * refl * wi.dot(&ns).abs() / pdf
        } else {
            Spectrum::black()
        }
    }

    fn specular_transmission(&self,
                             ray: &mut Ray,
                             isect: &SurfaceInteraction,
                             scene: &Scene,
                             bsdf: &bsdf::BSDF,
                             sampler: &mut Sampler,
                             depth: u32)
                             -> Spectrum {
        let flags = bsdf::BSDF_TRANSMISSION | bsdf::BSDF_SPECULAR;
        let (f, wi, pdf, bsdf_type) = bsdf.sample_f(&isect.wo, &sampler.get_2d(), flags);
        let ns = isect.shading.n;
        if !f.is_black() && pdf != 0.0 && wi.dot(&ns) != 0.0 {
            let mut r = isect.spawn_ray(&wi);
            let refr = self.li(scene, &mut r, sampler, depth + 1);
            f * refr * wi.dot(&ns).abs() / pdf
        } else {
            Spectrum::black()
        }
    }
}

pub fn uniform_sample_one_light(it: &SurfaceInteraction,
                                scene: &Scene,
                                sampler: &mut Sampler)
                                -> Spectrum {
    let n_lights = scene.lights.len();
    if n_lights == 0 {
        Spectrum::black()
    } else {
        // Randomly chose a light to sample
        let s = sampler.get_1d();
        let num_light = cmp::min(n_lights - 1, (s * n_lights as f32) as usize);
        debug!("sampler.get_1d()={}, n_lights={}, num_light={}, num_light as usize={}",
               s,
               n_lights,
               s * n_lights as f32,
               num_light);
        let light = &scene.lights[num_light];
        let u_light = sampler.get_2d();
        let u_scattering = sampler.get_2d();
        estimate_direct(it, &u_scattering, light, &u_light, scene, sampler) * n_lights as f32
    }
}

pub fn estimate_direct(it: &SurfaceInteraction,
                       u_scattering: &Point2f,
                       light: &Arc<Light + Send + Sync>,
                       u_light: &Point2f,
                       scene: &Scene,
                       sampler: &mut Sampler)
                       -> Spectrum {
    let specular = false;

    let bsdf_flags = if specular {
        bsdf::BxDFType::all()
    } else {
        bsdf::BxDFType::all() & !bsdf::BSDF_SPECULAR
    };
    let mut ld = Spectrum::black();
    // Sample light with multiple importance sampling
    let bsdf = it.bsdf.as_ref().expect("There should be a BSDF set at this point!");
    let (mut li, wi, light_pdf, vis) = light.sample_li(it, &u_light);
    if light_pdf > 0.0 && !li.is_black() {
        // Compute BSDF for light sample
        let f = bsdf.f(&it.wo, &wi, bsdf_flags) * wi.dot(&it.shading.n).abs();
        let scattering_pdf = bsdf.pdf(&it.wo, &wi, bsdf_flags);
        if !f.is_black() {
            if !vis.unoccluded(scene) {
                li = Spectrum::black();
            }
            // Add light's contribution to reflected radiance
            if !li.is_black() {
                if is_delta_light(light.flags()) {
                    ld += f * li / light_pdf;
                } else {
                    let weight = power_heuristic(1, light_pdf, 1, scattering_pdf);
                    ld += f * li * weight / light_pdf;
                }
            }
        }
        // TODO compute phase function for medium interaction when supported
    }
    // Sample BSDF with multiple importance sampling
    if !is_delta_light(light.flags()) {
        let (mut f, wi, scattering_pdf, sampled_type) =
            bsdf.sample_f(&it.wo, u_scattering, bsdf_flags);
        f = f * wi.dot(&it.shading.n).abs();
        let sampled_specular = sampled_type.contains(bsdf::BSDF_SPECULAR);
        // TODO compute medium interaction when supported
        if !f.is_black() && scattering_pdf > 0.0 {
            // Account for light contribution along sampled direction wi
            let mut weight = 1.0;
            if !sampled_specular {
                let light_pdf = light.pdf_li(it, &wi);
                if light_pdf == 0.0 {
                    return ld;
                }
                weight = power_heuristic(1, scattering_pdf, 1, light_pdf);
            }

            // Find intersection and compute transmittance
            let mut ray = it.spawn_ray(&wi);
            let li = match scene.intersect(&mut ray) {
                Some(light_isect) => {
                    // Add light contribution from material sampling
                    if let Some(area_light) = light_isect.primitive.and_then(|p| p.area_light()) {
                        if area_light.id() == light.id() {
                            light_isect.le(&(-wi))
                        } else {
                            Spectrum::black()
                        }
                    } else {
                        Spectrum::black()
                    }
                }
                None => light.le(&ray),
            };
            if !li.is_black() {
                ld += f * li * weight / scattering_pdf;
            }
        }
    }

    ld
}
<|endoftext|>


<commit_before>//! Error types.

use fallible_iterator::FallibleIterator;
use postgres_protocol::message::backend::ErrorFields;
use std::error;
use std::convert::From;
use std::fmt;
use std::io;
use std::result;

pub use self::sqlstate::SqlState;
use {Result, DbErrorNew};

mod sqlstate;

/// A Postgres error or notice.
#[derive(Clone, PartialEq, Eq)]
pub struct DbError {
    /// The field contents are ERROR, FATAL, or PANIC (in an error message),
    /// or WARNING, NOTICE, DEBUG, INFO, or LOG (in a notice message), or a
    /// localized translation of one of these.
    pub severity: String,

    /// The SQLSTATE code for the error.
    pub code: SqlState,

    /// The primary human-readable error message. This should be accurate but
    /// terse (typically one line).
    pub message: String,

    /// An optional secondary error message carrying more detail about the
    /// problem. Might run to multiple lines.
    pub detail: Option<String>,

    /// An optional suggestion what to do about the problem. This is intended
    /// to differ from Detail in that it offers advice (potentially
    /// inappropriate) rather than hard facts. Might run to multiple lines.
    pub hint: Option<String>,

    /// An optional error cursor position into either the original query string
    /// or an internally generated query.
    pub position: Option<ErrorPosition>,

    /// An indication of the context in which the error occurred. Presently
    /// this includes a call stack traceback of active procedural language
    /// functions and internally-generated queries. The trace is one entry per
    /// line, most recent first.
    pub where_: Option<String>,

    /// If the error was associated with a specific database object, the name
    /// of the schema containing that object, if any. (PostgreSQL 9.3+)
    pub schema: Option<String>,

    /// If the error was associated with a specific table, the name of the
    /// table. (Refer to the schema name field for the name of the table's
    /// schema.) (PostgreSQL 9.3+)
    pub table: Option<String>,

    /// If the error was associated with a specific table column, the name of
    /// the column. (Refer to the schema and table name fields to identify the
    /// table.) (PostgreSQL 9.3+)
    pub column: Option<String>,

    /// If the error was associated with a specific data type, the name of the
    /// data type. (Refer to the schema name field for the name of the data
    /// type's schema.) (PostgreSQL 9.3+)
    pub datatype: Option<String>,

    /// If the error was associated with a specific constraint, the name of the
    /// constraint. Refer to fields listed above for the associated table or
    /// domain. (For this purpose, indexes are treated as constraints, even if
    /// they weren't created with constraint syntax.) (PostgreSQL 9.3+)
    pub constraint: Option<String>,

    /// The file name of the source-code location where the error was reported.
    pub file: Option<String>,

    /// The line number of the source-code location where the error was
    /// reported.
    pub line: Option<u32>,

    /// The name of the source-code routine reporting the error.
    pub routine: Option<String>,

    _p: (),
}

impl DbErrorNew for DbError {
    fn new_raw(fields: &mut ErrorFields) -> io::Result<DbError> {
        let mut severity = None;
        let mut code = None;
        let mut message = None;
        let mut detail = None;
        let mut hint = None;
        let mut normal_position = None;
        let mut internal_position = None;
        let mut internal_query = None;
        let mut where_ = None;
        let mut schema = None;
        let mut table = None;
        let mut column = None;
        let mut datatype = None;
        let mut constraint = None;
        let mut file = None;
        let mut line = None;
        let mut routine = None;

        while let Some(field) = try!(fields.next()) {
            match field.type_() {
                b'S' => severity = Some(field.value().to_owned()),
                b'C' => code = Some(SqlState::from_code(field.value())),
                b'M' => message = Some(field.value().to_owned()),
                b'D' => detail = Some(field.value().to_owned()),
                b'H' => hint = Some(field.value().to_owned()),
                b'P' => normal_position = Some(try!(field.value().parse::<u32>().map_err(|_| ::bad_response()))),
                b'p' => internal_position = Some(try!(field.value().parse::<u32>().map_err(|_| ::bad_response()))),
                b'q' => internal_query = Some(field.value().to_owned()),
                b'W' => where_ = Some(field.value().to_owned()),
                b's' => schema = Some(field.value().to_owned()),
                b't' => table = Some(field.value().to_owned()),
                b'c' => column = Some(field.value().to_owned()),
                b'd' => datatype = Some(field.value().to_owned()),
                b'n' => constraint = Some(field.value().to_owned()),
                b'F' => file = Some(field.value().to_owned()),
                b'L' => line = Some(try!(field.value().parse::<u32>().map_err(|_| ::bad_response()))),
                b'R' => routine = Some(field.value().to_owned()),
                _ => {},
            }
        }

        Ok(DbError {
            severity: try!(severity.ok_or_else(|| ::bad_response())),
            code: try!(code.ok_or_else(|| ::bad_response())),
            message: try!(message.ok_or_else(|| ::bad_response())),
            detail: detail,
            hint: hint,
            position: match normal_position {
                Some(position) => Some(ErrorPosition::Normal(position)),
                None => {
                    match internal_position {
                        Some(position) => {
                            Some(ErrorPosition::Internal {
                                position: position,
                                query: try!(internal_query.ok_or_else(|| ::bad_response())),
                            })
                        }
                        None => None,
                    }
                }
            },
            where_: where_,
            schema: schema,
            table: table,
            column: column,
            datatype: datatype,
            constraint: constraint,
            file: file,
            line: line,
            routine: routine,
            _p: (),
        })
    }

    fn new_connect<T>(fields: &mut ErrorFields) -> result::Result<T, ConnectError> {
        match DbError::new_raw(fields) {
            Ok(err) => Err(ConnectError::Db(Box::new(err))),
            Err(e) => Err(ConnectError::Io(e)),
        }
    }

    fn new<T>(fields: &mut ErrorFields) -> Result<T> {
        match DbError::new_raw(fields) {
            Ok(err) => Err(Error::Db(Box::new(err))),
            Err(e) => Err(Error::Io(e)),
        }
    }
}

// manual impl to leave out _p
impl fmt::Debug for DbError {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt.debug_struct("DbError")
            .field("severity", &self.severity)
            .field("code", &self.code)
            .field("message", &self.message)
            .field("detail", &self.detail)
            .field("hint", &self.hint)
            .field("position", &self.position)
            .field("where_", &self.where_)
            .field("schema", &self.schema)
            .field("table", &self.table)
            .field("column", &self.column)
            .field("datatype", &self.datatype)
            .field("constraint", &self.constraint)
            .field("file", &self.file)
            .field("line", &self.line)
            .field("routine", &self.routine)
            .finish()
    }
}

impl fmt::Display for DbError {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        write!(fmt, "{}: {}", self.severity, self.message)
    }
}

impl error::Error for DbError {
    fn description(&self) -> &str {
        &self.message
    }
}

/// Reasons a new Postgres connection could fail.
#[derive(Debug)]
pub enum ConnectError {
    /// An error relating to connection parameters.
    ConnectParams(Box<error::Error + Sync + Send>),
    /// An error from the Postgres server itself.
    Db(Box<DbError>),
    /// An error initializing the TLS session.
    Tls(Box<error::Error + Sync + Send>),
    /// An error communicating with the server.
    Io(io::Error),
}

impl fmt::Display for ConnectError {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        try!(fmt.write_str(error::Error::description(self)));
        match *self {
            ConnectError::ConnectParams(ref msg) => write!(fmt, ": {}", msg),
            ConnectError::Db(ref err) => write!(fmt, ": {}", err),
            ConnectError::Tls(ref err) => write!(fmt, ": {}", err),
            ConnectError::Io(ref err) => write!(fmt, ": {}", err),
        }
    }
}

impl error::Error for ConnectError {
    fn description(&self) -> &str {
        match *self {
            ConnectError::ConnectParams(_) => "Invalid connection parameters",
            ConnectError::Db(_) => "Error reported by Postgres",
            ConnectError::Tls(_) => "Error initiating SSL session",
            ConnectError::Io(_) => "Error communicating with the server",
        }
    }

    fn cause(&self) -> Option<&error::Error> {
        match *self {
            ConnectError::ConnectParams(ref err) |
            ConnectError::Tls(ref err) => Some(&**err),
            ConnectError::Db(ref err) => Some(&**err),
            ConnectError::Io(ref err) => Some(err),
        }
    }
}

impl From<io::Error> for ConnectError {
    fn from(err: io::Error) -> ConnectError {
        ConnectError::Io(err)
    }
}

impl From<DbError> for ConnectError {
    fn from(err: DbError) -> ConnectError {
        ConnectError::Db(Box::new(err))
    }
}

/// Represents the position of an error in a query.
#[derive(Clone, PartialEq, Eq, Debug)]
pub enum ErrorPosition {
    /// A position in the original query.
    Normal(u32),
    /// A position in an internally generated query.
    Internal {
        /// The byte position.
        position: u32,
        /// A query generated by the Postgres server.
        query: String,
    },
}

/// An error encountered when communicating with the Postgres server.
#[derive(Debug)]
pub enum Error {
    /// An error reported by the Postgres server.
    Db(Box<DbError>),
    /// An error communicating with the Postgres server.
    Io(io::Error),
    /// An error converting between Postgres and Rust types.
    Conversion(Box<error::Error + Sync + Send>),
}

impl fmt::Display for Error {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        try!(fmt.write_str(error::Error::description(self)));
        match *self {
            Error::Db(ref err) => write!(fmt, ": {}", err),
            Error::Io(ref err) => write!(fmt, ": {}", err),
            Error::Conversion(ref err) => write!(fmt, ": {}", err),
        }
    }
}

impl error::Error for Error {
    fn description(&self) -> &str {
        match *self {
            Error::Db(_) => "Error reported by Postgres",
            Error::Io(_) => "Error communicating with the server",
            Error::Conversion(_) => "Error converting between Postgres and Rust types",
        }
    }

    fn cause(&self) -> Option<&error::Error> {
        match *self {
            Error::Db(ref err) => Some(&**err),
            Error::Io(ref err) => Some(err),
            Error::Conversion(ref err) => Some(&**err),
        }
    }
}

impl From<DbError> for Error {
    fn from(err: DbError) -> Error {
        Error::Db(Box::new(err))
    }
}

impl From<io::Error> for Error {
    fn from(err: io::Error) -> Error {
        Error::Io(err)
    }
}

impl From<Error> for io::Error {
    fn from(err: Error) -> io::Error {
        io::Error::new(io::ErrorKind::Other, err)
    }
}
<commit_msg>Support the new V error field.<commit_after>//! Error types.

use fallible_iterator::FallibleIterator;
use postgres_protocol::message::backend::ErrorFields;
use std::error;
use std::convert::From;
use std::fmt;
use std::io;
use std::result;

pub use self::sqlstate::SqlState;
use {Result, DbErrorNew};

mod sqlstate;

/// The severity of a Postgres error or notice.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum Severity {
    /// PANIC
    Panic,
    /// FATAL
    Fatal,
    /// ERROR
    Error,
    /// WARNING
    Warning,
    /// NOTICE
    Notice,
    /// DEBUG
    Debug,
    /// INFO
    Info,
    /// LOG
    Log,
}

impl fmt::Display for Severity {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        let s = match *self {
            Severity::Panic => "PANIC",
            Severity::Fatal => "FATAL",
            Severity::Error => "ERROR",
            Severity::Warning => "WARNING",
            Severity::Notice => "NOTICE",
            Severity::Debug => "DEBUG",
            Severity::Info => "INFO",
            Severity::Log => "LOG",
        };
        fmt.write_str(s)
    }
}

impl Severity {
    fn from_str(s: &str) -> Option<Severity> {
        match s {
            "PANIC" => Some(Severity::Panic),
            "FATAL" => Some(Severity::Fatal),
            "ERROR" => Some(Severity::Error),
            "WARNING" => Some(Severity::Warning),
            "NOTICE" => Some(Severity::Notice),
            "DEBUG" => Some(Severity::Debug),
            "INFO" => Some(Severity::Info),
            "LOG" => Some(Severity::Log),
            _ => None,
        }
    }
}

/// A Postgres error or notice.
#[derive(Clone, PartialEq, Eq)]
pub struct DbError {
    /// The field contents are ERROR, FATAL, or PANIC (in an error message),
    /// or WARNING, NOTICE, DEBUG, INFO, or LOG (in a notice message), or a
    /// localized translation of one of these.
    pub severity: String,

    /// A parsed, nonlocalized version of `severity`. (PostgreSQL 9.6+)
    pub parsed_severity: Option<Severity>,

    /// The SQLSTATE code for the error.
    pub code: SqlState,

    /// The primary human-readable error message. This should be accurate but
    /// terse (typically one line).
    pub message: String,

    /// An optional secondary error message carrying more detail about the
    /// problem. Might run to multiple lines.
    pub detail: Option<String>,

    /// An optional suggestion what to do about the problem. This is intended
    /// to differ from Detail in that it offers advice (potentially
    /// inappropriate) rather than hard facts. Might run to multiple lines.
    pub hint: Option<String>,

    /// An optional error cursor position into either the original query string
    /// or an internally generated query.
    pub position: Option<ErrorPosition>,

    /// An indication of the context in which the error occurred. Presently
    /// this includes a call stack traceback of active procedural language
    /// functions and internally-generated queries. The trace is one entry per
    /// line, most recent first.
    pub where_: Option<String>,

    /// If the error was associated with a specific database object, the name
    /// of the schema containing that object, if any. (PostgreSQL 9.3+)
    pub schema: Option<String>,

    /// If the error was associated with a specific table, the name of the
    /// table. (Refer to the schema name field for the name of the table's
    /// schema.) (PostgreSQL 9.3+)
    pub table: Option<String>,

    /// If the error was associated with a specific table column, the name of
    /// the column. (Refer to the schema and table name fields to identify the
    /// table.) (PostgreSQL 9.3+)
    pub column: Option<String>,

    /// If the error was associated with a specific data type, the name of the
    /// data type. (Refer to the schema name field for the name of the data
    /// type's schema.) (PostgreSQL 9.3+)
    pub datatype: Option<String>,

    /// If the error was associated with a specific constraint, the name of the
    /// constraint. Refer to fields listed above for the associated table or
    /// domain. (For this purpose, indexes are treated as constraints, even if
    /// they weren't created with constraint syntax.) (PostgreSQL 9.3+)
    pub constraint: Option<String>,

    /// The file name of the source-code location where the error was reported.
    pub file: Option<String>,

    /// The line number of the source-code location where the error was
    /// reported.
    pub line: Option<u32>,

    /// The name of the source-code routine reporting the error.
    pub routine: Option<String>,

    _p: (),
}

impl DbErrorNew for DbError {
    fn new_raw(fields: &mut ErrorFields) -> io::Result<DbError> {
        let mut severity = None;
        let mut parsed_severity = None;
        let mut code = None;
        let mut message = None;
        let mut detail = None;
        let mut hint = None;
        let mut normal_position = None;
        let mut internal_position = None;
        let mut internal_query = None;
        let mut where_ = None;
        let mut schema = None;
        let mut table = None;
        let mut column = None;
        let mut datatype = None;
        let mut constraint = None;
        let mut file = None;
        let mut line = None;
        let mut routine = None;

        while let Some(field) = try!(fields.next()) {
            match field.type_() {
                b'S' => severity = Some(field.value().to_owned()),
                b'C' => code = Some(SqlState::from_code(field.value())),
                b'M' => message = Some(field.value().to_owned()),
                b'D' => detail = Some(field.value().to_owned()),
                b'H' => hint = Some(field.value().to_owned()),
                b'P' => normal_position = Some(try!(field.value().parse::<u32>().map_err(|_| ::bad_response()))),
                b'p' => internal_position = Some(try!(field.value().parse::<u32>().map_err(|_| ::bad_response()))),
                b'q' => internal_query = Some(field.value().to_owned()),
                b'W' => where_ = Some(field.value().to_owned()),
                b's' => schema = Some(field.value().to_owned()),
                b't' => table = Some(field.value().to_owned()),
                b'c' => column = Some(field.value().to_owned()),
                b'd' => datatype = Some(field.value().to_owned()),
                b'n' => constraint = Some(field.value().to_owned()),
                b'F' => file = Some(field.value().to_owned()),
                b'L' => line = Some(try!(field.value().parse::<u32>().map_err(|_| ::bad_response()))),
                b'R' => routine = Some(field.value().to_owned()),
                b'V' => parsed_severity = Some(try!(Severity::from_str(field.value()).ok_or_else(::bad_response))),
                _ => {},
            }
        }

        Ok(DbError {
            severity: try!(severity.ok_or_else(|| ::bad_response())),
            parsed_severity: parsed_severity,
            code: try!(code.ok_or_else(|| ::bad_response())),
            message: try!(message.ok_or_else(|| ::bad_response())),
            detail: detail,
            hint: hint,
            position: match normal_position {
                Some(position) => Some(ErrorPosition::Normal(position)),
                None => {
                    match internal_position {
                        Some(position) => {
                            Some(ErrorPosition::Internal {
                                position: position,
                                query: try!(internal_query.ok_or_else(|| ::bad_response())),
                            })
                        }
                        None => None,
                    }
                }
            },
            where_: where_,
            schema: schema,
            table: table,
            column: column,
            datatype: datatype,
            constraint: constraint,
            file: file,
            line: line,
            routine: routine,
            _p: (),
        })
    }

    fn new_connect<T>(fields: &mut ErrorFields) -> result::Result<T, ConnectError> {
        match DbError::new_raw(fields) {
            Ok(err) => Err(ConnectError::Db(Box::new(err))),
            Err(e) => Err(ConnectError::Io(e)),
        }
    }

    fn new<T>(fields: &mut ErrorFields) -> Result<T> {
        match DbError::new_raw(fields) {
            Ok(err) => Err(Error::Db(Box::new(err))),
            Err(e) => Err(Error::Io(e)),
        }
    }
}

// manual impl to leave out _p
impl fmt::Debug for DbError {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt.debug_struct("DbError")
            .field("severity", &self.severity)
            .field("code", &self.code)
            .field("message", &self.message)
            .field("detail", &self.detail)
            .field("hint", &self.hint)
            .field("position", &self.position)
            .field("where_", &self.where_)
            .field("schema", &self.schema)
            .field("table", &self.table)
            .field("column", &self.column)
            .field("datatype", &self.datatype)
            .field("constraint", &self.constraint)
            .field("file", &self.file)
            .field("line", &self.line)
            .field("routine", &self.routine)
            .finish()
    }
}

impl fmt::Display for DbError {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        write!(fmt, "{}: {}", self.severity, self.message)
    }
}

impl error::Error for DbError {
    fn description(&self) -> &str {
        &self.message
    }
}

/// Reasons a new Postgres connection could fail.
#[derive(Debug)]
pub enum ConnectError {
    /// An error relating to connection parameters.
    ConnectParams(Box<error::Error + Sync + Send>),
    /// An error from the Postgres server itself.
    Db(Box<DbError>),
    /// An error initializing the TLS session.
    Tls(Box<error::Error + Sync + Send>),
    /// An error communicating with the server.
    Io(io::Error),
}

impl fmt::Display for ConnectError {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        try!(fmt.write_str(error::Error::description(self)));
        match *self {
            ConnectError::ConnectParams(ref msg) => write!(fmt, ": {}", msg),
            ConnectError::Db(ref err) => write!(fmt, ": {}", err),
            ConnectError::Tls(ref err) => write!(fmt, ": {}", err),
            ConnectError::Io(ref err) => write!(fmt, ": {}", err),
        }
    }
}

impl error::Error for ConnectError {
    fn description(&self) -> &str {
        match *self {
            ConnectError::ConnectParams(_) => "Invalid connection parameters",
            ConnectError::Db(_) => "Error reported by Postgres",
            ConnectError::Tls(_) => "Error initiating SSL session",
            ConnectError::Io(_) => "Error communicating with the server",
        }
    }

    fn cause(&self) -> Option<&error::Error> {
        match *self {
            ConnectError::ConnectParams(ref err) |
            ConnectError::Tls(ref err) => Some(&**err),
            ConnectError::Db(ref err) => Some(&**err),
            ConnectError::Io(ref err) => Some(err),
        }
    }
}

impl From<io::Error> for ConnectError {
    fn from(err: io::Error) -> ConnectError {
        ConnectError::Io(err)
    }
}

impl From<DbError> for ConnectError {
    fn from(err: DbError) -> ConnectError {
        ConnectError::Db(Box::new(err))
    }
}

/// Represents the position of an error in a query.
#[derive(Clone, PartialEq, Eq, Debug)]
pub enum ErrorPosition {
    /// A position in the original query.
    Normal(u32),
    /// A position in an internally generated query.
    Internal {
        /// The byte position.
        position: u32,
        /// A query generated by the Postgres server.
        query: String,
    },
}

/// An error encountered when communicating with the Postgres server.
#[derive(Debug)]
pub enum Error {
    /// An error reported by the Postgres server.
    Db(Box<DbError>),
    /// An error communicating with the Postgres server.
    Io(io::Error),
    /// An error converting between Postgres and Rust types.
    Conversion(Box<error::Error + Sync + Send>),
}

impl fmt::Display for Error {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        try!(fmt.write_str(error::Error::description(self)));
        match *self {
            Error::Db(ref err) => write!(fmt, ": {}", err),
            Error::Io(ref err) => write!(fmt, ": {}", err),
            Error::Conversion(ref err) => write!(fmt, ": {}", err),
        }
    }
}

impl error::Error for Error {
    fn description(&self) -> &str {
        match *self {
            Error::Db(_) => "Error reported by Postgres",
            Error::Io(_) => "Error communicating with the server",
            Error::Conversion(_) => "Error converting between Postgres and Rust types",
        }
    }

    fn cause(&self) -> Option<&error::Error> {
        match *self {
            Error::Db(ref err) => Some(&**err),
            Error::Io(ref err) => Some(err),
            Error::Conversion(ref err) => Some(&**err),
        }
    }
}

impl From<DbError> for Error {
    fn from(err: DbError) -> Error {
        Error::Db(Box::new(err))
    }
}

impl From<io::Error> for Error {
    fn from(err: io::Error) -> Error {
        Error::Io(err)
    }
}

impl From<Error> for io::Error {
    fn from(err: Error) -> io::Error {
        io::Error::new(io::ErrorKind::Other, err)
    }
}
<|endoftext|>