<commit_before>//! In rust-analyzer, we maintain a strict separation between pure abstract
//! semantic project model and a concrete model of a particular build system.
//!
//! Pure model is represented by the [`base_db::CrateGraph`] from another crate.
//!
//! In this crate, we are conserned with "real world" project models.
//!
//! Specifically, here we have a representation for a Cargo project
//! ([`CargoWorkspace`]) and for manually specified layout ([`ProjectJson`]).
//!
//! Roughly, the things we do here are:
//!
//! * Project discovery (where's the relevant Cargo.toml for the current dir).
//! * Custom build steps (`build.rs` code generation and compilation of
//!   procedural macros).
//! * Lowering of concrete model to a [`base_db::CrateGraph`]

mod cargo_workspace;
mod cfg_flag;
mod project_json;
mod sysroot;
mod workspace;
mod rustc_cfg;
mod build_data;

use std::{
    fs::{read_dir, ReadDir},
    io,
    process::Command,
};

use anyhow::{bail, Context, Result};
use paths::{AbsPath, AbsPathBuf};
use rustc_hash::FxHashSet;

pub use crate::{
    build_data::{BuildDataCollector, BuildDataResult},
    cargo_workspace::{
        CargoConfig, CargoWorkspace, Package, PackageData, PackageDependency, RustcSource, Target,
        TargetData, TargetKind,
    },
    project_json::{ProjectJson, ProjectJsonData},
    sysroot::Sysroot,
    workspace::{CfgOverrides, PackageRoot, ProjectWorkspace},
};

pub use proc_macro_api::ProcMacroClient;

#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
pub enum ProjectManifest {
    ProjectJson(AbsPathBuf),
    CargoToml(AbsPathBuf),
}

impl ProjectManifest {
    pub fn from_manifest_file(path: AbsPathBuf) -> Result<ProjectManifest> {
        if path.ends_with("rust-project.json") {
            return Ok(ProjectManifest::ProjectJson(path));
        }
        if path.ends_with("Cargo.toml") {
            return Ok(ProjectManifest::CargoToml(path));
        }
        bail!("project root must point to Cargo.toml or rust-project.json: {}", path.display())
    }

    pub fn discover_single(path: &AbsPath) -> Result<ProjectManifest> {
        let mut candidates = ProjectManifest::discover(path)?;
        let res = match candidates.pop() {
            None => bail!("no projects"),
            Some(it) => it,
        };

        if !candidates.is_empty() {
            bail!("more than one project")
        }
        Ok(res)
    }

    pub fn discover(path: &AbsPath) -> io::Result<Vec<ProjectManifest>> {
        if let Some(project_json) = find_in_parent_dirs(path, "rust-project.json") {
            return Ok(vec![ProjectManifest::ProjectJson(project_json)]);
        }
        return find_cargo_toml(path)
            .map(|paths| paths.into_iter().map(ProjectManifest::CargoToml).collect());

        fn find_cargo_toml(path: &AbsPath) -> io::Result<Vec<AbsPathBuf>> {
            match find_in_parent_dirs(path, "Cargo.toml") {
                Some(it) => Ok(vec![it]),
                None => Ok(find_cargo_toml_in_child_dir(read_dir(path)?)),
            }
        }

        fn find_in_parent_dirs(path: &AbsPath, target_file_name: &str) -> Option<AbsPathBuf> {
            if path.ends_with(target_file_name) {
                return Some(path.to_path_buf());
            }

            let mut curr = Some(path);

            while let Some(path) = curr {
                let candidate = path.join(target_file_name);
                if candidate.exists() {
                    return Some(candidate);
                }
                curr = path.parent();
            }

            None
        }

        fn find_cargo_toml_in_child_dir(entities: ReadDir) -> Vec<AbsPathBuf> {
            // Only one level down to avoid cycles the easy way and stop a runaway scan with large projects
            entities
                .filter_map(Result::ok)
                .map(|it| it.path().join("Cargo.toml"))
                .filter(|it| it.exists())
                .map(AbsPathBuf::assert)
                .collect()
        }
    }

    pub fn discover_all(paths: &[impl AsRef<AbsPath>]) -> Vec<ProjectManifest> {
        let mut res = paths
            .iter()
            .filter_map(|it| ProjectManifest::discover(it.as_ref()).ok())
            .flatten()
            .collect::<FxHashSet<_>>()
            .into_iter()
            .collect::<Vec<_>>();
        res.sort();
        res
    }
}

fn utf8_stdout(mut cmd: Command) -> Result<String> {
    let output = cmd.output().with_context(|| format!("{:?} failed", cmd))?;
    if !output.status.success() {
        match String::from_utf8(output.stderr) {
            Ok(stderr) if !stderr.is_empty() => {
                bail!("{:?} failed, {}\nstderr:\n{}", cmd, output.status, stderr)
            }
            _ => bail!("{:?} failed, {}", cmd, output.status),
        }
    }
    let stdout = String::from_utf8(output.stdout)?;
    Ok(stdout.trim().to_string())
}
<commit_msg>intenral: small improvements to compile time<commit_after>//! In rust-analyzer, we maintain a strict separation between pure abstract
//! semantic project model and a concrete model of a particular build system.
//!
//! Pure model is represented by the [`base_db::CrateGraph`] from another crate.
//!
//! In this crate, we are conserned with "real world" project models.
//!
//! Specifically, here we have a representation for a Cargo project
//! ([`CargoWorkspace`]) and for manually specified layout ([`ProjectJson`]).
//!
//! Roughly, the things we do here are:
//!
//! * Project discovery (where's the relevant Cargo.toml for the current dir).
//! * Custom build steps (`build.rs` code generation and compilation of
//!   procedural macros).
//! * Lowering of concrete model to a [`base_db::CrateGraph`]

mod cargo_workspace;
mod cfg_flag;
mod project_json;
mod sysroot;
mod workspace;
mod rustc_cfg;
mod build_data;

use std::{
    fs::{read_dir, ReadDir},
    io,
    process::Command,
};

use anyhow::{bail, Context, Result};
use paths::{AbsPath, AbsPathBuf};
use rustc_hash::FxHashSet;

pub use crate::{
    build_data::{BuildDataCollector, BuildDataResult},
    cargo_workspace::{
        CargoConfig, CargoWorkspace, Package, PackageData, PackageDependency, RustcSource, Target,
        TargetData, TargetKind,
    },
    project_json::{ProjectJson, ProjectJsonData},
    sysroot::Sysroot,
    workspace::{CfgOverrides, PackageRoot, ProjectWorkspace},
};

pub use proc_macro_api::ProcMacroClient;

#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
pub enum ProjectManifest {
    ProjectJson(AbsPathBuf),
    CargoToml(AbsPathBuf),
}

impl ProjectManifest {
    pub fn from_manifest_file(path: AbsPathBuf) -> Result<ProjectManifest> {
        if path.ends_with("rust-project.json") {
            return Ok(ProjectManifest::ProjectJson(path));
        }
        if path.ends_with("Cargo.toml") {
            return Ok(ProjectManifest::CargoToml(path));
        }
        bail!("project root must point to Cargo.toml or rust-project.json: {}", path.display())
    }

    pub fn discover_single(path: &AbsPath) -> Result<ProjectManifest> {
        let mut candidates = ProjectManifest::discover(path)?;
        let res = match candidates.pop() {
            None => bail!("no projects"),
            Some(it) => it,
        };

        if !candidates.is_empty() {
            bail!("more than one project")
        }
        Ok(res)
    }

    pub fn discover(path: &AbsPath) -> io::Result<Vec<ProjectManifest>> {
        if let Some(project_json) = find_in_parent_dirs(path, "rust-project.json") {
            return Ok(vec![ProjectManifest::ProjectJson(project_json)]);
        }
        return find_cargo_toml(path)
            .map(|paths| paths.into_iter().map(ProjectManifest::CargoToml).collect());

        fn find_cargo_toml(path: &AbsPath) -> io::Result<Vec<AbsPathBuf>> {
            match find_in_parent_dirs(path, "Cargo.toml") {
                Some(it) => Ok(vec![it]),
                None => Ok(find_cargo_toml_in_child_dir(read_dir(path)?)),
            }
        }

        fn find_in_parent_dirs(path: &AbsPath, target_file_name: &str) -> Option<AbsPathBuf> {
            if path.ends_with(target_file_name) {
                return Some(path.to_path_buf());
            }

            let mut curr = Some(path);

            while let Some(path) = curr {
                let candidate = path.join(target_file_name);
                if candidate.exists() {
                    return Some(candidate);
                }
                curr = path.parent();
            }

            None
        }

        fn find_cargo_toml_in_child_dir(entities: ReadDir) -> Vec<AbsPathBuf> {
            // Only one level down to avoid cycles the easy way and stop a runaway scan with large projects
            entities
                .filter_map(Result::ok)
                .map(|it| it.path().join("Cargo.toml"))
                .filter(|it| it.exists())
                .map(AbsPathBuf::assert)
                .collect()
        }
    }

    pub fn discover_all(paths: &[AbsPathBuf]) -> Vec<ProjectManifest> {
        let mut res = paths
            .iter()
            .filter_map(|it| ProjectManifest::discover(it.as_ref()).ok())
            .flatten()
            .collect::<FxHashSet<_>>()
            .into_iter()
            .collect::<Vec<_>>();
        res.sort();
        res
    }
}

fn utf8_stdout(mut cmd: Command) -> Result<String> {
    let output = cmd.output().with_context(|| format!("{:?} failed", cmd))?;
    if !output.status.success() {
        match String::from_utf8(output.stderr) {
            Ok(stderr) if !stderr.is_empty() => {
                bail!("{:?} failed, {}\nstderr:\n{}", cmd, output.status, stderr)
            }
            _ => bail!("{:?} failed, {}", cmd, output.status),
        }
    }
    let stdout = String::from_utf8(output.stdout)?;
    Ok(stdout.trim().to_string())
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

extern crate cc;
extern crate glsl_to_cxx;
extern crate webrender_build;

use std::collections::HashSet;
use std::fmt::Write;
use webrender_build::shader::{ShaderFeatureFlags, get_shader_features};

// Shader key is in "name feature,feature" format.
// File name needs to be formatted as "name_feature_feature".
fn shader_file(shader_key: &str) -> String {
    shader_key.replace(' ', "_").replace(',', "_")
}

fn write_load_shader(shader_keys: &[String]) {
    let mut load_shader = String::new();
    for s in shader_keys {
        let _ = write!(load_shader, "#include \"{}.h\"\n", shader_file(s));
    }
    load_shader.push_str("ProgramLoader load_shader(const char* name) {\n");
    for s in shader_keys {
        let _ = write!(load_shader, "  if (!strcmp(name, \"{}\")) {{ return {}_program::loader; }}\n",
                       s, shader_file(s));
    }
    load_shader.push_str("  return nullptr;\n}\n");
    std::fs::write(std::env::var("OUT_DIR").unwrap() + "/load_shader.h", load_shader).unwrap();
}

fn process_imports(shader_dir: &str, shader: &str, included: &mut HashSet<String>, output: &mut String) {
    if !included.insert(shader.into()) {
        return;
    }
    println!("cargo:rerun-if-changed={}/{}.glsl", shader_dir, shader);
    let source = std::fs::read_to_string(format!("{}/{}.glsl", shader_dir, shader)).unwrap();
    for line in source.lines() {
        if line.starts_with("#include ") {
            let imports = line["#include ".len() ..].split(',');
            for import in imports {
                process_imports(shader_dir, import, included, output);
            }
        } else if line.starts_with("#version ") || line.starts_with("#extension ") {
            // ignore
        } else {
            output.push_str(line);
            output.push('\n');
        }
    }
}

fn translate_shader(shader_key: &str, shader_dir: &str) {
    let mut imported = String::from("#define SWGL 1\n#define __VERSION__ 150\n");
    let _ = write!(imported, "#define WR_MAX_VERTEX_TEXTURE_WIDTH {}U\n",
                   webrender_build::MAX_VERTEX_TEXTURE_WIDTH);

    let (basename, features) =
        shader_key.split_at(shader_key.find(' ').unwrap_or(shader_key.len()));
    if !features.is_empty() {
        for feature in features.trim().split(',') {
            let _ = write!(imported, "#define WR_FEATURE_{}\n", feature);
        }
    }

    process_imports(shader_dir, basename, &mut HashSet::new(), &mut imported);

    let shader = shader_file(shader_key);

    let out_dir = std::env::var("OUT_DIR").unwrap();
    let imp_name = format!("{}/{}.c", out_dir, shader);
    std::fs::write(&imp_name, imported).unwrap();

    let mut build = cc::Build::new();
    build.no_default_flags(true);
    if build.get_compiler().is_like_msvc() {
        build.flag("/EP").flag("/clang:-undef");
    } else {
        build.flag("-xc").flag("-P").flag("-undef");
    }
    build.file(&imp_name);
    let vs = build.clone()
        .define("WR_VERTEX_SHADER", Some("1"))
        .expand();
    let fs = build.clone()
        .define("WR_FRAGMENT_SHADER", Some("1"))
        .expand();
    let vs_name = format!("{}/{}.vert", out_dir, shader);
    let fs_name = format!("{}/{}.frag", out_dir, shader);
    std::fs::write(&vs_name, vs).unwrap();
    std::fs::write(&fs_name, fs).unwrap();

    let args = vec![
        "glsl_to_cxx".to_string(),
        vs_name,
        fs_name,
    ];
    let result = glsl_to_cxx::translate(&mut args.into_iter());
    std::fs::write(format!("{}/{}.h", out_dir, shader), result).unwrap();
}

fn main() {
    let shader_dir = match std::env::var("MOZ_SRC") {
        Ok(dir) => dir + "/gfx/wr/webrender/res",
        Err(_) => std::env::var("CARGO_MANIFEST_DIR").unwrap() + "/../webrender/res",
    };

    let shader_flags =
        ShaderFeatureFlags::GL |
        ShaderFeatureFlags::DUAL_SOURCE_BLENDING |
        ShaderFeatureFlags::ADVANCED_BLEND_EQUATION |
        ShaderFeatureFlags::DEBUG;
    let mut shaders: Vec<String> = Vec::new();
    for (name, features) in get_shader_features(shader_flags) {
        shaders.extend(features.iter().map(|f| {
            if f.is_empty() { name.to_owned() } else { format!("{} {}", name, f) }
        }));
    }

    shaders.sort();

    for shader in &shaders {
        translate_shader(shader, &shader_dir);
    }

    write_load_shader(&shaders);

    println!("cargo:rerun-if-changed=src/blend.h");
    println!("cargo:rerun-if-changed=src/composite.h");
    println!("cargo:rerun-if-changed=src/gl_defs.h");
    println!("cargo:rerun-if-changed=src/glsl.h");
    println!("cargo:rerun-if-changed=src/program.h");
    println!("cargo:rerun-if-changed=src/rasterize.h");
    println!("cargo:rerun-if-changed=src/swgl_ext.h");
    println!("cargo:rerun-if-changed=src/texture.h");
    println!("cargo:rerun-if-changed=src/vector_type.h");
    println!("cargo:rerun-if-changed=src/gl.cc");
    let mut build = cc::Build::new();
    build.cpp(true);

    // SWGL relies heavily on inlining for performance so override -Oz with -O2
    if build.get_compiler().args().contains(&std::ffi::OsString::from("-Oz")) {
        build.flag("-O2");
    }

    build.file("src/gl.cc")
        .flag("-std=c++17")
        .flag("-UMOZILLA_CONFIG_H")
        .flag("-fno-exceptions")
        .flag("-fno-rtti")
        .flag("-fno-math-errno")
        .define("_GLIBCXX_USE_CXX11_ABI", Some("0"))
        .include(shader_dir)
        .include("src")
        .include(std::env::var("OUT_DIR").unwrap())
        .compile("gl_cc");
}
<commit_msg>Bug 1698156 - Set up SWGLPP env target with null CFLAGS and CXXFLAGS. r=jrmuizel<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

extern crate cc;
extern crate glsl_to_cxx;
extern crate webrender_build;

use std::collections::HashSet;
use std::fmt::Write;
use webrender_build::shader::{ShaderFeatureFlags, get_shader_features};

// Shader key is in "name feature,feature" format.
// File name needs to be formatted as "name_feature_feature".
fn shader_file(shader_key: &str) -> String {
    shader_key.replace(' ', "_").replace(',', "_")
}

fn write_load_shader(shader_keys: &[String]) {
    let mut load_shader = String::new();
    for s in shader_keys {
        let _ = write!(load_shader, "#include \"{}.h\"\n", shader_file(s));
    }
    load_shader.push_str("ProgramLoader load_shader(const char* name) {\n");
    for s in shader_keys {
        let _ = write!(load_shader, "  if (!strcmp(name, \"{}\")) {{ return {}_program::loader; }}\n",
                       s, shader_file(s));
    }
    load_shader.push_str("  return nullptr;\n}\n");
    std::fs::write(std::env::var("OUT_DIR").unwrap() + "/load_shader.h", load_shader).unwrap();
}

fn process_imports(shader_dir: &str, shader: &str, included: &mut HashSet<String>, output: &mut String) {
    if !included.insert(shader.into()) {
        return;
    }
    println!("cargo:rerun-if-changed={}/{}.glsl", shader_dir, shader);
    let source = std::fs::read_to_string(format!("{}/{}.glsl", shader_dir, shader)).unwrap();
    for line in source.lines() {
        if line.starts_with("#include ") {
            let imports = line["#include ".len() ..].split(',');
            for import in imports {
                process_imports(shader_dir, import, included, output);
            }
        } else if line.starts_with("#version ") || line.starts_with("#extension ") {
            // ignore
        } else {
            output.push_str(line);
            output.push('\n');
        }
    }
}

fn translate_shader(shader_key: &str, shader_dir: &str) {
    let mut imported = String::from("#define SWGL 1\n#define __VERSION__ 150\n");
    let _ = write!(imported, "#define WR_MAX_VERTEX_TEXTURE_WIDTH {}U\n",
                   webrender_build::MAX_VERTEX_TEXTURE_WIDTH);

    let (basename, features) =
        shader_key.split_at(shader_key.find(' ').unwrap_or(shader_key.len()));
    if !features.is_empty() {
        for feature in features.trim().split(',') {
            let _ = write!(imported, "#define WR_FEATURE_{}\n", feature);
        }
    }

    process_imports(shader_dir, basename, &mut HashSet::new(), &mut imported);

    let shader = shader_file(shader_key);

    let out_dir = std::env::var("OUT_DIR").unwrap();
    let imp_name = format!("{}/{}.c", out_dir, shader);
    std::fs::write(&imp_name, imported).unwrap();

    let mut build = cc::Build::new();
    // Use SWGLPP target to avoid pulling CFLAGS/CXXFLAGS.
    if let Ok(tool) = build.try_get_compiler() {
        // Force the compiler of the existing target before overriding it.
        build.compiler(tool.path());
    }
    build.target("SWGLPP");
    build.no_default_flags(true);
    if build.get_compiler().is_like_msvc() {
        build.flag("/EP").flag("/clang:-undef");
    } else {
        build.flag("-xc").flag("-P").flag("-undef");
    }
    build.file(&imp_name);
    let vs = build.clone()
        .define("WR_VERTEX_SHADER", Some("1"))
        .expand();
    let fs = build.clone()
        .define("WR_FRAGMENT_SHADER", Some("1"))
        .expand();
    let vs_name = format!("{}/{}.vert", out_dir, shader);
    let fs_name = format!("{}/{}.frag", out_dir, shader);
    std::fs::write(&vs_name, vs).unwrap();
    std::fs::write(&fs_name, fs).unwrap();

    let args = vec![
        "glsl_to_cxx".to_string(),
        vs_name,
        fs_name,
    ];
    let result = glsl_to_cxx::translate(&mut args.into_iter());
    std::fs::write(format!("{}/{}.h", out_dir, shader), result).unwrap();
}

fn main() {
    let shader_dir = match std::env::var("MOZ_SRC") {
        Ok(dir) => dir + "/gfx/wr/webrender/res",
        Err(_) => std::env::var("CARGO_MANIFEST_DIR").unwrap() + "/../webrender/res",
    };

    let shader_flags =
        ShaderFeatureFlags::GL |
        ShaderFeatureFlags::DUAL_SOURCE_BLENDING |
        ShaderFeatureFlags::ADVANCED_BLEND_EQUATION |
        ShaderFeatureFlags::DEBUG;
    let mut shaders: Vec<String> = Vec::new();
    for (name, features) in get_shader_features(shader_flags) {
        shaders.extend(features.iter().map(|f| {
            if f.is_empty() { name.to_owned() } else { format!("{} {}", name, f) }
        }));
    }

    shaders.sort();

    // We need to ensure that the C preprocessor does not pull compiler flags from
    // the host or target environment. Set up a SWGLPP target with empty flags to
    // work around this.
    std::env::set_var("CFLAGS_SWGLPP", "");
    std::env::set_var("CXXFLAGS_SWGLPP", "");

    for shader in &shaders {
        translate_shader(shader, &shader_dir);
    }

    write_load_shader(&shaders);

    println!("cargo:rerun-if-changed=src/blend.h");
    println!("cargo:rerun-if-changed=src/composite.h");
    println!("cargo:rerun-if-changed=src/gl_defs.h");
    println!("cargo:rerun-if-changed=src/glsl.h");
    println!("cargo:rerun-if-changed=src/program.h");
    println!("cargo:rerun-if-changed=src/rasterize.h");
    println!("cargo:rerun-if-changed=src/swgl_ext.h");
    println!("cargo:rerun-if-changed=src/texture.h");
    println!("cargo:rerun-if-changed=src/vector_type.h");
    println!("cargo:rerun-if-changed=src/gl.cc");
    let mut build = cc::Build::new();
    build.cpp(true);

    // SWGL relies heavily on inlining for performance so override -Oz with -O2
    if build.get_compiler().args().contains(&std::ffi::OsString::from("-Oz")) {
        build.flag("-O2");
    }

    build.file("src/gl.cc")
        .flag("-std=c++17")
        .flag("-UMOZILLA_CONFIG_H")
        .flag("-fno-exceptions")
        .flag("-fno-rtti")
        .flag("-fno-math-errno")
        .define("_GLIBCXX_USE_CXX11_ABI", Some("0"))
        .include(shader_dir)
        .include("src")
        .include(std::env::var("OUT_DIR").unwrap())
        .compile("gl_cc");
}
<|endoftext|>


<commit_before>use ::quote::Tokens;
use ::syn::Ident;
use ::yaml_rust::Yaml;

use infer::datatype;
use infer::path;
use infer::TokensResult;

type FieldsSupport = (Vec<Tokens>, Vec<Tokens>);

fn infer_ref_obj(schema: &Yaml, required: &Vec<Yaml>) -> FieldsSupport {
    let mut fields: Vec<Tokens> = Vec::new();
    let mut additional_types: Vec<Tokens> = Vec::new();

    for (field, schema) in schema["properties"].as_hash()
        .expect("Properties must be a map.")
    {
        let field_name = field.as_str()
            .expect("Property must be a string.");
        let field_ident = Ident::new(field_name);
        let (ty, maybe_at) = datatype::infer_v3(&schema).unwrap();
        let mandate: Tokens;

        if let Some(true) = schema["required"].as_bool() {
            mandate = quote!(::tapioca::datatype::Required);
        } else if required.contains(field) {
            mandate = quote!(::tapioca::datatype::Required);
        } else {
            mandate = quote!(::tapioca::datatype::Optional);
        }

        fields.push(quote!{ #field_ident: #mandate<#ty> });

        if let Some(additional_type) = maybe_at {
            additional_types.push(additional_type);
        }
    }

    (fields, additional_types)
}

fn infer_ref(ident: &Ident, schema: &Yaml, required: &Vec<Yaml>) -> TokensResult {
    match schema["properties"].as_hash() {
        Some(_) => {
            let (fields, additionals) = infer_ref_obj(&schema, &required);

            Ok(quote! {
                #(#additionals)*

                #[derive(Clone, Debug, Deserialize)]
                pub struct #ident {
                    #(pub #fields),*
                }
            })
        },
        None => {
            let (alias_to, maybe_at) = datatype::infer_v3(&schema)?;
            let additional_type = match maybe_at {
                Some(at) => at,
                None => quote!(),
            };

            Ok(quote! {
                #additional_type

                #[allow(dead_code)]
                pub type #ident = #alias_to;
            })
        },
    }
}

pub(super) fn infer_v3(schema: &Yaml) -> TokensResult {
    let paths = schema["paths"].clone();
    let path_impls: Vec<Tokens> = paths.as_hash()
        .expect("Paths must be a map.")
        .iter()
        .map(|(path, path_schema)| path::infer_v3(
            path.as_str().expect("Path must be a string."), &path_schema
        ).unwrap())
        .collect();

    let api_url = schema["servers"][0]["url"].as_str()
        .expect("Must have at least one server URL.");

    let mut schema_ref_defs: Vec<Tokens> = Vec::new();
    for (schema_ref, schema) in schema["components"]["schemas"].as_hash()
        .expect("#/components/schemas must be a map.")
    {
        let schema_ref_name = schema_ref.as_str()
            .expect("$ref name must be a string.");
        schema_ref_defs.push(infer_ref(
                &Ident::new(schema_ref_name),
                &schema,
                &schema["required"].as_vec().unwrap_or(&Vec::new())
        )?);
    }

    Ok(quote! {
        mod schema_ref {
            use super::schema_ref;

            #(#schema_ref_defs)*
        }

        const API_URL: &'static str = #api_url;

        #(#path_impls)*
    })
}
<commit_msg>Support schemata with no $ref schema section<commit_after>use ::quote::Tokens;
use ::syn::Ident;
use ::yaml_rust::Yaml;

use infer::datatype;
use infer::path;
use infer::TokensResult;

type FieldsSupport = (Vec<Tokens>, Vec<Tokens>);

fn infer_ref_obj(schema: &Yaml, required: &Vec<Yaml>) -> FieldsSupport {
    let mut fields: Vec<Tokens> = Vec::new();
    let mut additional_types: Vec<Tokens> = Vec::new();

    for (field, schema) in schema["properties"].as_hash()
        .expect("Properties must be a map.")
    {
        let field_name = field.as_str()
            .expect("Property must be a string.");
        let field_ident = Ident::new(field_name);
        let (ty, maybe_at) = datatype::infer_v3(&schema).unwrap();
        let mandate: Tokens;

        if let Some(true) = schema["required"].as_bool() {
            mandate = quote!(::tapioca::datatype::Required);
        } else if required.contains(field) {
            mandate = quote!(::tapioca::datatype::Required);
        } else {
            mandate = quote!(::tapioca::datatype::Optional);
        }

        fields.push(quote!{ #field_ident: #mandate<#ty> });

        if let Some(additional_type) = maybe_at {
            additional_types.push(additional_type);
        }
    }

    (fields, additional_types)
}

fn infer_ref(ident: &Ident, schema: &Yaml, required: &Vec<Yaml>) -> TokensResult {
    match schema["properties"].as_hash() {
        Some(_) => {
            let (fields, additionals) = infer_ref_obj(&schema, &required);

            Ok(quote! {
                #(#additionals)*

                #[derive(Clone, Debug, Deserialize)]
                pub struct #ident {
                    #(pub #fields),*
                }
            })
        },
        None => {
            let (alias_to, maybe_at) = datatype::infer_v3(&schema)?;
            let additional_type = match maybe_at {
                Some(at) => at,
                None => quote!(),
            };

            Ok(quote! {
                #additional_type

                #[allow(dead_code)]
                pub type #ident = #alias_to;
            })
        },
    }
}

pub(super) fn infer_v3(schema: &Yaml) -> TokensResult {
    let paths = schema["paths"].clone();
    let path_impls: Vec<Tokens> = paths.as_hash()
        .expect("Paths must be a map.")
        .iter()
        .map(|(path, path_schema)| path::infer_v3(
            path.as_str().expect("Path must be a string."), &path_schema
        ).unwrap())
        .collect();

    let api_url = schema["servers"][0]["url"].as_str()
        .expect("Must have at least one server URL.");

    let mut schema_ref_defs: Vec<Tokens> = Vec::new();
    let schema_refs = &schema["components"]["schemas"];

    if !schema_refs.is_badvalue() {
        for (schema_ref, schema) in schema_refs.as_hash()
            .expect("#/components/schemas must be a map.")
        {
            let schema_ref_name = schema_ref.as_str()
                .expect("$ref name must be a string.");
            schema_ref_defs.push(infer_ref(
                    &Ident::new(schema_ref_name),
                    &schema,
                    &schema["required"].as_vec().unwrap_or(&Vec::new())
            )?);
        }
    }

    Ok(quote! {
        mod schema_ref {
            use super::schema_ref;

            #(#schema_ref_defs)*
        }

        const API_URL: &'static str = #api_url;

        #(#path_impls)*
    })
}
<|endoftext|>


<commit_before>use std::fmt;
use std::str::FromStr;

use super::rfc5322::{Rfc5322Parser, MIME_LINE_LENGTH};
use super::header::{FromHeader, ToFoldedHeader};
use super::results::{ParsingResult,ParsingError};

/// Represents an RFC 5322 Address
#[derive(PartialEq, Eq, Debug)]
#[stable]
pub enum Address {
    /// A "regular" email address
    Mailbox(Mailbox),
    /// A named group of mailboxes
    Group(String, Vec<Mailbox>),
}

impl Address {
    /// Shortcut function to make a new Mailbox with the given address
    #[unstable]
    pub fn new_mailbox(address: String) -> Address {
        Address::Mailbox(Mailbox::new(address))
    }

    /// Shortcut function to make a new Mailbox with the address and given-name
    #[unstable]
    pub fn new_mailbox_with_name(name: String, address: String) -> Address {
        Address::Mailbox(Mailbox::new_with_name(name, address))
    }

    /// Shortcut function to make a new Group with a collection of mailboxes
    #[unstable]
    pub fn new_group(name: String, mailboxes: Vec<Mailbox>) -> Address {
        Address::Group(name, mailboxes)
    }
}

impl fmt::Display for Address {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Address::Mailbox(ref mbox) => mbox.fmt(fmt),
            Address::Group(ref name, ref mboxes) => {
                let mut mailbox_list = String::new();
                for mbox in mboxes.iter() {
                    if mailbox_list.len() > 0 {
                        // Insert the separator if there's already things in this list
                        mailbox_list.push_str(", ");
                    }
                    mailbox_list.push_str(mbox.to_string().as_slice());
                }
                write!(fmt, "{}: {};", name, mailbox_list)
            }
        }
    }
}

/// Represents an RFC 5322 mailbox
#[derive(PartialEq, Eq, Debug)]
#[stable]
pub struct Mailbox {
    /// The given name for this address
    pub name: Option<String>,
    /// The mailbox address
    pub address: String,
}

impl Mailbox {
    /// Create a new Mailbox without a display name
    #[stable]
    pub fn new(address: String) -> Mailbox {
        Mailbox {
            name: None,
            address: address
        }
    }

    /// Create a new Mailbox with a display name
    #[stable]
    pub fn new_with_name(name: String, address: String) -> Mailbox {
        Mailbox {
            name: Some(name),
            address: address
        }
    }
}

impl fmt::Display for Mailbox {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        match self.name {
            Some(ref name) => write!(fmt, "\"{}\" <{}>", name, self.address),
            None => write!(fmt, "<{}>", self.address),
        }
    }
}

impl FromStr for Mailbox {
    type Err = ParsingError;

    fn from_str(s: &str) -> ParsingResult<Mailbox> {
        AddressParser::new(s).parse_mailbox()
    }
}

impl FromHeader for Vec<Address> {
    fn from_header(value: String) -> ParsingResult<Vec<Address>> {
        AddressParser::new(value.as_slice()).parse_address_list()
    }
}

impl ToFoldedHeader for Vec<Address> {
    fn to_folded_header(start_pos: usize, value: Vec<Address>) -> ParsingResult<String> {
        let mut header = String::new();

        let mut line_len = start_pos;

        for addr in value.iter() {
            let addr_str = format!("{}, ", addr);

            if line_len + addr_str.len() > MIME_LINE_LENGTH {
                // Adding this would cause a wrap, so wrap before!
                header.push_str("\r\n\t");
                line_len = 0;
            }
            line_len += addr_str.len();
            header.push_str(addr_str.as_slice());
        }

        // Clear up the final ", "
        let real_len = header.len() - 2;
        header.truncate(real_len);

        Ok(header)
    }
}

#[stable]
pub struct AddressParser<'s> {
    p: Rfc5322Parser<'s>,
}

impl<'s> AddressParser<'s> {
    #[stable]
    pub fn new(s: &str) -> AddressParser {
        AddressParser {
            p: Rfc5322Parser::new(s)
        }
    }

    #[stable]
    pub fn parse_address_list(&mut self) -> ParsingResult<Vec<Address>> {
        let mut result = Vec::new();
        let mut expected_separator: char;

        while !self.p.eof() {
            self.p.push_position();

            match self.parse_group() {
                Ok(x) => {
                    // Is a group
                    result.push(x);
                    expected_separator = ';';
                },
                Err(e) => {
                    // If we failed to parse as group, try again as mailbox
                    self.p.pop_position();
                    result.push(Address::Mailbox(match self.parse_mailbox() {
                        Ok(x) => x,
                        Err(e2) => return Err(ParsingError::new(
                            format!("Failed to parse as group: {}\n\
                                     Failed to parse as mailbox: {}", e, e2)
                        ))
                    }));
                    expected_separator = ',';
                }
            };

            self.p.consume_linear_whitespace();
            if !self.p.eof() && self.p.peek() == expected_separator {
                // Clear the separator
                self.p.consume_char();
            }
        }

        Ok(result)
    }

    #[stable]
    pub fn parse_group(&mut self) -> ParsingResult<Address> {
        let name = match self.p.consume_phrase(false) {
            Some(x) => x,
            None => return Err(ParsingError::new(format!("Couldn't find group name: {}", self.p.peek_to_end())))
        };

        if !self.p.eof() && self.p.peek() != ':' {
            return Err(ParsingError::new(format!("Expected ':', found {}.",
                                                 self.p.peek())));
        };

        self.p.consume_char();
        let mut mailboxes = Vec::new();

        while !self.p.eof() && self.p.peek() != ';' {
            mailboxes.push(try!(self.parse_mailbox()));

            if !self.p.eof() && self.p.peek() == ',' {
                self.p.consume_char();
            }
        };

        Ok(Address::Group(name, mailboxes))
    }

    #[stable]
    pub fn parse_mailbox(&mut self) -> ParsingResult<Mailbox> {
        // Push the current position of the parser so we can back out later
        self.p.push_position();
        match self.parse_name_addr() {
            Ok(result) => Ok(result),
            Err(_) => {
                // Revert back to our original position to try to parse an addr-spec
                self.p.pop_position();
                Ok(Mailbox::new(try!(self.parse_addr_spec())))
            }
        }
    }

    fn parse_name_addr(&mut self) -> ParsingResult<Mailbox> {
        // Find display-name
        let display_name = self.p.consume_phrase(false);
        self.p.consume_linear_whitespace();
        if self.p.eof() {
            return Err(ParsingError::new("Reached EOF while parsing address header.".to_string()));
        };

        // Find angle-addr
        if self.p.peek() != '<' {
            return Err(ParsingError::new("Could only find display name while parsing address header.".to_string()));
        } else {
            self.p.consume_char();
        };

        let addr = try!(self.parse_addr_spec());
        if self.p.consume_char() != '>' {
            // Fail because we should have a closing RANGLE here (to match the opening one)
            Err(ParsingError::new("Missing '>' at end while parsing address header.".to_string()))
        } else {
            Ok(match display_name {
                Some(name) => Mailbox::new_with_name(name, addr),
                None => Mailbox::new(addr)
            })
        }
    }

    fn parse_addr_spec(&mut self) -> ParsingResult<String> {
        // local-part is a phrase, but allows dots in atoms
        let local_part = match self.p.consume_phrase(true) {
            Some(x) => x,
            None => return Err(ParsingError::new(format!("Couldn't find local part while parsing address.")))
        };

        if self.p.eof() {
            return Err(ParsingError::new("Reached EOF while parsing address.".to_string()));
        };

        let at_char = self.p.consume_char();
        if at_char != '@' {
            return Err(ParsingError::new(format!("Expected @, found {} instead.", at_char)));
        };

        let domain = try!(self.parse_domain());
        Ok(format!("{}@{}", local_part, domain))
    }

    fn parse_domain(&mut self) -> ParsingResult<String> {
        // TODO: support domain-literal
        match self.p.consume_atom(true) {
            Some(x) => Ok(x),
            None => Err(ParsingError::new("Failed to parse domain.".to_string()))
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    use super::super::header::Header;

    #[test]
    fn test_address_to_string() {
        let addr = Mailbox::new("foo@example.org".to_string());
        assert_eq!(addr.to_string(), "<foo@example.org>".to_string());

        let name_addr = Mailbox::new_with_name("Joe Blogs".to_string(), "foo@example.org".to_string());
        assert_eq!(name_addr.to_string(), "\"Joe Blogs\" <foo@example.org>".to_string());
    }

    #[test]
    fn test_address_from_string() {
        let addr = "\"Joe Blogs\" <joe@example.org>".parse::<Mailbox>().unwrap();
        assert_eq!(addr.name.unwrap(), "Joe Blogs".to_string());
        assert_eq!(addr.address, "joe@example.org".to_string());

        assert!("Not an address".parse::<Mailbox>().is_err());
    }

    #[test]
    fn test_address_parsing() {
        let mut parser = AddressParser::new("\"Joe Blogs\" <joe@example.org>");
        let mut addr = parser.parse_mailbox().unwrap();
        assert_eq!(addr.name.unwrap(), "Joe Blogs".to_string());
        assert_eq!(addr.address, "joe@example.org".to_string());

        parser = AddressParser::new("joe@example.org");
        addr = parser.parse_mailbox().unwrap();
        assert_eq!(addr.name, None);
        assert_eq!(addr.address, "joe@example.org".to_string());
    }

    #[test]
    fn test_address_group_to_string() {
        let addr = Address::new_group("undisclosed recipients".to_string(), vec![]);
        assert_eq!(addr.to_string(), "undisclosed recipients: ;".to_string());

        let addr = Address::new_group("group test".to_string(), vec![
            Mailbox::new("joe@example.org".to_string()),
            Mailbox::new_with_name("John Doe".to_string(), "john@example.org".to_string()),
        ]);
        assert_eq!(addr.to_string(), "group test: <joe@example.org>, \"John Doe\" <john@example.org>;".to_string());
    }

    #[test]
    fn test_address_group_parsing() {
        let mut parser = AddressParser::new("A Group:\"Joe Blogs\" <joe@example.org>,john@example.org;");
        let addr = parser.parse_group().unwrap();
        match addr {
            Address::Group(name, mboxes) => {
                assert_eq!(name, "A Group".to_string());
                assert_eq!(mboxes, vec![
                    Mailbox::new_with_name("Joe Blogs".to_string(), "joe@example.org".to_string()),
                    Mailbox::new("john@example.org".to_string()),
                ]);
            },
            _ => assert!(false),
        }
    }

    #[test]
    fn test_address_list_parsing() {
        let mut parser = AddressParser::new("\"Joe Blogs\" <joe@example.org>, \"John Doe\" <john@example.org>");
        assert_eq!(parser.parse_address_list().unwrap(), vec![
            Address::new_mailbox_with_name("Joe Blogs".to_string(), "joe@example.org".to_string()),
            Address::new_mailbox_with_name("John Doe".to_string(), "john@example.org".to_string()),
        ]);
    }

    #[test]
    fn test_address_list_parsing_groups() {
        let mut parser = AddressParser::new("A Group:\"Joe Blogs\" <joe@example.org>, \"John Doe\" <john@example.org>; <third@example.org>, <fourth@example.org>");
        assert_eq!(parser.parse_address_list().unwrap(), vec![
            Address::new_group("A Group".to_string(), vec![
                    Mailbox::new_with_name("Joe Blogs".to_string(), "joe@example.org".to_string()),
                    Mailbox::new_with_name("John Doe".to_string(), "john@example.org".to_string()),
            ]),
            Address::new_mailbox("third@example.org".to_string()),
            Address::new_mailbox("fourth@example.org".to_string()),
        ]);
    }

    #[test]
    fn test_from_header_parsing() {
        let header = Header::new(
            "From:".to_string(),
            "\"Joe Blogs\" <joe@example.org>, \"John Doe\" <john@example.org>".to_string());
        let addresses: Vec<Address> = header.get_value().unwrap();
        assert_eq!(addresses, vec![
            Address::new_mailbox_with_name("Joe Blogs".to_string(), "joe@example.org".to_string()),
            Address::new_mailbox_with_name("John Doe".to_string(), "john@example.org".to_string()),
        ]);
    }

    #[test]
    fn test_to_header_generation() {
        let addresses = vec![
            Address::new_mailbox_with_name("Joe Blogs".to_string(), "joe@example.org".to_string()),
            Address::new_mailbox_with_name("John Doe".to_string(), "john@example.org".to_string()),
        ];

        let header = Header::new_with_value("From:".to_string(), addresses).unwrap();
        assert_eq!(header.get_value::<String>().unwrap(),
                   "\"Joe Blogs\" <joe@example.org>, \"John Doe\" <john@example.org>".to_string());
    }

    #[test]
    fn test_to_header_line_wrap() {
        let addresses = vec![
            Address::new_mailbox_with_name("Joe Blogs".to_string(), "joe@example.org".to_string()),
            Address::new_mailbox_with_name("John Doe".to_string(), "john@example.org".to_string()),
            Address::new_mailbox_with_name("Mr Black".to_string(), "mafia_black@example.org".to_string()),
        ];

        let header = Header::new_with_value("To".to_string(), addresses).unwrap();
        assert_eq!(header.to_string().as_slice(),
                   "To: \"Joe Blogs\" <joe@example.org>, \"John Doe\" <john@example.org>, \r\n\
                   \t\"Mr Black\" <mafia_black@example.org>");
    }
}
<commit_msg>Use assert_char and assert_not_eof<commit_after>use std::fmt;
use std::str::FromStr;

use super::rfc5322::{Rfc5322Parser, MIME_LINE_LENGTH};
use super::header::{FromHeader, ToFoldedHeader};
use super::results::{ParsingResult,ParsingError};

/// Represents an RFC 5322 Address
#[derive(PartialEq, Eq, Debug)]
#[stable]
pub enum Address {
    /// A "regular" email address
    Mailbox(Mailbox),
    /// A named group of mailboxes
    Group(String, Vec<Mailbox>),
}

impl Address {
    /// Shortcut function to make a new Mailbox with the given address
    #[unstable]
    pub fn new_mailbox(address: String) -> Address {
        Address::Mailbox(Mailbox::new(address))
    }

    /// Shortcut function to make a new Mailbox with the address and given-name
    #[unstable]
    pub fn new_mailbox_with_name(name: String, address: String) -> Address {
        Address::Mailbox(Mailbox::new_with_name(name, address))
    }

    /// Shortcut function to make a new Group with a collection of mailboxes
    #[unstable]
    pub fn new_group(name: String, mailboxes: Vec<Mailbox>) -> Address {
        Address::Group(name, mailboxes)
    }
}

impl fmt::Display for Address {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Address::Mailbox(ref mbox) => mbox.fmt(fmt),
            Address::Group(ref name, ref mboxes) => {
                let mut mailbox_list = String::new();
                for mbox in mboxes.iter() {
                    if mailbox_list.len() > 0 {
                        // Insert the separator if there's already things in this list
                        mailbox_list.push_str(", ");
                    }
                    mailbox_list.push_str(mbox.to_string().as_slice());
                }
                write!(fmt, "{}: {};", name, mailbox_list)
            }
        }
    }
}

/// Represents an RFC 5322 mailbox
#[derive(PartialEq, Eq, Debug)]
#[stable]
pub struct Mailbox {
    /// The given name for this address
    pub name: Option<String>,
    /// The mailbox address
    pub address: String,
}

impl Mailbox {
    /// Create a new Mailbox without a display name
    #[stable]
    pub fn new(address: String) -> Mailbox {
        Mailbox {
            name: None,
            address: address
        }
    }

    /// Create a new Mailbox with a display name
    #[stable]
    pub fn new_with_name(name: String, address: String) -> Mailbox {
        Mailbox {
            name: Some(name),
            address: address
        }
    }
}

impl fmt::Display for Mailbox {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        match self.name {
            Some(ref name) => write!(fmt, "\"{}\" <{}>", name, self.address),
            None => write!(fmt, "<{}>", self.address),
        }
    }
}

impl FromStr for Mailbox {
    type Err = ParsingError;

    fn from_str(s: &str) -> ParsingResult<Mailbox> {
        AddressParser::new(s).parse_mailbox()
    }
}

impl FromHeader for Vec<Address> {
    fn from_header(value: String) -> ParsingResult<Vec<Address>> {
        AddressParser::new(value.as_slice()).parse_address_list()
    }
}

impl ToFoldedHeader for Vec<Address> {
    fn to_folded_header(start_pos: usize, value: Vec<Address>) -> ParsingResult<String> {
        let mut header = String::new();

        let mut line_len = start_pos;

        for addr in value.iter() {
            let addr_str = format!("{}, ", addr);

            if line_len + addr_str.len() > MIME_LINE_LENGTH {
                // Adding this would cause a wrap, so wrap before!
                header.push_str("\r\n\t");
                line_len = 0;
            }
            line_len += addr_str.len();
            header.push_str(addr_str.as_slice());
        }

        // Clear up the final ", "
        let real_len = header.len() - 2;
        header.truncate(real_len);

        Ok(header)
    }
}

#[stable]
pub struct AddressParser<'s> {
    p: Rfc5322Parser<'s>,
}

impl<'s> AddressParser<'s> {
    #[stable]
    pub fn new(s: &str) -> AddressParser {
        AddressParser {
            p: Rfc5322Parser::new(s)
        }
    }

    #[stable]
    pub fn parse_address_list(&mut self) -> ParsingResult<Vec<Address>> {
        let mut result = Vec::new();
        let mut expected_separator: char;

        while !self.p.eof() {
            self.p.push_position();

            match self.parse_group() {
                Ok(x) => {
                    // Is a group
                    result.push(x);
                    expected_separator = ';';
                },
                Err(e) => {
                    // If we failed to parse as group, try again as mailbox
                    self.p.pop_position();
                    result.push(Address::Mailbox(match self.parse_mailbox() {
                        Ok(x) => x,
                        Err(e2) => return Err(ParsingError::new(
                            format!("Failed to parse as group: {}\n\
                                     Failed to parse as mailbox: {}", e, e2)
                        ))
                    }));
                    expected_separator = ',';
                }
            };

            self.p.consume_linear_whitespace();
            if !self.p.eof() && self.p.peek() == expected_separator {
                // Clear the separator
                self.p.consume_char();
            }
        }

        Ok(result)
    }

    #[stable]
    pub fn parse_group(&mut self) -> ParsingResult<Address> {
        let name = match self.p.consume_phrase(false) {
            Some(x) => x,
            None => return Err(ParsingError::new(format!("Couldn't find group name: {}", self.p.peek_to_end())))
        };

        try!(self.p.assert_char(':'));
        self.p.consume_char();

        let mut mailboxes = Vec::new();

        while !self.p.eof() && self.p.peek() != ';' {
            mailboxes.push(try!(self.parse_mailbox()));

            if !self.p.eof() && self.p.peek() == ',' {
                self.p.consume_char();
            }
        };

        Ok(Address::Group(name, mailboxes))
    }

    #[stable]
    pub fn parse_mailbox(&mut self) -> ParsingResult<Mailbox> {
        // Push the current position of the parser so we can back out later
        self.p.push_position();
        match self.parse_name_addr() {
            Ok(result) => Ok(result),
            Err(_) => {
                // Revert back to our original position to try to parse an addr-spec
                self.p.pop_position();
                Ok(Mailbox::new(try!(self.parse_addr_spec())))
            }
        }
    }

    fn parse_name_addr(&mut self) -> ParsingResult<Mailbox> {
        // Find display-name
        let display_name = self.p.consume_phrase(false);
        self.p.consume_linear_whitespace();

        try!(self.p.assert_char('<'));
        self.p.consume_char();

        let addr = try!(self.parse_addr_spec());
        if self.p.consume_char() != '>' {
            // Fail because we should have a closing RANGLE here (to match the opening one)
            Err(ParsingError::new("Missing '>' at end while parsing address header.".to_string()))
        } else {
            Ok(match display_name {
                Some(name) => Mailbox::new_with_name(name, addr),
                None => Mailbox::new(addr)
            })
        }
    }

    fn parse_addr_spec(&mut self) -> ParsingResult<String> {
        // local-part is a phrase, but allows dots in atoms
        let local_part = match self.p.consume_phrase(true) {
            Some(x) => x,
            None => return Err(ParsingError::new(format!("Couldn't find local part while parsing address.")))
        };

        try!(self.p.assert_char('@'));

        let domain = try!(self.parse_domain());
        Ok(format!("{}@{}", local_part, domain))
    }

    fn parse_domain(&mut self) -> ParsingResult<String> {
        // TODO: support domain-literal
        match self.p.consume_atom(true) {
            Some(x) => Ok(x),
            None => Err(ParsingError::new("Failed to parse domain.".to_string()))
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    use super::super::header::Header;

    #[test]
    fn test_address_to_string() {
        let addr = Mailbox::new("foo@example.org".to_string());
        assert_eq!(addr.to_string(), "<foo@example.org>".to_string());

        let name_addr = Mailbox::new_with_name("Joe Blogs".to_string(), "foo@example.org".to_string());
        assert_eq!(name_addr.to_string(), "\"Joe Blogs\" <foo@example.org>".to_string());
    }

    #[test]
    fn test_address_from_string() {
        let addr = "\"Joe Blogs\" <joe@example.org>".parse::<Mailbox>().unwrap();
        assert_eq!(addr.name.unwrap(), "Joe Blogs".to_string());
        assert_eq!(addr.address, "joe@example.org".to_string());

        assert!("Not an address".parse::<Mailbox>().is_err());
    }

    #[test]
    fn test_address_parsing() {
        let mut parser = AddressParser::new("\"Joe Blogs\" <joe@example.org>");
        let mut addr = parser.parse_mailbox().unwrap();
        assert_eq!(addr.name.unwrap(), "Joe Blogs".to_string());
        assert_eq!(addr.address, "joe@example.org".to_string());

        parser = AddressParser::new("joe@example.org");
        addr = parser.parse_mailbox().unwrap();
        assert_eq!(addr.name, None);
        assert_eq!(addr.address, "joe@example.org".to_string());
    }

    #[test]
    fn test_address_group_to_string() {
        let addr = Address::new_group("undisclosed recipients".to_string(), vec![]);
        assert_eq!(addr.to_string(), "undisclosed recipients: ;".to_string());

        let addr = Address::new_group("group test".to_string(), vec![
            Mailbox::new("joe@example.org".to_string()),
            Mailbox::new_with_name("John Doe".to_string(), "john@example.org".to_string()),
        ]);
        assert_eq!(addr.to_string(), "group test: <joe@example.org>, \"John Doe\" <john@example.org>;".to_string());
    }

    #[test]
    fn test_address_group_parsing() {
        let mut parser = AddressParser::new("A Group:\"Joe Blogs\" <joe@example.org>,john@example.org;");
        let addr = parser.parse_group().unwrap();
        match addr {
            Address::Group(name, mboxes) => {
                assert_eq!(name, "A Group".to_string());
                assert_eq!(mboxes, vec![
                    Mailbox::new_with_name("Joe Blogs".to_string(), "joe@example.org".to_string()),
                    Mailbox::new("john@example.org".to_string()),
                ]);
            },
            _ => assert!(false),
        }
    }

    #[test]
    fn test_address_list_parsing() {
        let mut parser = AddressParser::new("\"Joe Blogs\" <joe@example.org>, \"John Doe\" <john@example.org>");
        assert_eq!(parser.parse_address_list().unwrap(), vec![
            Address::new_mailbox_with_name("Joe Blogs".to_string(), "joe@example.org".to_string()),
            Address::new_mailbox_with_name("John Doe".to_string(), "john@example.org".to_string()),
        ]);
    }

    #[test]
    fn test_address_list_parsing_groups() {
        let mut parser = AddressParser::new("A Group:\"Joe Blogs\" <joe@example.org>, \"John Doe\" <john@example.org>; <third@example.org>, <fourth@example.org>");
        assert_eq!(parser.parse_address_list().unwrap(), vec![
            Address::new_group("A Group".to_string(), vec![
                    Mailbox::new_with_name("Joe Blogs".to_string(), "joe@example.org".to_string()),
                    Mailbox::new_with_name("John Doe".to_string(), "john@example.org".to_string()),
            ]),
            Address::new_mailbox("third@example.org".to_string()),
            Address::new_mailbox("fourth@example.org".to_string()),
        ]);
    }

    #[test]
    fn test_from_header_parsing() {
        let header = Header::new(
            "From:".to_string(),
            "\"Joe Blogs\" <joe@example.org>, \"John Doe\" <john@example.org>".to_string());
        let addresses: Vec<Address> = header.get_value().unwrap();
        assert_eq!(addresses, vec![
            Address::new_mailbox_with_name("Joe Blogs".to_string(), "joe@example.org".to_string()),
            Address::new_mailbox_with_name("John Doe".to_string(), "john@example.org".to_string()),
        ]);
    }

    #[test]
    fn test_to_header_generation() {
        let addresses = vec![
            Address::new_mailbox_with_name("Joe Blogs".to_string(), "joe@example.org".to_string()),
            Address::new_mailbox_with_name("John Doe".to_string(), "john@example.org".to_string()),
        ];

        let header = Header::new_with_value("From:".to_string(), addresses).unwrap();
        assert_eq!(header.get_value::<String>().unwrap(),
                   "\"Joe Blogs\" <joe@example.org>, \"John Doe\" <john@example.org>".to_string());
    }

    #[test]
    fn test_to_header_line_wrap() {
        let addresses = vec![
            Address::new_mailbox_with_name("Joe Blogs".to_string(), "joe@example.org".to_string()),
            Address::new_mailbox_with_name("John Doe".to_string(), "john@example.org".to_string()),
            Address::new_mailbox_with_name("Mr Black".to_string(), "mafia_black@example.org".to_string()),
        ];

        let header = Header::new_with_value("To".to_string(), addresses).unwrap();
        assert_eq!(header.to_string().as_slice(),
                   "To: \"Joe Blogs\" <joe@example.org>, \"John Doe\" <john@example.org>, \r\n\
                   \t\"Mr Black\" <mafia_black@example.org>");
    }
}
<|endoftext|>


<commit_before>//! A server builder helper for the integration tests.

use std::io::{Read, Write};
use std::net;
use std::time::Duration;
use std::thread;

pub struct Server {
    addr: net::SocketAddr,
}

impl Server {
    pub fn addr(&self) -> net::SocketAddr {
        self.addr
    }
}

#[derive(Default)]
pub struct Txn {
    pub request: Vec<u8>,
    pub response: Vec<u8>,

    pub read_timeout: Option<Duration>,
    pub response_timeout: Option<Duration>,
    pub write_timeout: Option<Duration>,
}

static DEFAULT_USER_AGENT: &'static str =
    concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION"));

pub fn spawn(txns: Vec<Txn>) -> Server {
    let listener = net::TcpListener::bind("127.0.0.1:0").unwrap();
    let addr = listener.local_addr().unwrap();
    thread::spawn(
        move || for txn in txns {
            let mut expected = txn.request;
            let reply = txn.response;
            let (mut socket, _addr) = listener.accept().unwrap();
            replace_expected_vars(&mut expected, addr.to_string().as_ref(), DEFAULT_USER_AGENT.as_ref());

            if let Some(dur) = txn.read_timeout {
                thread::park_timeout(dur);
            }

            let mut buf = [0; 4096];
            let n = socket.read(&mut buf).unwrap();

            match (::std::str::from_utf8(&expected), ::std::str::from_utf8(&buf[..n])) {
                (Ok(expected), Ok(received)) => assert_eq!(expected, received),
                _ => assert_eq!(expected, &buf[..n]),
            }

            if let Some(dur) = txn.response_timeout {
                thread::park_timeout(dur);
            }

            if let Some(dur) = txn.write_timeout {
                let headers_end = ::std::str::from_utf8(&reply).unwrap().find("\r\n\r\n").unwrap() + 4;
                socket.write_all(&reply[..headers_end]).unwrap();
                thread::park_timeout(dur);
                socket.write_all(&reply[headers_end..]).unwrap();
            } else {
                socket.write_all(&reply).unwrap();
            }
        }
    );

    Server {
        addr: addr,
    }
}

fn replace_expected_vars(bytes: &mut Vec<u8>, host: &[u8], ua: &[u8]) {
    // plenty horrible, but these are just tests, and gets the job done
    let mut index = 0;
    loop {
        if index == bytes.len() {
            return;
        }

        for b in (&bytes[index..]).iter() {
            index += 1;
            if *b == b'$' {
                break;
            }
        }

        let has_host = (&bytes[index..]).starts_with(b"HOST");
        if has_host {
            bytes.drain(index - 1..index + 4);
            for (i, b) in host.iter().enumerate() {
                bytes.insert(index - 1 + i, *b);
            }
        } else {
            let has_ua = (&bytes[index..]).starts_with(b"USERAGENT");
            if has_ua {
                bytes.drain(index - 1..index + 9);
                for (i, b) in ua.iter().enumerate() {
                    bytes.insert(index - 1 + i, *b);
                }
            }
        }
    }
}

#[macro_export]
macro_rules! server {
    ($(request: $req:expr, response: $res:expr),*) => ({
        server!($(request: $req, response: $res;)*)
    });
    ($($($f:ident: $v:expr),*);*) => ({
        let txns = vec![
            $(__internal__txn! {
                $($f: $v,)*
            }),*
        ];
        ::support::server::spawn(txns)
    })
}

#[macro_export]
macro_rules! __internal__txn {
    ($($field:ident: $val:expr,)*) => (
        ::support::server::Txn {
            $( $field: __internal__prop!($field: $val), )*
            .. Default::default()
        }
    )
}

#[macro_export]
macro_rules! __internal__prop {
    (request: $val:expr) => (
        From::from(&$val[..])
    );
    (response: $val:expr) => (
        From::from(&$val[..])
    );
    ($field:ident: $val:expr) => (
        From::from($val)
    )
}
<commit_msg>fix flaky redirect test when first read doesn't include full body<commit_after>//! A server builder helper for the integration tests.

use std::io::{Read, Write};
use std::net;
use std::time::Duration;
use std::thread;

pub struct Server {
    addr: net::SocketAddr,
}

impl Server {
    pub fn addr(&self) -> net::SocketAddr {
        self.addr
    }
}

#[derive(Default)]
pub struct Txn {
    pub request: Vec<u8>,
    pub response: Vec<u8>,

    pub read_timeout: Option<Duration>,
    pub response_timeout: Option<Duration>,
    pub write_timeout: Option<Duration>,
}

static DEFAULT_USER_AGENT: &'static str =
    concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION"));

pub fn spawn(txns: Vec<Txn>) -> Server {
    let listener = net::TcpListener::bind("127.0.0.1:0").unwrap();
    let addr = listener.local_addr().unwrap();
    thread::spawn(
        move || for txn in txns {
            let mut expected = txn.request;
            let reply = txn.response;
            let (mut socket, _addr) = listener.accept().unwrap();

            socket.set_read_timeout(Some(Duration::from_secs(5))).unwrap();

            replace_expected_vars(&mut expected, addr.to_string().as_ref(), DEFAULT_USER_AGENT.as_ref());

            if let Some(dur) = txn.read_timeout {
                thread::park_timeout(dur);
            }

            let mut buf = [0; 4096];
            assert!(buf.len() >= expected.len());

            let mut n = 0;
            while n < expected.len() {
                n += socket.read(&mut buf).unwrap();
            }

            match (::std::str::from_utf8(&expected), ::std::str::from_utf8(&buf[..n])) {
                (Ok(expected), Ok(received)) => assert_eq!(expected, received),
                _ => assert_eq!(expected, &buf[..n]),
            }

            if let Some(dur) = txn.response_timeout {
                thread::park_timeout(dur);
            }

            if let Some(dur) = txn.write_timeout {
                let headers_end = ::std::str::from_utf8(&reply).unwrap().find("\r\n\r\n").unwrap() + 4;
                socket.write_all(&reply[..headers_end]).unwrap();
                thread::park_timeout(dur);
                socket.write_all(&reply[headers_end..]).unwrap();
            } else {
                socket.write_all(&reply).unwrap();
            }
        }
    );

    Server {
        addr: addr,
    }
}

fn replace_expected_vars(bytes: &mut Vec<u8>, host: &[u8], ua: &[u8]) {
    // plenty horrible, but these are just tests, and gets the job done
    let mut index = 0;
    loop {
        if index == bytes.len() {
            return;
        }

        for b in (&bytes[index..]).iter() {
            index += 1;
            if *b == b'$' {
                break;
            }
        }

        let has_host = (&bytes[index..]).starts_with(b"HOST");
        if has_host {
            bytes.drain(index - 1..index + 4);
            for (i, b) in host.iter().enumerate() {
                bytes.insert(index - 1 + i, *b);
            }
        } else {
            let has_ua = (&bytes[index..]).starts_with(b"USERAGENT");
            if has_ua {
                bytes.drain(index - 1..index + 9);
                for (i, b) in ua.iter().enumerate() {
                    bytes.insert(index - 1 + i, *b);
                }
            }
        }
    }
}

#[macro_export]
macro_rules! server {
    ($(request: $req:expr, response: $res:expr),*) => ({
        server!($(request: $req, response: $res;)*)
    });
    ($($($f:ident: $v:expr),*);*) => ({
        let txns = vec![
            $(__internal__txn! {
                $($f: $v,)*
            }),*
        ];
        ::support::server::spawn(txns)
    })
}

#[macro_export]
macro_rules! __internal__txn {
    ($($field:ident: $val:expr,)*) => (
        ::support::server::Txn {
            $( $field: __internal__prop!($field: $val), )*
            .. Default::default()
        }
    )
}

#[macro_export]
macro_rules! __internal__prop {
    (request: $val:expr) => (
        From::from(&$val[..])
    );
    (response: $val:expr) => (
        From::from(&$val[..])
    );
    ($field:ident: $val:expr) => (
        From::from($val)
    )
}
<|endoftext|>


<commit_before>#![deny(missing_docs)]

use endianity::{Endianity, EndianBuf};
use lookup::{LookupParser, LookupEntryIter, DebugLookup};
use parser::{parse_address_size, parse_debug_info_offset, parse_unit_length, parse_u16,
             parse_address, Error, Format, DebugInfoOffset, ParseResult};
use std::cmp::Ordering;
use std::marker::PhantomData;
use std::rc::Rc;

#[derive(Debug, PartialEq, Eq)]
pub struct ArangeHeader {
    format: Format,
    length: u64,
    version: u16,
    offset: DebugInfoOffset,
    address_size: u8,
    segment_size: u8,
}

/// A single parsed arange.
#[derive(Debug, Clone, Eq)]
pub struct ArangeEntry {
    segment: u64,
    address: u64,
    length: u64,
    header: Rc<ArangeHeader>,
}

impl ArangeEntry {
    /// Return the segment selector of this arange.
    pub fn segment(&self) -> Option<u64> {
        if self.header.segment_size != 0 {
            Some(self.segment)
        } else {
            None
        }
    }

    /// Return the beginning address of this arange.
    pub fn address(&self) -> u64 {
        self.address
    }

    /// Return the length of this arange.
    pub fn length(&self) -> u64 {
        self.length
    }

    /// Return the offset into the .debug_info section for this arange.
    pub fn debug_info_offset(&self) -> DebugInfoOffset {
        self.header.offset
    }
}

impl PartialEq for ArangeEntry {
    fn eq(&self, other: &ArangeEntry) -> bool {
        // The expected comparison, but verify that header matches if everything else does.
        match (self.segment == other.segment,
               self.address == other.address,
               self.length == other.length) {
            (true, true, true) => {
                debug_assert!(self.header == other.header);
                true
            }
            _ => false,
        }
    }
}

impl PartialOrd for ArangeEntry {
    fn partial_cmp(&self, other: &ArangeEntry) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

impl Ord for ArangeEntry {
    fn cmp(&self, other: &ArangeEntry) -> Ordering {
        // The expected comparison, but ignore header.
        match (self.segment.cmp(&other.segment),
               self.address.cmp(&other.address),
               self.length.cmp(&other.length)) {
            (Ordering::Equal, Ordering::Equal, Ordering::Equal) => Ordering::Equal,
            (Ordering::Less, _, _) |
            (Ordering::Equal, Ordering::Less, _) |
            (Ordering::Equal, Ordering::Equal, Ordering::Less) => Ordering::Less,
            (Ordering::Greater, _, _) |
            (Ordering::Equal, Ordering::Greater, _) |
            (Ordering::Equal, Ordering::Equal, Ordering::Greater) => Ordering::Greater,
        }
    }
}

pub struct ArangeParser<'input, Endian>
    where Endian: 'input + Endianity
{
    // This struct is never instantiated.
    phantom: PhantomData<&'input Endian>,
}

impl<'input, Endian> LookupParser<'input, Endian> for ArangeParser<'input, Endian>
    where Endian: Endianity
{
    type Header = ArangeHeader;
    type Entry = ArangeEntry;

    /// Parse an arange set header. Returns a tuple of the remaining arange sets, the aranges to be
    /// parsed for this set, and the newly created ArangeHeader struct.
    fn parse_header(input: EndianBuf<Endian>)
                    -> ParseResult<(EndianBuf<Endian>, EndianBuf<Endian>, Rc<Self::Header>)> {
        let (rest, (length, format)) = try!(parse_unit_length(input));
        if length as usize > rest.len() {
            return Err(Error::UnexpectedEof);
        }
        let after_set = rest.range_from(length as usize..);
        let rest = rest.range_to(..length as usize);

        let (rest, version) = try!(parse_u16(rest));
        if version != 2 {
            return Err(Error::UnknownVersion);
        }

        let (rest, offset) = try!(parse_debug_info_offset(rest, format));
        let (rest, address_size) = try!(parse_address_size(rest));
        let (rest, segment_size) = try!(parse_address_size(rest));

        // unit_length + version + offset + address_size + segment_size
        let header_length = match format {
            Format::Dwarf32 => 4 + 2 + 4 + 1 + 1,
            Format::Dwarf64 => 12 + 2 + 8 + 1 + 1,
        };

        // The first tuple following the header in each set begins at an offset that is
        // a multiple of the size of a single tuple (that is, the size of a segment selector
        // plus twice the size of an address).
        let tuple_length = (2 * address_size + segment_size) as usize;
        let padding = if header_length % tuple_length == 0 {
            0
        } else {
            tuple_length - header_length % tuple_length
        };
        if padding > rest.len() {
            return Err(Error::UnexpectedEof);
        }
        let rest = rest.range_from(padding..);

        Ok((after_set,
            rest,
            Rc::new(ArangeHeader {
            format: format,
            length: length,
            version: version,
            offset: offset,
            address_size: address_size,
            segment_size: segment_size,
        })))
    }

    /// Parse a single arange. Return `None` for the null arange, `Some` for an actual arange.
    fn parse_entry(input: EndianBuf<'input, Endian>,
                   header: &Rc<Self::Header>)
                   -> ParseResult<(EndianBuf<'input, Endian>, Option<Self::Entry>)> {
        let address_size = header.address_size;
        let segment_size = header.segment_size; // May be zero!

        let (rest, segment) = if segment_size != 0 {
            try!(parse_address(input, segment_size))
        } else {
            (input, 0)
        };
        let (rest, address) = try!(parse_address(rest, address_size));
        let (rest, length) = try!(parse_address(rest, address_size));

        match (segment, address, length) {
            (0, 0, 0) => Ok((EndianBuf::new(&[]), None)),
            _ => {
                Ok((rest,
                    Some(ArangeEntry {
                    segment: segment,
                    address: address,
                    length: length,
                    header: header.clone(),
                })))
            }
        }
    }
}

/// The `DebugAranges` struct represents the DWARF address range information
/// found in the `.debug_aranges` section.
///
/// Provides:
///
/// * `new(input: EndianBuf<'input, Endian>) -> DebugAranges<'input, Endian>`
///
///   Construct a new `DebugAranges` instance from the data in the `.debug_aranges`
///   section.
///
///   It is the caller's responsibility to read the `.debug_aranges` section and
///   present it as a `&[u8]` slice. That means using some ELF loader on
///   Linux, a Mach-O loader on OSX, etc.
///
///   ```
///   use gimli::{DebugAranges, LittleEndian};
///
///   # let buf = [];
///   # let read_debug_aranges_section = || &buf;
///   let debug_aranges = DebugAranges::<LittleEndian>::new(read_debug_aranges_section());
///   ```
///
/// * `items(&self) -> ArangeEntryIter<'input, Endian>`
///
///   Iterate the aranges in the `.debug_aranges` section.
///
///   ```
///   use gimli::{DebugAranges, LittleEndian};
///
///   # let buf = [];
///   # let read_debug_aranges_section = || &buf;
///   let debug_aranges = DebugAranges::<LittleEndian>::new(read_debug_aranges_section());
///
///   let mut iter = debug_aranges.items();
///   while let Some(arange) = iter.next().unwrap() {
///       println!("arange starts at {}, has length {}", arange.address(), arange.length());
///   }
///   ```
pub type DebugAranges<'input, Endian> = DebugLookup<'input, Endian, ArangeParser<'input, Endian>>;

/// An iterator over the aranges from a .debug_aranges section.
///
/// Provides:
///
/// * `next(self: &mut) -> ParseResult<Option<ArangeEntry>>`
///
///   Advance the iterator and return the next arange.
///
///   Returns the newly parsed arange as `Ok(Some(arange))`. Returns `Ok(None)`
///   when iteration is complete and all aranges have already been parsed and
///   yielded. If an error occurs while parsing the next arange, then this error
///   is returned on all subsequent calls as `Err(e)`.
///
///   Can be [used with
///   `FallibleIterator`](./index.html#using-with-fallibleiterator).
pub type ArangeEntryIter<'input, Endian> = LookupEntryIter<'input,
                                                           Endian,
                                                           ArangeParser<'input, Endian>>;

#[cfg(test)]
mod tests {
    use super::*;
    use lookup::LookupParser;
    use endianity::{EndianBuf, LittleEndian};
    use parser::{Format, DebugInfoOffset};
    use std::rc::Rc;

    #[test]
    #[cfg_attr(rustfmt, rustfmt_skip)]
    fn test_parse_header_ok() {
        let buf = [
            // 32-bit length = 32.
            0x20, 0x00, 0x00, 0x00,
            // Version.
            0x02, 0x00,
            // Offset.
            0x01, 0x02, 0x03, 0x04,
            // Address size.
            0x08,
            // Segment size.
            0x04,
            // Length to here = 12, tuple length = 20.
            // Padding to tuple length multiple = 4.
            0x10, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00,

            // Dummy arange tuple data.
            0x20, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00,

            // Dummy next arange.
            0x30, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00,
        ];

        let input = EndianBuf::<LittleEndian>::new(&buf);

        let (rest, tuples, header) = ArangeParser::parse_header(input)
            .expect("should parse header ok");

        assert_eq!(rest, EndianBuf::new(&buf[buf.len() - 16..]));
        assert_eq!(tuples, EndianBuf::new(&buf[buf.len() - 32..buf.len() - 16]));
        assert_eq!(*header,
                   ArangeHeader {
                       format: Format::Dwarf32,
                       length: 0x20,
                       version: 2,
                       offset: DebugInfoOffset(0x04030201),
                       address_size: 8,
                       segment_size: 4,
                   });
    }

    #[test]
    fn test_parse_entry_ok() {
        let header = Rc::new(ArangeHeader {
            format: Format::Dwarf32,
            length: 0,
            version: 2,
            offset: DebugInfoOffset(0),
            address_size: 4,
            segment_size: 0,
        });
        let buf = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09];
        let input = EndianBuf::<LittleEndian>::new(&buf);
        let (rest, entry) = ArangeParser::parse_entry(input, &header)
            .expect("should parse entry ok");
        assert_eq!(rest, EndianBuf::new(&buf[buf.len() - 1..]));
        assert_eq!(entry,
                   Some(ArangeEntry {
                       segment: 0,
                       address: 0x04030201,
                       length: 0x08070605,
                       header: header.clone(),
                   }));
    }

    #[test]
    #[cfg_attr(rustfmt, rustfmt_skip)]
    fn test_parse_entry_segment() {
        let header = Rc::new(ArangeHeader {
            format: Format::Dwarf32,
            length: 0,
            version: 2,
            offset: DebugInfoOffset(0),
            address_size: 4,
            segment_size: 8,
        });
        let buf = [
            // Segment.
            0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
            // Address.
            0x01, 0x02, 0x03, 0x04,
            // Length.
            0x05, 0x06, 0x07, 0x08,
            // Next tuple.
            0x09
        ];
        let input = EndianBuf::<LittleEndian>::new(&buf);
        let (rest, entry) = ArangeParser::parse_entry(input, &header)
            .expect("should parse entry ok");
        assert_eq!(rest, EndianBuf::new(&buf[buf.len() - 1..]));
        assert_eq!(entry,
                   Some(ArangeEntry {
                       segment: 0x1817161514131211,
                       address: 0x04030201,
                       length: 0x08070605,
                       header: header.clone(),
                   }));
    }
}
<commit_msg>arange: allow multiple sets of tuples<commit_after>#![deny(missing_docs)]

use endianity::{Endianity, EndianBuf};
use lookup::{LookupParser, LookupEntryIter, DebugLookup};
use parser::{parse_address_size, parse_debug_info_offset, parse_unit_length, parse_u16,
             parse_address, Error, Format, DebugInfoOffset, ParseResult};
use std::cmp::Ordering;
use std::marker::PhantomData;
use std::rc::Rc;

#[derive(Debug, PartialEq, Eq)]
pub struct ArangeHeader {
    format: Format,
    length: u64,
    version: u16,
    offset: DebugInfoOffset,
    address_size: u8,
    segment_size: u8,
}

/// A single parsed arange.
#[derive(Debug, Clone, Eq)]
pub struct ArangeEntry {
    segment: u64,
    address: u64,
    length: u64,
    header: Rc<ArangeHeader>,
}

impl ArangeEntry {
    /// Return the segment selector of this arange.
    pub fn segment(&self) -> Option<u64> {
        if self.header.segment_size != 0 {
            Some(self.segment)
        } else {
            None
        }
    }

    /// Return the beginning address of this arange.
    pub fn address(&self) -> u64 {
        self.address
    }

    /// Return the length of this arange.
    pub fn length(&self) -> u64 {
        self.length
    }

    /// Return the offset into the .debug_info section for this arange.
    pub fn debug_info_offset(&self) -> DebugInfoOffset {
        self.header.offset
    }
}

impl PartialEq for ArangeEntry {
    fn eq(&self, other: &ArangeEntry) -> bool {
        // The expected comparison, but verify that header matches if everything else does.
        match (self.segment == other.segment,
               self.address == other.address,
               self.length == other.length) {
            (true, true, true) => {
                debug_assert!(self.header == other.header);
                true
            }
            _ => false,
        }
    }
}

impl PartialOrd for ArangeEntry {
    fn partial_cmp(&self, other: &ArangeEntry) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

impl Ord for ArangeEntry {
    fn cmp(&self, other: &ArangeEntry) -> Ordering {
        // The expected comparison, but ignore header.
        match (self.segment.cmp(&other.segment),
               self.address.cmp(&other.address),
               self.length.cmp(&other.length)) {
            (Ordering::Equal, Ordering::Equal, Ordering::Equal) => Ordering::Equal,
            (Ordering::Less, _, _) |
            (Ordering::Equal, Ordering::Less, _) |
            (Ordering::Equal, Ordering::Equal, Ordering::Less) => Ordering::Less,
            (Ordering::Greater, _, _) |
            (Ordering::Equal, Ordering::Greater, _) |
            (Ordering::Equal, Ordering::Equal, Ordering::Greater) => Ordering::Greater,
        }
    }
}

pub struct ArangeParser<'input, Endian>
    where Endian: 'input + Endianity
{
    // This struct is never instantiated.
    phantom: PhantomData<&'input Endian>,
}

impl<'input, Endian> LookupParser<'input, Endian> for ArangeParser<'input, Endian>
    where Endian: 'input + Endianity
{
    type Header = ArangeHeader;
    type Entry = ArangeEntry;

    /// Parse an arange set header. Returns a tuple of the remaining arange sets, the aranges to be
    /// parsed for this set, and the newly created ArangeHeader struct.
    fn parse_header(input: EndianBuf<Endian>)
                    -> ParseResult<(EndianBuf<Endian>, EndianBuf<Endian>, Rc<Self::Header>)> {
        let (rest, (length, format)) = try!(parse_unit_length(input));
        if length as usize > rest.len() {
            return Err(Error::UnexpectedEof);
        }
        let after_set = rest.range_from(length as usize..);
        let rest = rest.range_to(..length as usize);

        let (rest, version) = try!(parse_u16(rest));
        if version != 2 {
            return Err(Error::UnknownVersion);
        }

        let (rest, offset) = try!(parse_debug_info_offset(rest, format));
        let (rest, address_size) = try!(parse_address_size(rest));
        let (rest, segment_size) = try!(parse_address_size(rest));

        // unit_length + version + offset + address_size + segment_size
        let header_length = match format {
            Format::Dwarf32 => 4 + 2 + 4 + 1 + 1,
            Format::Dwarf64 => 12 + 2 + 8 + 1 + 1,
        };

        // The first tuple following the header in each set begins at an offset that is
        // a multiple of the size of a single tuple (that is, the size of a segment selector
        // plus twice the size of an address).
        let tuple_length = (2 * address_size + segment_size) as usize;
        let padding = if header_length % tuple_length == 0 {
            0
        } else {
            tuple_length - header_length % tuple_length
        };
        if padding > rest.len() {
            return Err(Error::UnexpectedEof);
        }
        let rest = rest.range_from(padding..);

        Ok((after_set,
            rest,
            Rc::new(ArangeHeader {
            format: format,
            length: length,
            version: version,
            offset: offset,
            address_size: address_size,
            segment_size: segment_size,
        })))
    }

    /// Parse a single arange. Return `None` for the null arange, `Some` for an actual arange.
    fn parse_entry(input: EndianBuf<'input, Endian>,
                   header: &Rc<Self::Header>)
                   -> ParseResult<(EndianBuf<'input, Endian>, Option<Self::Entry>)> {
        let address_size = header.address_size;
        let segment_size = header.segment_size; // May be zero!

        let tuple_length = (2 * address_size + segment_size) as usize;
        if tuple_length > input.len() {
            return Ok((EndianBuf::new(&[]), None));
        }

        let (rest, segment) = if segment_size != 0 {
            try!(parse_address(input, segment_size))
        } else {
            (input, 0)
        };
        let (rest, address) = try!(parse_address(rest, address_size));
        let (rest, length) = try!(parse_address(rest, address_size));

        match (segment, address, length) {
            // There may be multiple sets of tuples, each terminated by a zero tuple.
            // It's not clear what purpose these zero tuples serve.  For now, we
            // simply skip them.
            (0, 0, 0) => Self::parse_entry(rest, header),
            _ => {
                Ok((rest,
                    Some(ArangeEntry {
                    segment: segment,
                    address: address,
                    length: length,
                    header: header.clone(),
                })))
            }
        }
    }
}

/// The `DebugAranges` struct represents the DWARF address range information
/// found in the `.debug_aranges` section.
///
/// Provides:
///
/// * `new(input: EndianBuf<'input, Endian>) -> DebugAranges<'input, Endian>`
///
///   Construct a new `DebugAranges` instance from the data in the `.debug_aranges`
///   section.
///
///   It is the caller's responsibility to read the `.debug_aranges` section and
///   present it as a `&[u8]` slice. That means using some ELF loader on
///   Linux, a Mach-O loader on OSX, etc.
///
///   ```
///   use gimli::{DebugAranges, LittleEndian};
///
///   # let buf = [];
///   # let read_debug_aranges_section = || &buf;
///   let debug_aranges = DebugAranges::<LittleEndian>::new(read_debug_aranges_section());
///   ```
///
/// * `items(&self) -> ArangeEntryIter<'input, Endian>`
///
///   Iterate the aranges in the `.debug_aranges` section.
///
///   ```
///   use gimli::{DebugAranges, LittleEndian};
///
///   # let buf = [];
///   # let read_debug_aranges_section = || &buf;
///   let debug_aranges = DebugAranges::<LittleEndian>::new(read_debug_aranges_section());
///
///   let mut iter = debug_aranges.items();
///   while let Some(arange) = iter.next().unwrap() {
///       println!("arange starts at {}, has length {}", arange.address(), arange.length());
///   }
///   ```
pub type DebugAranges<'input, Endian> = DebugLookup<'input, Endian, ArangeParser<'input, Endian>>;

/// An iterator over the aranges from a .debug_aranges section.
///
/// Provides:
///
/// * `next(self: &mut) -> ParseResult<Option<ArangeEntry>>`
///
///   Advance the iterator and return the next arange.
///
///   Returns the newly parsed arange as `Ok(Some(arange))`. Returns `Ok(None)`
///   when iteration is complete and all aranges have already been parsed and
///   yielded. If an error occurs while parsing the next arange, then this error
///   is returned on all subsequent calls as `Err(e)`.
///
///   Can be [used with
///   `FallibleIterator`](./index.html#using-with-fallibleiterator).
pub type ArangeEntryIter<'input, Endian> = LookupEntryIter<'input,
                                                           Endian,
                                                           ArangeParser<'input, Endian>>;

#[cfg(test)]
mod tests {
    use super::*;
    use lookup::LookupParser;
    use endianity::{EndianBuf, LittleEndian};
    use parser::{Format, DebugInfoOffset};
    use std::rc::Rc;

    #[test]
    #[cfg_attr(rustfmt, rustfmt_skip)]
    fn test_parse_header_ok() {
        let buf = [
            // 32-bit length = 32.
            0x20, 0x00, 0x00, 0x00,
            // Version.
            0x02, 0x00,
            // Offset.
            0x01, 0x02, 0x03, 0x04,
            // Address size.
            0x08,
            // Segment size.
            0x04,
            // Length to here = 12, tuple length = 20.
            // Padding to tuple length multiple = 4.
            0x10, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00,

            // Dummy arange tuple data.
            0x20, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00,

            // Dummy next arange.
            0x30, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00,
        ];

        let input = EndianBuf::<LittleEndian>::new(&buf);

        let (rest, tuples, header) = ArangeParser::parse_header(input)
            .expect("should parse header ok");

        assert_eq!(rest, EndianBuf::new(&buf[buf.len() - 16..]));
        assert_eq!(tuples, EndianBuf::new(&buf[buf.len() - 32..buf.len() - 16]));
        assert_eq!(*header,
                   ArangeHeader {
                       format: Format::Dwarf32,
                       length: 0x20,
                       version: 2,
                       offset: DebugInfoOffset(0x04030201),
                       address_size: 8,
                       segment_size: 4,
                   });
    }

    #[test]
    fn test_parse_entry_ok() {
        let header = Rc::new(ArangeHeader {
            format: Format::Dwarf32,
            length: 0,
            version: 2,
            offset: DebugInfoOffset(0),
            address_size: 4,
            segment_size: 0,
        });
        let buf = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09];
        let input = EndianBuf::<LittleEndian>::new(&buf);
        let (rest, entry) = ArangeParser::parse_entry(input, &header)
            .expect("should parse entry ok");
        assert_eq!(rest, EndianBuf::new(&buf[buf.len() - 1..]));
        assert_eq!(entry,
                   Some(ArangeEntry {
                       segment: 0,
                       address: 0x04030201,
                       length: 0x08070605,
                       header: header.clone(),
                   }));
    }

    #[test]
    #[cfg_attr(rustfmt, rustfmt_skip)]
    fn test_parse_entry_segment() {
        let header = Rc::new(ArangeHeader {
            format: Format::Dwarf32,
            length: 0,
            version: 2,
            offset: DebugInfoOffset(0),
            address_size: 4,
            segment_size: 8,
        });
        let buf = [
            // Segment.
            0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
            // Address.
            0x01, 0x02, 0x03, 0x04,
            // Length.
            0x05, 0x06, 0x07, 0x08,
            // Next tuple.
            0x09
        ];
        let input = EndianBuf::<LittleEndian>::new(&buf);
        let (rest, entry) = ArangeParser::parse_entry(input, &header)
            .expect("should parse entry ok");
        assert_eq!(rest, EndianBuf::new(&buf[buf.len() - 1..]));
        assert_eq!(entry,
                   Some(ArangeEntry {
                       segment: 0x1817161514131211,
                       address: 0x04030201,
                       length: 0x08070605,
                       header: header.clone(),
                   }));
    }

    #[test]
    #[cfg_attr(rustfmt, rustfmt_skip)]
    fn test_parse_entry_zero() {
        let header = Rc::new(ArangeHeader {
            format: Format::Dwarf32,
            length: 0,
            version: 2,
            offset: DebugInfoOffset(0),
            address_size: 4,
            segment_size: 0,
        });
        let buf = [
            // Zero tuple.
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            // Address.
            0x01, 0x02, 0x03, 0x04,
            // Length.
            0x05, 0x06, 0x07, 0x08,
            // Next tuple.
            0x09
        ];
        let input = EndianBuf::<LittleEndian>::new(&buf);
        let (rest, entry) = ArangeParser::parse_entry(input, &header)
            .expect("should parse entry ok");
        assert_eq!(rest, EndianBuf::new(&buf[buf.len() - 1..]));
        assert_eq!(entry,
                   Some(ArangeEntry {
                       segment: 0,
                       address: 0x04030201,
                       length: 0x08070605,
                       header: header.clone(),
                   }));
    }
}
<|endoftext|>


<commit_before>use crate::{generate::*, norm::Norm, types::*};
use ndarray::*;

#[derive(Debug, Clone)]
pub struct MGS<A> {
    dim: usize,
    q: Vec<Array1<A>>,
    r: Vec<Array1<A>>,
}

impl<A: Scalar + Lapack> MGS<A> {
    pub fn new(dim: usize) -> Self {
        Self {
            dim,
            q: Vec::new(),
            r: Vec::new(),
        }
    }

    pub fn dim(&self) -> usize {
        self.dim
    }

    pub fn len(&self) -> usize {
        self.q.len()
    }

    pub fn append<S>(&mut self, a: ArrayBase<S, Ix1>) -> A::Real
    where
        S: Data<Elem = A>,
    {
        assert_eq!(a.len(), self.dim());
        let mut a = a.into_owned();
        let mut coef = Array1::zeros(self.len() + 1);
        for i in 0..self.len() {
            let q = &self.q[i];
            let c = a.dot(q);
            azip!(mut a, q (q) in { *a = *a - c * q } );
            coef[i] = c;
        }
        let nrm = a.norm_l2();
        coef[self.len()] = A::from_real(nrm);
        self.r.push(coef);
        azip!(mut a in { *a = *a / A::from_real(nrm) });
        self.q.push(a);
        nrm
    }

    /// Get orthogonal basis as Q matrix
    pub fn get_q(&self) -> Array2<A> {
        hstack(&self.q).unwrap()
    }

    /// Get each vector norm and coefficients as R matrix
    pub fn get_r(&self) -> Array2<A> {
        let len = self.len();
        let mut r = Array2::zeros((len, len));
        for i in 0..len {
            for j in 0..=i {
                r[(j, i)] = self.r[i][j];
            }
        }
        r
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::assert::*;

    const N: usize = 5;

    #[test]
    fn new() {
        let mgs: MGS<f32> = MGS::new(N);
        assert_eq!(mgs.dim(), N);
        assert_eq!(mgs.len(), 0);
    }

    fn test<A: Scalar + Lapack>(rtol: A::Real) {
        let mut mgs: MGS<A> = MGS::new(N);
        let a: Array2<A> = crate::generate::random((N, 3));
        dbg!(&a);
        for col in a.axis_iter(Axis(1)) {
            let res = mgs.append(col);
            dbg!(res);
        }
        let q = mgs.get_q();
        dbg!(&q);
        let r = mgs.get_r();
        dbg!(&r);

        dbg!(q.dot(&r));
        close_l2(&q.dot(&r), &a, rtol).unwrap();

        let qt: Array2<_> = conjugate(&q);
        dbg!(qt.dot(&q));
        close_l2(&qt.dot(&q), &Array2::eye(3), rtol).unwrap();
    }

    #[test]
    fn test_f32() {
        test::<f32>(1e-5);
    }

    #[test]
    fn test_c32() {
        test::<c32>(1e-5);
    }
}
<commit_msg>Use inner product<commit_after>use crate::{generate::*, inner::*, norm::Norm, types::*};
use ndarray::*;

#[derive(Debug, Clone)]
pub struct MGS<A> {
    dim: usize,
    q: Vec<Array1<A>>,
    r: Vec<Array1<A>>,
}

impl<A: Scalar + Lapack> MGS<A> {
    pub fn new(dim: usize) -> Self {
        Self {
            dim,
            q: Vec::new(),
            r: Vec::new(),
        }
    }

    pub fn dim(&self) -> usize {
        self.dim
    }

    pub fn len(&self) -> usize {
        self.q.len()
    }

    pub fn append<S>(&mut self, a: ArrayBase<S, Ix1>) -> A::Real
    where
        S: Data<Elem = A>,
    {
        assert_eq!(a.len(), self.dim());
        let mut a = a.into_owned();
        let mut coef = Array1::zeros(self.len() + 1);
        for i in 0..self.len() {
            let q = &self.q[i];
            let c = q.inner(&a);
            azip!(mut a, q (q) in { *a = *a - c * q } );
            coef[i] = c;
        }
        let nrm = a.norm_l2();
        coef[self.len()] = A::from_real(nrm);
        self.r.push(coef);
        azip!(mut a in { *a = *a / A::from_real(nrm) });
        self.q.push(a);
        nrm
    }

    /// Get orthogonal basis as Q matrix
    pub fn get_q(&self) -> Array2<A> {
        hstack(&self.q).unwrap()
    }

    /// Get each vector norm and coefficients as R matrix
    pub fn get_r(&self) -> Array2<A> {
        let len = self.len();
        let mut r = Array2::zeros((len, len));
        for i in 0..len {
            for j in 0..=i {
                r[(j, i)] = self.r[i][j];
            }
        }
        r
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::assert::*;

    const N: usize = 5;

    #[test]
    fn new() {
        let mgs: MGS<f32> = MGS::new(N);
        assert_eq!(mgs.dim(), N);
        assert_eq!(mgs.len(), 0);
    }

    fn test<A: Scalar + Lapack>(rtol: A::Real) {
        let mut mgs: MGS<A> = MGS::new(N);
        let a: Array2<A> = crate::generate::random((N, 3));
        dbg!(&a);
        for col in a.axis_iter(Axis(1)) {
            let res = mgs.append(col);
            dbg!(res);
        }
        let q = mgs.get_q();
        dbg!(&q);
        let r = mgs.get_r();
        dbg!(&r);

        dbg!(q.dot(&r));
        close_l2(&q.dot(&r), &a, rtol).unwrap();

        let qt: Array2<_> = conjugate(&q);
        dbg!(qt.dot(&q));
        close_l2(&qt.dot(&q), &Array2::eye(3), rtol).unwrap();
    }

    #[test]
    fn test_f32() {
        test::<f32>(1e-5);
    }

    #[test]
    fn test_c32() {
        test::<c32>(1e-5);
    }

    #[test]
    fn test_f64() {
        test::<f64>(1e-9);
    }

    #[test]
    fn test_c64() {
        test::<c64>(1e-9);
    }
}
<|endoftext|>