<commit_before>use std::collections::HashMap;

use agent::AgentService;
use {Client, QueryOptions, QueryMeta};
use errors::Result;
use request::get;

#[serde(default)]
#[derive(Eq, Default, PartialEq, Serialize, Deserialize, Debug)]
pub struct HealthCheck {
    pub Node: String,
    pub CheckID: String,
    pub Name: String,
    pub Status: String,
    pub Notes: String,
    pub Output: String,
    pub ServiceID: String,
    pub ServiceName: String,
    pub ServiceTags: Vec<String>,
}

#[serde(default)]
#[derive(Eq, Default, PartialEq, Serialize, Deserialize, Debug)]
pub struct Node {
    pub ID: String,
    pub Node: String,
    pub Address: String,
    pub Datacenter: Option<String>,
    pub TaggedAddresses: HashMap<String, String>,
    pub Meta: HashMap<String, String>,
    pub CreateIndex: u64,
    pub ModifyIndex: u64,
}

#[serde(default)]
#[derive(Eq, Default, PartialEq, Serialize, Deserialize, Debug)]
pub struct ServiceEntry {
    pub Node: Node,
    pub Service: AgentService,
    pub Checks: Vec<HealthCheck>,
}

pub trait Health {
    fn service(
        &self,
        service: &str,
        tag: Option<&str>,
        passing_only: bool,
        options: Option<&QueryOptions>,
    ) -> Result<(Vec<ServiceEntry>, QueryMeta)>;
}

impl Health for Client {
    fn service(
        &self,
        service: &str,
        tag: Option<&str>,
        passing_only: bool,
        options: Option<&QueryOptions>,
    ) -> Result<(Vec<ServiceEntry>, QueryMeta)> {
        let mut params = HashMap::new();
        let path = format!("/v1/health/service/{}", service);
        if passing_only {
            params.insert(String::from("passing"), String::from("1"));
        }
        if let Some(tag) = tag {
            params.insert(String::from("tag"), tag.to_owned());
        }
        get(&path, &self.config, params, options)
    }
}
<commit_msg>These values can be null<commit_after>use std::collections::HashMap;

use agent::AgentService;
use {Client, QueryOptions, QueryMeta};
use errors::Result;
use request::get;

#[serde(default)]
#[derive(Eq, Default, PartialEq, Serialize, Deserialize, Debug)]
pub struct HealthCheck {
    pub Node: String,
    pub CheckID: String,
    pub Name: String,
    pub Status: String,
    pub Notes: String,
    pub Output: String,
    pub ServiceID: String,
    pub ServiceName: String,
    pub ServiceTags: Option<Vec<String>>,
}

#[serde(default)]
#[derive(Eq, Default, PartialEq, Serialize, Deserialize, Debug)]
pub struct Node {
    pub ID: String,
    pub Node: String,
    pub Address: String,
    pub Datacenter: Option<String>,
    pub TaggedAddresses: Option<HashMap<String, String>>,
    pub Meta: Option<HashMap<String, String>>,
    pub CreateIndex: u64,
    pub ModifyIndex: u64,
}

#[serde(default)]
#[derive(Eq, Default, PartialEq, Serialize, Deserialize, Debug)]
pub struct ServiceEntry {
    pub Node: Node,
    pub Service: AgentService,
    pub Checks: Vec<HealthCheck>,
}

pub trait Health {
    fn service(
        &self,
        service: &str,
        tag: Option<&str>,
        passing_only: bool,
        options: Option<&QueryOptions>,
    ) -> Result<(Vec<ServiceEntry>, QueryMeta)>;
}

impl Health for Client {
    fn service(
        &self,
        service: &str,
        tag: Option<&str>,
        passing_only: bool,
        options: Option<&QueryOptions>,
    ) -> Result<(Vec<ServiceEntry>, QueryMeta)> {
        let mut params = HashMap::new();
        let path = format!("/v1/health/service/{}", service);
        if passing_only {
            params.insert(String::from("passing"), String::from("1"));
        }
        if let Some(tag) = tag {
            params.insert(String::from("tag"), tag.to_owned());
        }
        get(&path, &self.config, params, options)
    }
}
<|endoftext|>


<commit_before>extern crate globset;

use globset::{GlobBuilder, GlobSet, GlobSetBuilder};
use std::collections::HashSet;
use std::fs;
use std::io;
use std::io::Read;
use std::path::{Path, PathBuf};

pub struct Ignore {
    files: Vec<IgnoreFile>,
}

#[derive(Debug)]
pub enum Error {
    GlobSet(globset::Error),
    Io(io::Error),
}

struct IgnoreFile {
    set: GlobSet,
    patterns: Vec<Pattern>,
    root: PathBuf,
}

struct Pattern {
    pattern: String,
    pattern_type: PatternType,
    anchored: bool,
}

enum PatternType {
    Ignore,
    Whitelist,
}

#[derive(PartialEq)]
enum MatchResult {
    Ignore,
    Whitelist,
    None,
}

pub fn load(paths: &[PathBuf]) -> Ignore {
    let mut files = vec![];
    let mut checked_dirs = HashSet::new();

    for path in paths {
        let mut p = path.to_owned();

        loop {
            if !checked_dirs.contains(&p) {
                checked_dirs.insert(p.clone());

                let ignore_path = p.join(".ignore");
                if ignore_path.exists() {
                    if let Ok(f) = IgnoreFile::new(&ignore_path) {
                        debug!("Loaded {:?}", ignore_path);
                        files.push(f);
                    } else {
                        debug!("Unable to load {:?}", ignore_path);
                    }
                }
            }

            if p.parent().is_none() {
                break;
            }

            p.pop();
        }
    }

    Ignore::new(files)
}

impl Ignore {
    const fn new(files: Vec<IgnoreFile>) -> Self {
        Self { files }
    }

    pub fn is_excluded(&self, path: &Path) -> bool {
        let mut applicable_files: Vec<&IgnoreFile> = self
            .files
            .iter()
            .filter(|f| path.starts_with(&f.root))
            .collect();
        applicable_files.sort_by(|l, r| l.root_len().cmp(&r.root_len()));

        // TODO: add user ignores

        let mut result = MatchResult::None;

        for file in applicable_files {
            match file.matches(path) {
                MatchResult::Ignore => result = MatchResult::Ignore,
                MatchResult::Whitelist => result = MatchResult::Whitelist,
                MatchResult::None => {}
            }
        }

        result == MatchResult::Ignore
    }
}

impl IgnoreFile {
    pub fn new(path: &Path) -> Result<Self, Error> {
        let mut file = fs::File::open(path)?;
        let mut contents = String::new();
        file.read_to_string(&mut contents)?;

        let lines: Vec<_> = contents.lines().collect();
        let root = path.parent().expect("ignore file is at filesystem root");

        Self::from_strings(&lines, root)
    }

    pub fn from_strings(strs: &[&str], root: &Path) -> Result<Self, Error> {
        let mut builder = GlobSetBuilder::new();
        let mut patterns = vec![];

        let parsed_patterns = Self::parse(strs);
        for p in parsed_patterns {
            let mut pat = p.pattern.clone();
            if !p.anchored && !pat.starts_with("**/") {
                pat = "**/".to_string() + &pat;
            }

            if !pat.ends_with("/**") {
                pat += "/**";
            }

            let glob = GlobBuilder::new(&pat).literal_separator(true).build()?;

            builder.add(glob);
            patterns.push(p);
        }

        Ok(Self {
            set: builder.build()?,
            patterns,
            root: root.to_owned(),
        })
    }

    #[cfg(test)]
    fn is_excluded(&self, path: &Path) -> bool {
        self.matches(path) == MatchResult::Ignore
    }

    fn matches(&self, path: &Path) -> MatchResult {
        if let Ok(stripped) = path.strip_prefix(&self.root) {
            let matches = self.set.matches(stripped);
            if let Some(i) = matches.iter().rev().next() {
                let pattern = &self.patterns[*i];
                return match pattern.pattern_type {
                    PatternType::Whitelist => MatchResult::Whitelist,
                    PatternType::Ignore => MatchResult::Ignore,
                };
            }
        }

        MatchResult::None
    }

    pub fn root_len(&self) -> usize {
        self.root.as_os_str().len()
    }

    fn parse(contents: &[&str]) -> Vec<Pattern> {
        contents
            .iter()
            .filter_map(|l| {
                if !l.is_empty() && !l.starts_with('#') {
                    Some(Pattern::parse(l))
                } else {
                    None
                }
            })
            .collect()
    }
}

impl Pattern {
    fn parse(pattern: &str) -> Self {
        let mut normalized = String::from(pattern);

        let pattern_type = if normalized.starts_with('!') {
            normalized.remove(0);
            PatternType::Whitelist
        } else {
            PatternType::Ignore
        };

        let anchored = if normalized.starts_with('/') {
            normalized.remove(0);
            true
        } else {
            false
        };

        if normalized.ends_with('/') {
            normalized.pop();
        }

        if normalized.starts_with("\\#") || normalized.starts_with("\\!") {
            normalized.remove(0);
        }

        Self {
            pattern: normalized,
            pattern_type,
            anchored,
        }
    }
}

impl From<globset::Error> for Error {
    fn from(error: globset::Error) -> Self {
        Self::GlobSet(error)
    }
}

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

#[cfg(test)]
mod tests {
    use super::IgnoreFile;
    use std::path::PathBuf;

    fn base_dir() -> PathBuf {
        PathBuf::from("/home/user/dir")
    }

    fn build_ignore(pattern: &str) -> IgnoreFile {
        IgnoreFile::from_strings(&[pattern], &base_dir()).expect("test ignore file invalid")
    }

    #[test]
    fn test_matches_exact() {
        let file = build_ignore("Cargo.toml");

        assert!(file.is_excluded(&base_dir().join("Cargo.toml")));
    }

    #[test]
    fn test_does_not_match() {
        let file = build_ignore("Cargo.toml");

        assert!(!file.is_excluded(&base_dir().join("src").join("main.rs")));
    }

    #[test]
    fn test_matches_simple_wildcard() {
        let file = build_ignore("targ*");

        assert!(file.is_excluded(&base_dir().join("target")));
    }

    #[test]
    fn test_matches_subdir_exact() {
        let file = build_ignore("target");

        assert!(file.is_excluded(&base_dir().join("target/")));
    }

    #[test]
    fn test_matches_subdir() {
        let file = build_ignore("target");

        assert!(file.is_excluded(&base_dir().join("target").join("file")));
        assert!(file.is_excluded(&base_dir().join("target").join("subdir").join("file")));
    }

    #[test]
    fn test_wildcard_with_dir() {
        let file = build_ignore("target/f*");

        assert!(file.is_excluded(&base_dir().join("target").join("file")));
        assert!(!file.is_excluded(&base_dir().join("target").join("subdir").join("file")));
    }

    #[test]
    fn test_leading_slash() {
        let file = build_ignore("/*.c");

        assert!(file.is_excluded(&base_dir().join("cat-file.c")));
        assert!(!file.is_excluded(&base_dir().join("mozilla-sha1").join("sha1.c")));
    }

    #[test]
    fn test_leading_double_wildcard() {
        let file = build_ignore("**/foo");

        assert!(file.is_excluded(&base_dir().join("foo")));
        assert!(file.is_excluded(&base_dir().join("target").join("foo")));
        assert!(file.is_excluded(&base_dir().join("target").join("subdir").join("foo")));
    }

    #[test]
    fn test_trailing_double_wildcard() {
        let file = build_ignore("abc/**");

        assert!(!file.is_excluded(&base_dir().join("def").join("foo")));
        assert!(file.is_excluded(&base_dir().join("abc").join("foo")));
        assert!(file.is_excluded(&base_dir().join("abc").join("subdir").join("foo")));
    }

    #[test]
    fn test_sandwiched_double_wildcard() {
        let file = build_ignore("a/**/b");

        assert!(file.is_excluded(&base_dir().join("a").join("b")));
        assert!(file.is_excluded(&base_dir().join("a").join("x").join("b")));
        assert!(file.is_excluded(&base_dir().join("a").join("x").join("y").join("b")));
    }

    #[test]
    fn test_empty_file_never_excludes() {
        let file = IgnoreFile::from_strings(&[], &base_dir()).expect("test ignore file invalid");

        assert!(!file.is_excluded(&base_dir().join("target")));
    }

    #[test]
    fn test_checks_all_patterns() {
        let patterns = vec!["target", "target2"];
        let file =
            IgnoreFile::from_strings(&patterns, &base_dir()).expect("test ignore file invalid");

        assert!(file.is_excluded(&base_dir().join("target").join("foo.txt")));
        assert!(file.is_excluded(&base_dir().join("target2").join("bar.txt")));
    }

    #[test]
    fn test_handles_whitelisting() {
        let patterns = vec!["target", "!target/foo.txt"];
        let file =
            IgnoreFile::from_strings(&patterns, &base_dir()).expect("test ignore file invalid");

        assert!(!file.is_excluded(&base_dir().join("target").join("foo.txt")));
        assert!(file.is_excluded(&base_dir().join("target").join("blah.txt")));
    }
}
<commit_msg>Look for .ignore files in subfolders of watched paths<commit_after>extern crate globset;
extern crate walkdir;

use globset::{GlobBuilder, GlobSet, GlobSetBuilder};
use std::collections::HashSet;
use std::fs;
use std::io;
use std::io::Read;
use std::path::{Path, PathBuf};
use walkdir::WalkDir;

pub struct Ignore {
    files: Vec<IgnoreFile>,
}

#[derive(Debug)]
pub enum Error {
    GlobSet(globset::Error),
    Io(io::Error),
}

struct IgnoreFile {
    set: GlobSet,
    patterns: Vec<Pattern>,
    root: PathBuf,
}

struct Pattern {
    pattern: String,
    pattern_type: PatternType,
    anchored: bool,
}

enum PatternType {
    Ignore,
    Whitelist,
}

#[derive(PartialEq)]
enum MatchResult {
    Ignore,
    Whitelist,
    None,
}

pub fn load(paths: &[PathBuf]) -> Ignore {
    let mut files = vec![];
    let mut checked_dirs = HashSet::new();

    for path in paths {
        let mut p = path.to_owned();

        // walk up to root
        loop {
            if !checked_dirs.contains(&p) {
                checked_dirs.insert(p.clone());

                let ignore_path = p.join(".ignore");
                if ignore_path.exists() {
                    if let Ok(f) = IgnoreFile::new(&ignore_path) {
                        debug!("Loaded {:?}", ignore_path);
                        files.push(f);
                    } else {
                        debug!("Unable to load {:?}", ignore_path);
                    }
                }
            }

            if p.parent().is_none() {
                break;
            }

            p.pop();
        }

        //also look in subfolders
        for entry in WalkDir::new(path)
            .into_iter()
            .filter_map(Result::ok)
            .filter(|e| e.file_type().is_file())
            .filter(|e| e.file_name() == ".ignore")
        {
            let ignore_path = entry.path();
            if let Ok(f) = IgnoreFile::new(&ignore_path) {
                debug!("Loaded {:?}", ignore_path);
                files.push(f);
            } else {
                debug!("Unable to load {:?}", ignore_path);
            }
        }
    }

    Ignore::new(files)
}

impl Ignore {
    const fn new(files: Vec<IgnoreFile>) -> Self {
        Self { files }
    }

    pub fn is_excluded(&self, path: &Path) -> bool {
        let mut applicable_files: Vec<&IgnoreFile> = self
            .files
            .iter()
            .filter(|f| path.starts_with(&f.root))
            .collect();
        applicable_files.sort_by(|l, r| l.root_len().cmp(&r.root_len()));

        // TODO: add user ignores

        let mut result = MatchResult::None;

        for file in applicable_files {
            match file.matches(path) {
                MatchResult::Ignore => result = MatchResult::Ignore,
                MatchResult::Whitelist => result = MatchResult::Whitelist,
                MatchResult::None => {}
            }
        }

        result == MatchResult::Ignore
    }
}

impl IgnoreFile {
    pub fn new(path: &Path) -> Result<Self, Error> {
        let mut file = fs::File::open(path)?;
        let mut contents = String::new();
        file.read_to_string(&mut contents)?;

        let lines: Vec<_> = contents.lines().collect();
        let root = path.parent().expect("ignore file is at filesystem root");

        Self::from_strings(&lines, root)
    }

    pub fn from_strings(strs: &[&str], root: &Path) -> Result<Self, Error> {
        let mut builder = GlobSetBuilder::new();
        let mut patterns = vec![];

        let parsed_patterns = Self::parse(strs);
        for p in parsed_patterns {
            let mut pat = p.pattern.clone();
            if !p.anchored && !pat.starts_with("**/") {
                pat = "**/".to_string() + &pat;
            }

            if !pat.ends_with("/**") {
                pat += "/**";
            }

            let glob = GlobBuilder::new(&pat).literal_separator(true).build()?;

            builder.add(glob);
            patterns.push(p);
        }

        Ok(Self {
            set: builder.build()?,
            patterns,
            root: root.to_owned(),
        })
    }

    #[cfg(test)]
    fn is_excluded(&self, path: &Path) -> bool {
        self.matches(path) == MatchResult::Ignore
    }

    fn matches(&self, path: &Path) -> MatchResult {
        if let Ok(stripped) = path.strip_prefix(&self.root) {
            let matches = self.set.matches(stripped);
            if let Some(i) = matches.iter().rev().next() {
                let pattern = &self.patterns[*i];
                return match pattern.pattern_type {
                    PatternType::Whitelist => MatchResult::Whitelist,
                    PatternType::Ignore => MatchResult::Ignore,
                };
            }
        }

        MatchResult::None
    }

    pub fn root_len(&self) -> usize {
        self.root.as_os_str().len()
    }

    fn parse(contents: &[&str]) -> Vec<Pattern> {
        contents
            .iter()
            .filter_map(|l| {
                if !l.is_empty() && !l.starts_with('#') {
                    Some(Pattern::parse(l))
                } else {
                    None
                }
            })
            .collect()
    }
}

impl Pattern {
    fn parse(pattern: &str) -> Self {
        let mut normalized = String::from(pattern);

        let pattern_type = if normalized.starts_with('!') {
            normalized.remove(0);
            PatternType::Whitelist
        } else {
            PatternType::Ignore
        };

        let anchored = if normalized.starts_with('/') {
            normalized.remove(0);
            true
        } else {
            false
        };

        if normalized.ends_with('/') {
            normalized.pop();
        }

        if normalized.starts_with("\\#") || normalized.starts_with("\\!") {
            normalized.remove(0);
        }

        Self {
            pattern: normalized,
            pattern_type,
            anchored,
        }
    }
}

impl From<globset::Error> for Error {
    fn from(error: globset::Error) -> Self {
        Self::GlobSet(error)
    }
}

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

#[cfg(test)]
mod tests {
    use super::IgnoreFile;
    use std::path::PathBuf;

    fn base_dir() -> PathBuf {
        PathBuf::from("/home/user/dir")
    }

    fn build_ignore(pattern: &str) -> IgnoreFile {
        IgnoreFile::from_strings(&[pattern], &base_dir()).expect("test ignore file invalid")
    }

    #[test]
    fn test_matches_exact() {
        let file = build_ignore("Cargo.toml");

        assert!(file.is_excluded(&base_dir().join("Cargo.toml")));
    }

    #[test]
    fn test_does_not_match() {
        let file = build_ignore("Cargo.toml");

        assert!(!file.is_excluded(&base_dir().join("src").join("main.rs")));
    }

    #[test]
    fn test_matches_simple_wildcard() {
        let file = build_ignore("targ*");

        assert!(file.is_excluded(&base_dir().join("target")));
    }

    #[test]
    fn test_matches_subdir_exact() {
        let file = build_ignore("target");

        assert!(file.is_excluded(&base_dir().join("target/")));
    }

    #[test]
    fn test_matches_subdir() {
        let file = build_ignore("target");

        assert!(file.is_excluded(&base_dir().join("target").join("file")));
        assert!(file.is_excluded(&base_dir().join("target").join("subdir").join("file")));
    }

    #[test]
    fn test_wildcard_with_dir() {
        let file = build_ignore("target/f*");

        assert!(file.is_excluded(&base_dir().join("target").join("file")));
        assert!(!file.is_excluded(&base_dir().join("target").join("subdir").join("file")));
    }

    #[test]
    fn test_leading_slash() {
        let file = build_ignore("/*.c");

        assert!(file.is_excluded(&base_dir().join("cat-file.c")));
        assert!(!file.is_excluded(&base_dir().join("mozilla-sha1").join("sha1.c")));
    }

    #[test]
    fn test_leading_double_wildcard() {
        let file = build_ignore("**/foo");

        assert!(file.is_excluded(&base_dir().join("foo")));
        assert!(file.is_excluded(&base_dir().join("target").join("foo")));
        assert!(file.is_excluded(&base_dir().join("target").join("subdir").join("foo")));
    }

    #[test]
    fn test_trailing_double_wildcard() {
        let file = build_ignore("abc/**");

        assert!(!file.is_excluded(&base_dir().join("def").join("foo")));
        assert!(file.is_excluded(&base_dir().join("abc").join("foo")));
        assert!(file.is_excluded(&base_dir().join("abc").join("subdir").join("foo")));
    }

    #[test]
    fn test_sandwiched_double_wildcard() {
        let file = build_ignore("a/**/b");

        assert!(file.is_excluded(&base_dir().join("a").join("b")));
        assert!(file.is_excluded(&base_dir().join("a").join("x").join("b")));
        assert!(file.is_excluded(&base_dir().join("a").join("x").join("y").join("b")));
    }

    #[test]
    fn test_empty_file_never_excludes() {
        let file = IgnoreFile::from_strings(&[], &base_dir()).expect("test ignore file invalid");

        assert!(!file.is_excluded(&base_dir().join("target")));
    }

    #[test]
    fn test_checks_all_patterns() {
        let patterns = vec!["target", "target2"];
        let file =
            IgnoreFile::from_strings(&patterns, &base_dir()).expect("test ignore file invalid");

        assert!(file.is_excluded(&base_dir().join("target").join("foo.txt")));
        assert!(file.is_excluded(&base_dir().join("target2").join("bar.txt")));
    }

    #[test]
    fn test_handles_whitelisting() {
        let patterns = vec!["target", "!target/foo.txt"];
        let file =
            IgnoreFile::from_strings(&patterns, &base_dir()).expect("test ignore file invalid");

        assert!(!file.is_excluded(&base_dir().join("target").join("foo.txt")));
        assert!(file.is_excluded(&base_dir().join("target").join("blah.txt")));
    }
}
<|endoftext|>


<commit_before>use std::str;

use crate::deserialize::{self, FromSql};
use crate::mysql::{Mysql, MysqlValue};
use crate::sql_types::{BigInt, Binary, Double, Float, Integer, SmallInt, Text};

impl FromSql<SmallInt, Mysql> for i16 {
    fn from_sql(value: Option<MysqlValue<'_>>) -> deserialize::Result<Self> {
        use crate::mysql::NumericRepresentation::*;

        let data = not_none!(value);
        match data.numeric_value()? {
            Tiny(x) => Ok(x.into()),
            Small(x) => Ok(x),
            Medium(x) => Ok(x as Self),
            Big(x) => Ok(x as Self),
            Float(x) => Ok(x as Self),
            Double(x) => Ok(x as Self),
            Decimal(bytes) => {
                let string = str::from_utf8(bytes)?;
                let integer_portion = string.split('.').nth(0).unwrap_or_default();
                Ok(integer_portion.parse()?)
            }
        }
    }
}

impl FromSql<Integer, Mysql> for i32 {
    fn from_sql(value: Option<MysqlValue<'_>>) -> deserialize::Result<Self> {
        use crate::mysql::NumericRepresentation::*;

        let data = not_none!(value);
        match data.numeric_value()? {
            Tiny(x) => Ok(x.into()),
            Small(x) => Ok(x.into()),
            Medium(x) => Ok(x),
            Big(x) => Ok(x as Self),
            Float(x) => Ok(x as Self),
            Double(x) => Ok(x as Self),
            Decimal(bytes) => {
                let string = str::from_utf8(bytes)?;
                let integer_portion = string.split('.').nth(0).unwrap_or_default();
                Ok(integer_portion.parse()?)
            }
        }
    }
}

impl FromSql<BigInt, Mysql> for i64 {
    fn from_sql(value: Option<MysqlValue<'_>>) -> deserialize::Result<Self> {
        use crate::mysql::NumericRepresentation::*;

        let data = not_none!(value);
        match data.numeric_value()? {
            Tiny(x) => Ok(x.into()),
            Small(x) => Ok(x.into()),
            Medium(x) => Ok(x.into()),
            Big(x) => Ok(x),
            Float(x) => Ok(x as Self),
            Double(x) => Ok(x as Self),
            Decimal(bytes) => {
                let string = str::from_utf8(bytes)?;
                let integer_portion = string.split('.').nth(0).unwrap_or_default();
                Ok(integer_portion.parse()?)
            }
        }
    }
}

impl FromSql<Float, Mysql> for f32 {
    fn from_sql(value: Option<MysqlValue<'_>>) -> deserialize::Result<Self> {
        use crate::mysql::NumericRepresentation::*;

        let data = not_none!(value);
        match data.numeric_value()? {
            Tiny(x) => Ok(x.into()),
            Small(x) => Ok(x.into()),
            Medium(x) => Ok(x as Self),
            Big(x) => Ok(x as Self),
            Float(x) => Ok(x),
            Double(x) => Ok(x as Self),
            Decimal(bytes) => Ok(str::from_utf8(bytes)?.parse()?),
        }
    }
}

impl FromSql<Double, Mysql> for f64 {
    fn from_sql(value: Option<MysqlValue<'_>>) -> deserialize::Result<Self> {
        use crate::mysql::NumericRepresentation::*;

        let data = not_none!(value);
        match data.numeric_value()? {
            Tiny(x) => Ok(x.into()),
            Small(x) => Ok(x.into()),
            Medium(x) => Ok(x.into()),
            Big(x) => Ok(x as Self),
            Float(x) => Ok(x.into()),
            Double(x) => Ok(x),
            Decimal(bytes) => Ok(str::from_utf8(bytes)?.parse()?),
        }
    }
}

impl FromSql<Text, Mysql> for String {
    fn from_sql(value: Option<MysqlValue<'_>>) -> deserialize::Result<Self> {
        let value = not_none!(value);
        String::from_utf8(value.as_bytes().into()).map_err(Into::into)
    }
}

impl FromSql<Binary, Mysql> for Vec<u8> {
    fn from_sql(value: Option<MysqlValue<'_>>) -> deserialize::Result<Self> {
        let value = not_none!(value);
        Ok(value.as_bytes().into())
    }
}
<commit_msg>Improve the conversion from decimal to various integer types in the mysql backend<commit_after>use std::error::Error;
use std::str::{self, FromStr};

use crate::deserialize::{self, FromSql};
use crate::mysql::{Mysql, MysqlValue};
use crate::sql_types::{BigInt, Binary, Double, Float, Integer, SmallInt, Text};

fn decimal_to_integer<T>(bytes: &[u8]) -> deserialize::Result<T>
where
    T: FromStr,
    T::Err: Error + Send + Sync + 'static,
{
    let string = str::from_utf8(bytes)?;
    let mut splited = string.split('.');
    let integer_portion = splited.next().unwrap_or_default();
    let decimal_portion = splited.next().unwrap_or_default();
    if splited.next().is_some() {
        Err(format!("Invalid decimal format: {:?}", string).into())
    } else if decimal_portion.chars().any(|c| c != '0') {
        Err(format!(
            "Tried to convert a decimal to an integer that contained /
             a non null decimal portion: {:?}",
            string
        )
        .into())
    } else {
        Ok(integer_portion.parse()?)
    }
}

impl FromSql<SmallInt, Mysql> for i16 {
    fn from_sql(value: Option<MysqlValue<'_>>) -> deserialize::Result<Self> {
        use crate::mysql::NumericRepresentation::*;

        let data = not_none!(value);
        match data.numeric_value()? {
            Tiny(x) => Ok(x.into()),
            Small(x) => Ok(x),
            Medium(x) => Ok(x as Self),
            Big(x) => Ok(x as Self),
            Float(x) => Ok(x as Self),
            Double(x) => Ok(x as Self),
            Decimal(bytes) => decimal_to_integer(bytes),
        }
    }
}

impl FromSql<Integer, Mysql> for i32 {
    fn from_sql(value: Option<MysqlValue<'_>>) -> deserialize::Result<Self> {
        use crate::mysql::NumericRepresentation::*;

        let data = not_none!(value);
        match data.numeric_value()? {
            Tiny(x) => Ok(x.into()),
            Small(x) => Ok(x.into()),
            Medium(x) => Ok(x),
            Big(x) => Ok(x as Self),
            Float(x) => Ok(x as Self),
            Double(x) => Ok(x as Self),
            Decimal(bytes) => decimal_to_integer(bytes),
        }
    }
}

impl FromSql<BigInt, Mysql> for i64 {
    fn from_sql(value: Option<MysqlValue<'_>>) -> deserialize::Result<Self> {
        use crate::mysql::NumericRepresentation::*;

        let data = not_none!(value);
        match data.numeric_value()? {
            Tiny(x) => Ok(x.into()),
            Small(x) => Ok(x.into()),
            Medium(x) => Ok(x.into()),
            Big(x) => Ok(x),
            Float(x) => Ok(x as Self),
            Double(x) => Ok(x as Self),
            Decimal(bytes) => decimal_to_integer(bytes),
        }
    }
}

impl FromSql<Float, Mysql> for f32 {
    fn from_sql(value: Option<MysqlValue<'_>>) -> deserialize::Result<Self> {
        use crate::mysql::NumericRepresentation::*;

        let data = not_none!(value);
        match data.numeric_value()? {
            Tiny(x) => Ok(x.into()),
            Small(x) => Ok(x.into()),
            Medium(x) => Ok(x as Self),
            Big(x) => Ok(x as Self),
            Float(x) => Ok(x),
            Double(x) => Ok(x as Self),
            Decimal(bytes) => Ok(str::from_utf8(bytes)?.parse()?),
        }
    }
}

impl FromSql<Double, Mysql> for f64 {
    fn from_sql(value: Option<MysqlValue<'_>>) -> deserialize::Result<Self> {
        use crate::mysql::NumericRepresentation::*;

        let data = not_none!(value);
        match data.numeric_value()? {
            Tiny(x) => Ok(x.into()),
            Small(x) => Ok(x.into()),
            Medium(x) => Ok(x.into()),
            Big(x) => Ok(x as Self),
            Float(x) => Ok(x.into()),
            Double(x) => Ok(x),
            Decimal(bytes) => Ok(str::from_utf8(bytes)?.parse()?),
        }
    }
}

impl FromSql<Text, Mysql> for String {
    fn from_sql(value: Option<MysqlValue<'_>>) -> deserialize::Result<Self> {
        let value = not_none!(value);
        String::from_utf8(value.as_bytes().into()).map_err(Into::into)
    }
}

impl FromSql<Binary, Mysql> for Vec<u8> {
    fn from_sql(value: Option<MysqlValue<'_>>) -> deserialize::Result<Self> {
        let value = not_none!(value);
        Ok(value.as_bytes().into())
    }
}
<|endoftext|>


<commit_before>use std::fs::{File, OpenOptions};
use std::io::{self, Write};

pub struct Logger {
    logfile: Option<File>,
}

impl Logger {
    pub fn new() -> Logger {
        Logger {
            logfile: None,
        }
    }

    pub fn set_logfile(&mut self, filename: &str) {
        let file = OpenOptions::new()
            .create(true)
            .append(true)
            .open(filename);

        match file {
            Ok(file) => self.logfile = Some(file),
            Err(error) => eprintln!("Couldn't open `{}' as a logfile: {}", filename, error),
        }
    }

    pub fn log(&mut self, message: &str) -> io::Result<()> {
        if let Some(ref mut logfile) = self.logfile {
            logfile.write_all(message.as_bytes())?;
        }

        Ok(())
    }
}

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

    use super::*;

    use self::tempfile::TempDir;
    use std::io::{self, Read};
    use std::path;

    fn setup_logger() -> io::Result<(TempDir, path::PathBuf, Logger)> {
        let tmp = TempDir::new()?;
        let logfile = {
            let mut logfile = tmp.path().to_owned();
            logfile.push("example.log");
            logfile
        };
        assert!(!logfile.exists());

        let mut logger = Logger::new();
        logger.set_logfile(logfile.to_str().unwrap());

        Ok((tmp, logfile, logger))
    }

    #[test]
    fn t_set_logfile() -> io::Result<()> {
        let (_tmp, logfile, _logger) = setup_logger()?;

        assert!(logfile.exists());

        Ok(())
    }

    #[test]
    fn t_log_writes_message_to_the_file() -> io::Result<()> {
        let (_tmp, logfile, mut logger) = setup_logger()?;

        let message = "Hello, world!";
        logger.log(message)?;

        // Dropping logger to force it to flush the log and close the file
        drop(logger);

        let mut file = File::open(logfile)?;
        let mut contents = String::new();
        file.read_to_string(&mut contents)?;

        assert_eq!(contents, message);

        Ok(())
    }
}
<commit_msg>Logger::log adds a newline after the message<commit_after>use std::fs::{File, OpenOptions};
use std::io::{self, Write};

pub struct Logger {
    logfile: Option<File>,
}

impl Logger {
    pub fn new() -> Logger {
        Logger {
            logfile: None,
        }
    }

    pub fn set_logfile(&mut self, filename: &str) {
        let file = OpenOptions::new()
            .create(true)
            .append(true)
            .open(filename);

        match file {
            Ok(file) => self.logfile = Some(file),
            Err(error) => eprintln!("Couldn't open `{}' as a logfile: {}", filename, error),
        }
    }

    pub fn log(&mut self, message: &str) -> io::Result<()> {
        if let Some(ref mut logfile) = self.logfile {
            logfile.write_all(message.as_bytes())?;
            logfile.write_all("\n".as_bytes())?;
        }

        Ok(())
    }
}

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

    use super::*;

    use self::tempfile::TempDir;
    use std::io::{self, Read};
    use std::path;

    fn setup_logger() -> io::Result<(TempDir, path::PathBuf, Logger)> {
        let tmp = TempDir::new()?;
        let logfile = {
            let mut logfile = tmp.path().to_owned();
            logfile.push("example.log");
            logfile
        };
        assert!(!logfile.exists());

        let mut logger = Logger::new();
        logger.set_logfile(logfile.to_str().unwrap());

        Ok((tmp, logfile, logger))
    }

    #[test]
    fn t_set_logfile() -> io::Result<()> {
        let (_tmp, logfile, _logger) = setup_logger()?;

        assert!(logfile.exists());

        Ok(())
    }

    #[test]
    fn t_log_writes_message_to_the_file() -> io::Result<()> {
        let (_tmp, logfile, mut logger) = setup_logger()?;

        let message = "Hello, world!";
        logger.log(message)?;

        // Dropping logger to force it to flush the log and close the file
        drop(logger);

        let mut file = File::open(logfile)?;
        let mut contents = String::new();
        file.read_to_string(&mut contents)?;

        assert_eq!(contents, message.to_owned() + "\n");

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


<commit_before>// See the cfg-if crate.
macro_rules! cfg_if {
    // match if/else chains with a final `else`
    ($(
        if #[cfg($($meta:meta),*)] { $($it:item)* }
    ) else * else {
        $($it2:item)*
    }) => {
        cfg_if! {
            @__items
            () ;
            $( ( ($($meta),*) ($($it)*) ), )*
            ( () ($($it2)*) ),
        }
    };

    // match if/else chains lacking a final `else`
    (
        if #[cfg($($i_met:meta),*)] { $($i_it:item)* }
        $(
            else if #[cfg($($e_met:meta),*)] { $($e_it:item)* }
        )*
    ) => {
        cfg_if! {
            @__items
            () ;
            ( ($($i_met),*) ($($i_it)*) ),
            $( ( ($($e_met),*) ($($e_it)*) ), )*
            ( () () ),
        }
    };

    // Internal and recursive macro to emit all the items
    //
    // Collects all the negated cfgs in a list at the beginning and after the
    // semicolon is all the remaining items
    (@__items ($($not:meta,)*) ; ) => {};
    (@__items ($($not:meta,)*) ; ( ($($m:meta),*) ($($it:item)*) ), $($rest:tt)*) => {
        // Emit all items within one block, applying an approprate #[cfg]. The
        // #[cfg] will require all `$m` matchers specified and must also negate
        // all previous matchers.
        cfg_if! { @__apply cfg(all($($m,)* not(any($($not),*)))), $($it)* }

        // Recurse to emit all other items in `$rest`, and when we do so add all
        // our `$m` matchers to the list of `$not` matchers as future emissions
        // will have to negate everything we just matched as well.
        cfg_if! { @__items ($($not,)* $($m,)*) ; $($rest)* }
    };

    // Internal macro to Apply a cfg attribute to a list of items
    (@__apply $m:meta, $($it:item)*) => {
        $(#[$m] $it)*
    };
}

// Helper macro for specialization. This also helps avoid parse errors if the
// default fn syntax for specialization changes in the future.
#[cfg(feature = "nightly")]
macro_rules! default_fn {
	(#[$($a:tt)*] $($tt:tt)*) => {
        #[$($a)*] default $($tt)*
    }
}
#[cfg(not(feature = "nightly"))]
macro_rules! default_fn {
	($($tt:tt)*) => {
        $($tt)*
    }
}
<commit_msg> Allow unused macro rules for macro `cfg_if`.<commit_after>// See the cfg-if crate.
#[allow(unused)]
macro_rules! cfg_if {
    // match if/else chains with a final `else`
    ($(
        if #[cfg($($meta:meta),*)] { $($it:item)* }
    ) else * else {
        $($it2:item)*
    }) => {
        cfg_if! {
            @__items
            () ;
            $( ( ($($meta),*) ($($it)*) ), )*
            ( () ($($it2)*) ),
        }
    };

    // match if/else chains lacking a final `else`
    (
        if #[cfg($($i_met:meta),*)] { $($i_it:item)* }
        $(
            else if #[cfg($($e_met:meta),*)] { $($e_it:item)* }
        )*
    ) => {
        cfg_if! {
            @__items
            () ;
            ( ($($i_met),*) ($($i_it)*) ),
            $( ( ($($e_met),*) ($($e_it)*) ), )*
            ( () () ),
        }
    };

    // Internal and recursive macro to emit all the items
    //
    // Collects all the negated cfgs in a list at the beginning and after the
    // semicolon is all the remaining items
    (@__items ($($not:meta,)*) ; ) => {};
    (@__items ($($not:meta,)*) ; ( ($($m:meta),*) ($($it:item)*) ), $($rest:tt)*) => {
        // Emit all items within one block, applying an approprate #[cfg]. The
        // #[cfg] will require all `$m` matchers specified and must also negate
        // all previous matchers.
        cfg_if! { @__apply cfg(all($($m,)* not(any($($not),*)))), $($it)* }

        // Recurse to emit all other items in `$rest`, and when we do so add all
        // our `$m` matchers to the list of `$not` matchers as future emissions
        // will have to negate everything we just matched as well.
        cfg_if! { @__items ($($not,)* $($m,)*) ; $($rest)* }
    };

    // Internal macro to Apply a cfg attribute to a list of items
    (@__apply $m:meta, $($it:item)*) => {
        $(#[$m] $it)*
    };
}

// Helper macro for specialization. This also helps avoid parse errors if the
// default fn syntax for specialization changes in the future.
#[cfg(feature = "nightly")]
macro_rules! default_fn {
	(#[$($a:tt)*] $($tt:tt)*) => {
        #[$($a)*] default $($tt)*
    }
}
#[cfg(not(feature = "nightly"))]
macro_rules! default_fn {
	($($tt:tt)*) => {
        $($tt)*
    }
}
<|endoftext|>


<commit_before>use rom;

pub struct Memory {
    pub rom: rom::Rom,
    ram: [u8, ..0x1000],
    hram: [u8, ..0x80]
}

impl Memory {
    pub fn new(rom: rom::Rom) -> Memory {
        Memory {
            rom: rom,
            ram: [0x00, ..0x1000],
            hram: [0x00, ..0x80]
        }
    }

    pub fn read_byte(&self, address: u16) -> u8 {
        match address {
            0x0000..0x3FFF => self.rom.bytes[address as uint],
            0xC000..0xDFFF => self.ram[address as uint - 0xC000],
            0xFF80..0xFFFF => self.hram[address as uint - 0xFF80],
            _ => 0x00
        }
    }

    pub fn write_byte(&mut self, address: u16, value: u8) {
        match address {
            0xC000..0xDFFF => {
                self.ram[address as uint - 0xC000] = value
            },
            0xFF00..0xFF7F => {
                print!("Implement I/O ports stuff... ")
            },
            0xFF80..0xFFFF => {
                self.hram[address as uint - 0xFF80] = value
            },
            _ => println!("Can't write to memory address {:#06x}", address)
        }
    }

    pub fn read_word(&self, address: u16) -> u16 {
        self.read_byte(address) as u16 |
            self.read_byte(address + 1) as u16 << 8
    }

    pub fn write_word(&mut self, address: u16, value: u16) {
        self.write_byte(address, (value & 0xFF) as u8);
        self.write_byte(address + 1, (value >> 8) as u8);
    }
}
<commit_msg>Increase RAM size<commit_after>use rom;

pub struct Memory {
    pub rom: rom::Rom,
    ram: [u8, ..0x2000],
    hram: [u8, ..0x80]
}

impl Memory {
    pub fn new(rom: rom::Rom) -> Memory {
        Memory {
            rom: rom,
            ram: [0x00, ..0x2000],
            hram: [0x00, ..0x80]
        }
    }

    pub fn read_byte(&self, address: u16) -> u8 {
        match address {
            0x0000..0x3FFF => self.rom.bytes[address as uint],
            0xC000..0xDFFF => self.ram[address as uint - 0xC000],
            0xFF80..0xFFFF => self.hram[address as uint - 0xFF80],
            _ => 0x00
        }
    }

    pub fn write_byte(&mut self, address: u16, value: u8) {
        match address {
            0xC000..0xDFFF => {
                self.ram[address as uint - 0xC000] = value
            },
            0xFF00..0xFF7F => {
                print!("Implement I/O ports stuff... ")
            },
            0xFF80..0xFFFF => {
                self.hram[address as uint - 0xFF80] = value
            },
            _ => println!("Can't write to memory address {:#06x}", address)
        }
    }

    pub fn read_word(&self, address: u16) -> u16 {
        self.read_byte(address) as u16 |
            self.read_byte(address + 1) as u16 << 8
    }

    pub fn write_word(&mut self, address: u16, value: u16) {
        self.write_byte(address, (value & 0xFF) as u8);
        self.write_byte(address + 1, (value >> 8) as u8);
    }
}
<|endoftext|>


<commit_before>use std::ops::{Add, Sub, Mul, Div, Rem};
use ::value::Value;
use ::interpreter::Interpreter;

macro_rules! check_arity {
    ($name:expr, $len:expr, $exact:expr) => ({
        let len = $len as u32;
        if len != $exact {
            raise_condition!(format!("arity mismatch for {}: expected: {}, got: {}", $name, $exact, len));
        }
    });

    ($name:expr, $len:expr, $lo:expr, $hi:expr) => ({
        let len = $len as u32;
        if len < $lo || len > $hi {
            raise_condition!(format!("arity mismatch for {}: expected: {}..{}, got: {}", $name, $lo, $hi, len));
        }
    });

    ($name:expr, $len:expr, min => $min:expr) => ({
        let len = $len as u32;
        if len < $min {
            raise_condition!(format!("arity mismatch for {}: expected: {}.., got: {}", $name, $min, len));
        }
    });
}

macro_rules! try_unwrap_type {
    ($fn_name:expr, $type_name:expr, $unwrap_fn:path, $t:expr, $interpreter:expr) => ({
        match $unwrap_fn($t) {
            Some(x) => x,
            None => {
                let s = format!("{} expected {}, got: {}", $fn_name, $type_name, $t.to_string($interpreter.get_interner()));
                return Value::new_condition(Value::new_string(s));
            }
        }
    });
}

macro_rules! new_condition {
    ($msg:expr) => (
        Value::new_condition(Value::new_string($msg))
    )
}

macro_rules! raise_condition {
    ($msg:expr) => (
        return new_condition!($msg);
    )
}

macro_rules! assert_or_condition {
    ($b:expr, $msg:expr) => ({
        if !$b {
            raise_condition!($msg)
        }
    })
}

pub fn quote(_: &mut Interpreter, args: &mut [Value]) -> Value {
    check_arity!("quote", args.len(), 1);
    args[0].clone()
}

pub fn define(interpreter: &mut Interpreter, args: &mut [Value]) -> Value {
    check_arity!("define", args.len(), 2);

    let s = try_unwrap_type!("define", "symbol", Value::get_symbol, &args[0], interpreter);
    let item = interpreter.evaluate(&args[1]);
    interpreter.current_scope.add_symbol(s, item);
    args[0].clone()
}

pub fn set(interpreter: &mut Interpreter, args: &mut [Value]) -> Value {
    check_arity!("set!", args.len(), 2);

    let s = try_unwrap_type!("set!", "symbol", Value::get_symbol, &args[0], interpreter);
    assert_or_condition!(
        interpreter.current_scope.lookup_symbol(s).is_some(),
        format!("set!: unknown identifier {}", args[0].to_string(interpreter.get_interner()))
    );
    let item = interpreter.evaluate(&args[1]);
    interpreter.current_scope.add_symbol(s, item);
    args[0].clone()
}

pub fn if_(interpreter: &mut Interpreter, args: &mut [Value]) -> Value {
    check_arity!("if", args.len(), 3);

    let condition = interpreter.evaluate(&args[0]);
    if let Some(b) = condition.get_bool() {
        if b {
            interpreter.evaluate(&args[1])
        } else {
            interpreter.evaluate(&args[2])
        }
    } else {
        raise_condition!(format!("if: argument mismatch: expected bool, got: {}", condition.to_string(interpreter.get_interner())));
    }
}

pub fn lambda(interpreter: &mut Interpreter, args: &mut [Value]) -> Value {
    check_arity!("lambda", args.len(), 2, 3);

    let name;
    let binding_list;
    let code;
    if args.len() == 2 {
        name = None;
        binding_list = try_unwrap_type!("lambda", "list", Value::get_list, &args[0], interpreter);
        code = args[1].clone();
    } else {
        // type check name
        let name_id = try_unwrap_type!("lambda", "list", Value::get_symbol, &args[0], interpreter);
        name = interpreter.get_interner().lookup(name_id).map(Into::into);
        binding_list = try_unwrap_type!("lambda", "list", Value::get_list, &args[1], interpreter);
        code = args[2].clone();
    }

    let mut bindings: Vec<u64> = Vec::with_capacity(binding_list.len());
    for v in binding_list.iter() {
        // type check
        bindings.push(try_unwrap_type!("lambda", "symbol", Value::get_symbol, v, interpreter));
    }

    Value::new_proc(name, interpreter.current_scope.clone(), bindings, code)
}

macro_rules! eval_args {
    (fn $func:ident($args:ident : $arg_ty:ty) -> $ret_ty:ty $blk:block) =>
    (
        pub fn $func(interpreter: &mut Interpreter, args: $arg_ty) -> $ret_ty {
            fn inner($args: $arg_ty) -> $ret_ty $blk;
            for x in args.iter_mut() {
                *x = interpreter.evaluate(x);
            }
            inner(args)
        }
    );

    (fn $func:ident($interpreter:ident : &mut Interpreter, $args:ident : $arg_ty:ty) -> $ret_ty:ty $blk:block) =>
    (
        pub fn $func(interpreter: &mut Interpreter, args: $arg_ty) -> $ret_ty {
            fn inner($interpreter: &mut Interpreter, $args: $arg_ty) -> $ret_ty $blk;
            for x in args.iter_mut() {
                *x = interpreter.evaluate(x);
            }
            inner(interpreter, args)
        }
    );
}

// Polymorphic equality
eval_args!(fn eq_(args: &mut [Value]) -> Value {
    check_arity!("eq?", args.len(), min => 2);
    Value::new_bool(args.windows(2).all(|window| window[0] == window[1]))
});

// Type checking
macro_rules! type_checker {
    ($func:ident, $lisp_name:expr, $checking_fn:ident) =>
    (eval_args!(fn $func(args: &mut [Value]) -> Value {
        check_arity!($lisp_name, args.len(), 1);
        Value::new_bool(args[0].$checking_fn().is_some())
    }););
}

type_checker!(null_, "null?", get_empty_list);
type_checker!(boolean_, "boolean?", get_bool);
type_checker!(symbol_, "symbol?", get_symbol);
type_checker!(integer_, "integer?", get_integer);
type_checker!(char_, "char?", get_char);
type_checker!(string_, "string?", get_string);
type_checker!(pair_, "pair?", get_pair);
type_checker!(procedure_, "procedure?", get_native_fn_ptr);

// Type conversions
macro_rules! type_conversion {
    ($func:ident, $lisp_name:expr, $conversion_fn:ident) =>
    (eval_args!(fn $func(args: &mut [Value]) -> Value {
        check_arity!($lisp_name, args.len(), 1);
        Value::$conversion_fn(&args[0])
    }););
}

type_conversion!(char_integer, "char->integer", from_char_to_integer);
type_conversion!(integer_char, "integer->char", from_integer_to_char);
type_conversion!(number_string, "number->string", from_number_to_string);
type_conversion!(string_number, "string->number", from_string_to_number);

pub fn symbol_string(interpreter: &mut Interpreter, args: &mut [Value]) -> Value {
    check_arity!("symbol->string", args.len(), 1);
    let evaled = interpreter.evaluate(&args[0]);
    let id = try_unwrap_type!("symbol->string", "symbol", Value::get_symbol, &evaled, interpreter);
    if let Some(string) = interpreter.get_interner().lookup(id) {
        Value::new_string(string)
    } else {
        raise_condition!("internal error: invalid symbol")
    }
}

pub fn string_symbol(interpreter: &mut Interpreter, args: &mut [Value]) -> Value {
    check_arity!("string->symbol", args.len(), 1);
    let evaled = interpreter.evaluate(&args[0]);
    let string = try_unwrap_type!("string->symbol", "string", Value::get_string, &evaled, interpreter);
    let id = interpreter.get_interner().intern(string);
    Value::new_symbol(id)
}

// Arithmetic operators
macro_rules! arithmetic_operator {
    ($func:ident, $operator:path, $default:expr) =>
    (eval_args!(fn $func(interpreter: &mut Interpreter, args: &mut [Value]) -> Value {
        let mut res = if args.len() < 2 {
            $default as i64
        } else {
            match args[0].get_integer() {
                Some(i) => i,
                None => raise_condition!(format!("expected integer, got: {}", &args[0].to_string(interpreter.get_interner())))
            }
        };
        for x in args[1..].iter() {
            if let Some(i) = x.get_integer() {
                res = $operator(res, i);
            } else {
                raise_condition!(format!("expected integer, got: {}", x.to_string(interpreter.get_interner())))
            }
        }
        Value::new_integer(res)
    }););
}

arithmetic_operator!(plus, Add::add, 0);
arithmetic_operator!(minus, Sub::sub, 0);
arithmetic_operator!(multiply, Mul::mul, 1);
arithmetic_operator!(quotient, Div::div, 1);
arithmetic_operator!(remainder, Rem::rem, 1);

// Comparison Operators
macro_rules! comparison_operator {
    ($func:ident, $lisp_name:expr, $operator:path) =>
    (eval_args!(fn $func(interpreter: &mut Interpreter, args: &mut [Value]) -> Value {
        check_arity!($lisp_name, args.len(), 1);

        let mut res = true;
        let compared_element = match args[0].get_integer() {
            Some(i) => i,
            None => raise_condition!(format!("expected integer, got: {}", &args[0].to_string(interpreter.get_interner())))
        };

        for x in &args[1..] {
            let num = match x.get_integer() {
                Some(i) => i,
                None => raise_condition!(format!("expected integer, got: {}", x.to_string(interpreter.get_interner())))
            };
            res = res && $operator(&compared_element, &num);
        }
        Value::new_bool(res)
    }););
}

comparison_operator!(eq, "=", PartialEq::eq);
comparison_operator!(lt, "<", PartialOrd::lt);
comparison_operator!(le, "<=", PartialOrd::le);
comparison_operator!(gt, ">", PartialOrd::gt);
comparison_operator!(ge, ">=", PartialOrd::ge);

// List operations:
eval_args!(fn car(interpreter: &mut Interpreter, args: &mut [Value]) -> Value {
    check_arity!("car", args.len(), 1);

    if let Some((a, _)) = args[0].get_pair() {
        a.clone()
    } else {
        raise_condition!(format!("expected pair, got {}", &args[0].to_string(interpreter.get_interner())));
    }
});

eval_args!(fn cdr(interpreter: &mut Interpreter, args: &mut [Value]) -> Value {
    check_arity!("cdr", args.len(), 1);

    if let Some((_, b)) = args[0].get_pair() {
        b.clone()
    } else {
        raise_condition!(format!("expected pair, got {}", &args[0].to_string(interpreter.get_interner())));
    }
});

eval_args!(fn cons(args: &mut [Value]) -> Value {
    check_arity!("cons", args.len(), 2);
    Value::new_pair(args[0].clone(), args[1].clone())
});

eval_args!(fn list(args: &mut [Value]) -> Value {
    Value::new_list(args)
});

pub fn set_car_(interpreter: &mut Interpreter, args: &mut [Value]) -> Value {
    check_arity!("set-car!", args.len(), 2);

    let (f, elem) = args.split_at(1);
    let (f, elem) = (&f[0], &elem[0]);
    if f.get_symbol().is_some() {
        let old_pair = interpreter.evaluate(f);
        if let Some((_, b)) = old_pair.get_pair() {
            let new_pair = Value::new_pair(elem.clone(), b.clone());
            let quote_id = interpreter.get_interner().intern("quote");
            let quoted = Value::new_list(&[Value::new_symbol(quote_id), new_pair]);
            define(interpreter, &mut [f.clone(), quoted])
        } else {
            raise_condition!(format!("expected pair, got {}", old_pair.to_string(interpreter.get_interner())));
        }
    } else {
        raise_condition!(format!("expected symbol, got {}", f.to_string(interpreter.get_interner())));
    }
}

pub fn set_cdr_(interpreter: &mut Interpreter, args: &mut [Value]) -> Value {
    check_arity!("set-cdr!", args.len(), 2);

    let (f, elem) = args.split_at(1);
    let (f, elem) = (&f[0], &elem[0]);
    if f.get_symbol().is_some() {
        let old_pair = interpreter.evaluate(f);
        if let Some((a, _)) = old_pair.get_pair() {
            let new_pair = Value::new_pair(a.clone(), elem.clone());
            let quote_id = interpreter.get_interner().intern("quote");
            let quoted = Value::new_list(&[Value::new_symbol(quote_id), new_pair]);
            define(interpreter, &mut [f.clone(), quoted])
        } else {
            raise_condition!(format!("expected pair, got {}", old_pair.to_string(interpreter.get_interner())))
        }
    } else {
        raise_condition!(format!("expected symbol, got {}", f.to_string(interpreter.get_interner())));
    }
}

pub fn symbol_space(interpreter: &mut Interpreter, args: &mut [Value]) -> Value {
    check_arity!("symbol-space", args.len(), 0);

    let symbols: Vec<Value> = interpreter.current_scope.symbol_ids()
    .into_iter()
    .map(|s| Value::new_symbol(s))
    .collect();

    Value::new_list(&symbols)
}
<commit_msg>fix procedure to return true for native and non-native procs<commit_after>use std::ops::{Add, Sub, Mul, Div, Rem};
use ::value::Value;
use ::interpreter::Interpreter;

macro_rules! check_arity {
    ($name:expr, $len:expr, $exact:expr) => ({
        let len = $len as u32;
        if len != $exact {
            raise_condition!(format!("arity mismatch for {}: expected: {}, got: {}", $name, $exact, len));
        }
    });

    ($name:expr, $len:expr, $lo:expr, $hi:expr) => ({
        let len = $len as u32;
        if len < $lo || len > $hi {
            raise_condition!(format!("arity mismatch for {}: expected: {}..{}, got: {}", $name, $lo, $hi, len));
        }
    });

    ($name:expr, $len:expr, min => $min:expr) => ({
        let len = $len as u32;
        if len < $min {
            raise_condition!(format!("arity mismatch for {}: expected: {}.., got: {}", $name, $min, len));
        }
    });
}

macro_rules! try_unwrap_type {
    ($fn_name:expr, $type_name:expr, $unwrap_fn:path, $t:expr, $interpreter:expr) => ({
        match $unwrap_fn($t) {
            Some(x) => x,
            None => {
                let s = format!("{} expected {}, got: {}", $fn_name, $type_name, $t.to_string($interpreter.get_interner()));
                return Value::new_condition(Value::new_string(s));
            }
        }
    });
}

macro_rules! new_condition {
    ($msg:expr) => (
        Value::new_condition(Value::new_string($msg))
    )
}

macro_rules! raise_condition {
    ($msg:expr) => (
        return new_condition!($msg);
    )
}

macro_rules! assert_or_condition {
    ($b:expr, $msg:expr) => ({
        if !$b {
            raise_condition!($msg)
        }
    })
}

pub fn quote(_: &mut Interpreter, args: &mut [Value]) -> Value {
    check_arity!("quote", args.len(), 1);
    args[0].clone()
}

pub fn define(interpreter: &mut Interpreter, args: &mut [Value]) -> Value {
    check_arity!("define", args.len(), 2);

    let s = try_unwrap_type!("define", "symbol", Value::get_symbol, &args[0], interpreter);
    let item = interpreter.evaluate(&args[1]);
    interpreter.current_scope.add_symbol(s, item);
    args[0].clone()
}

pub fn set(interpreter: &mut Interpreter, args: &mut [Value]) -> Value {
    check_arity!("set!", args.len(), 2);

    let s = try_unwrap_type!("set!", "symbol", Value::get_symbol, &args[0], interpreter);
    assert_or_condition!(
        interpreter.current_scope.lookup_symbol(s).is_some(),
        format!("set!: unknown identifier {}", args[0].to_string(interpreter.get_interner()))
    );
    let item = interpreter.evaluate(&args[1]);
    interpreter.current_scope.add_symbol(s, item);
    args[0].clone()
}

pub fn if_(interpreter: &mut Interpreter, args: &mut [Value]) -> Value {
    check_arity!("if", args.len(), 3);

    let condition = interpreter.evaluate(&args[0]);
    if let Some(b) = condition.get_bool() {
        if b {
            interpreter.evaluate(&args[1])
        } else {
            interpreter.evaluate(&args[2])
        }
    } else {
        raise_condition!(format!("if: argument mismatch: expected bool, got: {}", condition.to_string(interpreter.get_interner())));
    }
}

pub fn lambda(interpreter: &mut Interpreter, args: &mut [Value]) -> Value {
    check_arity!("lambda", args.len(), 2, 3);

    let name;
    let binding_list;
    let code;
    if args.len() == 2 {
        name = None;
        binding_list = try_unwrap_type!("lambda", "list", Value::get_list, &args[0], interpreter);
        code = args[1].clone();
    } else {
        // type check name
        let name_id = try_unwrap_type!("lambda", "list", Value::get_symbol, &args[0], interpreter);
        name = interpreter.get_interner().lookup(name_id).map(Into::into);
        binding_list = try_unwrap_type!("lambda", "list", Value::get_list, &args[1], interpreter);
        code = args[2].clone();
    }

    let mut bindings: Vec<u64> = Vec::with_capacity(binding_list.len());
    for v in binding_list.iter() {
        // type check
        bindings.push(try_unwrap_type!("lambda", "symbol", Value::get_symbol, v, interpreter));
    }

    Value::new_proc(name, interpreter.current_scope.clone(), bindings, code)
}

macro_rules! eval_args {
    (fn $func:ident($args:ident : $arg_ty:ty) -> $ret_ty:ty $blk:block) =>
    (
        pub fn $func(interpreter: &mut Interpreter, args: $arg_ty) -> $ret_ty {
            fn inner($args: $arg_ty) -> $ret_ty $blk;
            for x in args.iter_mut() {
                *x = interpreter.evaluate(x);
            }
            inner(args)
        }
    );

    (fn $func:ident($interpreter:ident : &mut Interpreter, $args:ident : $arg_ty:ty) -> $ret_ty:ty $blk:block) =>
    (
        pub fn $func(interpreter: &mut Interpreter, args: $arg_ty) -> $ret_ty {
            fn inner($interpreter: &mut Interpreter, $args: $arg_ty) -> $ret_ty $blk;
            for x in args.iter_mut() {
                *x = interpreter.evaluate(x);
            }
            inner(interpreter, args)
        }
    );
}

// Polymorphic equality
eval_args!(fn eq_(args: &mut [Value]) -> Value {
    check_arity!("eq?", args.len(), min => 2);
    Value::new_bool(args.windows(2).all(|window| window[0] == window[1]))
});

// Type checking
macro_rules! type_checker {
    ($func:ident, $lisp_name:expr, $checking_fn:ident) =>
    (eval_args!(fn $func(args: &mut [Value]) -> Value {
        check_arity!($lisp_name, args.len(), 1);
        Value::new_bool(args[0].$checking_fn().is_some())
    }););
}

type_checker!(null_, "null?", get_empty_list);
type_checker!(boolean_, "boolean?", get_bool);
type_checker!(symbol_, "symbol?", get_symbol);
type_checker!(integer_, "integer?", get_integer);
type_checker!(char_, "char?", get_char);
type_checker!(string_, "string?", get_string);
type_checker!(pair_, "pair?", get_pair);

eval_args!(fn procedure_(args: &mut [Value]) -> Value {
    check_arity!("procedure?", args.len(), 1);
    let is_proc = Value::get_native_fn_ptr(&args[0]).is_some() || Value::get_proc(&args[0]).is_some();
    Value::new_bool(is_proc)
});

// Type conversions
macro_rules! type_conversion {
    ($func:ident, $lisp_name:expr, $conversion_fn:ident) =>
    (eval_args!(fn $func(args: &mut [Value]) -> Value {
        check_arity!($lisp_name, args.len(), 1);
        Value::$conversion_fn(&args[0])
    }););
}

type_conversion!(char_integer, "char->integer", from_char_to_integer);
type_conversion!(integer_char, "integer->char", from_integer_to_char);
type_conversion!(number_string, "number->string", from_number_to_string);
type_conversion!(string_number, "string->number", from_string_to_number);

pub fn symbol_string(interpreter: &mut Interpreter, args: &mut [Value]) -> Value {
    check_arity!("symbol->string", args.len(), 1);
    let evaled = interpreter.evaluate(&args[0]);
    let id = try_unwrap_type!("symbol->string", "symbol", Value::get_symbol, &evaled, interpreter);
    if let Some(string) = interpreter.get_interner().lookup(id) {
        Value::new_string(string)
    } else {
        raise_condition!("internal error: invalid symbol")
    }
}

pub fn string_symbol(interpreter: &mut Interpreter, args: &mut [Value]) -> Value {
    check_arity!("string->symbol", args.len(), 1);
    let evaled = interpreter.evaluate(&args[0]);
    let string = try_unwrap_type!("string->symbol", "string", Value::get_string, &evaled, interpreter);
    let id = interpreter.get_interner().intern(string);
    Value::new_symbol(id)
}

// Arithmetic operators
macro_rules! arithmetic_operator {
    ($func:ident, $operator:path, $default:expr) =>
    (eval_args!(fn $func(interpreter: &mut Interpreter, args: &mut [Value]) -> Value {
        let mut res = if args.len() < 2 {
            $default as i64
        } else {
            match args[0].get_integer() {
                Some(i) => i,
                None => raise_condition!(format!("expected integer, got: {}", &args[0].to_string(interpreter.get_interner())))
            }
        };
        for x in args[1..].iter() {
            if let Some(i) = x.get_integer() {
                res = $operator(res, i);
            } else {
                raise_condition!(format!("expected integer, got: {}", x.to_string(interpreter.get_interner())))
            }
        }
        Value::new_integer(res)
    }););
}

arithmetic_operator!(plus, Add::add, 0);
arithmetic_operator!(minus, Sub::sub, 0);
arithmetic_operator!(multiply, Mul::mul, 1);
arithmetic_operator!(quotient, Div::div, 1);
arithmetic_operator!(remainder, Rem::rem, 1);

// Comparison Operators
macro_rules! comparison_operator {
    ($func:ident, $lisp_name:expr, $operator:path) =>
    (eval_args!(fn $func(interpreter: &mut Interpreter, args: &mut [Value]) -> Value {
        check_arity!($lisp_name, args.len(), 1);

        let mut res = true;
        let compared_element = match args[0].get_integer() {
            Some(i) => i,
            None => raise_condition!(format!("expected integer, got: {}", &args[0].to_string(interpreter.get_interner())))
        };

        for x in &args[1..] {
            let num = match x.get_integer() {
                Some(i) => i,
                None => raise_condition!(format!("expected integer, got: {}", x.to_string(interpreter.get_interner())))
            };
            res = res && $operator(&compared_element, &num);
        }
        Value::new_bool(res)
    }););
}

comparison_operator!(eq, "=", PartialEq::eq);
comparison_operator!(lt, "<", PartialOrd::lt);
comparison_operator!(le, "<=", PartialOrd::le);
comparison_operator!(gt, ">", PartialOrd::gt);
comparison_operator!(ge, ">=", PartialOrd::ge);

// List operations:
eval_args!(fn car(interpreter: &mut Interpreter, args: &mut [Value]) -> Value {
    check_arity!("car", args.len(), 1);

    if let Some((a, _)) = args[0].get_pair() {
        a.clone()
    } else {
        raise_condition!(format!("expected pair, got {}", &args[0].to_string(interpreter.get_interner())));
    }
});

eval_args!(fn cdr(interpreter: &mut Interpreter, args: &mut [Value]) -> Value {
    check_arity!("cdr", args.len(), 1);

    if let Some((_, b)) = args[0].get_pair() {
        b.clone()
    } else {
        raise_condition!(format!("expected pair, got {}", &args[0].to_string(interpreter.get_interner())));
    }
});

eval_args!(fn cons(args: &mut [Value]) -> Value {
    check_arity!("cons", args.len(), 2);
    Value::new_pair(args[0].clone(), args[1].clone())
});

eval_args!(fn list(args: &mut [Value]) -> Value {
    Value::new_list(args)
});

pub fn set_car_(interpreter: &mut Interpreter, args: &mut [Value]) -> Value {
    check_arity!("set-car!", args.len(), 2);

    let (f, elem) = args.split_at(1);
    let (f, elem) = (&f[0], &elem[0]);
    if f.get_symbol().is_some() {
        let old_pair = interpreter.evaluate(f);
        if let Some((_, b)) = old_pair.get_pair() {
            let new_pair = Value::new_pair(elem.clone(), b.clone());
            let quote_id = interpreter.get_interner().intern("quote");
            let quoted = Value::new_list(&[Value::new_symbol(quote_id), new_pair]);
            define(interpreter, &mut [f.clone(), quoted])
        } else {
            raise_condition!(format!("expected pair, got {}", old_pair.to_string(interpreter.get_interner())));
        }
    } else {
        raise_condition!(format!("expected symbol, got {}", f.to_string(interpreter.get_interner())));
    }
}

pub fn set_cdr_(interpreter: &mut Interpreter, args: &mut [Value]) -> Value {
    check_arity!("set-cdr!", args.len(), 2);

    let (f, elem) = args.split_at(1);
    let (f, elem) = (&f[0], &elem[0]);
    if f.get_symbol().is_some() {
        let old_pair = interpreter.evaluate(f);
        if let Some((a, _)) = old_pair.get_pair() {
            let new_pair = Value::new_pair(a.clone(), elem.clone());
            let quote_id = interpreter.get_interner().intern("quote");
            let quoted = Value::new_list(&[Value::new_symbol(quote_id), new_pair]);
            define(interpreter, &mut [f.clone(), quoted])
        } else {
            raise_condition!(format!("expected pair, got {}", old_pair.to_string(interpreter.get_interner())))
        }
    } else {
        raise_condition!(format!("expected symbol, got {}", f.to_string(interpreter.get_interner())));
    }
}

pub fn symbol_space(interpreter: &mut Interpreter, args: &mut [Value]) -> Value {
    check_arity!("symbol-space", args.len(), 0);

    let symbols: Vec<Value> = interpreter.current_scope.symbol_ids()
    .into_iter()
    .map(|s| Value::new_symbol(s))
    .collect();

    Value::new_list(&symbols)
}
<|endoftext|>


<commit_before>use nom::{digit, multispace, eof, ErrorKind, IResult};
use std::str;
use std::fmt;
use super::value::Value;

static UTF8_ERROR: &'static str = "File is no valid UTF8!";

macro_rules! fix {
    ($i:expr, $it:ident) => (fix_error!($i, ParserError, $it));
    ($i:expr, $submac:ident!( $($args:tt)* )) => (
      fix_error!($i, ParserError, $submac!($($args)*))
    );
}

named!(bool_<&[u8], Value, ParserError>, fix!(map!(
    alt!(
        tag!("true") |
        tag!("false")),
    |x|{ Value::new_bool(x == b"true") })));

named!(char_<&[u8], Value, ParserError>, fix!(chain!(
    tag!("'") ~
    error!(ErrorKind::Custom(ParserError::MissingChar), not!(tag!("'"))) ~
    c: take!(1) ~
    error!(ErrorKind::Custom(ParserError::MultipleChars),
        fix!(tag!("'"))) ,
    ||{ Value::new_char(c[0] as char) })));

named!(end_of_item<&[u8], &[u8], ParserError>,
    fix!(alt!(
        multispace |
        eof |
        tag!(")"))));

fn is_valid_in_ident(x: u8) -> bool {
    (x >= 0x3A && x <= 0x7E) ||
    (x >= 0x2A && x <= 0x2F) ||
    (x >= 0x23 && x <= 0x27) ||
    x == '!' as u8
}

named!(ident<&[u8], Value, ParserError>, fix!(chain!(
    peek!(none_of!("0123456789().")) ~
    x: take_while1!(is_valid_in_ident),
    || Value::new_ident((str::from_utf8(x).unwrap())))));

named!(integer<&[u8], Value, ParserError>,
    fix!(chain!(
        s: opt!(char!('-')) ~
        x: digit ,
        ||{
            let num: i64 = str::from_utf8(x).expect(UTF8_ERROR).parse().unwrap();
            if s.is_some() {
                Value::new_integer(-num)
            } else {
                Value::new_integer(num)
            }
        })));

named!(item<&[u8], Value, ParserError>,
    chain!(
        opt!(multispace) ~
        value: alt_complete!(
            bool_ |
            char_ |
            integer |
            ident |
            pair |
            list) ~
        peek!(end_of_item),
        || value));

named!(pair<&[u8], Value, ParserError>,
    error!(ErrorKind::Custom(ParserError::InvalidPair), complete!(delimited!(
            fix!(tag!("(")),
            chain!(
                a: item ~
                fix!(multispace) ~
                fix!(tag!(".")) ~
                fix!(multispace) ~
                b: item ,
                || { Value::new_pair(a,b) }
            ),
            fix!(tag!(")"))))));

named!(list_inner<Vec<Value> >,
    many0!(item));

named!(list<&[u8], Value, ParserError>,
    alt!(
        fix!(map!(
            tag!("()") ,
            |_| Value::empty_list())) |
        fix!(map!(
            delimited!(
                tag!("("),
                list_inner,
                tag!(")")),
            |x: Vec<Value>| {
                let mut iter = x.into_iter().rev();
                let last = iter.next().unwrap(); // safe because list len must be >= 1
                iter.fold(Value::new_pair(last, Value::empty_list()), |prev_pair, value| Value::new_pair(value, prev_pair))
            }))
        ));

pub fn parse(input: &[u8]) -> Result<Value, String> {
    use nom::Err as NomErr;
    named!(complete_item<&[u8], Value, ParserError>, complete!(item));
    match complete_item(input) {
        IResult::Done(_, value) => Ok(value),
        IResult::Incomplete(_) => unimplemented!(), // because of complete
        IResult::Error(NomErr::Position(err, _)) => Err(create_error_string(err)),
        IResult::Error(NomErr::NodePosition(err, _, _)) => Err(create_error_string(err)),
        _ => unimplemented!(),
    }
}

fn create_error_string(err: ErrorKind<ParserError>) -> String {
    let msg = match err {
        ErrorKind::Custom(x) => format!("{}", x),
        x => format!("parser error {}", x.description()),
    };

    format!("error: {}", msg)
}

#[derive(Debug, Clone, Copy, PartialEq)]
enum ParserError {
    MultipleChars, // more than 1 character between '', eg. 'ab'
    MissingChar,   // no char between '', eg. ''
    InvalidPair,   // eg. (1 2 . 3) or (1 . 2 3)
}

impl fmt::Display for ParserError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            ParserError::MultipleChars => write!(f, "more than one character between ''"), // more than 1 character between '', eg. 'ab'
            ParserError::MissingChar => write!(f, "no character between ''"),   // no char between '', eg. ''
            ParserError::InvalidPair => write!(f, "invalid pair"),   // eg. (1 2 . 3) or (1 . 2 3)
        }
    }
}

#[cfg(test)]
mod test {
    use super::{ParserError, bool_, char_, integer, ident, pair, list, item};
    use nom::{IResult, Err, ErrorKind};
    use super::super::value::Value;

    macro_rules! expect_error {
        // with error kind
        ($parser:ident, $input:expr, $pos:expr, $errorkind:expr) => (
            let input = $input.as_bytes();
            let error_pos = &input[$pos..];
            let res = $parser(input);
            assert!(match res {
                IResult::Error(
                    Err::NodePosition(
                        ErrorKind::Custom(kind),
                        pos,
                        ref boxed_err)
                ) if (pos == error_pos && kind == $errorkind) => { true },
                x => { println!("{:?}", x); false }
            });
        );
        // without error kind
        ($parser:ident, $input:expr, $pos:expr) => (
            let input = $input.as_bytes();
            let error_pos = &input[$pos..];
            assert!(match $parser(input) {
                IResult::Error(Err::Position(_, pos)) if pos == error_pos => true,
                x => { println!("{:?}", x); false },
            });
        )
    }

    macro_rules! expect_ok {
        // with rest
        ($parser:ident, $input:expr, $expected:expr, $rest:expr) => (
            let input = $input.as_bytes();
            let rest = $rest.as_bytes();
            assert_eq!(
                $parser(input),
                IResult::Done(rest, $expected)););

        // without rest
        ($parser:ident, $input:expr, $expected:expr) => (
            expect_ok!($parser, $input, $expected, "");
        )
    }

    #[test]
    fn bool() {
        expect_ok!(bool_, "true", Value::new_bool(true));
        expect_ok!(bool_, "false", Value::new_bool(false));
        expect_error!(bool_, "trude", 0);
        expect_error!(bool_, "fale", 0);
    }

    #[test]
    fn char() {
        use std::u8;
        for x in 0..127 {
            if x == '\'' as u8 { continue } // skip ''', which is invalid (tested below)
            let input = String::from_utf8(vec!['\'' as u8, x, '\'' as u8]).unwrap();
            expect_ok!(char_, input, Value::new_char(x as char));
        }
        expect_error!(char_, "'ab'", 2, ParserError::MultipleChars);
        expect_error!(char_, "''", 1, ParserError::MissingChar);
    }

    #[test]
    fn integer_() {
        expect_ok!(integer, "007", Value::new_integer(7));
        expect_ok!(integer, "-007", Value::new_integer(-7));
        expect_ok!(integer, "123456789", Value::new_integer(123456789));
        expect_ok!(integer, "-123456789", Value::new_integer(-123456789));
        expect_ok!(integer, "123b456789", Value::new_integer(123), "b456789");
        expect_ok!(integer, "123456789c", Value::new_integer(123456789), "c");
        expect_ok!(integer, "00-7", Value::new_integer(0), "-7");

        expect_error!(integer, "a123456789", 0);
        expect_error!(integer, "--7", 1);
    }

    #[test]
    fn ident_() {
        expect_ok!(ident, "+", Value::new_ident("+"));
        expect_ok!(ident, "a1a", Value::new_ident("a"), "1a");
        expect_ok!(ident, "num->str", Value::new_ident("num->str"));
        expect_ok!(ident, "//", Value::new_ident("//"));

        expect_error!(ident, "1a", 0);
    }

    #[test]
    fn list_() {
        expect_ok!(list, "()", Value::empty_list());
        //TODO: add more tests for lists with content
    }

    #[test]
    fn pair_() {
        let t = Value::new_bool(true);
        let f = Value::new_bool(false);
        let e = Value::empty_list();

        expect_ok!(pair, "(true . false)", Value::new_pair(t.clone(), f.clone()));
        expect_ok!(pair, "(true . (false . ()))", Value::new_pair(t, Value::new_pair(f.clone(), e.clone())));

        expect_error!(pair, "(1 .)", 0, ParserError::InvalidPair);
        expect_error!(pair, "(. 2)", 0, ParserError::InvalidPair);
        expect_error!(pair, "(1 . 2 3)", 0, ParserError::InvalidPair);
        expect_error!(pair, "(1 . 2 . 3)", 0, ParserError::InvalidPair);
        expect_error!(pair, "(1 2 . 3)", 0, ParserError::InvalidPair);
    }
}
<commit_msg>add item todo<commit_after>use nom::{digit, multispace, eof, ErrorKind, IResult};
use std::str;
use std::fmt;
use super::value::Value;

static UTF8_ERROR: &'static str = "File is no valid UTF8!";

macro_rules! fix {
    ($i:expr, $it:ident) => (fix_error!($i, ParserError, $it));
    ($i:expr, $submac:ident!( $($args:tt)* )) => (
      fix_error!($i, ParserError, $submac!($($args)*))
    );
}

named!(bool_<&[u8], Value, ParserError>, fix!(map!(
    alt!(
        tag!("true") |
        tag!("false")),
    |x|{ Value::new_bool(x == b"true") })));

named!(char_<&[u8], Value, ParserError>, fix!(chain!(
    tag!("'") ~
    error!(ErrorKind::Custom(ParserError::MissingChar), not!(tag!("'"))) ~
    c: take!(1) ~
    error!(ErrorKind::Custom(ParserError::MultipleChars),
        fix!(tag!("'"))) ,
    ||{ Value::new_char(c[0] as char) })));

named!(end_of_item<&[u8], &[u8], ParserError>,
    fix!(alt!(
        multispace |
        eof |
        tag!(")"))));

fn is_valid_in_ident(x: u8) -> bool {
    (x >= 0x3A && x <= 0x7E) ||
    (x >= 0x2A && x <= 0x2F) ||
    (x >= 0x23 && x <= 0x27) ||
    x == '!' as u8
}

named!(ident<&[u8], Value, ParserError>, fix!(chain!(
    peek!(none_of!("0123456789().")) ~
    x: take_while1!(is_valid_in_ident),
    || Value::new_ident((str::from_utf8(x).unwrap())))));

named!(integer<&[u8], Value, ParserError>,
    fix!(chain!(
        s: opt!(char!('-')) ~
        x: digit ,
        ||{
            let num: i64 = str::from_utf8(x).expect(UTF8_ERROR).parse().unwrap();
            if s.is_some() {
                Value::new_integer(-num)
            } else {
                Value::new_integer(num)
            }
        })));

named!(item<&[u8], Value, ParserError>,
    chain!(
        opt!(multispace) ~
        value: alt_complete!(
            bool_ |
            char_ |
            integer |
            ident |
            pair |
            list) ~
        peek!(end_of_item),
        || value));

named!(pair<&[u8], Value, ParserError>,
    error!(ErrorKind::Custom(ParserError::InvalidPair), complete!(delimited!(
            fix!(tag!("(")),
            chain!(
                a: item ~
                fix!(multispace) ~
                fix!(tag!(".")) ~
                fix!(multispace) ~
                b: item ,
                || { Value::new_pair(a,b) }
            ),
            fix!(tag!(")"))))));

named!(list_inner<Vec<Value> >,
    many0!(item));

named!(list<&[u8], Value, ParserError>,
    alt!(
        fix!(map!(
            tag!("()") ,
            |_| Value::empty_list())) |
        fix!(map!(
            delimited!(
                tag!("("),
                list_inner,
                tag!(")")),
            |x: Vec<Value>| {
                let mut iter = x.into_iter().rev();
                let last = iter.next().unwrap(); // safe because list len must be >= 1
                iter.fold(Value::new_pair(last, Value::empty_list()), |prev_pair, value| Value::new_pair(value, prev_pair))
            }))
        ));

pub fn parse(input: &[u8]) -> Result<Value, String> {
    use nom::Err as NomErr;
    named!(complete_item<&[u8], Value, ParserError>, complete!(item));
    match complete_item(input) {
        IResult::Done(_, value) => Ok(value),
        IResult::Incomplete(_) => unimplemented!(), // because of complete
        IResult::Error(NomErr::Position(err, _)) => Err(create_error_string(err)),
        IResult::Error(NomErr::NodePosition(err, _, _)) => Err(create_error_string(err)),
        _ => unimplemented!(),
    }
}

fn create_error_string(err: ErrorKind<ParserError>) -> String {
    let msg = match err {
        ErrorKind::Custom(x) => format!("{}", x),
        x => format!("parser error {}", x.description()),
    };

    format!("error: {}", msg)
}

#[derive(Debug, Clone, Copy, PartialEq)]
enum ParserError {
    MultipleChars, // more than 1 character between '', eg. 'ab'
    MissingChar,   // no char between '', eg. ''
    InvalidPair,   // eg. (1 2 . 3) or (1 . 2 3)
}

impl fmt::Display for ParserError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            ParserError::MultipleChars => write!(f, "more than one character between ''"), // more than 1 character between '', eg. 'ab'
            ParserError::MissingChar => write!(f, "no character between ''"),   // no char between '', eg. ''
            ParserError::InvalidPair => write!(f, "invalid pair"),   // eg. (1 2 . 3) or (1 . 2 3)
        }
    }
}

#[cfg(test)]
mod test {
    use super::{ParserError, bool_, char_, integer, ident, pair, list, item};
    use nom::{IResult, Err, ErrorKind};
    use super::super::value::Value;

    macro_rules! expect_error {
        // with error kind
        ($parser:ident, $input:expr, $pos:expr, $errorkind:expr) => (
            let input = $input.as_bytes();
            let error_pos = &input[$pos..];
            let res = $parser(input);
            assert!(match res {
                IResult::Error(
                    Err::NodePosition(
                        ErrorKind::Custom(kind),
                        pos,
                        ref boxed_err)
                ) if (pos == error_pos && kind == $errorkind) => { true },
                x => { println!("{:?}", x); false }
            });
        );
        // without error kind
        ($parser:ident, $input:expr, $pos:expr) => (
            let input = $input.as_bytes();
            let error_pos = &input[$pos..];
            assert!(match $parser(input) {
                IResult::Error(Err::Position(_, pos)) if pos == error_pos => true,
                x => { println!("{:?}", x); false },
            });
        )
    }

    macro_rules! expect_ok {
        // with rest
        ($parser:ident, $input:expr, $expected:expr, $rest:expr) => (
            let input = $input.as_bytes();
            let rest = $rest.as_bytes();
            assert_eq!(
                $parser(input),
                IResult::Done(rest, $expected)););

        // without rest
        ($parser:ident, $input:expr, $expected:expr) => (
            expect_ok!($parser, $input, $expected, "");
        )
    }

    #[test]
    fn bool() {
        expect_ok!(bool_, "true", Value::new_bool(true));
        expect_ok!(bool_, "false", Value::new_bool(false));
        expect_error!(bool_, "trude", 0);
        expect_error!(bool_, "fale", 0);
    }

    #[test]
    fn char() {
        use std::u8;
        for x in 0..127 {
            if x == '\'' as u8 { continue } // skip ''', which is invalid (tested below)
            let input = String::from_utf8(vec!['\'' as u8, x, '\'' as u8]).unwrap();
            expect_ok!(char_, input, Value::new_char(x as char));
        }
        expect_error!(char_, "'ab'", 2, ParserError::MultipleChars);
        expect_error!(char_, "''", 1, ParserError::MissingChar);
    }

    #[test]
    fn integer_() {
        expect_ok!(integer, "007", Value::new_integer(7));
        expect_ok!(integer, "-007", Value::new_integer(-7));
        expect_ok!(integer, "123456789", Value::new_integer(123456789));
        expect_ok!(integer, "-123456789", Value::new_integer(-123456789));
        expect_ok!(integer, "123b456789", Value::new_integer(123), "b456789");
        expect_ok!(integer, "123456789c", Value::new_integer(123456789), "c");
        expect_ok!(integer, "00-7", Value::new_integer(0), "-7");

        expect_error!(integer, "a123456789", 0);
        expect_error!(integer, "--7", 1);
    }

    #[test]
    fn ident_() {
        expect_ok!(ident, "+", Value::new_ident("+"));
        expect_ok!(ident, "a1a", Value::new_ident("a"), "1a");
        expect_ok!(ident, "num->str", Value::new_ident("num->str"));
        expect_ok!(ident, "//", Value::new_ident("//"));

        expect_error!(ident, "1a", 0);
    }

    #[test]
    fn list_() {
        expect_ok!(list, "()", Value::empty_list());
        //TODO: add more tests for lists with content
    }

    #[test]
    fn pair_() {
        let t = Value::new_bool(true);
        let f = Value::new_bool(false);
        let e = Value::empty_list();

        expect_ok!(pair, "(true . false)", Value::new_pair(t.clone(), f.clone()));
        expect_ok!(pair, "(true . (false . ()))", Value::new_pair(t, Value::new_pair(f.clone(), e.clone())));

        expect_error!(pair, "(1 .)", 0, ParserError::InvalidPair);
        expect_error!(pair, "(. 2)", 0, ParserError::InvalidPair);
        expect_error!(pair, "(1 . 2 3)", 0, ParserError::InvalidPair);
        expect_error!(pair, "(1 . 2 . 3)", 0, ParserError::InvalidPair);
        expect_error!(pair, "(1 2 . 3)", 0, ParserError::InvalidPair);
    }

    //TODO
    fn item_() {
        // add test, eg. "1 1" should not parse as item
    }
}
<|endoftext|>