<commit_before>// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! This module contains types and functions to support formatting specific line ranges.

use std::{cmp, iter, path, str};

use itertools::Itertools;
use multimap::MultiMap;
use rustc_serialize::{self, json};

use codemap::LineRange;

/// A range that is inclusive of both ends.
#[derive(Clone, Copy, Debug, Eq, PartialEq, RustcDecodable)]
struct Range {
    pub lo: usize,
    pub hi: usize,
}

impl<'a> From<&'a LineRange> for Range {
    fn from(range: &'a LineRange) -> Range {
        Range::new(range.lo, range.hi)
    }
}

impl Range {
    fn new(lo: usize, hi: usize) -> Range {
        Range { lo: lo, hi: hi }
    }

    fn is_empty(self) -> bool {
        self.lo > self.hi
    }

    fn contains(self, other: Range) -> bool {
        if other.is_empty() {
            true
        } else {
            !self.is_empty() && self.lo <= other.lo && self.hi >= other.hi
        }
    }

    fn intersects(self, other: Range) -> bool {
        if self.is_empty() || other.is_empty() {
            false
        } else {
            (self.lo <= other.hi && other.hi <= self.hi) ||
            (other.lo <= self.hi && self.hi <= other.hi)
        }
    }

    fn adjacent_to(self, other: Range) -> bool {
        if self.is_empty() || other.is_empty() {
            false
        } else {
            self.hi + 1 == other.lo || other.hi + 1 == self.lo
        }
    }

    /// Returns a new `Range` with lines from `self` and `other` if they were adjacent or
    /// intersect; returns `None` otherwise.
    fn merge(self, other: Range) -> Option<Range> {
        if self.adjacent_to(other) || self.intersects(other) {
            Some(Range::new(cmp::min(self.lo, other.lo), cmp::max(self.hi, other.hi)))
        } else {
            None
        }
    }
}

/// A set of lines in files.
///
/// It is represented as a multimap keyed on file names, with values a collection of
/// non-overlapping ranges sorted by their start point. An inner `None` is interpreted to mean all
/// lines in all files.
#[derive(Clone, Debug, Default)]
pub struct FileLines(Option<MultiMap<String, Range>>);

/// Normalizes the ranges so that the invariants for `FileLines` hold: ranges are non-overlapping,
/// and ordered by their start point.
fn normalize_ranges(map: &mut MultiMap<String, Range>) {
    for (_, ranges) in map.iter_all_mut() {
        ranges.sort_by_key(|x| x.lo);
        let merged = ranges
            .drain(..)
            .coalesce(|x, y| x.merge(y).ok_or((x, y)))
            .collect();
        *ranges = merged;
    }
}

impl FileLines {
    /// Creates a `FileLines` that contains all lines in all files.
    pub fn all() -> FileLines {
        FileLines(None)
    }

    /// Creates a `FileLines` from a `MultiMap`, ensuring that the invariants hold.
    fn from_multimap(map: MultiMap<String, Range>) -> FileLines {
        let mut map = map;
        normalize_ranges(&mut map);
        FileLines(Some(map))
    }

    /// Returns an iterator over the files contained in `self`.
    pub fn files(&self) -> Files {
        Files(self.0.as_ref().map(MultiMap::keys))
    }

    /// Returns true if `range` is fully contained in `self`.
    pub fn contains(&self, range: &LineRange) -> bool {
        let map = match self.0 {
            // `None` means "all lines in all files".
            None => return true,
            Some(ref map) => map,
        };

        match canonicalize_path_string(range.file_name()).and_then(|canonical| {
                                                                       map.get_vec(&canonical)
                                                                           .ok_or(())
                                                                   }) {
            Ok(ranges) => ranges.iter().any(|r| r.contains(Range::from(range))),
            Err(_) => false,
        }
    }

    /// Returns true if any lines in `range` are in `self`.
    pub fn intersects(&self, range: &LineRange) -> bool {
        let map = match self.0 {
            // `None` means "all lines in all files".
            None => return true,
            Some(ref map) => map,
        };

        match map.get_vec(range.file_name()) {
            None => false,
            Some(ranges) => ranges.iter().any(|r| r.intersects(Range::from(range))),
        }
    }
}

/// FileLines files iterator.
pub struct Files<'a>(Option<::std::collections::hash_map::Keys<'a, String, Vec<Range>>>);

impl<'a> iter::Iterator for Files<'a> {
    type Item = &'a String;

    fn next(&mut self) -> Option<&'a String> {
        self.0.as_mut().and_then(Iterator::next)
    }
}

fn canonicalize_path_string(s: &str) -> Result<String, ()> {
    match path::PathBuf::from(s).canonicalize() {
        Ok(canonicalized) => canonicalized.to_str().map(|s| s.to_string()).ok_or(()),
        _ => Err(()),
    }
}

// This impl is needed for `Config::override_value` to work for use in tests.
impl str::FromStr for FileLines {
    type Err = String;

    fn from_str(s: &str) -> Result<FileLines, String> {
        let v: Vec<JsonSpan> = try!(json::decode(s).map_err(|e| e.to_string()));
        let m = try!(v.into_iter().map(JsonSpan::into_tuple).collect());
        Ok(FileLines::from_multimap(m))
    }
}

// For JSON decoding.
#[derive(Clone, Debug, RustcDecodable)]
struct JsonSpan {
    file: String,
    range: (usize, usize),
}

impl JsonSpan {
    // To allow `collect()`ing into a `MultiMap`.
    fn into_tuple(self) -> Result<(String, Range), String> {
        let (lo, hi) = self.range;
        let canonical = try!(canonicalize_path_string(&self.file).map_err(|_| {
            format!("Can't canonicalize {}", &self.file)
        }));
        Ok((canonical, Range::new(lo, hi)))
    }
}

// This impl is needed for inclusion in the `Config` struct. We don't have a toml representation
// for `FileLines`, so it will just panic instead.
impl rustc_serialize::Decodable for FileLines {
    fn decode<D: rustc_serialize::Decoder>(_: &mut D) -> Result<Self, D::Error> {
        panic!("FileLines cannot be deserialized from a project rustfmt.toml file: please \
                specify it via the `--file-lines` option instead");
    }
}

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

    #[test]
    fn test_range_intersects() {
        assert!(Range::new(1, 2).intersects(Range::new(1, 1)));
        assert!(Range::new(1, 2).intersects(Range::new(2, 2)));
        assert!(!Range::new(1, 2).intersects(Range::new(0, 0)));
        assert!(!Range::new(1, 2).intersects(Range::new(3, 10)));
        assert!(!Range::new(1, 3).intersects(Range::new(5, 5)));
    }

    #[test]
    fn test_range_adjacent_to() {
        assert!(!Range::new(1, 2).adjacent_to(Range::new(1, 1)));
        assert!(!Range::new(1, 2).adjacent_to(Range::new(2, 2)));
        assert!(Range::new(1, 2).adjacent_to(Range::new(0, 0)));
        assert!(Range::new(1, 2).adjacent_to(Range::new(3, 10)));
        assert!(!Range::new(1, 3).adjacent_to(Range::new(5, 5)));
    }

    #[test]
    fn test_range_contains() {
        assert!(Range::new(1, 2).contains(Range::new(1, 1)));
        assert!(Range::new(1, 2).contains(Range::new(2, 2)));
        assert!(!Range::new(1, 2).contains(Range::new(0, 0)));
        assert!(!Range::new(1, 2).contains(Range::new(3, 10)));
    }

    #[test]
    fn test_range_merge() {
        assert_eq!(None, Range::new(1, 3).merge(Range::new(5, 5)));
        assert_eq!(None, Range::new(4, 7).merge(Range::new(0, 1)));
        assert_eq!(Some(Range::new(3, 7)),
                   Range::new(3, 5).merge(Range::new(4, 7)));
        assert_eq!(Some(Range::new(3, 7)),
                   Range::new(3, 5).merge(Range::new(5, 7)));
        assert_eq!(Some(Range::new(3, 7)),
                   Range::new(3, 5).merge(Range::new(6, 7)));
        assert_eq!(Some(Range::new(3, 7)),
                   Range::new(3, 7).merge(Range::new(4, 5)));
    }
}
<commit_msg>Canonicalize range path in FileLines::intersects()<commit_after>// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! This module contains types and functions to support formatting specific line ranges.

use std::{cmp, iter, path, str};

use itertools::Itertools;
use multimap::MultiMap;
use rustc_serialize::{self, json};

use codemap::LineRange;

/// A range that is inclusive of both ends.
#[derive(Clone, Copy, Debug, Eq, PartialEq, RustcDecodable)]
struct Range {
    pub lo: usize,
    pub hi: usize,
}

impl<'a> From<&'a LineRange> for Range {
    fn from(range: &'a LineRange) -> Range {
        Range::new(range.lo, range.hi)
    }
}

impl Range {
    fn new(lo: usize, hi: usize) -> Range {
        Range { lo: lo, hi: hi }
    }

    fn is_empty(self) -> bool {
        self.lo > self.hi
    }

    fn contains(self, other: Range) -> bool {
        if other.is_empty() {
            true
        } else {
            !self.is_empty() && self.lo <= other.lo && self.hi >= other.hi
        }
    }

    fn intersects(self, other: Range) -> bool {
        if self.is_empty() || other.is_empty() {
            false
        } else {
            (self.lo <= other.hi && other.hi <= self.hi) ||
            (other.lo <= self.hi && self.hi <= other.hi)
        }
    }

    fn adjacent_to(self, other: Range) -> bool {
        if self.is_empty() || other.is_empty() {
            false
        } else {
            self.hi + 1 == other.lo || other.hi + 1 == self.lo
        }
    }

    /// Returns a new `Range` with lines from `self` and `other` if they were adjacent or
    /// intersect; returns `None` otherwise.
    fn merge(self, other: Range) -> Option<Range> {
        if self.adjacent_to(other) || self.intersects(other) {
            Some(Range::new(cmp::min(self.lo, other.lo), cmp::max(self.hi, other.hi)))
        } else {
            None
        }
    }
}

/// A set of lines in files.
///
/// It is represented as a multimap keyed on file names, with values a collection of
/// non-overlapping ranges sorted by their start point. An inner `None` is interpreted to mean all
/// lines in all files.
#[derive(Clone, Debug, Default)]
pub struct FileLines(Option<MultiMap<String, Range>>);

/// Normalizes the ranges so that the invariants for `FileLines` hold: ranges are non-overlapping,
/// and ordered by their start point.
fn normalize_ranges(map: &mut MultiMap<String, Range>) {
    for (_, ranges) in map.iter_all_mut() {
        ranges.sort_by_key(|x| x.lo);
        let merged = ranges
            .drain(..)
            .coalesce(|x, y| x.merge(y).ok_or((x, y)))
            .collect();
        *ranges = merged;
    }
}

impl FileLines {
    /// Creates a `FileLines` that contains all lines in all files.
    pub fn all() -> FileLines {
        FileLines(None)
    }

    /// Creates a `FileLines` from a `MultiMap`, ensuring that the invariants hold.
    fn from_multimap(map: MultiMap<String, Range>) -> FileLines {
        let mut map = map;
        normalize_ranges(&mut map);
        FileLines(Some(map))
    }

    /// Returns an iterator over the files contained in `self`.
    pub fn files(&self) -> Files {
        Files(self.0.as_ref().map(MultiMap::keys))
    }

    /// Returns true if `range` is fully contained in `self`.
    pub fn contains(&self, range: &LineRange) -> bool {
        let map = match self.0 {
            // `None` means "all lines in all files".
            None => return true,
            Some(ref map) => map,
        };

        match canonicalize_path_string(range.file_name()).and_then(|canonical| {
                                                                       map.get_vec(&canonical)
                                                                           .ok_or(())
                                                                   }) {
            Ok(ranges) => ranges.iter().any(|r| r.contains(Range::from(range))),
            Err(_) => false,
        }
    }

    /// Returns true if any lines in `range` are in `self`.
    pub fn intersects(&self, range: &LineRange) -> bool {
        let map = match self.0 {
            // `None` means "all lines in all files".
            None => return true,
            Some(ref map) => map,
        };

        match canonicalize_path_string(range.file_name()).and_then(|canonical| {
                                                                       map.get_vec(&canonical)
                                                                           .ok_or(())
                                                                   }) {
            Ok(ranges) => ranges.iter().any(|r| r.intersects(Range::from(range))),
            Err(_) => false,
        }
    }
}

/// FileLines files iterator.
pub struct Files<'a>(Option<::std::collections::hash_map::Keys<'a, String, Vec<Range>>>);

impl<'a> iter::Iterator for Files<'a> {
    type Item = &'a String;

    fn next(&mut self) -> Option<&'a String> {
        self.0.as_mut().and_then(Iterator::next)
    }
}

fn canonicalize_path_string(s: &str) -> Result<String, ()> {
    match path::PathBuf::from(s).canonicalize() {
        Ok(canonicalized) => canonicalized.to_str().map(|s| s.to_string()).ok_or(()),
        _ => Err(()),
    }
}

// This impl is needed for `Config::override_value` to work for use in tests.
impl str::FromStr for FileLines {
    type Err = String;

    fn from_str(s: &str) -> Result<FileLines, String> {
        let v: Vec<JsonSpan> = try!(json::decode(s).map_err(|e| e.to_string()));
        let m = try!(v.into_iter().map(JsonSpan::into_tuple).collect());
        Ok(FileLines::from_multimap(m))
    }
}

// For JSON decoding.
#[derive(Clone, Debug, RustcDecodable)]
struct JsonSpan {
    file: String,
    range: (usize, usize),
}

impl JsonSpan {
    // To allow `collect()`ing into a `MultiMap`.
    fn into_tuple(self) -> Result<(String, Range), String> {
        let (lo, hi) = self.range;
        let canonical = try!(canonicalize_path_string(&self.file).map_err(|_| {
            format!("Can't canonicalize {}", &self.file)
        }));
        Ok((canonical, Range::new(lo, hi)))
    }
}

// This impl is needed for inclusion in the `Config` struct. We don't have a toml representation
// for `FileLines`, so it will just panic instead.
impl rustc_serialize::Decodable for FileLines {
    fn decode<D: rustc_serialize::Decoder>(_: &mut D) -> Result<Self, D::Error> {
        panic!("FileLines cannot be deserialized from a project rustfmt.toml file: please \
                specify it via the `--file-lines` option instead");
    }
}

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

    #[test]
    fn test_range_intersects() {
        assert!(Range::new(1, 2).intersects(Range::new(1, 1)));
        assert!(Range::new(1, 2).intersects(Range::new(2, 2)));
        assert!(!Range::new(1, 2).intersects(Range::new(0, 0)));
        assert!(!Range::new(1, 2).intersects(Range::new(3, 10)));
        assert!(!Range::new(1, 3).intersects(Range::new(5, 5)));
    }

    #[test]
    fn test_range_adjacent_to() {
        assert!(!Range::new(1, 2).adjacent_to(Range::new(1, 1)));
        assert!(!Range::new(1, 2).adjacent_to(Range::new(2, 2)));
        assert!(Range::new(1, 2).adjacent_to(Range::new(0, 0)));
        assert!(Range::new(1, 2).adjacent_to(Range::new(3, 10)));
        assert!(!Range::new(1, 3).adjacent_to(Range::new(5, 5)));
    }

    #[test]
    fn test_range_contains() {
        assert!(Range::new(1, 2).contains(Range::new(1, 1)));
        assert!(Range::new(1, 2).contains(Range::new(2, 2)));
        assert!(!Range::new(1, 2).contains(Range::new(0, 0)));
        assert!(!Range::new(1, 2).contains(Range::new(3, 10)));
    }

    #[test]
    fn test_range_merge() {
        assert_eq!(None, Range::new(1, 3).merge(Range::new(5, 5)));
        assert_eq!(None, Range::new(4, 7).merge(Range::new(0, 1)));
        assert_eq!(Some(Range::new(3, 7)),
                   Range::new(3, 5).merge(Range::new(4, 7)));
        assert_eq!(Some(Range::new(3, 7)),
                   Range::new(3, 5).merge(Range::new(5, 7)));
        assert_eq!(Some(Range::new(3, 7)),
                   Range::new(3, 5).merge(Range::new(6, 7)));
        assert_eq!(Some(Range::new(3, 7)),
                   Range::new(3, 7).merge(Range::new(4, 5)));
    }
}
<|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/. */

use std::collections::HashMap;
use std::fs::File;
use std::io::Read;
use std::path::{Path, PathBuf};

use syn;

use bindgen::cargo::{Cargo, PackageRef};
use bindgen::ir::Cfg;

const STD_CRATES: &'static [&'static str] = &["std",
                                              "std_unicode",
                                              "alloc",
                                              "collections",
                                              "core",
                                              "proc_macro"];

type ParseResult = Result<(), String>;

/// Parses a single rust source file, not following `mod` or `extern crate`.
pub fn parse_src<F>(src_file: &Path,
                    items_callback: &mut F) -> ParseResult
    where F: FnMut(&str, &Vec<syn::Item>)
{
    let src_parsed = {
        let mut s = String::new();
        let mut f = File::open(src_file).map_err(|_| format!("parsing: cannot open file `{:?}`", src_file))?;
        f.read_to_string(&mut s).map_err(|_| format!("parsing: cannot open file `{:?}`", src_file))?;
        syn::parse_crate(&s).map_err(|msg| format!("parsing:\n{}", msg))?
    };

    items_callback("", &src_parsed.items);

    Ok(())
}

/// Recursively parses a rust library starting at the root crate's directory.
///
/// Inside a crate, `mod` and `extern crate` declarations are followed
/// and parsed. To find an external crate, the parser uses the `cargo metadata`
/// command to find the location of dependencies.
pub fn parse_lib<F>(lib: Cargo,
                    parse_deps: bool,
                    include: &Option<Vec<String>>,
                    exclude: &Vec<String>,
                    expand: &Vec<String>,
                    items_callback: &mut F) -> ParseResult
    where F: FnMut(&str, &str, Option<Cfg>, &Vec<syn::Item>)
{
    let mut context = ParseLibContext {
        lib: lib,
        parse_deps: parse_deps,
        include: include.clone(),
        exclude: exclude.clone(),
        expand: expand.clone(),
        cache_src: HashMap::new(),
        cache_expanded_crate: HashMap::new(),

        cfg_stack: Vec::new(),

        items_callback: items_callback,
    };

    parse_crate(&context.lib.binding_crate_ref(), &mut context)
}

struct ParseLibContext<F>
    where F: FnMut(&str, &str, Option<Cfg>, &Vec<syn::Item>)
{
    lib: Cargo,
    parse_deps: bool,
    include: Option<Vec<String>>,
    exclude: Vec<String>,
    expand: Vec<String>,
    cache_src: HashMap<PathBuf, Vec<syn::Item>>,
    cache_expanded_crate: HashMap<String, Vec<syn::Item>>,

    cfg_stack: Vec<Cfg>,

    items_callback: F,
}

impl<F> ParseLibContext<F>
    where F: FnMut(&str, &str, Option<Cfg>, &Vec<syn::Item>)
{
    fn should_parse_dependency(&self, pkg_name: &String) -> bool {
        if !self.parse_deps {
            return false;
        }

        // Skip any whitelist or blacklist for expand
        if self.expand.contains(&pkg_name) {
            return true;
        }

        // If we have a whitelist, check it
        if let Some(ref include) = self.include {
            if !include.contains(&pkg_name) {
                return false;
            }
        }

        // Check the blacklist
        return !STD_CRATES.contains(&pkg_name.as_ref()) &&
               !self.exclude.contains(&pkg_name);
    }
}

fn parse_crate<F>(pkg: &PackageRef, context: &mut ParseLibContext<F>) -> ParseResult
    where F: FnMut(&str, &str, Option<Cfg>, &Vec<syn::Item>)
{
    // Check if we should use cargo expand for this crate
    if context.expand.contains(&pkg.name) {
        return parse_expand_crate(pkg, context);
    }

    // Otherwise do our normal parse
    let crate_src = context.lib.find_crate_src(pkg);

    match crate_src {
        Some(crate_src) => {
            parse_mod(pkg, crate_src.as_path(), context)
        },
        None => {
            // This should be an error, but is common enough to just elicit a warning
            warn!("parsing crate `{}`: can't find lib.rs with `cargo metadata`", pkg.name);
            Ok(())
        },
    }
}

fn parse_expand_crate<F>(pkg: &PackageRef, context: &mut ParseLibContext<F>) -> ParseResult
    where F: FnMut(&str, &str, Option<Cfg>, &Vec<syn::Item>)
{
    let mod_parsed = {
        if !context.cache_expanded_crate.contains_key(&pkg.name) {
            let s = context.lib.expand_crate(pkg)?;
            let i = syn::parse_crate(&s).map_err(|msg| format!("parsing crate `{}`:\n{}", pkg.name, msg))?;
            context.cache_expanded_crate.insert(pkg.name.clone(), i.items);
        }

        context.cache_expanded_crate.get(&pkg.name).unwrap().clone()
    };

    process_expanded_mod(pkg, &mod_parsed, context)
}

fn process_expanded_mod<F>(pkg: &PackageRef,
                           items: &Vec<syn::Item>,
                           context: &mut ParseLibContext<F>) -> ParseResult
    where F: FnMut(&str, &str, Option<Cfg>, &Vec<syn::Item>)
{
    (context.items_callback)(context.lib.binding_crate_name(),
                             &pkg.name,
                             Cfg::join(&context.cfg_stack),
                             items);

    for item in items {
        match item.node {
            syn::ItemKind::Mod(ref inline_items) => {
                let cfg = Cfg::load(&item.attrs);
                if let &Some(ref cfg) = &cfg {
                    context.cfg_stack.push(cfg.clone());
                }

                if let &Some(ref inline_items) = inline_items {
                    process_expanded_mod(pkg, inline_items, context)?;
                } else {
                    error!("parsing crate `{}`: external mod found in expanded source", pkg.name);
                }

                if cfg.is_some() {
                    context.cfg_stack.pop();
                }
            }
            syn::ItemKind::ExternCrate(_) => {
                let dep_pkg_name = item.ident.to_string();

                let cfg = Cfg::load(&item.attrs);
                if let &Some(ref cfg) = &cfg {
                    context.cfg_stack.push(cfg.clone());
                }

                if context.should_parse_dependency(&dep_pkg_name) {
                    let dep_pkg_ref = context.lib.find_dep_ref(pkg, &dep_pkg_name);

                    if let Some(dep_pkg_ref) = dep_pkg_ref {
                        parse_crate(&dep_pkg_ref, context)?;
                    } else {
                        error!("parsing crate `{}`: can't find dependency version for {}`", pkg.name, dep_pkg_name);
                    }
                }

                if cfg.is_some() {
                    context.cfg_stack.pop();
                }
            }
            _ => {}
        }
    }

    Ok(())
}

fn parse_mod<F>(pkg: &PackageRef,
                mod_path: &Path,
                context: &mut ParseLibContext<F>) -> ParseResult
    where F: FnMut(&str, &str, Option<Cfg>, &Vec<syn::Item>)
{
    let mod_parsed = {
        let owned_mod_path = mod_path.to_path_buf();

        if !context.cache_src.contains_key(&owned_mod_path) {
            let mut s = String::new();
            let mut f = File::open(mod_path).map_err(|_| format!("parsing crate `{}`: cannot open file `{:?}`", pkg.name, mod_path))?;
            f.read_to_string(&mut s).map_err(|_| format!("parsing crate `{}`: cannot open file `{:?}`", pkg.name, mod_path))?;
            let i = syn::parse_crate(&s).map_err(|msg| format!("parsing crate `{}`:\n{}", pkg.name, msg))?;
            context.cache_src.insert(owned_mod_path.clone(), i.items);
        }

        context.cache_src.get(&owned_mod_path).unwrap().clone()
    };

    let mod_dir = mod_path.parent().unwrap();

    process_mod(pkg,
                mod_dir,
                &mod_parsed,
                context)
}

fn process_mod<F>(pkg: &PackageRef,
                  mod_dir: &Path,
                  items: &Vec<syn::Item>,
                  context: &mut ParseLibContext<F>) -> ParseResult
    where F: FnMut(&str, &str, Option<Cfg>, &Vec<syn::Item>)
{
    (context.items_callback)(context.lib.binding_crate_name(),
                             &pkg.name,
                             Cfg::join(&context.cfg_stack),
                             items);

    for item in items {
        match item.node {
            syn::ItemKind::Mod(ref inline_items) => {
                let next_mod_name = item.ident.to_string();

                let cfg = Cfg::load(&item.attrs);
                if let &Some(ref cfg) = &cfg {
                    context.cfg_stack.push(cfg.clone());
                }

                if let &Some(ref inline_items) = inline_items {
                    process_mod(pkg,
                                &mod_dir.join(&next_mod_name),
                                inline_items,
                                context)?;
                } else {
                    let next_mod_path1 = mod_dir.join(next_mod_name.clone() + ".rs");
                    let next_mod_path2 = mod_dir.join(next_mod_name.clone()).join("mod.rs");

                    if next_mod_path1.exists() {
                        parse_mod(pkg,
                                  next_mod_path1.as_path(),
                                  context)?;
                    } else if next_mod_path2.exists() {
                        parse_mod(pkg,
                                  next_mod_path2.as_path(),
                                  context)?;
                    } else {
                        // This should be an error, but is common enough to just elicit a warning
                        warn!("parsing crate `{}`: can't find mod {}`", pkg.name, next_mod_name);
                    }
                }

                if cfg.is_some() {
                    context.cfg_stack.pop();
                }
            }
            syn::ItemKind::ExternCrate(_) => {
                let dep_pkg_name = item.ident.to_string();

                let cfg = Cfg::load(&item.attrs);
                if let &Some(ref cfg) = &cfg {
                    context.cfg_stack.push(cfg.clone());
                }

                if context.should_parse_dependency(&dep_pkg_name) {
                    let dep_pkg_ref = context.lib.find_dep_ref(pkg, &dep_pkg_name);

                    if let Some(dep_pkg_ref) = dep_pkg_ref {
                        parse_crate(&dep_pkg_ref, context)?;
                    } else {
                        error!("parsing crate `{}`: can't find dependency version for {}`", pkg.name, dep_pkg_name);
                    }
                }

                if cfg.is_some() {
                    context.cfg_stack.pop();
                }
            }
            _ => {}
        }
    }

    Ok(())
}
<commit_msg>Don't parse crates that we've already parsed<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/. */

use std::collections::{HashMap, HashSet};
use std::fs::File;
use std::io::Read;
use std::path::{Path, PathBuf};

use syn;

use bindgen::cargo::{Cargo, PackageRef};
use bindgen::ir::Cfg;

const STD_CRATES: &'static [&'static str] = &["std",
                                              "std_unicode",
                                              "alloc",
                                              "collections",
                                              "core",
                                              "proc_macro"];

type ParseResult = Result<(), String>;

/// Parses a single rust source file, not following `mod` or `extern crate`.
pub fn parse_src<F>(src_file: &Path,
                    items_callback: &mut F) -> ParseResult
    where F: FnMut(&str, &Vec<syn::Item>)
{
    let src_parsed = {
        let mut s = String::new();
        let mut f = File::open(src_file).map_err(|_| format!("parsing: cannot open file `{:?}`", src_file))?;
        f.read_to_string(&mut s).map_err(|_| format!("parsing: cannot open file `{:?}`", src_file))?;
        syn::parse_crate(&s).map_err(|msg| format!("parsing:\n{}", msg))?
    };

    items_callback("", &src_parsed.items);

    Ok(())
}

/// Recursively parses a rust library starting at the root crate's directory.
///
/// Inside a crate, `mod` and `extern crate` declarations are followed
/// and parsed. To find an external crate, the parser uses the `cargo metadata`
/// command to find the location of dependencies.
pub fn parse_lib<F>(lib: Cargo,
                    parse_deps: bool,
                    include: &Option<Vec<String>>,
                    exclude: &Vec<String>,
                    expand: &Vec<String>,
                    items_callback: &mut F) -> ParseResult
    where F: FnMut(&str, &str, Option<Cfg>, &Vec<syn::Item>)
{
    let mut context = ParseLibContext {
        lib: lib,
        parse_deps: parse_deps,
        include: include.clone(),
        exclude: exclude.clone(),
        expand: expand.clone(),
        parsed_crates: HashSet::new(),
        cache_src: HashMap::new(),
        cache_expanded_crate: HashMap::new(),

        cfg_stack: Vec::new(),

        items_callback: items_callback,
    };

    parse_crate(&context.lib.binding_crate_ref(), &mut context)
}

struct ParseLibContext<F>
    where F: FnMut(&str, &str, Option<Cfg>, &Vec<syn::Item>)
{
    lib: Cargo,
    parse_deps: bool,

    include: Option<Vec<String>>,
    exclude: Vec<String>,
    expand: Vec<String>,

    parsed_crates: HashSet<String>,
    cache_src: HashMap<PathBuf, Vec<syn::Item>>,
    cache_expanded_crate: HashMap<String, Vec<syn::Item>>,

    cfg_stack: Vec<Cfg>,

    items_callback: F,
}

impl<F> ParseLibContext<F>
    where F: FnMut(&str, &str, Option<Cfg>, &Vec<syn::Item>)
{
    fn should_parse_dependency(&self, pkg_name: &String) -> bool {
        if self.parsed_crates.contains(pkg_name) {
            return false;
        }

        if !self.parse_deps {
            return false;
        }

        // Skip any whitelist or blacklist for expand
        if self.expand.contains(&pkg_name) {
            return true;
        }

        // If we have a whitelist, check it
        if let Some(ref include) = self.include {
            if !include.contains(&pkg_name) {
                return false;
            }
        }

        // Check the blacklist
        return !STD_CRATES.contains(&pkg_name.as_ref()) &&
               !self.exclude.contains(&pkg_name);
    }
}

fn parse_crate<F>(pkg: &PackageRef, context: &mut ParseLibContext<F>) -> ParseResult
    where F: FnMut(&str, &str, Option<Cfg>, &Vec<syn::Item>)
{
    context.parsed_crates.insert(pkg.name.clone());

    // Check if we should use cargo expand for this crate
    if context.expand.contains(&pkg.name) {
        return parse_expand_crate(pkg, context);
    }

    // Otherwise do our normal parse
    let crate_src = context.lib.find_crate_src(pkg);

    match crate_src {
        Some(crate_src) => {
            parse_mod(pkg, crate_src.as_path(), context)
        },
        None => {
            // This should be an error, but is common enough to just elicit a warning
            warn!("parsing crate `{}`: can't find lib.rs with `cargo metadata`", pkg.name);
            Ok(())
        },
    }
}

fn parse_expand_crate<F>(pkg: &PackageRef, context: &mut ParseLibContext<F>) -> ParseResult
    where F: FnMut(&str, &str, Option<Cfg>, &Vec<syn::Item>)
{
    let mod_parsed = {
        if !context.cache_expanded_crate.contains_key(&pkg.name) {
            let s = context.lib.expand_crate(pkg)?;
            let i = syn::parse_crate(&s).map_err(|msg| format!("parsing crate `{}`:\n{}", pkg.name, msg))?;
            context.cache_expanded_crate.insert(pkg.name.clone(), i.items);
        }

        context.cache_expanded_crate.get(&pkg.name).unwrap().clone()
    };

    process_expanded_mod(pkg, &mod_parsed, context)
}

fn process_expanded_mod<F>(pkg: &PackageRef,
                           items: &Vec<syn::Item>,
                           context: &mut ParseLibContext<F>) -> ParseResult
    where F: FnMut(&str, &str, Option<Cfg>, &Vec<syn::Item>)
{
    (context.items_callback)(context.lib.binding_crate_name(),
                             &pkg.name,
                             Cfg::join(&context.cfg_stack),
                             items);

    for item in items {
        match item.node {
            syn::ItemKind::Mod(ref inline_items) => {
                let cfg = Cfg::load(&item.attrs);
                if let &Some(ref cfg) = &cfg {
                    context.cfg_stack.push(cfg.clone());
                }

                if let &Some(ref inline_items) = inline_items {
                    process_expanded_mod(pkg, inline_items, context)?;
                } else {
                    error!("parsing crate `{}`: external mod found in expanded source", pkg.name);
                }

                if cfg.is_some() {
                    context.cfg_stack.pop();
                }
            }
            syn::ItemKind::ExternCrate(_) => {
                let dep_pkg_name = item.ident.to_string();

                let cfg = Cfg::load(&item.attrs);
                if let &Some(ref cfg) = &cfg {
                    context.cfg_stack.push(cfg.clone());
                }

                if context.should_parse_dependency(&dep_pkg_name) {
                    let dep_pkg_ref = context.lib.find_dep_ref(pkg, &dep_pkg_name);

                    if let Some(dep_pkg_ref) = dep_pkg_ref {
                        parse_crate(&dep_pkg_ref, context)?;
                    } else {
                        error!("parsing crate `{}`: can't find dependency version for {}`", pkg.name, dep_pkg_name);
                    }
                }

                if cfg.is_some() {
                    context.cfg_stack.pop();
                }
            }
            _ => {}
        }
    }

    Ok(())
}

fn parse_mod<F>(pkg: &PackageRef,
                mod_path: &Path,
                context: &mut ParseLibContext<F>) -> ParseResult
    where F: FnMut(&str, &str, Option<Cfg>, &Vec<syn::Item>)
{
    let mod_parsed = {
        let owned_mod_path = mod_path.to_path_buf();

        if !context.cache_src.contains_key(&owned_mod_path) {
            let mut s = String::new();
            let mut f = File::open(mod_path).map_err(|_| format!("parsing crate `{}`: cannot open file `{:?}`", pkg.name, mod_path))?;
            f.read_to_string(&mut s).map_err(|_| format!("parsing crate `{}`: cannot open file `{:?}`", pkg.name, mod_path))?;
            let i = syn::parse_crate(&s).map_err(|msg| format!("parsing crate `{}`:\n{}", pkg.name, msg))?;
            context.cache_src.insert(owned_mod_path.clone(), i.items);
        }

        context.cache_src.get(&owned_mod_path).unwrap().clone()
    };

    let mod_dir = mod_path.parent().unwrap();

    process_mod(pkg,
                mod_dir,
                &mod_parsed,
                context)
}

fn process_mod<F>(pkg: &PackageRef,
                  mod_dir: &Path,
                  items: &Vec<syn::Item>,
                  context: &mut ParseLibContext<F>) -> ParseResult
    where F: FnMut(&str, &str, Option<Cfg>, &Vec<syn::Item>)
{
    (context.items_callback)(context.lib.binding_crate_name(),
                             &pkg.name,
                             Cfg::join(&context.cfg_stack),
                             items);

    for item in items {
        match item.node {
            syn::ItemKind::Mod(ref inline_items) => {
                let next_mod_name = item.ident.to_string();

                let cfg = Cfg::load(&item.attrs);
                if let &Some(ref cfg) = &cfg {
                    context.cfg_stack.push(cfg.clone());
                }

                if let &Some(ref inline_items) = inline_items {
                    process_mod(pkg,
                                &mod_dir.join(&next_mod_name),
                                inline_items,
                                context)?;
                } else {
                    let next_mod_path1 = mod_dir.join(next_mod_name.clone() + ".rs");
                    let next_mod_path2 = mod_dir.join(next_mod_name.clone()).join("mod.rs");

                    if next_mod_path1.exists() {
                        parse_mod(pkg,
                                  next_mod_path1.as_path(),
                                  context)?;
                    } else if next_mod_path2.exists() {
                        parse_mod(pkg,
                                  next_mod_path2.as_path(),
                                  context)?;
                    } else {
                        // This should be an error, but is common enough to just elicit a warning
                        warn!("parsing crate `{}`: can't find mod {}`", pkg.name, next_mod_name);
                    }
                }

                if cfg.is_some() {
                    context.cfg_stack.pop();
                }
            }
            syn::ItemKind::ExternCrate(_) => {
                let dep_pkg_name = item.ident.to_string();

                let cfg = Cfg::load(&item.attrs);
                if let &Some(ref cfg) = &cfg {
                    context.cfg_stack.push(cfg.clone());
                }

                if context.should_parse_dependency(&dep_pkg_name) {
                    let dep_pkg_ref = context.lib.find_dep_ref(pkg, &dep_pkg_name);

                    if let Some(dep_pkg_ref) = dep_pkg_ref {
                        parse_crate(&dep_pkg_ref, context)?;
                    } else {
                        error!("parsing crate `{}`: can't find dependency version for {}`", pkg.name, dep_pkg_name);
                    }
                }

                if cfg.is_some() {
                    context.cfg_stack.pop();
                }
            }
            _ => {}
        }
    }

    Ok(())
}
<|endoftext|>


<commit_before>use std::str;
use super::ast::*;

pub struct Parser<'a> {
    source: str::Chars<'a>,
    ch: Option<char>,
    pos: u16,
}

impl<'a> Parser<'a> {
    pub fn new(source: &'a str) -> Parser<'a> {
        Parser {
            source: source.chars(),
            ch: None,
            pos: 0,
        }
    }

    fn bump(&mut self) {
        self.ch = self.source.next();

        self.pos += 1;
    }

    fn ch_is(&self, ch: char) -> bool {
        self.ch == Some(ch)
    }

    fn get_ws(&mut self) {
        while self.ch_is(' ') || self.ch_is('\n') || self.ch_is('\t') || self.ch_is('\r') {
            self.bump();
        }
    }

    fn get_line_ws(&mut self) {
        while self.ch_is(' ') || self.ch_is('\t') {
            self.bump();
        }
    }

    pub fn parse(&mut self) -> Vec<Entry> {
        let mut entries: Vec<Entry> = Vec::new();

        self.get_ws();

        self.bump();

        loop {
            if self.ch == None {
                break;
            }

            let entry = self.get_entry();
            if let Some(x) = entry {
                entries.push(x);
            }
            self.get_ws();
        }
        entries
    }

    fn get_entry(&mut self) -> Option<Entry> {
        let mut comment: Option<Entry> = None;

        if self.ch_is('#') {
            comment = Some(self.get_comment());
        }

        self.get_line_ws();

        if self.ch_is('[') {
            return Some(self.get_section());
        }

        if self.ch != None && !self.ch_is('\n') {
            return Some(self.get_entity());
        }

        comment
    }

    fn get_section(&mut self) -> Entry {
        self.bump();
        self.bump();

        self.get_line_ws();

        let key = self.get_keyword();

        self.get_line_ws();

        self.bump();
        self.bump();

        let mut body = vec![];
        Entry::Section {
            key: key,
            body: body,
        }
    }

    fn get_entity(&mut self) -> Entry {
        let id = self.get_identifier();
        self.get_line_ws();

        if !self.ch_is('=') {
            panic!();
        }
        self.bump();

        self.get_line_ws();

        let value = self.get_pattern();

        if self.ch_is('\n') {
            self.bump();
            self.get_line_ws();
        }

        let mut members: Option<Vec<Member>> = None;
        if self.ch_is('[') {
            members = Some(self.get_members());
        }

        Entry::Entity {
            id: id,
            value: value,
            traits: members,
        }
    }

    fn get_identifier(&mut self) -> Identifier {
        let mut name = String::new();

        let ch = match self.ch {
            Some(c) => c,
            None => panic!(),
        };

        match ch {
            'a'...'z' | 'A'...'Z' | '_' => name.push(ch),
            _ => return Identifier { name: name },
        }
        self.bump();

        loop {
            let ch = match self.ch {
                Some(c) => c,
                None => break,
            };

            match ch {
                'a'...'z' | 'A'...'Z' | '0'...'9' | '_' | '-' => name.push(ch),
                _ => break,
            }
            self.bump();
        }

        Identifier { name: name }
    }

    fn get_keyword(&mut self) -> Keyword {
        let mut name = String::new();

        let ch = match self.ch {
            Some(c) => c,
            None => panic!(),
        };

        match ch {
            'a'...'z' | 'A'...'Z' | '_' => name.push(ch),
            _ => return Keyword { name: name },
        }
        self.bump();

        loop {
            let ch = match self.ch {
                Some(c) => c,
                None => break,
            };

            match ch {
                'a'...'z' | 'A'...'Z' | '0'...'9' | '_' | '-' | ' ' => name.push(ch),
                _ => break,
            }
            self.bump();
        }

        Keyword { name: name }
    }

    fn get_pattern(&mut self) -> Value {
        let mut buffer = String::new();
        let mut source = String::new();
        let mut content = vec![];
        let mut quote_delimited: bool = false;
        let mut first_line = true;

        if self.ch_is('"') {
            quote_delimited = true;
        }

        loop {
            match self.ch {
                Some(c) if c == '\n' => {
                    if quote_delimited {
                        panic!("Unclosed string");
                    }
                    self.bump();
                    self.get_line_ws();

                    if !self.ch_is('|') {
                        break;
                    }
                    if first_line && buffer.len() != 0 {
                        panic!("Multiline string should have the ID line empty");
                    }
                    first_line = false;
                    self.bump();
                    if self.ch_is(' ') {
                        self.bump();
                    }
                    if buffer.len() != 0 {
                        buffer.push('\n');
                    }
                    continue;
                }
                Some(c) if c == '"' => {
                    self.bump();
                    quote_delimited = false;
                    break;
                }
                Some(c) => source.push(c),
                None => break,
            }
            match self.ch {
                Some(c) => buffer.push(c),
                None => continue,
            };
            self.bump();
        }

        if quote_delimited {
            panic!("Unclosed string");
        }

        if buffer.len() != 0 {
            // source.append(buffer);
            content.push(PatternElement::TextElement { value: source.clone() });
        }

        if content.len() == 0 {
            // return Value::Pattern(source: source, elements: content);
        }

        content.push(PatternElement::TextElement { value: source.clone() });

        Value::Pattern {
            source: source,
            elements: content,
        }
    }

    fn get_members(&mut self) -> Vec<Member> {
        let mut members = vec![];

        loop {
            if !self.ch_is('[') {
                break;
            }

            self.bump();

            let key = self.get_keyword();

            self.get_line_ws();

            let value = self.get_pattern();

            let member = Member {
                key: key,
                value: value,
                default: false,
            };

            members.push(member);

            self.get_ws()

        }

        members
    }

    fn get_comment(&mut self) -> Entry {
        self.bump();
        if self.ch_is(' ') {
            self.bump();
        }

        let mut content = String::new();

        loop {
            while !self.ch_is('\n') {
                let ch = match self.ch {
                    Some(c) => c,
                    None => break,
                };

                content.push(ch);
                self.bump();
            }

            self.bump();

            if self.ch_is('#') {
                content.push('\n');
                self.bump();
            } else {
                break;
            }
        }

        Entry::Comment { content: content }

    }
}
<commit_msg>Work on quote delimited strings<commit_after>use std::str;
use super::ast::*;

pub struct Parser<'a> {
    source: str::Chars<'a>,
    ch: Option<char>,
    pos: u16,
}

impl<'a> Parser<'a> {
    pub fn new(source: &'a str) -> Parser<'a> {
        Parser {
            source: source.chars(),
            ch: None,
            pos: 0,
        }
    }

    fn bump(&mut self) {
        self.ch = self.source.next();

        self.pos += 1;
    }

    fn ch_is(&self, ch: char) -> bool {
        self.ch == Some(ch)
    }

    fn get_ws(&mut self) {
        while self.ch_is(' ') || self.ch_is('\n') || self.ch_is('\t') || self.ch_is('\r') {
            self.bump();
        }
    }

    fn get_line_ws(&mut self) {
        while self.ch_is(' ') || self.ch_is('\t') {
            self.bump();
        }
    }

    pub fn parse(&mut self) -> Vec<Entry> {
        let mut entries: Vec<Entry> = Vec::new();

        self.get_ws();

        self.bump();

        loop {
            if self.ch == None {
                break;
            }

            let entry = self.get_entry();
            if let Some(x) = entry {
                entries.push(x);
            }
            self.get_ws();
        }
        entries
    }

    fn get_entry(&mut self) -> Option<Entry> {
        let mut comment: Option<Entry> = None;

        if self.ch_is('#') {
            comment = Some(self.get_comment());
        }

        self.get_line_ws();

        if self.ch_is('[') {
            return Some(self.get_section());
        }

        if self.ch != None && !self.ch_is('\n') {
            return Some(self.get_entity());
        }

        comment
    }

    fn get_section(&mut self) -> Entry {
        self.bump();
        self.bump();

        self.get_line_ws();

        let key = self.get_keyword();

        self.get_line_ws();

        self.bump();
        self.bump();

        let mut body = vec![];
        Entry::Section {
            key: key,
            body: body,
        }
    }

    fn get_entity(&mut self) -> Entry {
        let id = self.get_identifier();
        self.get_line_ws();

        if !self.ch_is('=') {
            panic!();
        }
        self.bump();

        self.get_line_ws();

        let value = self.get_pattern();

        if self.ch_is('\n') {
            self.bump();
            self.get_line_ws();
        }

        let mut members: Option<Vec<Member>> = None;
        if self.ch_is('[') {
            members = Some(self.get_members());
        }

        Entry::Entity {
            id: id,
            value: value,
            traits: members,
        }
    }

    fn get_identifier(&mut self) -> Identifier {
        let mut name = String::new();

        let ch = match self.ch {
            Some(c) => c,
            None => panic!(),
        };

        match ch {
            'a'...'z' | 'A'...'Z' | '_' => name.push(ch),
            _ => return Identifier { name: name },
        }
        self.bump();

        loop {
            let ch = match self.ch {
                Some(c) => c,
                None => break,
            };

            match ch {
                'a'...'z' | 'A'...'Z' | '0'...'9' | '_' | '-' => name.push(ch),
                _ => break,
            }
            self.bump();
        }

        Identifier { name: name }
    }

    fn get_keyword(&mut self) -> Keyword {
        let mut name = String::new();

        let ch = match self.ch {
            Some(c) => c,
            None => panic!(),
        };

        match ch {
            'a'...'z' | 'A'...'Z' | '_' => name.push(ch),
            _ => return Keyword { name: name },
        }
        self.bump();

        loop {
            let ch = match self.ch {
                Some(c) => c,
                None => break,
            };

            match ch {
                'a'...'z' | 'A'...'Z' | '0'...'9' | '_' | '-' | ' ' => name.push(ch),
                _ => break,
            }
            self.bump();
        }

        Keyword { name: name }
    }

    fn get_pattern(&mut self) -> Value {
        let mut buffer = String::new();
        let mut source = String::new();
        let mut content = vec![];
        let mut quote_delimited: bool = false;
        let mut first_line = true;

        if self.ch_is('"') {
            quote_delimited = true;
            self.bump();
        }

        loop {
            match self.ch {
                Some(c) if c == '\n' => {
                    if quote_delimited {
                        panic!("Unclosed string");
                    }
                    self.bump();
                    self.get_line_ws();

                    if !self.ch_is('|') {
                        break;
                    }
                    if first_line && buffer.len() != 0 {
                        panic!("Multiline string should have the ID line empty");
                    }
                    first_line = false;
                    self.bump();
                    if self.ch_is(' ') {
                        self.bump();
                    }
                    if buffer.len() != 0 {
                        buffer.push('\n');
                    }
                    continue;
                }
                Some(c) if c == '"' => {
                    self.bump();
                    quote_delimited = false;
                    break;
                }
                Some(c) => source.push(c),
                None => break,
            }
            match self.ch {
                Some(c) => buffer.push(c),
                None => continue,
            };
            self.bump();
        }

        if quote_delimited {
            panic!("Unclosed string");
        }

        if buffer.len() != 0 {
            // source.append(buffer);
            content.push(PatternElement::TextElement { value: source.clone() });
        }

        if content.len() == 0 {
            // return Value::Pattern(source: source, elements: content);
        }

        content.push(PatternElement::TextElement { value: source.clone() });

        Value::Pattern {
            source: source,
            elements: content,
        }
    }

    fn get_members(&mut self) -> Vec<Member> {
        let mut members = vec![];

        loop {
            if !self.ch_is('[') {
                break;
            }

            self.bump();

            let key = self.get_keyword();

            self.bump();

            self.get_line_ws();

            let value = self.get_pattern();

            let member = Member {
                key: key,
                value: value,
                default: false,
            };

            members.push(member);

            self.get_ws()

        }

        members
    }

    fn get_comment(&mut self) -> Entry {
        self.bump();
        if self.ch_is(' ') {
            self.bump();
        }

        let mut content = String::new();

        loop {
            while !self.ch_is('\n') {
                let ch = match self.ch {
                    Some(c) => c,
                    None => break,
                };

                content.push(ch);
                self.bump();
            }

            self.bump();

            if self.ch_is('#') {
                content.push('\n');
                self.bump();
            } else {
                break;
            }
        }

        Entry::Comment { content: content }

    }
}
<|endoftext|>


<commit_before>use libc::c_char;
use std::c_str::CString;

pub fn _string(raw_ptr: *const c_char) -> String {
    let c_string = unsafe { CString::new(raw_ptr, false) };
    return String::from_str(c_string.as_str().unwrap());
}
<commit_msg>use to_string()<commit_after>use libc::c_char;
use std::c_str::CString;

pub fn _string(raw_ptr: *const c_char) -> String {
    let c_string = unsafe { CString::new(raw_ptr, false) };
    return c_string.as_str().unwrap().to_string();
}
<|endoftext|>


<commit_before>// Generators
use std::marker::PhantomData;
use std::mem::size_of;

use data::*;

pub struct BoolGenerator;
pub struct IntGenerator<N>(PhantomData<N>);
pub struct VecGenerator<G>(G);
pub struct InfoPoolGenerator(usize);

pub struct Filtered<G, F>(G, F);
pub struct Const<V>(V);

pub trait Generator {
    type Item;
    fn generate(&self, source: &mut InfoTap) -> Maybe<Self::Item>;

    fn filter<F: Fn(&Self::Item) -> bool>(self, pred: F) -> Filtered<Self, F>
    where
        Self: Sized,
    {
        Filtered(self, pred)
    }
}

pub fn booleans() -> BoolGenerator {
    BoolGenerator
}

pub fn vecs<G>(inner: G) -> VecGenerator<G> {
    VecGenerator(inner)
}

pub fn consts<V>(val: V) -> Const<V> {
    Const(val)
}

pub fn info_pools(len: usize) -> InfoPoolGenerator {
    InfoPoolGenerator(len)
}

impl<G: Generator> Generator for VecGenerator<G> {
    type Item = Vec<G::Item>;
    fn generate(&self, src: &mut InfoTap) -> Maybe<Self::Item> {
        let mut result = Vec::new();
        let bs = booleans();
        while bs.generate(src)? {
            let item = self.0.generate(src)?;
            result.push(item)
        }

        Ok(result)
    }
}

impl Generator for InfoPoolGenerator {
    type Item = InfoPool;
    fn generate(&self, src: &mut InfoTap) -> Maybe<Self::Item> {
        let mut result = Vec::new();
        let vals = u8s();
        for _ in 0..self.0 {
            let item = vals.generate(src)?;
            result.push(item)
        }

        Ok(InfoPool::of_vec(result))
    }
}

impl Generator for BoolGenerator {
    type Item = bool;
    fn generate(&self, src: &mut InfoTap) -> Maybe<Self::Item> {
        src.next_byte().map(|next| next >= 0x80)
    }
}

macro_rules! integer_gen {
    ($name:ident, $ty:ty) => {
        pub fn $name() -> IntGenerator<$ty> {
            IntGenerator(PhantomData)
        }

        impl Generator for IntGenerator<$ty> {
            type Item = $ty;
            fn generate(&self, src: &mut InfoTap) -> Maybe<Self::Item> {
                assert!(size_of::<u8>() == 1);
                let nbytes = size_of::<$ty>() / size_of::<u8>();
                let mut val: $ty = 0;
                for _ in 0..nbytes {
                    val = val.wrapping_shl(8) | src.next_byte()? as $ty;
                }
                // src.next_byte().map(|next| next >= 0x80)
                Ok(val)
            }
        }
    }
}

integer_gen!(u8s, u8);
integer_gen!(u16s, u16);
integer_gen!(u32s, u32);
integer_gen!(u64s, u64);

impl<G: Generator, F: Fn(&G::Item) -> bool> Generator for Filtered<G, F> {
    type Item = G::Item;
    fn generate(&self, src: &mut InfoTap) -> Maybe<Self::Item> {
        let &Filtered(ref gen, ref pred) = self;
        let val = gen.generate(src)?;
        if pred(&val) {
            Ok(val)
        } else {
            Err(DataError::SkipItem)
        }
    }
}

impl<V: Clone> Generator for Const<V> {
    type Item = V;
    fn generate(&self, _: &mut InfoTap) -> Maybe<Self::Item> {
        Ok(self.0.clone())
    }
}

pub fn find_minimal<G: Generator, F: Fn(G::Item) -> bool>(
    gen: &G,
    pool: InfoPool,
    check: F,
) -> InfoPool {
    minimize(&pool, &|mut t| {
        gen.generate(&mut t).map(|v| check(v)).unwrap_or(false)
    }).unwrap_or(pool)
}

#[cfg(test)]
mod tests {
    extern crate env_logger;
    use rand::random;
    use std::iter;
    use std::fmt;
    use super::*;
    use data::InfoPool;
    const SHORT_VEC_SIZE: usize = 256;

    fn gen_random_vec() -> Vec<u8> {
        (0..SHORT_VEC_SIZE).map(|_| random()).collect::<Vec<u8>>()
    }

    fn should_generate_same_output_given_same_input<G: Generator>(gen: G)
    where
        G::Item: fmt::Debug + PartialEq,
    {
        for (p0, p1, v0, v1) in iter::repeat(())
            .map(|_| gen_random_vec())
            .map(|v0| (InfoPool::of_vec(v0.clone()), InfoPool::of_vec(v0)))
            .flat_map(|(p0, p1)| {
                gen.generate(&mut p0.tap()).and_then(|v0| {
                    gen.generate(&mut p1.tap()).map(|v1| (p0, p1, v0, v1))
                })
            })
            .take(100)
        {
            assert!(v0 == v1, "({:?} == {:?}) -> ({:?} == {:?})", p0, p1, v0, v1);
        }
    }

    fn usually_generates_different_output_for_different_inputs<G: Generator>(gen: G)
    where
        G::Item: PartialEq,
    {
        let nitems = 100;
        let differing = iter::repeat(())
            .map(|_| (gen_random_vec(), gen_random_vec()))
            .filter(|&(ref v0, ref v1)| v0 != v1)
            .map(|(v0, v1)| (InfoPool::of_vec(v0), InfoPool::of_vec(v1)))
            .flat_map(|(p0, p1)| {
                gen.generate(&mut p0.tap()).and_then(|v0| {
                    gen.generate(&mut p1.tap()).map(|v1| (v0, v1))
                })
            })
            .take(nitems)
            .filter(|&(ref v0, ref v1)| v0 != v1)
            .count();
        assert!(differing > 0, "Differing items:{} > 0", differing);

    }

    // Mostly only useful for scalar quantities. For collections, we basically say:
    // `while booleans() { elemnt() }`
    // So because booleans are imprecisly generated (ie: a pool of [0xff] ~ [0x80]),
    // the source and result can have a differing ordering.
    fn should_partially_order_same_as_source<G: Generator>(gen: G)
    where
        G::Item: PartialOrd + fmt::Debug,
    {
        let nitems = 100;
        for (p0, p1, v0, v1) in iter::repeat(())
            .map(|_| (gen_random_vec(), gen_random_vec()))
            .filter(|&(ref v0, ref v1)| v0 < v1)
            .map(|(v0, v1)| (InfoPool::of_vec(v0), InfoPool::of_vec(v1)))
            .flat_map(|(p0, p1)| {
                gen.generate(&mut p0.tap()).and_then(|v0| {
                    gen.generate(&mut p1.tap()).map(|v1| (p0, p1, v0, v1))
                })
            })
            .take(nitems)
        {
            assert!(v0 <= v1, "({:?} == {:?}) -> ({:?} == {:?})", p0, p1, v0, v1);
        }
    }

    #[test]
    fn consts_should_generate_same_values() {
        let v1 = gen_random_vec();
        let gen = consts("fourty two");
        assert_eq!(
            gen.generate(&mut InfoPool::of_vec(v1).tap()),
            Ok("fourty two")
        );
    }

    // If only I had some kind of property testing library.
    #[test]
    fn bools_should_generate_false_booleans_from_zeros() {
        let v1 = vec![0];
        let bools = booleans();
        assert_eq!(bools.generate(&mut InfoPool::of_vec(v1).tap()), Ok(false));
    }

    #[test]
    fn bools_should_generate_true_booleans_from_saturated_values() {
        let v1 = vec![0xff];
        let bools = booleans();
        assert_eq!(bools.generate(&mut InfoPool::of_vec(v1).tap()), Ok(true));
    }

    fn should_minimize_to<G: Generator>(gen: G, expected: G::Item)
    where
        G::Item: fmt::Debug + PartialEq,
    {
        let mut p;
        loop {
            p = InfoPool::random_of_size(SHORT_VEC_SIZE);
            match gen.generate(&mut p.tap()) {
                Ok(_) => break,
                Err(DataError::SkipItem) => (),
                Err(DataError::PoolExhausted) => panic!("Not enough pool to generate data"),
            }
        }
        debug!("Before: {:?}", p);
        let p = find_minimal(&gen, p, |_| true);
        debug!("After: {:?}", p);

        let val = gen.generate(&mut p.tap()).expect("generated value");
        assert_eq!(val, expected);

    }
    #[test]
    fn bools_should_generate_same_output_given_same_input() {
        should_generate_same_output_given_same_input(booleans())
    }

    // These really need to be proper statistical tests.
    #[test]
    fn bools_usually_generates_different_output_for_different_inputs() {
        usually_generates_different_output_for_different_inputs(booleans())
    }

    #[test]
    fn bools_minimize_to_false() {
        should_minimize_to(booleans(), false)
    }

    #[test]
    fn bools_should_partially_order_same_as_source() {
        should_partially_order_same_as_source(booleans())
    }

    #[test]
    fn vecs_should_generate_same_output_given_same_input() {
        should_generate_same_output_given_same_input(vecs(booleans()));
    }

    #[test]
    fn vecs_usually_generates_different_output_for_different_inputs() {
        usually_generates_different_output_for_different_inputs(vecs(booleans()))
    }

    #[test]
    fn vec_bools_minimize_to_empty() {
        env_logger::init().unwrap_or(());
        should_minimize_to(vecs(booleans()), vec![])
    }

    #[test]
    fn vec_bools_can_minimise_with_predicate() {
        env_logger::init().unwrap_or(());
        should_minimize_to(
            vecs(booleans()).filter(|v| v.len() > 2),
            vec![false, false, false],
        );
    }

    #[test]
    fn info_pools_should_generate_same_output_given_same_input() {
        should_generate_same_output_given_same_input(info_pools(8))
    }

    #[test]
    fn info_pools_usually_generates_different_output_for_different_inputs() {
        usually_generates_different_output_for_different_inputs(info_pools(8))
    }

    #[test]
    fn info_pools_minimize_to_empty() {
        env_logger::init().unwrap_or(());
        // We force the generator to output a fixed length.
        // This is perhaps not the best idea ever; but it'll do for now.
        should_minimize_to(info_pools(8), InfoPool::of_vec(vec![0; 8]))
    }

    #[test]
    fn u8s_should_generate_same_output_given_same_input() {
        should_generate_same_output_given_same_input(u8s())
    }

    // These really need to be proper statistical tests.
    #[test]
    fn u8s_usually_generates_different_output_for_different_inputs() {
        usually_generates_different_output_for_different_inputs(u8s());
    }

    #[test]
    fn u8s_minimize_to_zero() {
        should_minimize_to(u8s(), 0);
    }

    #[test]
    fn u8s_should_partially_order_same_as_source() {
        should_partially_order_same_as_source(u8s());
    }

    #[test]
    fn u64s_should_generate_same_output_given_same_input() {
        should_generate_same_output_given_same_input(u64s())
    }

    // These really need to be proper statistical tests.
    #[test]
    fn u64s_usually_generates_different_output_for_different_inputs() {
        usually_generates_different_output_for_different_inputs(u64s());
    }

    #[test]
    fn u64s_minimize_to_zero() {
        should_minimize_to(u64s(), 0);
    }

    #[test]
    fn u64s_should_partially_order_same_as_source() {
        should_partially_order_same_as_source(u64s());
    }

    #[test]
    fn filter_should_pass_through_when_true() {
        let gen = consts(()).filter(|&_| true);
        let p = InfoPool::random_of_size(4);
        assert_eq!(gen.generate(&mut p.tap()), Ok(()));
    }

    #[test]
    fn filter_should_skip_when_false() {
        let gen = consts(()).filter(|&_| false);
        let p = InfoPool::random_of_size(4);
        assert_eq!(gen.generate(&mut p.tap()), Err(DataError::SkipItem));
    }

}
<commit_msg>Generator tuples.<commit_after>// Generators
use std::marker::PhantomData;
use std::mem::size_of;

use data::*;

pub struct BoolGenerator;
pub struct IntGenerator<N>(PhantomData<N>);
pub struct VecGenerator<G>(G);
pub struct InfoPoolGenerator(usize);

pub struct Filtered<G, F>(G, F);
pub struct Const<V>(V);

pub trait Generator {
    type Item;
    fn generate(&self, source: &mut InfoTap) -> Maybe<Self::Item>;

    fn filter<F: Fn(&Self::Item) -> bool>(self, pred: F) -> Filtered<Self, F>
    where
        Self: Sized,
    {
        Filtered(self, pred)
    }
}

pub fn booleans() -> BoolGenerator {
    BoolGenerator
}

pub fn vecs<G>(inner: G) -> VecGenerator<G> {
    VecGenerator(inner)
}

pub fn consts<V>(val: V) -> Const<V> {
    Const(val)
}

pub fn info_pools(len: usize) -> InfoPoolGenerator {
    InfoPoolGenerator(len)
}

impl<G: Generator> Generator for VecGenerator<G> {
    type Item = Vec<G::Item>;
    fn generate(&self, src: &mut InfoTap) -> Maybe<Self::Item> {
        let mut result = Vec::new();
        let bs = booleans();
        while bs.generate(src)? {
            let item = self.0.generate(src)?;
            result.push(item)
        }

        Ok(result)
    }
}

impl Generator for InfoPoolGenerator {
    type Item = InfoPool;
    fn generate(&self, src: &mut InfoTap) -> Maybe<Self::Item> {
        let mut result = Vec::new();
        let vals = u8s();
        for _ in 0..self.0 {
            let item = vals.generate(src)?;
            result.push(item)
        }

        Ok(InfoPool::of_vec(result))
    }
}

impl Generator for BoolGenerator {
    type Item = bool;
    fn generate(&self, src: &mut InfoTap) -> Maybe<Self::Item> {
        src.next_byte().map(|next| next >= 0x80)
    }
}

macro_rules! integer_gen {
    ($name:ident, $ty:ty) => {
        pub fn $name() -> IntGenerator<$ty> {
            IntGenerator(PhantomData)
        }

        impl Generator for IntGenerator<$ty> {
            type Item = $ty;
            fn generate(&self, src: &mut InfoTap) -> Maybe<Self::Item> {
                assert!(size_of::<u8>() == 1);
                let nbytes = size_of::<$ty>() / size_of::<u8>();
                let mut val: $ty = 0;
                for _ in 0..nbytes {
                    val = val.wrapping_shl(8) | src.next_byte()? as $ty;
                }
                // src.next_byte().map(|next| next >= 0x80)
                Ok(val)
            }
        }
    }
}

integer_gen!(u8s, u8);
integer_gen!(u16s, u16);
integer_gen!(u32s, u32);
integer_gen!(u64s, u64);

impl<G: Generator, F: Fn(&G::Item) -> bool> Generator for Filtered<G, F> {
    type Item = G::Item;
    fn generate(&self, src: &mut InfoTap) -> Maybe<Self::Item> {
        let &Filtered(ref gen, ref pred) = self;
        let val = gen.generate(src)?;
        if pred(&val) {
            Ok(val)
        } else {
            Err(DataError::SkipItem)
        }
    }
}

impl<V: Clone> Generator for Const<V> {
    type Item = V;
    fn generate(&self, _: &mut InfoTap) -> Maybe<Self::Item> {
        Ok(self.0.clone())
    }
}

impl<G: Generator, H: Generator> Generator for (G, H) {
    type Item = (G::Item, H::Item);
    fn generate(&self, src: &mut InfoTap) -> Maybe<Self::Item> {
        let v0 = self.0.generate(src)?;
        let v1 = self.1.generate(src)?;
        Ok((v0, v1))
    }
}

pub fn find_minimal<G: Generator, F: Fn(G::Item) -> bool>(
    gen: &G,
    pool: InfoPool,
    check: F,
) -> InfoPool {
    minimize(&pool, &|mut t| {
        gen.generate(&mut t).map(|v| check(v)).unwrap_or(false)
    }).unwrap_or(pool)
}

#[cfg(test)]
mod tests {
    extern crate env_logger;
    use rand::random;
    use std::iter;
    use std::fmt;
    use super::*;
    use data::InfoPool;
    const SHORT_VEC_SIZE: usize = 256;

    fn gen_random_vec() -> Vec<u8> {
        (0..SHORT_VEC_SIZE).map(|_| random()).collect::<Vec<u8>>()
    }

    fn should_generate_same_output_given_same_input<G: Generator>(gen: G)
    where
        G::Item: fmt::Debug + PartialEq,
    {
        for (p0, p1, v0, v1) in iter::repeat(())
            .map(|_| gen_random_vec())
            .map(|v0| (InfoPool::of_vec(v0.clone()), InfoPool::of_vec(v0)))
            .flat_map(|(p0, p1)| {
                gen.generate(&mut p0.tap()).and_then(|v0| {
                    gen.generate(&mut p1.tap()).map(|v1| (p0, p1, v0, v1))
                })
            })
            .take(100)
        {
            assert!(v0 == v1, "({:?} == {:?}) -> ({:?} == {:?})", p0, p1, v0, v1);
        }
    }

    fn usually_generates_different_output_for_different_inputs<G: Generator>(gen: G)
    where
        G::Item: PartialEq,
    {
        let nitems = 100;
        let differing = iter::repeat(())
            .map(|_| (gen_random_vec(), gen_random_vec()))
            .filter(|&(ref v0, ref v1)| v0 != v1)
            .map(|(v0, v1)| (InfoPool::of_vec(v0), InfoPool::of_vec(v1)))
            .flat_map(|(p0, p1)| {
                gen.generate(&mut p0.tap()).and_then(|v0| {
                    gen.generate(&mut p1.tap()).map(|v1| (v0, v1))
                })
            })
            .take(nitems)
            .filter(|&(ref v0, ref v1)| v0 != v1)
            .count();
        assert!(differing > 0, "Differing items:{} > 0", differing);

    }

    // Mostly only useful for scalar quantities. For collections, we basically say:
    // `while booleans() { elemnt() }`
    // So because booleans are imprecisly generated (ie: a pool of [0xff] ~ [0x80]),
    // the source and result can have a differing ordering.
    fn should_partially_order_same_as_source<G: Generator>(gen: G)
    where
        G::Item: PartialOrd + fmt::Debug,
    {
        let nitems = 100;
        for (p0, p1, v0, v1) in iter::repeat(())
            .map(|_| (gen_random_vec(), gen_random_vec()))
            .filter(|&(ref v0, ref v1)| v0 < v1)
            .map(|(v0, v1)| (InfoPool::of_vec(v0), InfoPool::of_vec(v1)))
            .flat_map(|(p0, p1)| {
                gen.generate(&mut p0.tap()).and_then(|v0| {
                    gen.generate(&mut p1.tap()).map(|v1| (p0, p1, v0, v1))
                })
            })
            .take(nitems)
        {
            assert!(v0 <= v1, "({:?} == {:?}) -> ({:?} == {:?})", p0, p1, v0, v1);
        }
    }

    #[test]
    fn consts_should_generate_same_values() {
        let v1 = gen_random_vec();
        let gen = consts("fourty two");
        assert_eq!(
            gen.generate(&mut InfoPool::of_vec(v1).tap()),
            Ok("fourty two")
        );
    }

    // If only I had some kind of property testing library.
    #[test]
    fn bools_should_generate_false_booleans_from_zeros() {
        let v1 = vec![0];
        let bools = booleans();
        assert_eq!(bools.generate(&mut InfoPool::of_vec(v1).tap()), Ok(false));
    }

    #[test]
    fn bools_should_generate_true_booleans_from_saturated_values() {
        let v1 = vec![0xff];
        let bools = booleans();
        assert_eq!(bools.generate(&mut InfoPool::of_vec(v1).tap()), Ok(true));
    }

    fn should_minimize_to<G: Generator>(gen: G, expected: G::Item)
    where
        G::Item: fmt::Debug + PartialEq,
    {
        let mut p;
        loop {
            p = InfoPool::random_of_size(SHORT_VEC_SIZE);
            match gen.generate(&mut p.tap()) {
                Ok(_) => break,
                Err(DataError::SkipItem) => (),
                Err(DataError::PoolExhausted) => panic!("Not enough pool to generate data"),
            }
        }
        debug!("Before: {:?}", p);
        let p = find_minimal(&gen, p, |_| true);
        debug!("After: {:?}", p);

        let val = gen.generate(&mut p.tap()).expect("generated value");
        assert_eq!(val, expected);

    }
    #[test]
    fn bools_should_generate_same_output_given_same_input() {
        should_generate_same_output_given_same_input(booleans())
    }

    // These really need to be proper statistical tests.
    #[test]
    fn bools_usually_generates_different_output_for_different_inputs() {
        usually_generates_different_output_for_different_inputs(booleans())
    }

    #[test]
    fn bools_minimize_to_false() {
        should_minimize_to(booleans(), false)
    }

    #[test]
    fn bools_should_partially_order_same_as_source() {
        should_partially_order_same_as_source(booleans())
    }

    #[test]
    fn vecs_should_generate_same_output_given_same_input() {
        should_generate_same_output_given_same_input(vecs(booleans()));
    }

    #[test]
    fn vecs_usually_generates_different_output_for_different_inputs() {
        usually_generates_different_output_for_different_inputs(vecs(booleans()))
    }

    #[test]
    fn vec_bools_minimize_to_empty() {
        env_logger::init().unwrap_or(());
        should_minimize_to(vecs(booleans()), vec![])
    }

    #[test]
    fn vec_bools_can_minimise_with_predicate() {
        env_logger::init().unwrap_or(());
        should_minimize_to(
            vecs(booleans()).filter(|v| v.len() > 2),
            vec![false, false, false],
        );
    }

    #[test]
    fn info_pools_should_generate_same_output_given_same_input() {
        should_generate_same_output_given_same_input(info_pools(8))
    }

    #[test]
    fn info_pools_usually_generates_different_output_for_different_inputs() {
        usually_generates_different_output_for_different_inputs(info_pools(8))
    }

    #[test]
    fn info_pools_minimize_to_empty() {
        env_logger::init().unwrap_or(());
        // We force the generator to output a fixed length.
        // This is perhaps not the best idea ever; but it'll do for now.
        should_minimize_to(info_pools(8), InfoPool::of_vec(vec![0; 8]))
    }

    #[test]
    fn u8s_should_generate_same_output_given_same_input() {
        should_generate_same_output_given_same_input(u8s())
    }

    // These really need to be proper statistical tests.
    #[test]
    fn u8s_usually_generates_different_output_for_different_inputs() {
        usually_generates_different_output_for_different_inputs(u8s());
    }

    #[test]
    fn u8s_minimize_to_zero() {
        should_minimize_to(u8s(), 0);
    }

    #[test]
    fn u8s_should_partially_order_same_as_source() {
        should_partially_order_same_as_source(u8s());
    }

    #[test]
    fn u64s_should_generate_same_output_given_same_input() {
        should_generate_same_output_given_same_input(u64s())
    }

    // These really need to be proper statistical tests.
    #[test]
    fn u64s_usually_generates_different_output_for_different_inputs() {
        usually_generates_different_output_for_different_inputs(u64s());
    }

    #[test]
    fn u64s_minimize_to_zero() {
        should_minimize_to(u64s(), 0);
    }

    #[test]
    fn u64s_should_partially_order_same_as_source() {
        should_partially_order_same_as_source(u64s());
    }

    #[test]
    fn tuple_u8s_u8s_should_generate_same_output_given_same_input() {
        should_generate_same_output_given_same_input((u8s(), u8s()))
    }

    // These really need to be proper statistical tests.
    #[test]
    fn tuple_u8s_u8s_usually_generates_different_output_for_different_inputs() {
        usually_generates_different_output_for_different_inputs((u8s(), u8s()));
    }

    #[test]
    fn tuple_u8s_u8s_minimize_to_zero() {
        should_minimize_to((u8s(), u8s()), (0, 0));
    }

    #[test]
    fn tuple_u8s_u8s_should_partially_order_same_as_source() {
        should_partially_order_same_as_source((u8s(), u8s()));
    }

    #[test]
    fn filter_should_pass_through_when_true() {
        let gen = consts(()).filter(|&_| true);
        let p = InfoPool::random_of_size(4);
        assert_eq!(gen.generate(&mut p.tap()), Ok(()));
    }

    #[test]
    fn filter_should_skip_when_false() {
        let gen = consts(()).filter(|&_| false);
        let p = InfoPool::random_of_size(4);
        assert_eq!(gen.generate(&mut p.tap()), Err(DataError::SkipItem));
    }

}
<|endoftext|>


<commit_before>use std::fmt::{ Display, Formatter, Error };
use std::iter::Iterator;
use std::collections::HashMap;
use std::vec::Vec;
use std::io::prelude::*;
use std::io::BufReader;
use std::fs::File;

use std::clone::Clone;

/**
 * test can be deleted.
 * clauses will house all clauses, key is int so that we can use occurrences
 * ID for simplifications later
 * occurrences tracks which clauses literals are used in
 */
#[derive(Debug)]
pub struct CNF {
    nvar        : i32,
    nclause     : i32,
    clauses     : HashMap<i32, Vec<i32>>,
    occurrences : HashMap<i32, Vec<i32>>
}

impl CNF {
    pub fn new(nvar: i32, nclause: i32) -> CNF {
        CNF {
            nvar        : nvar,
            nclause     : nclause,
            clauses     : HashMap::new(),
            occurrences : HashMap::new()
        }
    }

    // Add a clause, return the ID of the inserted clause
    fn add_clause(&mut self, clause : Vec<i32>) -> i32 {
        let id : i32 = self.clauses.len() as i32;
        for var in &clause {
            let occ = self.occurrences.entry(*var).or_insert(Vec::new());
            occ.push(id);
        }
        self.clauses.insert(id, clause);
        id
    }

    pub fn to_string(self) -> String {
        format!("Nvar: {:?} Nclause: {:?}\nClauses: {:?}\nOccurrrences: {:?}",
                self.nvar, self.nclause,
                self.clauses, self.occurrences)
    }
}

impl Clone for CNF {
    fn clone(&self) -> CNF {
        CNF {
            nvar : self.nvar,
            nclause : self.nclause,
            clauses : self.clauses.clone(),
            occurrences : self.occurrences.clone(),
        }
    }
}

impl Display for CNF {
    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
        let mut comma_separated = String::new();
        comma_separated.push_str("Need to implement formatter");
        write!(f, "{}", comma_separated)
    }
}

// End CNF

// Begin Parsing

#[derive(Debug)]
pub enum SatError {
    InvalidSyntax,
    InvalidProblem
}

fn parse_dimacs(reader: &mut BufReader<File>) -> Result<CNF, SatError> {
    let mut line_iterator = reader.lines();

    let mut nvar: i32 = -1;
    let mut nclause: i32 = -1;
    // DIMACS file must have a problem statement before other lines.
    // This first loop searches for the problem statement.
    for line in &mut line_iterator {
        let line = line.expect("Could not read file");  // Unwrap result
        let words: Vec<&str> = line.split_whitespace().collect();
        match words[0] {
            "c" => { }
            "p" => { // Problem statement
                // Must have format "p cnf nvar nclause"
                if words.len() != 4 || words[1] != "cnf" {
                    return Err(SatError::InvalidSyntax);
                }
                nvar = words[2].parse()
                    .expect(&format!("Invalid number of variables {}", words[2]));
                nclause = words[3].parse()
                    .expect(&format!("Invalid number of clauses {}", words[3]));
                break;
            }
            _ => { return Err(SatError::InvalidSyntax); }
        }
    }
    // Then nvar, nclause were never initialized
    if nvar == -1 || nclause == -1 {
       return Err(SatError::InvalidProblem);
    }
    // TODO: Different errors for different descriptions (hence why I've split up this if
    // statement)
    if nvar == 0 || nclause == 0 {
        return Err(SatError::InvalidProblem)
    }

    // Initialize CNF and parse the rest of the file
    let mut cnf = CNF::new(nvar, nclause);
    for line in &mut line_iterator {
        let line = line.expect("Could not read filce");
        let words: Vec<&str> = line.split_whitespace().collect();
        match words[0] {
            "c" => { }
            _   => {
                let tokens: Vec<i32> = words.iter()
                    .filter_map(|s| {
                        let n = s.parse::<i32>().expect("Invalid DIMACS File");
                        // FIXME: This ignores zeros not just as line enders but in the formulas
                        // themselves. TODO: Split on zeros at the end here.
                        if n == 0 { return None; }
                        if n > nvar { panic!(format!("Variable out of range: {}", n)); }
                        Some(if n < 0 { (-n) << 1 | 1 } else { n << 1 })
                    })
                    .collect();
                cnf.add_clause(tokens);
            }
        }
    }
    Ok(cnf)
}

pub fn parse_dimacs_file(f: File) -> Result<CNF, SatError> {
    // Read the file
    let mut reader = BufReader::new(f);

    // TODO: This is definitely not the correct way to handle errors.
    // Should parse_dimacs have options for returning an error AND panicking?
    let line = parse_dimacs(&mut reader);
    line
}
<commit_msg>Add testing framework for cnf.rs<commit_after>extern crate tempfile;

use std::fmt::{ Display, Formatter, Error };
use std::iter::Iterator;
use std::collections::HashMap;
use std::vec::Vec;
use std::io::prelude::*;
use std::io::BufReader;
use std::fs::File;

use std::clone::Clone;

/**
 * test can be deleted.
 * clauses will house all clauses, key is int so that we can use occurrences
 * ID for simplifications later
 * occurrences tracks which clauses literals are used in
 */
#[derive(Debug)]
pub struct CNF {
    nvar        : i32,
    nclause     : i32,
    clauses     : HashMap<i32, Vec<i32>>,
    occurrences : HashMap<i32, Vec<i32>>
}

impl CNF {
    pub fn new(nvar: i32, nclause: i32) -> CNF {
        CNF {
            nvar        : nvar,
            nclause     : nclause,
            clauses     : HashMap::new(),
            occurrences : HashMap::new()
        }
    }

    // Add a clause, return the ID of the inserted clause
    fn add_clause(&mut self, clause : Vec<i32>) -> i32 {
        let id : i32 = self.clauses.len() as i32;
        for var in &clause {
            let occ = self.occurrences.entry(*var).or_insert(Vec::new());
            occ.push(id);
        }
        self.clauses.insert(id, clause);
        id
    }

    pub fn to_string(self) -> String {
        format!("Nvar: {:?} Nclause: {:?}\nClauses: {:?}\nOccurrrences: {:?}",
                self.nvar, self.nclause,
                self.clauses, self.occurrences)
    }
}

impl Clone for CNF {
    fn clone(&self) -> CNF {
        CNF {
            nvar : self.nvar,
            nclause : self.nclause,
            clauses : self.clauses.clone(),
            occurrences : self.occurrences.clone(),
        }
    }
}

impl Display for CNF {
    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
        let mut comma_separated = String::new();
        comma_separated.push_str("Need to implement formatter");
        write!(f, "{}", comma_separated)
    }
}

// End CNF

// Begin Parsing

#[derive(Debug)]
pub enum SatError {
    InvalidSyntax,
    InvalidProblem
}

fn parse_dimacs(reader: &mut BufReader<File>) -> Result<CNF, SatError> {
    let mut line_iterator = reader.lines();

    let mut nvar: i32 = -1;
    let mut nclause: i32 = -1;
    // DIMACS file must have a problem statement before other lines.
    // This first loop searches for the problem statement.
    for line in &mut line_iterator {
        let line = line.expect("Could not read file");  // Unwrap result
        let words: Vec<&str> = line.split_whitespace().collect();
        match words[0] {
            "c" => { }
            "p" => { // Problem statement
                // Must have format "p cnf nvar nclause"
                if words.len() != 4 || words[1] != "cnf" {
                    return Err(SatError::InvalidSyntax);
                }
                nvar = words[2].parse()
                    .expect(&format!("Invalid number of variables {}", words[2]));
                nclause = words[3].parse()
                    .expect(&format!("Invalid number of clauses {}", words[3]));
                break;
            }
            _ => { return Err(SatError::InvalidSyntax); }
        }
    }
    // Then nvar, nclause were never initialized
    if nvar == -1 || nclause == -1 {
       return Err(SatError::InvalidProblem);
    }
    // TODO: Different errors for different descriptions (hence why I've split up this if
    // statement)
    if nvar == 0 || nclause == 0 {
        return Err(SatError::InvalidProblem)
    }

    // Initialize CNF and parse the rest of the file
    let mut cnf = CNF::new(nvar, nclause);
    for line in &mut line_iterator {
        let line = line.expect("Could not read filce");
        let words: Vec<&str> = line.split_whitespace().collect();
        match words[0] {
            "c" => { }
            _   => {
                let tokens: Vec<i32> = words.iter()
                    .filter_map(|s| {
                        let n = s.parse::<i32>().expect("Invalid DIMACS File");
                        // FIXME: This ignores zeros not just as line enders but in the formulas
                        // themselves. TODO: Split on zeros at the end here.
                        if n == 0 { return None; }
                        if n > nvar { panic!(format!("Variable out of range: {}", n)); }
                        Some(if n < 0 { (-n) << 1 | 1 } else { n << 1 })
                    })
                    .collect();
                cnf.add_clause(tokens);
            }
        }
    }
    Ok(cnf)
}

pub fn parse_dimacs_file(f: File) -> Result<CNF, SatError> {
    // Read the file
    let mut reader = BufReader::new(f);

    // TODO: This is definitely not the correct way to handle errors.
    // Should parse_dimacs have options for returning an error AND panicking?
    let line = parse_dimacs(&mut reader);
    line
}

#[cfg(test)]
mod tests {
	extern crate tempfile;
	use std::fs::File;
	use std::io::{Write, Seek, SeekFrom};

    use super::parse_dimacs_file;

    macro_rules! create_tempfile {
        ($x: expr) => {{
            let mut tmpfile: File = tempfile::tempfile().unwrap();
            let _ = write!(tmpfile, $x);
            tmpfile.seek(SeekFrom::Start(0)).unwrap();

            tmpfile
        }};
    }

    #[test]
    #[should_panic]
    fn variable_out_of_range() {
        let tmpfile = create_tempfile!("
            p cnf 2 3
            1 2 0
            4 1 0
        ");
        let _ = parse_dimacs_file(tmpfile).unwrap();
    }
}
<|endoftext|>