<commit_before>#[macro_use]
extern crate clap;
extern crate env_logger;
#[macro_use]
extern crate log;
extern crate rls_analysis;
extern crate rls_data as data;
extern crate tools;

use data::GlobalCrateId;
use data::DefKind;
use rls_analysis::{AnalysisHost, AnalysisLoader, SearchDirectory};
use std::collections::{BTreeSet, HashMap};
use std::io::{BufRead, BufReader};
use std::fs::{self, File};
use std::path::{Path, PathBuf};
use tools::file_format::analysis::{AnalysisKind, AnalysisSource, AnalysisTarget, LineRange, Location, WithLocation};

/// A global definition id in a crate.
///
/// FIXME(emilio): This key is kind of slow, because GlobalCrateId contains a
/// String. There's a "disambiguator" field which may be more than enough for
/// our purposes.
#[derive(Clone, Hash, Debug, Eq, PartialEq)]
pub struct DefId(GlobalCrateId, u32);

/// A map from global definition ids to the actual definition.
pub struct Defs {
    map: HashMap<DefId, data::Def>,
}

struct TreeInfo<'a> {
    src_dir: &'a Path,
    output_dir: &'a Path,
    objdir: &'a Path,
}

// Given a definition, and the global crate id where that definition is found,
// return a qualified name that identifies the definition unambiguously.
fn crate_independent_qualname(
    def: &data::Def,
    crate_id: &data::GlobalCrateId,
) -> String {
    // For functions with "no_mangle", we just use the name.
    if def.kind == DefKind::Function &&
        def.attributes.iter().any(|attr| attr.value == "no_mangle")
    {
        return def.name.clone();
    }

    format!("{}{}", crate_id.name, def.qualname)
}

impl Defs {
    fn new() -> Self {
        Self { map: HashMap::new() }
    }

    fn insert(&mut self, analysis: &data::Analysis, def: &data::Def) {
        let crate_id = analysis.prelude.as_ref().unwrap().crate_id.clone();
        let mut definition = def.clone();
        definition.qualname = crate_independent_qualname(&def, &crate_id);

        let index = definition.id.index;
        let previous = self.map.insert(DefId(crate_id, index), definition);
        if let Some(previous) = previous {
            // This shouldn't happen, but as of right now it can happen with
            // some builtin definitions when highly generic types are involved.
            // This is probably a rust bug, just ignore it for now.
            debug!(
                "Found a definition with the same ID twice? {:?}, {:?}",
                previous,
                def,
            );
        }
    }

    /// Getter for a given local id, which takes care of converting to a global
    /// ID and returning the definition if present.
    fn get(&self, analysis: &data::Analysis, id: data::Id) -> Option<data::Def> {
        let prelude = analysis.prelude.as_ref().unwrap();
        let krate_id = if id.krate == 0 {
            prelude.crate_id.clone()
        } else {
            // TODO(emilio): This escales with the number of crates in this
            // particular crate, but it's probably not too bad, since it should
            // be a pretty fast linear search.
            let krate = prelude.external_crates.iter().find(|krate| {
                krate.num == id.krate
            });

            let krate = match krate {
                Some(k) => k,
                None => {
                    debug!("Crate not found: {:?}", id);
                    return None;
                }
            };

            krate.id.clone()
        };

        let id = DefId(krate_id, id.index);
        self.map.get(&id).cloned()
    }
}

#[derive(Clone)]
pub struct Loader {
    deps_dirs: Vec<PathBuf>,
}

impl Loader {
    pub fn new(deps_dirs: Vec<PathBuf>) -> Self {
        Self { deps_dirs }
    }
}

impl AnalysisLoader for Loader {
    fn needs_hard_reload(&self, _: &Path) -> bool {
        true
    }

    fn fresh_host(&self) -> AnalysisHost<Self> {
        AnalysisHost::new_with_loader(self.clone())
    }

    fn set_path_prefix(&mut self, _: &Path) {}

    fn abs_path_prefix(&self) -> Option<PathBuf> {
        None
    }
    fn search_directories(&self) -> Vec<SearchDirectory> {
        self.deps_dirs.iter().map(|pb| {
            SearchDirectory {
                path: pb.clone(),
                prefix_rewrite: None,
            }
        }).collect()
    }
}

fn def_kind_to_human(kind: DefKind) -> &'static str {
    match kind {
        DefKind::Enum => "enum",
        DefKind::Local => "local",
        DefKind::ExternType => "extern type",
        DefKind::Const => "constant",
        DefKind::Field => "field",
        DefKind::Function | DefKind::ForeignFunction => "function",
        DefKind::Macro => "macro",
        DefKind::Method => "method",
        DefKind::Mod => "module",
        DefKind::Static | DefKind::ForeignStatic => "static",
        DefKind::Struct => "struct",
        DefKind::Tuple => "tuple",
        DefKind::TupleVariant => "tuple variant",
        DefKind::Union => "union",
        DefKind::Type => "type",
        DefKind::Trait => "trait",
        DefKind::StructVariant => "struct variant",
    }
}

fn visit(
    out_data: &mut BTreeSet<String>,
    kind: AnalysisKind,
    location: &data::SpanData,
    qualname: &str,
    def: &data::Def,
    context: Option<&str>,
) {
    // Searchfox uses 1-indexed lines, 0-indexed columns.
    let col_end = if location.line_start != location.line_end {
        // Rust spans are multi-line... So we just use the start column as
        // the end column if it spans multiple rows, searchfox has fallback
        // code to handle this.
        location.column_start.zero_indexed().0
    } else {
        location.column_end.zero_indexed().0
    };
    let loc = Location {
        lineno: location.line_start.0,
        col_start: location.column_start.zero_indexed().0,
        col_end,
    };

    let target_data = WithLocation {
        data: AnalysisTarget {
            kind,
            pretty: String::from(qualname),
            sym: String::from(qualname),
            context: String::from(context.unwrap_or("")),
            contextsym: String::from(context.unwrap_or("")),
            peek_range: LineRange { start_lineno: 0, end_lineno: 0 },
        },
        loc: loc.clone(),
    };
    out_data.insert(format!("{}", target_data));

    let pretty = {
        let mut pretty = def_kind_to_human(def.kind).to_owned();
        pretty.push_str(" ");
        pretty.push_str(qualname);

        pretty
    };

    let source_data = WithLocation {
        data: AnalysisSource {
            syntax: vec![],
            pretty,
            sym: vec![ String::from(qualname) ],
            no_crossref: false,
        },
        loc,
    };
    out_data.insert(format!("{}", source_data));
}

fn find_generated_or_src_file(
    file_name: &Path,
    tree_info: &TreeInfo,
) -> Option<PathBuf> {
    if let Ok(generated_path) = file_name.strip_prefix(tree_info.objdir) {
        return Some(Path::new("__GENERATED__").join(generated_path))
    }
    file_name.strip_prefix(tree_info.src_dir).ok().map(From::from)
}

fn read_existing_contents(
    map: &mut BTreeSet<String>,
    file: &Path,
) {
    if let Ok(f) = File::open(file) {
        let mut reader = BufReader::new(f);
        for line in reader.lines() {
            map.insert(line.unwrap());
        }
    }
}

fn analyze_file(
    file_name: &PathBuf,
    defs: &Defs,
    file_analysis: &data::Analysis,
    tree_info: &TreeInfo,
) {
    use std::io::Write;

    let file = match find_generated_or_src_file(file_name, tree_info) {
        Some(f) => f,
        None => {
            error!("File not in the source directory or objdir: {}", file_name.display());
            return;
        }
    };

    let output_file = tree_info.output_dir.join(file);
    let mut dataset = BTreeSet::new();
    read_existing_contents(&mut dataset, &output_file);
    let mut output_dir = output_file.clone();
    output_dir.pop();
    if let Err(err) = fs::create_dir_all(output_dir) {
        error!(
            "Couldn't create dir for: {}, {:?}",
            output_file.display(),
            err
        );
        return;
    }
    let mut file = match File::create(&output_file) {
        Ok(f) => f,
        Err(err) => {
            error!(
                "Couldn't open output file: {}, {:?}",
                output_file.display(),
                err
            );
            return;
        }
    };

    for import in &file_analysis.imports {
        let id = match import.ref_id {
            Some(id) => id,
            None => {
                debug!("Dropping import {} ({:?}): {}, no ref", import.name, import.kind, import.value);
                continue;
            }
        };

        let def = match defs.get(file_analysis, id) {
            Some(def) => def,
            None => {
                debug!("Dropping import {} ({:?}): {}, no def for ref {:?}", import.name, import.kind, import.value, id);
                continue;
            }
        };

        visit(&mut dataset, AnalysisKind::Use, &import.span, &def.qualname, &def, None)
    }

    for def in &file_analysis.defs {
        let parent =
            def.parent.and_then(|parent_id| defs.get(file_analysis, parent_id));

        if let Some(ref parent) = parent {
            if parent.kind == DefKind::Trait {
                let trait_dependent_name =
                    format!("{}::{}", parent.qualname, def.name);
                visit(
                    &mut dataset,
                    AnalysisKind::Def,
                    &def.span,
                    &trait_dependent_name,
                    &def,
                    Some(&parent.qualname),
                )
            }
        }

        let crate_id = &file_analysis.prelude.as_ref().unwrap().crate_id;
        let qualname = crate_independent_qualname(&def, crate_id);
        visit(&mut dataset, AnalysisKind::Def, &def.span, &qualname, &def, parent.as_ref().map(|p| &*p.qualname))
    }

    for ref_ in &file_analysis.refs {
        let def = match defs.get(file_analysis, ref_.ref_id) {
            Some(d) => d,
            None => {
                debug!("Dropping ref {:?}, kind {:?}, no def", ref_.ref_id, ref_.kind);
                continue;
            }
        };
        visit(
            &mut dataset,
            AnalysisKind::Use,
            &ref_.span,
            &def.qualname,
            &def,
            /* context = */ None, // TODO
        )
    }

    for obj in &dataset {
        file.write_all(obj.as_bytes()).unwrap();
        write!(file, "\n").unwrap();
    }
}

fn analyze_crate(
    analysis: &data::Analysis,
    defs: &Defs,
    tree_info: &TreeInfo,
) {
    let mut per_file = HashMap::new();

    println!("Analyzing crate: {:?}", analysis.prelude);

    macro_rules! flat_map_per_file {
        ($field:ident) => {
            for item in &analysis.$field {
                let mut file_analysis =
                    per_file.entry(item.span.file_name.clone())
                        .or_insert_with(|| {
                            let prelude = analysis.prelude.clone();
                            let mut analysis = data::Analysis::new(analysis.config.clone());
                            analysis.prelude = prelude;
                            analysis
                        });
                file_analysis.$field.push(item.clone());
            }
        }
    }

    flat_map_per_file!(imports);
    flat_map_per_file!(defs);
    flat_map_per_file!(impls);
    flat_map_per_file!(refs);
    flat_map_per_file!(macro_refs);
    flat_map_per_file!(relations);

    for (mut name, analysis) in per_file.drain() {
        if name.is_relative() {
            name = tree_info.src_dir.join(name);
        }
        analyze_file(&name, defs, &analysis, tree_info);
    }
}

fn main() {
    use clap::Arg;
    env_logger::init();
    let matches = app_from_crate!()
        .args_from_usage(
            "<src>      'Points to the source root'
             <output>   'Points to the directory where searchfox metadata should go'
             <objdir>   'Points to the objdir generated files may come from'"
        )
        .arg(Arg::with_name("input")
            .required(false)
            .multiple(true)
            .help("rustc analysis directories")
        )
        .get_matches();

    let src_dir = Path::new(matches.value_of("src").unwrap());
    let output_dir = Path::new(matches.value_of("output").unwrap());
    let objdir = Path::new(matches.value_of("objdir").unwrap());

    let tree_info = TreeInfo { src_dir, output_dir, objdir };

    let input_dirs = match matches.values_of("input") {
        Some(inputs) => inputs.map(PathBuf::from).collect(),
        None => vec![],
    };
    let loader = Loader::new(input_dirs);

    let crates = rls_analysis::read_analysis_from_files(&loader, Default::default(), &[]);

    println!("{:?}", crates.iter().map(|k| &k.id.name).collect::<Vec<_>>());

    let mut defs = Defs::new();
    for krate in &crates {
        for def in &krate.analysis.defs {
            // println!("Indexing def: {:?}", def);
            defs.insert(&krate.analysis, def);
        }
    }

    for krate in crates {
        analyze_crate(&krate.analysis, &defs, &tree_info);
    }
}
<commit_msg>Bug 1503547 - Turn Windows paths into Linux paths for rust indexing.<commit_after>#[macro_use]
extern crate clap;
extern crate env_logger;
#[macro_use]
extern crate log;
extern crate rls_analysis;
extern crate rls_data as data;
extern crate tools;

use data::GlobalCrateId;
use data::DefKind;
use rls_analysis::{AnalysisHost, AnalysisLoader, SearchDirectory};
use std::collections::{BTreeSet, HashMap};
use std::io::{BufRead, BufReader};
use std::fs::{self, File};
use std::path::{Path, PathBuf};
use tools::file_format::analysis::{AnalysisKind, AnalysisSource, AnalysisTarget, LineRange, Location, WithLocation};

/// A global definition id in a crate.
///
/// FIXME(emilio): This key is kind of slow, because GlobalCrateId contains a
/// String. There's a "disambiguator" field which may be more than enough for
/// our purposes.
#[derive(Clone, Hash, Debug, Eq, PartialEq)]
pub struct DefId(GlobalCrateId, u32);

/// A map from global definition ids to the actual definition.
pub struct Defs {
    map: HashMap<DefId, data::Def>,
}

struct TreeInfo<'a> {
    src_dir: &'a Path,
    output_dir: &'a Path,
    objdir: &'a Path,
}

// Given a definition, and the global crate id where that definition is found,
// return a qualified name that identifies the definition unambiguously.
fn crate_independent_qualname(
    def: &data::Def,
    crate_id: &data::GlobalCrateId,
) -> String {
    // For functions with "no_mangle", we just use the name.
    if def.kind == DefKind::Function &&
        def.attributes.iter().any(|attr| attr.value == "no_mangle")
    {
        return def.name.clone();
    }

    format!("{}{}", crate_id.name, def.qualname)
}

impl Defs {
    fn new() -> Self {
        Self { map: HashMap::new() }
    }

    fn insert(&mut self, analysis: &data::Analysis, def: &data::Def) {
        let crate_id = analysis.prelude.as_ref().unwrap().crate_id.clone();
        let mut definition = def.clone();
        definition.qualname = crate_independent_qualname(&def, &crate_id);

        let index = definition.id.index;
        let previous = self.map.insert(DefId(crate_id, index), definition);
        if let Some(previous) = previous {
            // This shouldn't happen, but as of right now it can happen with
            // some builtin definitions when highly generic types are involved.
            // This is probably a rust bug, just ignore it for now.
            debug!(
                "Found a definition with the same ID twice? {:?}, {:?}",
                previous,
                def,
            );
        }
    }

    /// Getter for a given local id, which takes care of converting to a global
    /// ID and returning the definition if present.
    fn get(&self, analysis: &data::Analysis, id: data::Id) -> Option<data::Def> {
        let prelude = analysis.prelude.as_ref().unwrap();
        let krate_id = if id.krate == 0 {
            prelude.crate_id.clone()
        } else {
            // TODO(emilio): This escales with the number of crates in this
            // particular crate, but it's probably not too bad, since it should
            // be a pretty fast linear search.
            let krate = prelude.external_crates.iter().find(|krate| {
                krate.num == id.krate
            });

            let krate = match krate {
                Some(k) => k,
                None => {
                    debug!("Crate not found: {:?}", id);
                    return None;
                }
            };

            krate.id.clone()
        };

        let id = DefId(krate_id, id.index);
        self.map.get(&id).cloned()
    }
}

#[derive(Clone)]
pub struct Loader {
    deps_dirs: Vec<PathBuf>,
}

impl Loader {
    pub fn new(deps_dirs: Vec<PathBuf>) -> Self {
        Self { deps_dirs }
    }
}

impl AnalysisLoader for Loader {
    fn needs_hard_reload(&self, _: &Path) -> bool {
        true
    }

    fn fresh_host(&self) -> AnalysisHost<Self> {
        AnalysisHost::new_with_loader(self.clone())
    }

    fn set_path_prefix(&mut self, _: &Path) {}

    fn abs_path_prefix(&self) -> Option<PathBuf> {
        None
    }
    fn search_directories(&self) -> Vec<SearchDirectory> {
        self.deps_dirs.iter().map(|pb| {
            SearchDirectory {
                path: pb.clone(),
                prefix_rewrite: None,
            }
        }).collect()
    }
}

fn def_kind_to_human(kind: DefKind) -> &'static str {
    match kind {
        DefKind::Enum => "enum",
        DefKind::Local => "local",
        DefKind::ExternType => "extern type",
        DefKind::Const => "constant",
        DefKind::Field => "field",
        DefKind::Function | DefKind::ForeignFunction => "function",
        DefKind::Macro => "macro",
        DefKind::Method => "method",
        DefKind::Mod => "module",
        DefKind::Static | DefKind::ForeignStatic => "static",
        DefKind::Struct => "struct",
        DefKind::Tuple => "tuple",
        DefKind::TupleVariant => "tuple variant",
        DefKind::Union => "union",
        DefKind::Type => "type",
        DefKind::Trait => "trait",
        DefKind::StructVariant => "struct variant",
    }
}

fn visit(
    out_data: &mut BTreeSet<String>,
    kind: AnalysisKind,
    location: &data::SpanData,
    qualname: &str,
    def: &data::Def,
    context: Option<&str>,
) {
    // Searchfox uses 1-indexed lines, 0-indexed columns.
    let col_end = if location.line_start != location.line_end {
        // Rust spans are multi-line... So we just use the start column as
        // the end column if it spans multiple rows, searchfox has fallback
        // code to handle this.
        location.column_start.zero_indexed().0
    } else {
        location.column_end.zero_indexed().0
    };
    let loc = Location {
        lineno: location.line_start.0,
        col_start: location.column_start.zero_indexed().0,
        col_end,
    };

    let target_data = WithLocation {
        data: AnalysisTarget {
            kind,
            pretty: String::from(qualname),
            sym: String::from(qualname),
            context: String::from(context.unwrap_or("")),
            contextsym: String::from(context.unwrap_or("")),
            peek_range: LineRange { start_lineno: 0, end_lineno: 0 },
        },
        loc: loc.clone(),
    };
    out_data.insert(format!("{}", target_data));

    let pretty = {
        let mut pretty = def_kind_to_human(def.kind).to_owned();
        pretty.push_str(" ");
        pretty.push_str(qualname);

        pretty
    };

    let source_data = WithLocation {
        data: AnalysisSource {
            syntax: vec![],
            pretty,
            sym: vec![ String::from(qualname) ],
            no_crossref: false,
        },
        loc,
    };
    out_data.insert(format!("{}", source_data));
}

fn find_generated_or_src_file(
    file_name: &Path,
    tree_info: &TreeInfo,
) -> Option<PathBuf> {
    if let Ok(generated_path) = file_name.strip_prefix(tree_info.objdir) {
        return Some(Path::new("__GENERATED__").join(generated_path))
    }
    file_name.strip_prefix(tree_info.src_dir).ok().map(From::from)
}

fn read_existing_contents(
    map: &mut BTreeSet<String>,
    file: &Path,
) {
    if let Ok(f) = File::open(file) {
        let mut reader = BufReader::new(f);
        for line in reader.lines() {
            map.insert(line.unwrap());
        }
    }
}

fn analyze_file(
    file_name: &PathBuf,
    defs: &Defs,
    file_analysis: &data::Analysis,
    tree_info: &TreeInfo,
) {
    use std::io::Write;

    let file = match find_generated_or_src_file(file_name, tree_info) {
        Some(f) => f,
        None => {
            error!("File not in the source directory or objdir: {}", file_name.display());
            return;
        }
    };

    let output_file = tree_info.output_dir.join(file);
    let mut dataset = BTreeSet::new();
    read_existing_contents(&mut dataset, &output_file);
    let mut output_dir = output_file.clone();
    output_dir.pop();
    if let Err(err) = fs::create_dir_all(output_dir) {
        error!(
            "Couldn't create dir for: {}, {:?}",
            output_file.display(),
            err
        );
        return;
    }
    let mut file = match File::create(&output_file) {
        Ok(f) => f,
        Err(err) => {
            error!(
                "Couldn't open output file: {}, {:?}",
                output_file.display(),
                err
            );
            return;
        }
    };

    for import in &file_analysis.imports {
        let id = match import.ref_id {
            Some(id) => id,
            None => {
                debug!("Dropping import {} ({:?}): {}, no ref", import.name, import.kind, import.value);
                continue;
            }
        };

        let def = match defs.get(file_analysis, id) {
            Some(def) => def,
            None => {
                debug!("Dropping import {} ({:?}): {}, no def for ref {:?}", import.name, import.kind, import.value, id);
                continue;
            }
        };

        visit(&mut dataset, AnalysisKind::Use, &import.span, &def.qualname, &def, None)
    }

    for def in &file_analysis.defs {
        let parent =
            def.parent.and_then(|parent_id| defs.get(file_analysis, parent_id));

        if let Some(ref parent) = parent {
            if parent.kind == DefKind::Trait {
                let trait_dependent_name =
                    format!("{}::{}", parent.qualname, def.name);
                visit(
                    &mut dataset,
                    AnalysisKind::Def,
                    &def.span,
                    &trait_dependent_name,
                    &def,
                    Some(&parent.qualname),
                )
            }
        }

        let crate_id = &file_analysis.prelude.as_ref().unwrap().crate_id;
        let qualname = crate_independent_qualname(&def, crate_id);
        visit(&mut dataset, AnalysisKind::Def, &def.span, &qualname, &def, parent.as_ref().map(|p| &*p.qualname))
    }

    for ref_ in &file_analysis.refs {
        let def = match defs.get(file_analysis, ref_.ref_id) {
            Some(d) => d,
            None => {
                debug!("Dropping ref {:?}, kind {:?}, no def", ref_.ref_id, ref_.kind);
                continue;
            }
        };
        visit(
            &mut dataset,
            AnalysisKind::Use,
            &ref_.span,
            &def.qualname,
            &def,
            /* context = */ None, // TODO
        )
    }

    for obj in &dataset {
        file.write_all(obj.as_bytes()).unwrap();
        write!(file, "\n").unwrap();
    }
}

/// Uses heuristics to determine if the path is a Windows path,
/// and if so, returns a Linux "equivalent". Not really equivalent,
/// because there is no such thing, but equivalent enough for our
/// purposes - that is, is_relative() returns a correct thing, and
/// strip_prefix will work on relative paths.
/// Note that this is a "linuxzed" path and not a "unixed" path
/// this code should ever only really run inside the linux vagrant
/// machine.
/// There are real-life cases in which this code is too simple, see
/// https://github.com/nrc/rls-data/issues/20 for an example.
fn linuxized_path(path: &PathBuf) -> PathBuf {
    // Windows paths have backslashes and so on a Linux host will
    // just have a single long component
    if path.components().count() == 1 {
        if let Some(pathstr) = path.to_str() {
            if pathstr.find('\\').is_some() {
                // Has a backslash, so probably a Windows path. Let's lean
                // those slashes forward
                let converted = pathstr.replace('\\', "/");
                if converted.find(":/") == Some(1) {
                    // Starts with a drive letter, so let's turn this into
                    // an absolute path
                    let abs = "/".to_string() + &converted;
                    return PathBuf::from(abs);
                }
                // Turn it into a relative path
                return PathBuf::from(converted);
            }
        }
    }
    // Probably a Linux path, or a Windows path that's some sort of
    // weird edge case. In that case just return it as-is
    path.clone()
}

fn analyze_crate(
    analysis: &data::Analysis,
    defs: &Defs,
    tree_info: &TreeInfo,
) {
    let mut per_file = HashMap::new();

    println!("Analyzing crate: {:?}", analysis.prelude);

    macro_rules! flat_map_per_file {
        ($field:ident) => {
            for item in &analysis.$field {
                let mut file_analysis =
                    per_file.entry(linuxized_path(&item.span.file_name))
                        .or_insert_with(|| {
                            let prelude = analysis.prelude.clone();
                            let mut analysis = data::Analysis::new(analysis.config.clone());
                            analysis.prelude = prelude;
                            analysis
                        });
                file_analysis.$field.push(item.clone());
            }
        }
    }

    flat_map_per_file!(imports);
    flat_map_per_file!(defs);
    flat_map_per_file!(impls);
    flat_map_per_file!(refs);
    flat_map_per_file!(macro_refs);
    flat_map_per_file!(relations);

    for (mut name, analysis) in per_file.drain() {
        if name.is_relative() {
            name = tree_info.src_dir.join(name);
        }
        analyze_file(&name, defs, &analysis, tree_info);
    }
}

fn main() {
    use clap::Arg;
    env_logger::init();
    let matches = app_from_crate!()
        .args_from_usage(
            "<src>      'Points to the source root'
             <output>   'Points to the directory where searchfox metadata should go'
             <objdir>   'Points to the objdir generated files may come from'"
        )
        .arg(Arg::with_name("input")
            .required(false)
            .multiple(true)
            .help("rustc analysis directories")
        )
        .get_matches();

    let src_dir = Path::new(matches.value_of("src").unwrap());
    let output_dir = Path::new(matches.value_of("output").unwrap());
    let objdir = Path::new(matches.value_of("objdir").unwrap());

    let tree_info = TreeInfo { src_dir, output_dir, objdir };

    let input_dirs = match matches.values_of("input") {
        Some(inputs) => inputs.map(PathBuf::from).collect(),
        None => vec![],
    };
    let loader = Loader::new(input_dirs);

    let crates = rls_analysis::read_analysis_from_files(&loader, Default::default(), &[]);

    println!("{:?}", crates.iter().map(|k| &k.id.name).collect::<Vec<_>>());

    let mut defs = Defs::new();
    for krate in &crates {
        for def in &krate.analysis.defs {
            // println!("Indexing def: {:?}", def);
            defs.insert(&krate.analysis, def);
        }
    }

    for krate in crates {
        analyze_crate(&krate.analysis, &defs, &tree_info);
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2018 Chef Software Inc. and/or applicable contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use clap;
use std::fs;
use std::io::Write;

use common::ui::{UI, Status};
use export_docker;
use export_docker::Result;
use export_k8s::{Manifest, ManifestJson};

use chartfile::ChartFile;
use values::Values;

pub struct Chart<'a> {
    name: String,
    chartfile: ChartFile,
    manifest_template: ManifestJson,
    values: Values,
    ui: &'a mut UI,
}

impl<'a> Chart<'a> {
    pub fn new_for_cli_matches(ui: &'a mut UI, matches: &clap::ArgMatches) -> Result<Self> {
        let image = if !matches.is_present("NO_DOCKER_IMAGE") {
            export_docker::export_for_cli_matches(ui, &matches)?
        } else {
            None
        };
        let manifest = Manifest::new_from_cli_matches(ui, &matches, image)?;

        let name = matches
            .value_of("CHART")
            .unwrap_or(&manifest.metadata_name)
            .to_string();
        let version = matches.value_of("VERSION");
        let description = matches.value_of("DESCRIPTION");
        let chartfile = ChartFile::new(&name, version, description);

        Ok(Self::new_for_manifest(manifest, name, chartfile, ui))
    }

    fn new_for_manifest(
        manifest: Manifest,
        name: String,
        chartfile: ChartFile,
        ui: &'a mut UI,
    ) -> Self {
        let main = json!({
            "metadata_name": "{{.Values.metadataName}}",
            "image": "{{.Values.imageName}}",
            "count": "{{.Values.instanceCount}}",
            "service_topology": "{{.Values.serviceTopology}}",
            "service_group": manifest.service_group.clone().map(|_| "{{.Values.serviceGroup}}"),
            "config": manifest.config
                .clone()
                .map(|_| "{{.Values.config}}"),
            "ring_secret_name": manifest.ring_secret_name
                .clone()
                .map(|_| "{{.Values.ringSecretName}}"),
            "bind": !manifest.binds.is_empty(),
        });

        let mut values = Values::new();
        values.add_entry("metadataName", &manifest.metadata_name);
        values.add_entry("imageName", &manifest.image);
        values.add_entry("instanceCount", &manifest.count.to_string());
        values.add_entry("serviceTopology", &manifest.service_topology.to_string());
        if let Some(ref group) = manifest.service_group {
            values.add_entry("serviceGroup", group);
        }
        if let Some(ref config) = manifest.config {
            values.add_entry("config", config);
        }
        if let Some(ref name) = manifest.ring_secret_name {
            values.add_entry("ringSecretName", name);
        }

        let mut binds = Vec::new();
        let mut i = 0;
        for bind in &manifest.binds {
            let name_var = format!("bindName{}", i);
            let service_var = format!("bindService{}", i);
            let group_var = format!("bindGroup{}", i);
            i = i + 1;

            values.add_entry(&name_var, &bind.name);
            values.add_entry(&service_var, &bind.service_group.service());
            values.add_entry(&group_var, &bind.service_group.group());

            let json = json!({
                "name": format!("{{{{.Values.{}}}}}", name_var),
                "service": format!("{{{{.Values.{}}}}}", service_var),
                "group": format!("{{{{.Values.{}}}}}", group_var),
            });

            binds.push(json);
        }

        let manifest_template = ManifestJson {
            main: main,
            binds: binds,
        };

        Chart {
            name,
            chartfile,
            manifest_template,
            values,
            ui,
        }
    }

    pub fn generate(mut self) -> Result<()> {
        self.ui.status(
            Status::Creating,
            format!("chart directory `{}`", self.name),
        )?;
        fs::create_dir_all(&self.name)?;

        self.generate_chartfile()?;

        let template_path = format!("{}/{}", self.name, "templates");
        self.ui.status(
            Status::Creating,
            format!("templates directory `{}`", template_path),
        )?;

        self.generate_values()?;

        fs::create_dir_all(&template_path)?;
        self.generate_manifest_template(&template_path)
    }

    pub fn generate_chartfile(&mut self) -> Result<()> {
        let path = format!("{}/Chart.yaml", self.name);
        self.ui.status(
            Status::Creating,
            format!("chart file `{}`", path),
        )?;
        let mut write = fs::File::create(path)?;
        let out = self.chartfile.into_string()?;

        write.write(out.as_bytes())?;

        Ok(())
    }

    pub fn generate_manifest_template(self, template_path: &str) -> Result<()> {
        let manifest_path = format!("{}/{}.yaml", template_path, self.name);
        self.ui.status(
            Status::Creating,
            format!("manifest template `{}`", manifest_path),
        )?;
        let mut write = fs::File::create(manifest_path)?;
        let out: String = self.manifest_template.into();

        write.write(out.as_bytes())?;

        Ok(())
    }

    pub fn generate_values(&mut self) -> Result<()> {
        let path = format!("{}/values.yaml", self.name);
        self.ui.status(
            Status::Creating,
            format!("values file `{}`", path),
        )?;
        let mut write = fs::File::create(path)?;

        self.values.generate(&mut write)?;

        Ok(())
    }
}
<commit_msg>pkg-export-helm: Less verbose output<commit_after>// Copyright (c) 2018 Chef Software Inc. and/or applicable contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use clap;
use std::fs;
use std::io::Write;

use common::ui::{UI, Status};
use export_docker;
use export_docker::Result;
use export_k8s::{Manifest, ManifestJson};

use chartfile::ChartFile;
use values::Values;

pub struct Chart<'a> {
    name: String,
    chartfile: ChartFile,
    manifest_template: ManifestJson,
    values: Values,
    ui: &'a mut UI,
}

impl<'a> Chart<'a> {
    pub fn new_for_cli_matches(ui: &'a mut UI, matches: &clap::ArgMatches) -> Result<Self> {
        let image = if !matches.is_present("NO_DOCKER_IMAGE") {
            export_docker::export_for_cli_matches(ui, &matches)?
        } else {
            None
        };
        let manifest = Manifest::new_from_cli_matches(ui, &matches, image)?;

        let name = matches
            .value_of("CHART")
            .unwrap_or(&manifest.metadata_name)
            .to_string();
        let version = matches.value_of("VERSION");
        let description = matches.value_of("DESCRIPTION");
        let chartfile = ChartFile::new(&name, version, description);

        Ok(Self::new_for_manifest(manifest, name, chartfile, ui))
    }

    fn new_for_manifest(
        manifest: Manifest,
        name: String,
        chartfile: ChartFile,
        ui: &'a mut UI,
    ) -> Self {
        let main = json!({
            "metadata_name": "{{.Values.metadataName}}",
            "image": "{{.Values.imageName}}",
            "count": "{{.Values.instanceCount}}",
            "service_topology": "{{.Values.serviceTopology}}",
            "service_group": manifest.service_group.clone().map(|_| "{{.Values.serviceGroup}}"),
            "config": manifest.config
                .clone()
                .map(|_| "{{.Values.config}}"),
            "ring_secret_name": manifest.ring_secret_name
                .clone()
                .map(|_| "{{.Values.ringSecretName}}"),
            "bind": !manifest.binds.is_empty(),
        });

        let mut values = Values::new();
        values.add_entry("metadataName", &manifest.metadata_name);
        values.add_entry("imageName", &manifest.image);
        values.add_entry("instanceCount", &manifest.count.to_string());
        values.add_entry("serviceTopology", &manifest.service_topology.to_string());
        if let Some(ref group) = manifest.service_group {
            values.add_entry("serviceGroup", group);
        }
        if let Some(ref config) = manifest.config {
            values.add_entry("config", config);
        }
        if let Some(ref name) = manifest.ring_secret_name {
            values.add_entry("ringSecretName", name);
        }

        let mut binds = Vec::new();
        let mut i = 0;
        for bind in &manifest.binds {
            let name_var = format!("bindName{}", i);
            let service_var = format!("bindService{}", i);
            let group_var = format!("bindGroup{}", i);
            i = i + 1;

            values.add_entry(&name_var, &bind.name);
            values.add_entry(&service_var, &bind.service_group.service());
            values.add_entry(&group_var, &bind.service_group.group());

            let json = json!({
                "name": format!("{{{{.Values.{}}}}}", name_var),
                "service": format!("{{{{.Values.{}}}}}", service_var),
                "group": format!("{{{{.Values.{}}}}}", group_var),
            });

            binds.push(json);
        }

        let manifest_template = ManifestJson {
            main: main,
            binds: binds,
        };

        Chart {
            name,
            chartfile,
            manifest_template,
            values,
            ui,
        }
    }

    pub fn generate(mut self) -> Result<()> {
        self.ui.status(
            Status::Creating,
            format!("directory `{}`", self.name),
        )?;
        fs::create_dir_all(&self.name)?;

        self.generate_chartfile()?;

        let template_path = format!("{}/{}", self.name, "templates");
        self.ui.status(
            Status::Creating,
            format!("directory `{}`", template_path),
        )?;

        self.generate_values()?;

        fs::create_dir_all(&template_path)?;
        self.generate_manifest_template(&template_path)
    }

    pub fn generate_chartfile(&mut self) -> Result<()> {
        let path = format!("{}/Chart.yaml", self.name);
        self.ui.status(Status::Creating, format!("file `{}`", path))?;
        let mut write = fs::File::create(path)?;
        let out = self.chartfile.into_string()?;

        write.write(out.as_bytes())?;

        Ok(())
    }

    pub fn generate_manifest_template(self, template_path: &str) -> Result<()> {
        let manifest_path = format!("{}/{}.yaml", template_path, self.name);
        self.ui.status(
            Status::Creating,
            format!("file `{}`", manifest_path),
        )?;
        let mut write = fs::File::create(manifest_path)?;
        let out: String = self.manifest_template.into();

        write.write(out.as_bytes())?;

        Ok(())
    }

    pub fn generate_values(&mut self) -> Result<()> {
        let path = format!("{}/values.yaml", self.name);
        self.ui.status(Status::Creating, format!("file `{}`", path))?;
        let mut write = fs::File::create(path)?;

        self.values.generate(&mut write)?;

        Ok(())
    }
}
<|endoftext|>


<commit_before>/*
 * Copyright 2017 Sreejith Krishnan R
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
*/

use std::any::Any;

use super::super::{Layout, PresentationLayout, ConcreteLayout};
use ::draw::{Drawable, BoundingBox, MeasureMode};
use ::platform::Context;
use ::paint::{Canvas, Point, MathRuler, Rect};
use ::props::{DisplayStyle, MathSize};

pub struct MmultiscriptLayout {
    pub(crate) base_layout: Box<Layout>,
    pub(crate) prescript_layout: Vec<(Box<Layout>, Box<Layout>)>,
    pub(crate) postscript_layout: Vec<(Box<Layout>, Box<Layout>)>,
    pub(crate) subscript_shift: f32,
    pub(crate) superscript_shift: f32,

    pub(crate) presentation_layout: PresentationLayout,
}

impl Layout for MmultiscriptLayout {
    fn layout<'a>(&'a self, context: &Context) -> Box<Drawable + 'a> {
        let mut wrapper = self.presentation_layout.layout(context);

        wrapper.wrap(MultiscriptDrawable {
            base: self.base_layout.layout(context),
            base_pos: Point::new(0f32, 0f32),
            prescripts: self.prescript_layout.iter().map(|&(ref superscript, ref subscript)| {
                MultiscriptDrawableChild {
                    subscript: subscript.layout(context),
                    subscript_pos: Point::new(0f32, 0f32),
                    superscript: superscript.layout(context),
                    superscript_pos: Point::new(0f32, 0f32),
                }
            }).collect(),
            postscripts: self.postscript_layout.iter().map(|&(ref superscript, ref subscript)| {
                MultiscriptDrawableChild {
                    subscript: subscript.layout(context),
                    subscript_pos: Point::new(0f32, 0f32),
                    superscript: superscript.layout(context),
                    superscript_pos: Point::new(0f32, 0f32),
                }
            }).collect(),
            superscript_shift: self.superscript_shift,
            subscript_shift: self.subscript_shift,
            display_style: self.presentation_layout.display_style,
            base_size: self.presentation_layout.script_level.get_font_size(context, &MathSize::NORMAL),
            bounding_box: BoundingBox::default(),
        });

        wrapper.calculate(context, &MeasureMode::Wrap, &MeasureMode::Wrap);

        Box::new(wrapper)
    }

    fn as_any(&self) -> &Any {
        self
    }

    fn as_any_mut(&mut self) -> &mut Any {
        self
    }
}

struct MultiscriptDrawable<'a> {
    base: Box<Drawable + 'a>,
    base_pos: Point,

    prescripts: Vec<MultiscriptDrawableChild<'a>>,
    postscripts: Vec<MultiscriptDrawableChild<'a>>,

    superscript_shift: f32,
    subscript_shift: f32,
    display_style: DisplayStyle,
    base_size: f32,

    bounding_box: BoundingBox,
}

struct MultiscriptDrawableChild<'a> {
    subscript: Box<Drawable + 'a>,
    subscript_pos: Point,
    superscript: Box<Drawable + 'a>,
    superscript_pos: Point,
}

struct ScriptPosition {
    base_baseline_pos: f32,
    superscript_baseline_pos: f32,
    subscript_baseline_pos: f32
}

impl ScriptPosition {
    fn max(&self, rhs: &ScriptPosition) -> ScriptPosition {
        ScriptPosition {
            base_baseline_pos: self.base_baseline_pos.max(rhs.base_baseline_pos),
            superscript_baseline_pos: self.superscript_baseline_pos.max(rhs.superscript_baseline_pos),
            subscript_baseline_pos: self.subscript_baseline_pos.max(rhs.subscript_baseline_pos),
        }
    }
}

impl<'a> MultiscriptDrawable<'a> {
    fn find_script_y_pos(&self, child: &MultiscriptDrawableChild<'a>, ruler: &MathRuler) -> ScriptPosition {

        let base = &self.base;
        let subscript = &child.superscript;
        let superscript = &child.subscript;

        let has_subscript = subscript.bounding_box().width() > 0f32 || subscript.bounding_box().height() > 0f32;
        let has_superscript = superscript.bounding_box().width() > 0f32 || superscript.bounding_box().height() > 0f32;

        // We will fix the position of superscript first
        let superscript_y_pos = 0f32;

        let base_y_pos = if has_superscript {
            // Recommended shift between baseline of base and baseline of superscript
            let superscript_shift = if self.superscript_shift.is_nan() {
                if self.display_style {
                    ruler.superscript_shift_up()
                } else {
                    ruler.superscript_shift_up_cramped()
                }
            } else {
                self.superscript_shift
            };

            let mut base_y_pos = superscript.bounding_box().baseline_pos() +
                superscript_shift -
                base.bounding_box().baseline_pos();

            // Minimum shift between superscript bottom and baseline of base
            let superscript_bottom_min = ruler.superscript_bottom_min();

            base_y_pos = base_y_pos.max(
                superscript.bounding_box().height() +
                    superscript_bottom_min -
                    base.bounding_box().baseline_pos()
            );

            // Maximum drop of baseline of superscript from top of base
            let superscript_baseline_drop_max = ruler.superscript_baseline_drop_max();

            base_y_pos = base_y_pos.max(
                superscript.bounding_box().baseline_pos() - superscript_baseline_drop_max
            );

            if has_subscript {
                // Maximum shift between superscript bottom and baseline of base when subscript is present
                let superscript_bottom_max_with_subscript = ruler.superscript_bottom_max_with_subscript();
                base_y_pos = base_y_pos.min(
                    superscript.bounding_box().height() +
                        superscript_bottom_max_with_subscript -
                        base.bounding_box().baseline_pos()
                );
            }

            base_y_pos
        } else {
            0f32
        };

        let subscript_y_pos = if has_subscript {
            // Recommended shift between baseline of base and baseline of subscript
            let subscript_shift = if self.subscript_shift.is_nan() {
                ruler.subscript_shift_down()
            } else {
                self.subscript_shift
            };

            let mut subscript_y_pos = base_y_pos + base.bounding_box().baseline_pos() +
                subscript_shift -
                subscript.bounding_box().baseline_pos();

            // Minimum shift between baseline of subscript and bottom of base
            let subscript_baseline_drop_min = ruler.subscript_baseline_drop_min();

            subscript_y_pos = subscript_y_pos.max(
                base_y_pos + base.bounding_box().height() +
                    subscript_baseline_drop_min -
                    subscript.bounding_box().baseline_pos()
            );

            // Maximum shift between top of subscript and baseline of base
            let subscript_top_max = ruler.subscript_top_max();

            subscript_y_pos = subscript_y_pos.max(
                base_y_pos +
                    base.bounding_box().baseline_pos() -
                    subscript_top_max
            );

            if has_superscript {
                // Minimum gap between bottom of superscript and top of subscript
                let sub_superscript_gap_min = ruler.sub_superscript_gap_min();

                subscript_y_pos.max(
                    superscript_y_pos +
                        superscript.bounding_box().height() +
                        sub_superscript_gap_min
                );
            }

            subscript_y_pos
        } else {
            base_y_pos + base.bounding_box().height()
        };

        return ScriptPosition {
            base_baseline_pos: base_y_pos + base.bounding_box().baseline_pos(),
            superscript_baseline_pos: superscript_y_pos + superscript.bounding_box().baseline_pos(),
            subscript_baseline_pos: subscript_y_pos + subscript.bounding_box().baseline_pos(),
        }
    }

    fn find_max_script_y_pos(&self, scripts: &Vec<MultiscriptDrawableChild<'a>>, ruler: &MathRuler) -> ScriptPosition {
        let mut script_pos = ScriptPosition {
            base_baseline_pos: 0.0,
            superscript_baseline_pos: 0.0,
            subscript_baseline_pos: 0.0,
        };

        for prescript in scripts.iter() {
            let current_script_pos =
                self.find_script_y_pos(prescript, ruler);

            script_pos = script_pos.max(&current_script_pos);
        }

        return script_pos;
    }

    fn draw_scripts(&self, scripts: &Vec<MultiscriptDrawableChild<'a>>, canvas: &Canvas, pen_pos: &Point) {
        for script in scripts.iter() {
            script.superscript.draw(canvas, &(pen_pos+&script.superscript_pos));
            script.subscript.draw(canvas, &(pen_pos+&script.subscript_pos));
        }
    }

    fn set_script_positions(scripts: &mut Vec<MultiscriptDrawableChild<'a>>,
                            script_pos: &ScriptPosition, current_pen_x: f32) -> Rect {
        let mut pen_x = current_pen_x;
        let mut y_max = 0f32;
        for script in scripts.iter_mut() {
            let width = script.subscript.bounding_box().width()
                .max(script.superscript.bounding_box().width());

            script.subscript_pos = Point::new(
                pen_x+width-script.subscript.bounding_box().width(),
                script_pos.subscript_baseline_pos-script.subscript.bounding_box().baseline_pos());

            script.superscript_pos = Point::new(
                pen_x+width-script.superscript.bounding_box().width(),
                script_pos.superscript_baseline_pos-script.superscript.bounding_box().baseline_pos());

            pen_x += width;
            y_max = y_max.max(script_pos.subscript_baseline_pos+script.superscript.bounding_box().baseline())
        }

        return Rect::new(pen_x-current_pen_x, y_max);
    }
}

impl<'a> Drawable for MultiscriptDrawable<'a> {
    fn draw(&self, canvas: &Canvas, pen_pos: &Point) {
        self.draw_scripts(&self.prescripts, canvas, pen_pos);
        self.base.draw(canvas, &(pen_pos+&self.base_pos));
        self.draw_scripts(&self.postscripts, canvas, pen_pos);
    }

    fn calculate(&mut self, context: &Context, width_mode: &MeasureMode, height_mode: &MeasureMode) {
        self.base.calculate(context, width_mode, height_mode);

        let ruler = context.platform().get_math_ruler(self.base_size);

        let script_pos = self.find_max_script_y_pos(&self.prescripts, ruler)
            .max(&self.find_max_script_y_pos(&self.postscripts, ruler));

        let subscript_baseline_pos = script_pos.subscript_baseline_pos;

        let base_baseline_pos = script_pos.base_baseline_pos
            .max(self.base.bounding_box().baseline_pos());

        let superscript_baseline_pos = script_pos.superscript_baseline_pos;

        let mut pen_x = 0f32;

        let prescript_bounds = MultiscriptDrawable::set_script_positions(
            &mut self.prescripts, &script_pos, pen_x);
        pen_x += prescript_bounds.width();

        self.base_pos = Point::new(pen_x, base_baseline_pos-self.base.bounding_box().baseline_pos());
        pen_x += self.base.bounding_box().width();

        let postscript_bounds = MultiscriptDrawable::set_script_positions(
            &mut self.postscripts, &script_pos, pen_x);
        pen_x += postscript_bounds.width();

        let height = prescript_bounds.height()
            .max(postscript_bounds.height())
            .max(self.base.bounding_box().height());

        self.bounding_box = BoundingBox::new(
            Rect::new(pen_x, height),
            height - (base_baseline_pos),
            height - (self.base.bounding_box().axis_pos() - self.base.bounding_box().baseline_pos() + base_baseline_pos),
        );
    }

    fn bounding_box(&self) -> &BoundingBox {
        &self.bounding_box
    }
}<commit_msg>Fix ordering<commit_after>/*
 * Copyright 2017 Sreejith Krishnan R
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
*/

use std::any::Any;

use super::super::{Layout, PresentationLayout, ConcreteLayout};
use ::draw::{Drawable, BoundingBox, MeasureMode};
use ::platform::Context;
use ::paint::{Canvas, Point, MathRuler, Rect};
use ::props::{DisplayStyle, MathSize};

pub struct MmultiscriptLayout {
    pub(crate) base_layout: Box<Layout>,
    pub(crate) prescript_layout: Vec<(Box<Layout>, Box<Layout>)>,
    pub(crate) postscript_layout: Vec<(Box<Layout>, Box<Layout>)>,
    pub(crate) subscript_shift: f32,
    pub(crate) superscript_shift: f32,

    pub(crate) presentation_layout: PresentationLayout,
}

impl Layout for MmultiscriptLayout {
    fn layout<'a>(&'a self, context: &Context) -> Box<Drawable + 'a> {
        let mut wrapper = self.presentation_layout.layout(context);

        wrapper.wrap(MultiscriptDrawable {
            base: self.base_layout.layout(context),
            base_pos: Point::new(0f32, 0f32),
            prescripts: self.prescript_layout.iter().map(|&(ref superscript, ref subscript)| {
                MultiscriptDrawableChild {
                    subscript: subscript.layout(context),
                    subscript_pos: Point::new(0f32, 0f32),
                    superscript: superscript.layout(context),
                    superscript_pos: Point::new(0f32, 0f32),
                }
            }).collect(),
            postscripts: self.postscript_layout.iter().map(|&(ref superscript, ref subscript)| {
                MultiscriptDrawableChild {
                    subscript: subscript.layout(context),
                    subscript_pos: Point::new(0f32, 0f32),
                    superscript: superscript.layout(context),
                    superscript_pos: Point::new(0f32, 0f32),
                }
            }).collect(),
            superscript_shift: self.superscript_shift,
            subscript_shift: self.subscript_shift,
            display_style: self.presentation_layout.display_style,
            base_size: self.presentation_layout.script_level.get_font_size(context, &MathSize::NORMAL),
            bounding_box: BoundingBox::default(),
        });

        wrapper.calculate(context, &MeasureMode::Wrap, &MeasureMode::Wrap);

        Box::new(wrapper)
    }

    fn as_any(&self) -> &Any {
        self
    }

    fn as_any_mut(&mut self) -> &mut Any {
        self
    }
}

struct MultiscriptDrawable<'a> {
    base: Box<Drawable + 'a>,
    base_pos: Point,

    prescripts: Vec<MultiscriptDrawableChild<'a>>,
    postscripts: Vec<MultiscriptDrawableChild<'a>>,

    superscript_shift: f32,
    subscript_shift: f32,
    display_style: DisplayStyle,
    base_size: f32,

    bounding_box: BoundingBox,
}

struct MultiscriptDrawableChild<'a> {
    subscript: Box<Drawable + 'a>,
    subscript_pos: Point,
    superscript: Box<Drawable + 'a>,
    superscript_pos: Point,
}

struct ScriptPosition {
    base_baseline_pos: f32,
    superscript_baseline_pos: f32,
    subscript_baseline_pos: f32
}

impl ScriptPosition {
    fn max(&self, rhs: &ScriptPosition) -> ScriptPosition {
        ScriptPosition {
            base_baseline_pos: self.base_baseline_pos.max(rhs.base_baseline_pos),
            superscript_baseline_pos: self.superscript_baseline_pos.max(rhs.superscript_baseline_pos),
            subscript_baseline_pos: self.subscript_baseline_pos.max(rhs.subscript_baseline_pos),
        }
    }
}

impl<'a> MultiscriptDrawable<'a> {
    fn find_script_y_pos(&self, child: &MultiscriptDrawableChild<'a>, ruler: &MathRuler) -> ScriptPosition {

        let base = &self.base;
        let subscript = &child.subscript;
        let superscript = &child.superscript;

        let has_subscript = subscript.bounding_box().width() > 0f32 || subscript.bounding_box().height() > 0f32;
        let has_superscript = superscript.bounding_box().width() > 0f32 || superscript.bounding_box().height() > 0f32;

        // We will fix the position of superscript first
        let superscript_y_pos = 0f32;

        let base_y_pos = if has_superscript {
            // Recommended shift between baseline of base and baseline of superscript
            let superscript_shift = if self.superscript_shift.is_nan() {
                if self.display_style {
                    ruler.superscript_shift_up()
                } else {
                    ruler.superscript_shift_up_cramped()
                }
            } else {
                self.superscript_shift
            };

            let mut base_y_pos = superscript.bounding_box().baseline_pos() +
                superscript_shift -
                base.bounding_box().baseline_pos();

            // Minimum shift between superscript bottom and baseline of base
            let superscript_bottom_min = ruler.superscript_bottom_min();

            base_y_pos = base_y_pos.max(
                superscript.bounding_box().height() +
                    superscript_bottom_min -
                    base.bounding_box().baseline_pos()
            );

            // Maximum drop of baseline of superscript from top of base
            let superscript_baseline_drop_max = ruler.superscript_baseline_drop_max();

            base_y_pos = base_y_pos.max(
                superscript.bounding_box().baseline_pos() - superscript_baseline_drop_max
            );

            if has_subscript {
                // Maximum shift between superscript bottom and baseline of base when subscript is present
                let superscript_bottom_max_with_subscript = ruler.superscript_bottom_max_with_subscript();
                base_y_pos = base_y_pos.min(
                    superscript.bounding_box().height() +
                        superscript_bottom_max_with_subscript -
                        base.bounding_box().baseline_pos()
                );
            }

            base_y_pos
        } else {
            0f32
        };

        let subscript_y_pos = if has_subscript {
            // Recommended shift between baseline of base and baseline of subscript
            let subscript_shift = if self.subscript_shift.is_nan() {
                ruler.subscript_shift_down()
            } else {
                self.subscript_shift
            };

            let mut subscript_y_pos = base_y_pos + base.bounding_box().baseline_pos() +
                subscript_shift -
                subscript.bounding_box().baseline_pos();

            // Minimum shift between baseline of subscript and bottom of base
            let subscript_baseline_drop_min = ruler.subscript_baseline_drop_min();

            subscript_y_pos = subscript_y_pos.max(
                base_y_pos + base.bounding_box().height() +
                    subscript_baseline_drop_min -
                    subscript.bounding_box().baseline_pos()
            );

            // Maximum shift between top of subscript and baseline of base
            let subscript_top_max = ruler.subscript_top_max();

            subscript_y_pos = subscript_y_pos.max(
                base_y_pos +
                    base.bounding_box().baseline_pos() -
                    subscript_top_max
            );

            if has_superscript {
                // Minimum gap between bottom of superscript and top of subscript
                let sub_superscript_gap_min = ruler.sub_superscript_gap_min();

                subscript_y_pos.max(
                    superscript_y_pos +
                        superscript.bounding_box().height() +
                        sub_superscript_gap_min
                );
            }

            subscript_y_pos
        } else {
            base_y_pos + base.bounding_box().height()
        };

        return ScriptPosition {
            base_baseline_pos: base_y_pos + base.bounding_box().baseline_pos(),
            superscript_baseline_pos: superscript_y_pos + superscript.bounding_box().baseline_pos(),
            subscript_baseline_pos: subscript_y_pos + subscript.bounding_box().baseline_pos(),
        }
    }

    fn find_max_script_y_pos(&self, scripts: &Vec<MultiscriptDrawableChild<'a>>, ruler: &MathRuler) -> ScriptPosition {
        let mut script_pos = ScriptPosition {
            base_baseline_pos: 0.0,
            superscript_baseline_pos: 0.0,
            subscript_baseline_pos: 0.0,
        };

        for prescript in scripts.iter() {
            let current_script_pos =
                self.find_script_y_pos(prescript, ruler);

            script_pos = script_pos.max(&current_script_pos);
        }

        return script_pos;
    }

    fn draw_scripts(&self, scripts: &Vec<MultiscriptDrawableChild<'a>>, canvas: &Canvas, pen_pos: &Point) {
        for script in scripts.iter() {
            script.superscript.draw(canvas, &(pen_pos+&script.superscript_pos));
            script.subscript.draw(canvas, &(pen_pos+&script.subscript_pos));
        }
    }

    fn set_script_positions(scripts: &mut Vec<MultiscriptDrawableChild<'a>>,
                            script_pos: &ScriptPosition, current_pen_x: f32) -> Rect {
        let mut pen_x = current_pen_x;
        let mut y_max = 0f32;
        for script in scripts.iter_mut() {
            let width = script.subscript.bounding_box().width()
                .max(script.superscript.bounding_box().width());

            script.subscript_pos = Point::new(
                pen_x+width-script.subscript.bounding_box().width(),
                script_pos.subscript_baseline_pos-script.subscript.bounding_box().baseline_pos());

            script.superscript_pos = Point::new(
                pen_x+width-script.superscript.bounding_box().width(),
                script_pos.superscript_baseline_pos-script.superscript.bounding_box().baseline_pos());

            pen_x += width;
            y_max = y_max.max(script_pos.subscript_baseline_pos+script.superscript.bounding_box().baseline())
        }

        return Rect::new(pen_x-current_pen_x, y_max);
    }
}

impl<'a> Drawable for MultiscriptDrawable<'a> {
    fn draw(&self, canvas: &Canvas, pen_pos: &Point) {
        self.draw_scripts(&self.prescripts, canvas, pen_pos);
        self.base.draw(canvas, &(pen_pos+&self.base_pos));
        self.draw_scripts(&self.postscripts, canvas, pen_pos);
    }

    fn calculate(&mut self, context: &Context, width_mode: &MeasureMode, height_mode: &MeasureMode) {
        self.base.calculate(context, width_mode, height_mode);

        let ruler = context.platform().get_math_ruler(self.base_size);

        let script_pos = self.find_max_script_y_pos(&self.prescripts, ruler)
            .max(&self.find_max_script_y_pos(&self.postscripts, ruler));

        let subscript_baseline_pos = script_pos.subscript_baseline_pos;

        let base_baseline_pos = script_pos.base_baseline_pos
            .max(self.base.bounding_box().baseline_pos());

        let superscript_baseline_pos = script_pos.superscript_baseline_pos;

        let mut pen_x = 0f32;

        let prescript_bounds = MultiscriptDrawable::set_script_positions(
            &mut self.prescripts, &script_pos, pen_x);
        pen_x += prescript_bounds.width();

        self.base_pos = Point::new(pen_x, base_baseline_pos-self.base.bounding_box().baseline_pos());
        pen_x += self.base.bounding_box().width();

        let postscript_bounds = MultiscriptDrawable::set_script_positions(
            &mut self.postscripts, &script_pos, pen_x);
        pen_x += postscript_bounds.width();

        let height = prescript_bounds.height()
            .max(postscript_bounds.height())
            .max(self.base.bounding_box().height());

        self.bounding_box = BoundingBox::new(
            Rect::new(pen_x, height),
            height - (base_baseline_pos),
            height - (self.base.bounding_box().axis_pos() - self.base.bounding_box().baseline_pos() + base_baseline_pos),
        );
    }

    fn bounding_box(&self) -> &BoundingBox {
        &self.bounding_box
    }
}<|endoftext|>


<commit_before>extern crate chrono;
extern crate mysqlclient_sys as ffi;

use self::chrono::*;
use std::error::Error;
use std::io::Write;
use std::os::raw as libc;
use std::{ptr, mem, slice};

use mysql::Mysql;
use types::{ToSql, ToSqlOutput, FromSql, IsNull, Timestamp, Time, Date};

macro_rules! mysql_time_impls {
    ($ty:ty) => {
        impl ToSql<$ty, Mysql> for ffi::MYSQL_TIME {
            fn to_sql<W: Write>(&self, out: &mut ToSqlOutput<W, Mysql>) -> Result<IsNull, Box<Error+Send+Sync>> {
                let bytes = unsafe {
                    let bytes_ptr = self as *const ffi::MYSQL_TIME as *const u8;
                    slice::from_raw_parts(bytes_ptr, mem::size_of::<ffi::MYSQL_TIME>())
                };
                out.write_all(bytes)?;
                Ok(IsNull::No)
            }
        }

        impl FromSql<$ty, Mysql> for ffi::MYSQL_TIME {
            fn from_sql(bytes: Option<&[u8]>) -> Result<Self, Box<Error+Send+Sync>> {
                let bytes = not_none!(bytes);
                let bytes_ptr = bytes.as_ptr() as *const ffi::MYSQL_TIME;
                unsafe {
                    let mut result = mem::uninitialized();
                    ptr::copy_nonoverlapping(bytes_ptr, &mut result, 1);
                    if result.neg == 0 {
                        Ok(result)
                    } else {
                        Err("Negative dates/times are not yet supported".into())
                    }
                }
            }
        }
    }
}

mysql_time_impls!(Timestamp);
mysql_time_impls!(Time);
mysql_time_impls!(Date);

impl ToSql<Timestamp, Mysql> for NaiveDateTime {
    fn to_sql<W: Write>(&self, out: &mut ToSqlOutput<W, Mysql>) -> Result<IsNull, Box<Error+Send+Sync>> {
        let mut mysql_time: ffi::MYSQL_TIME = unsafe { mem::zeroed() };

        mysql_time.year = self.year() as libc::c_uint;
        mysql_time.month = self.month() as libc::c_uint;
        mysql_time.day = self.day() as libc::c_uint;
        mysql_time.hour = self.hour() as libc::c_uint;
        mysql_time.minute = self.minute() as libc::c_uint;
        mysql_time.second = self.second() as libc::c_uint;
        mysql_time.second_part = self.timestamp_subsec_micros() as libc::c_ulong;

        <ffi::MYSQL_TIME as ToSql<Timestamp, Mysql>>::to_sql(&mysql_time, out)
    }
}

impl FromSql<Timestamp, Mysql> for NaiveDateTime {
    fn from_sql(bytes: Option<&[u8]>) -> Result<Self, Box<Error+Send+Sync>> {
        let mysql_time = <ffi::MYSQL_TIME as FromSql<Timestamp, Mysql>>::from_sql(bytes)?;
        Ok(NaiveDate::from_ymd(
            mysql_time.year as i32,
            mysql_time.month as u32,
            mysql_time.day as u32,
        ).and_hms_micro(
            mysql_time.hour as u32,
            mysql_time.minute as u32,
            mysql_time.second as u32,
            mysql_time.second_part as u32,
        ))
    }
}

impl ToSql<Time, Mysql> for NaiveTime {
    fn to_sql<W: Write>(&self, out: &mut ToSqlOutput<W, Mysql>) -> Result<IsNull, Box<Error+Send+Sync>> {
        let mut mysql_time: ffi::MYSQL_TIME = unsafe { mem::zeroed() };

        mysql_time.hour = self.hour() as libc::c_uint;
        mysql_time.minute = self.minute() as libc::c_uint;
        mysql_time.second = self.second() as libc::c_uint;

        <ffi::MYSQL_TIME as ToSql<Time, Mysql>>::to_sql(&mysql_time, out)
    }
}

impl FromSql<Time, Mysql> for NaiveTime {
    fn from_sql(bytes: Option<&[u8]>) -> Result<Self, Box<Error+Send+Sync>> {
        let mysql_time = <ffi::MYSQL_TIME as FromSql<Time, Mysql>>::from_sql(bytes)?;
        Ok(NaiveTime::from_hms(
            mysql_time.hour as u32,
            mysql_time.minute as u32,
            mysql_time.second as u32,
        ))
    }
}

impl ToSql<Date, Mysql> for NaiveDate {
    fn to_sql<W: Write>(&self, out: &mut ToSqlOutput<W, Mysql>) -> Result<IsNull, Box<Error+Send+Sync>> {
        let mut mysql_time: ffi::MYSQL_TIME = unsafe { mem::zeroed() };

        mysql_time.year = self.year() as libc::c_uint;
        mysql_time.month = self.month() as libc::c_uint;
        mysql_time.day = self.day() as libc::c_uint;

        <ffi::MYSQL_TIME as ToSql<Date, Mysql>>::to_sql(&mysql_time, out)
    }
}

impl FromSql<Date, Mysql> for NaiveDate {
    fn from_sql(bytes: Option<&[u8]>) -> Result<Self, Box<Error+Send+Sync>> {
        let mysql_time = <ffi::MYSQL_TIME as FromSql<Date, Mysql>>::from_sql(bytes)?;
        Ok(NaiveDate::from_ymd(
            mysql_time.year as i32,
            mysql_time.month as u32,
            mysql_time.day as u32,
        ))
    }
}

#[cfg(test)]
mod tests {
    extern crate dotenv;
    extern crate chrono;

    use self::chrono::{Duration, NaiveDate, NaiveTime, Utc, TimeZone, FixedOffset};
    use self::dotenv::dotenv;

    use expression::dsl::{sql, now};
    use prelude::*;
    use select;
    use types::{Date, Time, Timestamp};

    fn connection() -> MysqlConnection {
        dotenv().ok();

        let connection_url = ::std::env::var("MYSQL_UNIT_TEST_DATABASE_URL")
            .or_else(|_| ::std::env::var("MYSQL_DATABASE_URL"))
            .or_else(|_| ::std::env::var("DATABASE_URL"))
            .expect("DATABASE_URL must be set in order to run tests");
        MysqlConnection::establish(&connection_url).unwrap()
    }

    #[test]
    fn unix_epoch_encodes_correctly() {
        let connection = connection();
        let time = NaiveDate::from_ymd(1970, 1, 1).and_hms(0, 0, 0);
        let query = select(sql::<Timestamp>("CAST('1970-01-01' AS DATETIME)").eq(time));
        assert!(query.get_result::<bool>(&connection).unwrap());
    }

    #[test]
    fn unix_epoch_decodes_correctly() {
        let connection = connection();
        let time = NaiveDate::from_ymd(1970, 1, 1).and_hms(0, 0, 0);
        let epoch_from_sql = select(sql::<Timestamp>("CAST('1970-01-01' AS DATETIME)"))
            .get_result(&connection);
        assert_eq!(Ok(time), epoch_from_sql);
    }

    #[test]
    fn times_relative_to_now_encode_correctly() {
        let connection = connection();
        let time = Utc::now().naive_utc() + Duration::days(1);
        let query = select(now.lt(time));
        assert!(query.get_result::<bool>(&connection).unwrap());

        let time = Utc::now().naive_utc() - Duration::days(1);
        let query = select(now.gt(time));
        assert!(query.get_result::<bool>(&connection).unwrap());
    }

    #[test]
    fn times_of_day_encode_correctly() {
        let connection = connection();

        let midnight = NaiveTime::from_hms(0, 0, 0);
        let query = select(sql::<Time>("CAST('00:00:00' AS TIME)").eq(midnight));
        assert!(query.get_result::<bool>(&connection).unwrap());

        let noon = NaiveTime::from_hms(12, 0, 0);
        let query = select(sql::<Time>("CAST('12:00:00' AS TIME)").eq(noon));
        assert!(query.get_result::<bool>(&connection).unwrap());

        let roughly_half_past_eleven = NaiveTime::from_hms(23, 37, 4);
        let query = select(sql::<Time>("CAST('23:37:04' AS TIME)").eq(roughly_half_past_eleven));
        assert!(query.get_result::<bool>(&connection).unwrap());
    }

    #[test]
    fn times_of_day_decode_correctly() {
        let connection = connection();
        let midnight = NaiveTime::from_hms(0, 0, 0);
        let query = select(sql::<Time>("CAST('00:00:00' AS TIME)"));
        assert_eq!(Ok(midnight), query.get_result::<NaiveTime>(&connection));

        let noon = NaiveTime::from_hms(12, 0, 0);
        let query = select(sql::<Time>("CAST('12:00:00' AS TIME)"));
        assert_eq!(Ok(noon), query.get_result::<NaiveTime>(&connection));

        let roughly_half_past_eleven = NaiveTime::from_hms(23, 37, 4);
        let query = select(sql::<Time>("CAST('23:37:04' AS TIME)"));
        assert_eq!(Ok(roughly_half_past_eleven), query.get_result::<NaiveTime>(&connection));
    }

    #[test]
    fn dates_encode_correctly() {
        let connection = connection();
        let january_first_2000 = NaiveDate::from_ymd(2000, 1, 1);
        let query = select(sql::<Date>("CAST('2000-1-1' AS DATE)").eq(january_first_2000));
        assert!(query.get_result::<bool>(&connection).unwrap());

        let january_first_2018 = NaiveDate::from_ymd(2018, 1, 1);
        let query = select(sql::<Date>("CAST('2018-1-1' AS DATE)").eq(january_first_2018));
        assert!(query.get_result::<bool>(&connection).unwrap());
    }

    #[test]
    fn dates_decode_correctly() {
        let connection = connection();
        let january_first_2000 = NaiveDate::from_ymd(2000, 1, 1);
        let query = select(sql::<Date>("CAST('2000-1-1' AS DATE)"));
        assert_eq!(Ok(january_first_2000), query.get_result::<NaiveDate>(&connection));

        let january_first_2018 = NaiveDate::from_ymd(2018, 1, 1);
        let query = select(sql::<Date>("CAST('2018-1-1' AS DATE)"));
        assert_eq!(Ok(january_first_2018), query.get_result::<NaiveDate>(&connection));
    }
}
<commit_msg>Fix warnings<commit_after>extern crate chrono;
extern crate mysqlclient_sys as ffi;

use self::chrono::*;
use std::error::Error;
use std::io::Write;
use std::os::raw as libc;
use std::{ptr, mem, slice};

use mysql::Mysql;
use types::{ToSql, ToSqlOutput, FromSql, IsNull, Timestamp, Time, Date};

macro_rules! mysql_time_impls {
    ($ty:ty) => {
        impl ToSql<$ty, Mysql> for ffi::MYSQL_TIME {
            fn to_sql<W: Write>(&self, out: &mut ToSqlOutput<W, Mysql>) -> Result<IsNull, Box<Error+Send+Sync>> {
                let bytes = unsafe {
                    let bytes_ptr = self as *const ffi::MYSQL_TIME as *const u8;
                    slice::from_raw_parts(bytes_ptr, mem::size_of::<ffi::MYSQL_TIME>())
                };
                out.write_all(bytes)?;
                Ok(IsNull::No)
            }
        }

        impl FromSql<$ty, Mysql> for ffi::MYSQL_TIME {
            fn from_sql(bytes: Option<&[u8]>) -> Result<Self, Box<Error+Send+Sync>> {
                let bytes = not_none!(bytes);
                let bytes_ptr = bytes.as_ptr() as *const ffi::MYSQL_TIME;
                unsafe {
                    let mut result = mem::uninitialized();
                    ptr::copy_nonoverlapping(bytes_ptr, &mut result, 1);
                    if result.neg == 0 {
                        Ok(result)
                    } else {
                        Err("Negative dates/times are not yet supported".into())
                    }
                }
            }
        }
    }
}

mysql_time_impls!(Timestamp);
mysql_time_impls!(Time);
mysql_time_impls!(Date);

impl ToSql<Timestamp, Mysql> for NaiveDateTime {
    fn to_sql<W: Write>(&self, out: &mut ToSqlOutput<W, Mysql>) -> Result<IsNull, Box<Error+Send+Sync>> {
        let mut mysql_time: ffi::MYSQL_TIME = unsafe { mem::zeroed() };

        mysql_time.year = self.year() as libc::c_uint;
        mysql_time.month = self.month() as libc::c_uint;
        mysql_time.day = self.day() as libc::c_uint;
        mysql_time.hour = self.hour() as libc::c_uint;
        mysql_time.minute = self.minute() as libc::c_uint;
        mysql_time.second = self.second() as libc::c_uint;
        mysql_time.second_part = self.timestamp_subsec_micros() as libc::c_ulong;

        <ffi::MYSQL_TIME as ToSql<Timestamp, Mysql>>::to_sql(&mysql_time, out)
    }
}

impl FromSql<Timestamp, Mysql> for NaiveDateTime {
    fn from_sql(bytes: Option<&[u8]>) -> Result<Self, Box<Error+Send+Sync>> {
        let mysql_time = <ffi::MYSQL_TIME as FromSql<Timestamp, Mysql>>::from_sql(bytes)?;
        Ok(NaiveDate::from_ymd(
            mysql_time.year as i32,
            mysql_time.month as u32,
            mysql_time.day as u32,
        ).and_hms_micro(
            mysql_time.hour as u32,
            mysql_time.minute as u32,
            mysql_time.second as u32,
            mysql_time.second_part as u32,
        ))
    }
}

impl ToSql<Time, Mysql> for NaiveTime {
    fn to_sql<W: Write>(&self, out: &mut ToSqlOutput<W, Mysql>) -> Result<IsNull, Box<Error+Send+Sync>> {
        let mut mysql_time: ffi::MYSQL_TIME = unsafe { mem::zeroed() };

        mysql_time.hour = self.hour() as libc::c_uint;
        mysql_time.minute = self.minute() as libc::c_uint;
        mysql_time.second = self.second() as libc::c_uint;

        <ffi::MYSQL_TIME as ToSql<Time, Mysql>>::to_sql(&mysql_time, out)
    }
}

impl FromSql<Time, Mysql> for NaiveTime {
    fn from_sql(bytes: Option<&[u8]>) -> Result<Self, Box<Error+Send+Sync>> {
        let mysql_time = <ffi::MYSQL_TIME as FromSql<Time, Mysql>>::from_sql(bytes)?;
        Ok(NaiveTime::from_hms(
            mysql_time.hour as u32,
            mysql_time.minute as u32,
            mysql_time.second as u32,
        ))
    }
}

impl ToSql<Date, Mysql> for NaiveDate {
    fn to_sql<W: Write>(&self, out: &mut ToSqlOutput<W, Mysql>) -> Result<IsNull, Box<Error+Send+Sync>> {
        let mut mysql_time: ffi::MYSQL_TIME = unsafe { mem::zeroed() };

        mysql_time.year = self.year() as libc::c_uint;
        mysql_time.month = self.month() as libc::c_uint;
        mysql_time.day = self.day() as libc::c_uint;

        <ffi::MYSQL_TIME as ToSql<Date, Mysql>>::to_sql(&mysql_time, out)
    }
}

impl FromSql<Date, Mysql> for NaiveDate {
    fn from_sql(bytes: Option<&[u8]>) -> Result<Self, Box<Error+Send+Sync>> {
        let mysql_time = <ffi::MYSQL_TIME as FromSql<Date, Mysql>>::from_sql(bytes)?;
        Ok(NaiveDate::from_ymd(
            mysql_time.year as i32,
            mysql_time.month as u32,
            mysql_time.day as u32,
        ))
    }
}

#[cfg(test)]
mod tests {
    extern crate dotenv;
    extern crate chrono;

    use self::chrono::{Duration, NaiveDate, NaiveTime, Utc};
    use self::dotenv::dotenv;

    use expression::dsl::{sql, now};
    use prelude::*;
    use select;
    use types::{Date, Time, Timestamp};

    fn connection() -> MysqlConnection {
        dotenv().ok();

        let connection_url = ::std::env::var("MYSQL_UNIT_TEST_DATABASE_URL")
            .or_else(|_| ::std::env::var("MYSQL_DATABASE_URL"))
            .or_else(|_| ::std::env::var("DATABASE_URL"))
            .expect("DATABASE_URL must be set in order to run tests");
        MysqlConnection::establish(&connection_url).unwrap()
    }

    #[test]
    fn unix_epoch_encodes_correctly() {
        let connection = connection();
        let time = NaiveDate::from_ymd(1970, 1, 1).and_hms(0, 0, 0);
        let query = select(sql::<Timestamp>("CAST('1970-01-01' AS DATETIME)").eq(time));
        assert!(query.get_result::<bool>(&connection).unwrap());
    }

    #[test]
    fn unix_epoch_decodes_correctly() {
        let connection = connection();
        let time = NaiveDate::from_ymd(1970, 1, 1).and_hms(0, 0, 0);
        let epoch_from_sql = select(sql::<Timestamp>("CAST('1970-01-01' AS DATETIME)"))
            .get_result(&connection);
        assert_eq!(Ok(time), epoch_from_sql);
    }

    #[test]
    fn times_relative_to_now_encode_correctly() {
        let connection = connection();
        let time = Utc::now().naive_utc() + Duration::days(1);
        let query = select(now.lt(time));
        assert!(query.get_result::<bool>(&connection).unwrap());

        let time = Utc::now().naive_utc() - Duration::days(1);
        let query = select(now.gt(time));
        assert!(query.get_result::<bool>(&connection).unwrap());
    }

    #[test]
    fn times_of_day_encode_correctly() {
        let connection = connection();

        let midnight = NaiveTime::from_hms(0, 0, 0);
        let query = select(sql::<Time>("CAST('00:00:00' AS TIME)").eq(midnight));
        assert!(query.get_result::<bool>(&connection).unwrap());

        let noon = NaiveTime::from_hms(12, 0, 0);
        let query = select(sql::<Time>("CAST('12:00:00' AS TIME)").eq(noon));
        assert!(query.get_result::<bool>(&connection).unwrap());

        let roughly_half_past_eleven = NaiveTime::from_hms(23, 37, 4);
        let query = select(sql::<Time>("CAST('23:37:04' AS TIME)").eq(roughly_half_past_eleven));
        assert!(query.get_result::<bool>(&connection).unwrap());
    }

    #[test]
    fn times_of_day_decode_correctly() {
        let connection = connection();
        let midnight = NaiveTime::from_hms(0, 0, 0);
        let query = select(sql::<Time>("CAST('00:00:00' AS TIME)"));
        assert_eq!(Ok(midnight), query.get_result::<NaiveTime>(&connection));

        let noon = NaiveTime::from_hms(12, 0, 0);
        let query = select(sql::<Time>("CAST('12:00:00' AS TIME)"));
        assert_eq!(Ok(noon), query.get_result::<NaiveTime>(&connection));

        let roughly_half_past_eleven = NaiveTime::from_hms(23, 37, 4);
        let query = select(sql::<Time>("CAST('23:37:04' AS TIME)"));
        assert_eq!(Ok(roughly_half_past_eleven), query.get_result::<NaiveTime>(&connection));
    }

    #[test]
    fn dates_encode_correctly() {
        let connection = connection();
        let january_first_2000 = NaiveDate::from_ymd(2000, 1, 1);
        let query = select(sql::<Date>("CAST('2000-1-1' AS DATE)").eq(january_first_2000));
        assert!(query.get_result::<bool>(&connection).unwrap());

        let january_first_2018 = NaiveDate::from_ymd(2018, 1, 1);
        let query = select(sql::<Date>("CAST('2018-1-1' AS DATE)").eq(january_first_2018));
        assert!(query.get_result::<bool>(&connection).unwrap());
    }

    #[test]
    fn dates_decode_correctly() {
        let connection = connection();
        let january_first_2000 = NaiveDate::from_ymd(2000, 1, 1);
        let query = select(sql::<Date>("CAST('2000-1-1' AS DATE)"));
        assert_eq!(Ok(january_first_2000), query.get_result::<NaiveDate>(&connection));

        let january_first_2018 = NaiveDate::from_ymd(2018, 1, 1);
        let query = select(sql::<Date>("CAST('2018-1-1' AS DATE)"));
        assert_eq!(Ok(january_first_2018), query.get_result::<NaiveDate>(&connection));
    }
}
<|endoftext|>