<commit_before>use yaml_rust::{YamlLoader, Yaml, ScanError};
use std::collections::{HashMap, BTreeMap};
use onig::{Regex, Captures, Syntax, self};

pub type ScopeElement = String;
pub type CaptureMapping = HashMap<usize, ScopeElement>;

#[derive(Debug)]
pub struct SyntaxDefinition {
    pub name: String,
    pub file_extensions: Vec<String>,
    pub scope: ScopeElement,
    pub first_line_match: Option<Regex>,
    pub hidden: bool,

    pub variables: HashMap<String, String>,
    pub contexts: HashMap<String, Context>,
}

#[derive(Debug)]
pub struct Context {
    pub meta_scope: Option<ScopeElement>,
    pub meta_content_scope: Option<ScopeElement>,
    pub meta_include_prototype: bool,

    pub patterns: Vec<Pattern>,
}

#[derive(Debug)]
pub enum Pattern {
    Match(MatchPattern),
    Include(ContextReference),
}

#[derive(Debug)]
pub struct MatchPattern {
    pub regex_str: String,
    // present unless contains backrefs and has to be dynamically compiled
    pub regex: Option<Regex>,
    pub scope: Option<ScopeElement>,
    pub captures: Option<CaptureMapping>,
    pub operation: MatchOperation,
}

#[derive(Debug)]
pub enum ContextReference {
    Named(String),
    ByScope {
        name: String,
        sub_context: Option<String>,
    },
    File(String),
    Inline(Box<Context>),
}

#[derive(Debug)]
pub enum MatchOperation {
    Push(Vec<ContextReference>),
    Set(Vec<ContextReference>),
    Pop,
    None,
}

#[derive(Debug)]
pub enum ParseError {
    InvalidYaml(ScanError),
    EmptyFile,
    MissingMandatoryKey(&'static str),
    RegexCompileError(onig::Error),
    TypeMismatch,
}

fn get_key<'a, R, F: FnOnce(&'a Yaml) -> Option<R>>(map: &'a BTreeMap<Yaml, Yaml>,
                                                    key: &'static str,
                                                    f: F)
                                                    -> Result<R, ParseError> {
    map.get(&Yaml::String(key.to_owned()))
        .ok_or(ParseError::MissingMandatoryKey(key))
        .and_then(|x| f(x).ok_or(ParseError::TypeMismatch))
}

struct ParserState {
    variables: HashMap<String, String>,
    variable_regex: Regex,
    backref_regex: Regex,
    short_multibyte_regex: Regex,
}

impl SyntaxDefinition {
    pub fn load_from_str(s: &str) -> Result<SyntaxDefinition, ParseError> {
        let docs = match YamlLoader::load_from_str(s) {
            Ok(x) => x,
            Err(e) => return Err(ParseError::InvalidYaml(e)),
        };
        if docs.len() == 0 {
            return Err(ParseError::EmptyFile);
        }
        let doc = &docs[0];
        SyntaxDefinition::parse_top_level(doc)
    }

    fn parse_top_level(doc: &Yaml) -> Result<SyntaxDefinition, ParseError> {
        let h = try!(doc.as_hash().ok_or(ParseError::TypeMismatch));

        let mut variables = HashMap::new();
        if let Ok(map) = get_key(h, "variables", |x| x.as_hash()) {
            for (key, value) in map.iter() {
                if let (Some(key_str), Some(val_str)) = (key.as_str(), value.as_str()) {
                    variables.insert(key_str.to_owned(), val_str.to_owned());
                }
            }
        }
        let state = ParserState {
            variables: variables,
            variable_regex: Regex::new(r"\{\{([A-Za-z0-9_]+)\}\}").unwrap(),
            backref_regex: Regex::new(r"\\\d").unwrap(),
            short_multibyte_regex: Regex::new(r"\\x([a-fA-F][a-fA-F0-9])").unwrap(),
        };

        let contexts_hash = try!(get_key(h, "contexts", |x| x.as_hash()));
        let contexts = try!(SyntaxDefinition::parse_contexts(contexts_hash, &state));

        let defn = SyntaxDefinition {
            name: try!(get_key(h, "name", |x| x.as_str())).to_owned(),
            scope: try!(get_key(h, "scope", |x| x.as_str())).to_owned(),
            file_extensions: {
                get_key(h, "file_extensions", |x| x.as_vec())
                    .map(|v| v.iter().filter_map(|y| y.as_str()).map(|x| x.to_owned()).collect())
                    .unwrap_or_else(|_| Vec::new())
            },
            first_line_match: if let Ok(s) = get_key(h, "first_line_match", |x| x.as_str()) {
                Some(try!(Regex::new(s).map_err(|e| ParseError::RegexCompileError(e))))
            } else {
                None
            },
            hidden: get_key(h, "hidden", |x| x.as_bool()).unwrap_or(false),

            variables: state.variables.clone(),
            contexts: contexts,
        };
        Ok(defn)
    }

    fn parse_contexts(map: &BTreeMap<Yaml, Yaml>,
                      state: &ParserState)
                      -> Result<HashMap<String, Context>, ParseError> {
        let mut contexts = HashMap::new();
        for (key, value) in map.iter() {
            if let (Some(name), Some(val_vec)) = (key.as_str(), value.as_vec()) {
                let context = try!(SyntaxDefinition::parse_context(val_vec, state));
                contexts.insert(name.to_owned(), context);
            }
        }
        return Ok(contexts);
    }

    fn parse_context(vec: &Vec<Yaml>,
                     state: &ParserState)
                     -> Result<Context, ParseError> {
        let mut context = Context {
            meta_scope: None,
            meta_content_scope: None,
            meta_include_prototype: true,
            patterns: Vec::new(),
        };
        for y in vec.iter() {
            let map = try!(y.as_hash().ok_or(ParseError::TypeMismatch));

            if let Some(x) = get_key(map, "meta_scope", |x| x.as_str()).ok() {
                context.meta_scope = Some(x.to_owned());
            } else if let Some(x) = get_key(map, "meta_content_scope", |x| x.as_str()).ok() {
                context.meta_scope = Some(x.to_owned());
            } else if let Some(x) = get_key(map, "meta_include_prototype", |x| x.as_bool()).ok() {
                context.meta_include_prototype = x;
            } else if let Some(x) = get_key(map, "include", |x| Some(x)).ok() {
                let reference = try!(SyntaxDefinition::parse_reference(x, state));
                context.patterns.push(Pattern::Include(reference));
            } else {
                let pattern = try!(SyntaxDefinition::parse_match_pattern(map, state));
                context.patterns.push(Pattern::Match(pattern));
            }

        }
        return Ok(context);
    }

    fn parse_reference(y: &Yaml,
                       state: &ParserState)
                       -> Result<ContextReference, ParseError> {
        if let Some(s) = y.as_str() {
            if s.starts_with("scope:") {
                let scope_ref = &s[6..];
                let parts: Vec<&str> = scope_ref.split("#").collect();
                Ok(ContextReference::ByScope {
                    name: parts[0].to_owned(),
                    sub_context: if parts.len() > 1 {
                        Some(parts[1].to_owned())
                    } else {
                        None
                    },
                })
            } else if s.ends_with(".sublime-syntax") {
                Ok(ContextReference::File(s.to_owned()))
            } else {
                Ok(ContextReference::Named(s.to_owned()))
            }
        } else if let Some(v) = y.as_vec() {
            let context = try!(SyntaxDefinition::parse_context(v, state));
            Ok(ContextReference::Inline(Box::new(context)))
        } else {
            Err(ParseError::TypeMismatch)
        }
    }

    fn parse_match_pattern(map: &BTreeMap<Yaml, Yaml>,
                           state: &ParserState)
                           -> Result<MatchPattern, ParseError> {
        let raw_regex = try!(get_key(map, "match", |x| x.as_str()));
        let regex_str_1 = state.variable_regex.replace_all(raw_regex, |caps: &Captures| {
            state.variables.get(caps.at(1).unwrap_or("")).map(|x| &**x).unwrap_or("").to_owned()
        });
        // bug triggered by CSS.sublime-syntax, dunno why this is necessary
        let regex_str = state.short_multibyte_regex.replace_all(&regex_str_1, |caps: &Captures| {
            format!("\\x{{000000{}}}", caps.at(1).unwrap_or(""))
        });
        // println!("{:?}", regex_str);

        // if it contains back references we can't resolve it until runtime
        let regex = if state.backref_regex.find(&regex_str).is_some() {
            None
        } else {
            Some(try!(Regex::with_options(&regex_str, onig::REGEX_OPTION_CAPTURE_GROUP, Syntax::default()).map_err(|e| ParseError::RegexCompileError(e))))
        };

        let scope = get_key(map, "scope", |x| x.as_str()).ok().map(|s| s.to_owned());

        let captures = if let Ok(map) = get_key(map, "captures", |x| x.as_hash()) {
            let mut res_map = HashMap::new();
            for (key, value) in map.iter() {
                if let (Some(key_int), Some(val_str)) = (key.as_i64(), value.as_str()) {
                    res_map.insert(key_int as usize, val_str.to_owned());
                }
            }
            Some(res_map)
        } else {
            None
        };

        let operation = if let Ok(_) = get_key(map, "pop", |x| Some(x)) {
            MatchOperation::Pop
        } else if let Ok(y) = get_key(map, "push", |x| Some(x)) {
            MatchOperation::Push(try!(SyntaxDefinition::parse_pushargs(y, state)))
        } else if let Ok(y) = get_key(map, "set", |x| Some(x)) {
            MatchOperation::Set(try!(SyntaxDefinition::parse_pushargs(y, state)))
        } else {
            MatchOperation::None
        };

        let pattern = MatchPattern {
            regex_str: regex_str,
            regex: regex,
            scope: scope,
            captures: captures,
            operation: operation,
        };
        return Ok(pattern);
    }

    fn parse_pushargs(y: &Yaml,
                      state: &ParserState)
                      -> Result<Vec<ContextReference>, ParseError> {
        // check for a push of multiple items
        if y.as_vec().map(|v| !v.is_empty() && v[0].as_str().is_some()).unwrap_or(false) {
            // this works because Result implements FromIterator to handle the errors
            y.as_vec().unwrap().iter().map(|x| SyntaxDefinition::parse_reference(x, state)).collect()
        } else {
            Ok(vec![try!(SyntaxDefinition::parse_reference(y, state))])
        }
    }
}

#[cfg(test)]
mod tests {
    #[test]
    fn can_parse() {
        use syntax_definition::{SyntaxDefinition, Pattern, CaptureMapping};
        let defn: SyntaxDefinition =
            SyntaxDefinition::load_from_str("name: C\nscope: source.c\ncontexts: {}").unwrap();
        assert_eq!(defn.name, "C");
        assert_eq!(defn.scope, "source.c");
        let exts_empty: Vec<String> = Vec::new();
        assert_eq!(defn.file_extensions, exts_empty);
        assert_eq!(defn.hidden, false);
        assert!(defn.variables.is_empty());
        let defn2: SyntaxDefinition =
            SyntaxDefinition::load_from_str("
        name: C
        scope: source.c
        file_extensions: [c, h]
        hidden: true
        variables:
          ident: '[QY]+'
        contexts:
          main:
            - match: \\b(if|else|for|while|{{ident}})\\b
              scope: keyword.control.c
              captures:
                  1: meta.preprocessor.c++
                  2: keyword.control.include.c++
              push: [string, 'scope:source.c#main']
            - match: '\"'
              push: string
          string:
            - meta_scope: string.quoted.double.c
            - match: \\\\.
              scope: constant.character.escape.c
            - match: '\"'
              pop: true
        ")
                .unwrap();
        assert_eq!(defn2.name, "C");
        assert_eq!(defn2.scope, "source.c");
        let exts: Vec<String> = vec![String::from("c"), String::from("h")];
        assert_eq!(defn2.file_extensions, exts);
        assert_eq!(defn2.hidden, true);
        assert_eq!(defn2.variables.get("ident").unwrap(), "[QY]+");

        let n: Option<String> = None;
        println!("{:?}", defn2);
        // assert!(false);
        assert_eq!(defn2.contexts["main"].meta_scope, n);
        assert_eq!(defn2.contexts["main"].meta_include_prototype, true);
        assert_eq!(defn2.contexts["string"].meta_scope,
                   Some(String::from("string.quoted.double.c")));
        let first_pattern: &Pattern = &defn2.contexts["main"].patterns[0];
        match first_pattern {
            &Pattern::Match(ref match_pat) => {
                let m : &CaptureMapping = match_pat.captures.as_ref().expect("test failed");
                let x : &String = &m[&1];
                assert_eq!(x, "meta.preprocessor.c++");
                assert_eq!(format!("{:?}",match_pat.operation),
                    "Push([Named(\"string\"), ByScope { name: \"source.c\", sub_context: Some(\"main\") }])");

                let r = match_pat.regex.as_ref().unwrap();
                assert!(r.is_match("else"));
                assert!(!r.is_match("elses"));
                assert!(!r.is_match("elose"));
                assert!(r.is_match("QYYQQQ"));
                assert!(!r.is_match("QYYQZQQ"));
            },
            _ => assert!(false)
        }
    }
}
<commit_msg>Ability to push multiple scopes<commit_after>use yaml_rust::{YamlLoader, Yaml, ScanError};
use std::collections::{HashMap, BTreeMap};
use onig::{Regex, Captures, Syntax, self};

pub type ScopeElement = String;
pub type CaptureMapping = HashMap<usize, ScopeElement>;

#[derive(Debug)]
pub struct SyntaxDefinition {
    pub name: String,
    pub file_extensions: Vec<String>,
    pub scope: ScopeElement,
    pub first_line_match: Option<Regex>,
    pub hidden: bool,

    pub variables: HashMap<String, String>,
    pub contexts: HashMap<String, Context>,
}

#[derive(Debug)]
pub struct Context {
    pub meta_scope: Option<ScopeElement>,
    pub meta_content_scope: Option<ScopeElement>,
    pub meta_include_prototype: bool,

    pub patterns: Vec<Pattern>,
}

#[derive(Debug)]
pub enum Pattern {
    Match(MatchPattern),
    Include(ContextReference),
}

#[derive(Debug)]
pub struct MatchPattern {
    pub regex_str: String,
    // present unless contains backrefs and has to be dynamically compiled
    pub regex: Option<Regex>,
    pub scope: Vec<ScopeElement>,
    pub captures: Option<CaptureMapping>,
    pub operation: MatchOperation,
}

#[derive(Debug)]
pub enum ContextReference {
    Named(String),
    ByScope {
        name: String,
        sub_context: Option<String>,
    },
    File(String),
    Inline(Box<Context>),
}

#[derive(Debug)]
pub enum MatchOperation {
    Push(Vec<ContextReference>),
    Set(Vec<ContextReference>),
    Pop,
    None,
}

#[derive(Debug)]
pub enum ParseError {
    InvalidYaml(ScanError),
    EmptyFile,
    MissingMandatoryKey(&'static str),
    RegexCompileError(onig::Error),
    TypeMismatch,
}

fn get_key<'a, R, F: FnOnce(&'a Yaml) -> Option<R>>(map: &'a BTreeMap<Yaml, Yaml>,
                                                    key: &'static str,
                                                    f: F)
                                                    -> Result<R, ParseError> {
    map.get(&Yaml::String(key.to_owned()))
        .ok_or(ParseError::MissingMandatoryKey(key))
        .and_then(|x| f(x).ok_or(ParseError::TypeMismatch))
}

struct ParserState {
    variables: HashMap<String, String>,
    variable_regex: Regex,
    backref_regex: Regex,
    short_multibyte_regex: Regex,
}

impl SyntaxDefinition {
    pub fn load_from_str(s: &str) -> Result<SyntaxDefinition, ParseError> {
        let docs = match YamlLoader::load_from_str(s) {
            Ok(x) => x,
            Err(e) => return Err(ParseError::InvalidYaml(e)),
        };
        if docs.len() == 0 {
            return Err(ParseError::EmptyFile);
        }
        let doc = &docs[0];
        SyntaxDefinition::parse_top_level(doc)
    }

    fn parse_top_level(doc: &Yaml) -> Result<SyntaxDefinition, ParseError> {
        let h = try!(doc.as_hash().ok_or(ParseError::TypeMismatch));

        let mut variables = HashMap::new();
        if let Ok(map) = get_key(h, "variables", |x| x.as_hash()) {
            for (key, value) in map.iter() {
                if let (Some(key_str), Some(val_str)) = (key.as_str(), value.as_str()) {
                    variables.insert(key_str.to_owned(), val_str.to_owned());
                }
            }
        }
        let state = ParserState {
            variables: variables,
            variable_regex: Regex::new(r"\{\{([A-Za-z0-9_]+)\}\}").unwrap(),
            backref_regex: Regex::new(r"\\\d").unwrap(),
            short_multibyte_regex: Regex::new(r"\\x([a-fA-F][a-fA-F0-9])").unwrap(),
        };

        let contexts_hash = try!(get_key(h, "contexts", |x| x.as_hash()));
        let contexts = try!(SyntaxDefinition::parse_contexts(contexts_hash, &state));

        let defn = SyntaxDefinition {
            name: try!(get_key(h, "name", |x| x.as_str())).to_owned(),
            scope: try!(get_key(h, "scope", |x| x.as_str())).to_owned(),
            file_extensions: {
                get_key(h, "file_extensions", |x| x.as_vec())
                    .map(|v| v.iter().filter_map(|y| y.as_str()).map(|x| x.to_owned()).collect())
                    .unwrap_or_else(|_| Vec::new())
            },
            first_line_match: if let Ok(s) = get_key(h, "first_line_match", |x| x.as_str()) {
                Some(try!(Regex::new(s).map_err(|e| ParseError::RegexCompileError(e))))
            } else {
                None
            },
            hidden: get_key(h, "hidden", |x| x.as_bool()).unwrap_or(false),

            variables: state.variables.clone(),
            contexts: contexts,
        };
        Ok(defn)
    }

    fn parse_contexts(map: &BTreeMap<Yaml, Yaml>,
                      state: &ParserState)
                      -> Result<HashMap<String, Context>, ParseError> {
        let mut contexts = HashMap::new();
        for (key, value) in map.iter() {
            if let (Some(name), Some(val_vec)) = (key.as_str(), value.as_vec()) {
                let context = try!(SyntaxDefinition::parse_context(val_vec, state));
                contexts.insert(name.to_owned(), context);
            }
        }
        return Ok(contexts);
    }

    fn parse_context(vec: &Vec<Yaml>,
                     state: &ParserState)
                     -> Result<Context, ParseError> {
        let mut context = Context {
            meta_scope: None,
            meta_content_scope: None,
            meta_include_prototype: true,
            patterns: Vec::new(),
        };
        for y in vec.iter() {
            let map = try!(y.as_hash().ok_or(ParseError::TypeMismatch));

            if let Some(x) = get_key(map, "meta_scope", |x| x.as_str()).ok() {
                context.meta_scope = Some(x.to_owned());
            } else if let Some(x) = get_key(map, "meta_content_scope", |x| x.as_str()).ok() {
                context.meta_scope = Some(x.to_owned());
            } else if let Some(x) = get_key(map, "meta_include_prototype", |x| x.as_bool()).ok() {
                context.meta_include_prototype = x;
            } else if let Some(x) = get_key(map, "include", |x| Some(x)).ok() {
                let reference = try!(SyntaxDefinition::parse_reference(x, state));
                context.patterns.push(Pattern::Include(reference));
            } else {
                let pattern = try!(SyntaxDefinition::parse_match_pattern(map, state));
                context.patterns.push(Pattern::Match(pattern));
            }

        }
        return Ok(context);
    }

    fn parse_reference(y: &Yaml,
                       state: &ParserState)
                       -> Result<ContextReference, ParseError> {
        if let Some(s) = y.as_str() {
            if s.starts_with("scope:") {
                let scope_ref = &s[6..];
                let parts: Vec<&str> = scope_ref.split("#").collect();
                Ok(ContextReference::ByScope {
                    name: parts[0].to_owned(),
                    sub_context: if parts.len() > 1 {
                        Some(parts[1].to_owned())
                    } else {
                        None
                    },
                })
            } else if s.ends_with(".sublime-syntax") {
                Ok(ContextReference::File(s.to_owned()))
            } else {
                Ok(ContextReference::Named(s.to_owned()))
            }
        } else if let Some(v) = y.as_vec() {
            let context = try!(SyntaxDefinition::parse_context(v, state));
            Ok(ContextReference::Inline(Box::new(context)))
        } else {
            Err(ParseError::TypeMismatch)
        }
    }

    fn parse_match_pattern(map: &BTreeMap<Yaml, Yaml>,
                           state: &ParserState)
                           -> Result<MatchPattern, ParseError> {
        let raw_regex = try!(get_key(map, "match", |x| x.as_str()));
        let regex_str_1 = state.variable_regex.replace_all(raw_regex, |caps: &Captures| {
            state.variables.get(caps.at(1).unwrap_or("")).map(|x| &**x).unwrap_or("").to_owned()
        });
        // bug triggered by CSS.sublime-syntax, dunno why this is necessary
        let regex_str = state.short_multibyte_regex.replace_all(&regex_str_1, |caps: &Captures| {
            format!("\\x{{000000{}}}", caps.at(1).unwrap_or(""))
        });
        // println!("{:?}", regex_str);

        // if it contains back references we can't resolve it until runtime
        let regex = if state.backref_regex.find(&regex_str).is_some() {
            None
        } else {
            Some(try!(Regex::with_options(&regex_str, onig::REGEX_OPTION_CAPTURE_GROUP, Syntax::default()).map_err(|e| ParseError::RegexCompileError(e))))
        };

        let scope = get_key(map, "scope", |x| x.as_str()).ok().map(|s| s.split_whitespace().map(|scope| scope.to_owned()).collect()).unwrap_or_else(|| vec![]);

        let captures = if let Ok(map) = get_key(map, "captures", |x| x.as_hash()) {
            let mut res_map = HashMap::new();
            for (key, value) in map.iter() {
                if let (Some(key_int), Some(val_str)) = (key.as_i64(), value.as_str()) {
                    res_map.insert(key_int as usize, val_str.to_owned());
                }
            }
            Some(res_map)
        } else {
            None
        };

        let operation = if let Ok(_) = get_key(map, "pop", |x| Some(x)) {
            MatchOperation::Pop
        } else if let Ok(y) = get_key(map, "push", |x| Some(x)) {
            MatchOperation::Push(try!(SyntaxDefinition::parse_pushargs(y, state)))
        } else if let Ok(y) = get_key(map, "set", |x| Some(x)) {
            MatchOperation::Set(try!(SyntaxDefinition::parse_pushargs(y, state)))
        } else {
            MatchOperation::None
        };

        let pattern = MatchPattern {
            regex_str: regex_str,
            regex: regex,
            scope: scope,
            captures: captures,
            operation: operation,
        };
        return Ok(pattern);
    }

    fn parse_pushargs(y: &Yaml,
                      state: &ParserState)
                      -> Result<Vec<ContextReference>, ParseError> {
        // check for a push of multiple items
        if y.as_vec().map(|v| !v.is_empty() && v[0].as_str().is_some()).unwrap_or(false) {
            // this works because Result implements FromIterator to handle the errors
            y.as_vec().unwrap().iter().map(|x| SyntaxDefinition::parse_reference(x, state)).collect()
        } else {
            Ok(vec![try!(SyntaxDefinition::parse_reference(y, state))])
        }
    }
}

#[cfg(test)]
mod tests {
    #[test]
    fn can_parse() {
        use syntax_definition::{SyntaxDefinition, Pattern, CaptureMapping};
        let defn: SyntaxDefinition =
            SyntaxDefinition::load_from_str("name: C\nscope: source.c\ncontexts: {}").unwrap();
        assert_eq!(defn.name, "C");
        assert_eq!(defn.scope, "source.c");
        let exts_empty: Vec<String> = Vec::new();
        assert_eq!(defn.file_extensions, exts_empty);
        assert_eq!(defn.hidden, false);
        assert!(defn.variables.is_empty());
        let defn2: SyntaxDefinition =
            SyntaxDefinition::load_from_str("
        name: C
        scope: source.c
        file_extensions: [c, h]
        hidden: true
        variables:
          ident: '[QY]+'
        contexts:
          main:
            - match: \\b(if|else|for|while|{{ident}})\\b
              scope: keyword.control.c keyword.looping.c
              captures:
                  1: meta.preprocessor.c++
                  2: keyword.control.include.c++
              push: [string, 'scope:source.c#main']
            - match: '\"'
              push: string
          string:
            - meta_scope: string.quoted.double.c
            - match: \\\\.
              scope: constant.character.escape.c
            - match: '\"'
              pop: true
        ")
                .unwrap();
        assert_eq!(defn2.name, "C");
        assert_eq!(defn2.scope, "source.c");
        let exts: Vec<String> = vec![String::from("c"), String::from("h")];
        assert_eq!(defn2.file_extensions, exts);
        assert_eq!(defn2.hidden, true);
        assert_eq!(defn2.variables.get("ident").unwrap(), "[QY]+");

        let n: Option<String> = None;
        println!("{:?}", defn2);
        // assert!(false);
        assert_eq!(defn2.contexts["main"].meta_scope, n);
        assert_eq!(defn2.contexts["main"].meta_include_prototype, true);
        assert_eq!(defn2.contexts["string"].meta_scope,
                   Some(String::from("string.quoted.double.c")));
        let first_pattern: &Pattern = &defn2.contexts["main"].patterns[0];
        match first_pattern {
            &Pattern::Match(ref match_pat) => {
                let m : &CaptureMapping = match_pat.captures.as_ref().expect("test failed");
                let x : &String = &m[&1];
                assert_eq!(x, "meta.preprocessor.c++");
                assert_eq!(format!("{:?}",match_pat.operation),
                    "Push([Named(\"string\"), ByScope { name: \"source.c\", sub_context: Some(\"main\") }])");

                assert_eq!(match_pat.scope, vec![String::from("keyword.control.c"), String::from("keyword.looping.c")]);

                let r = match_pat.regex.as_ref().unwrap();
                assert!(r.is_match("else"));
                assert!(!r.is_match("elses"));
                assert!(!r.is_match("elose"));
                assert!(r.is_match("QYYQQQ"));
                assert!(!r.is_match("QYYQZQQ"));
            },
            _ => assert!(false)
        }
    }
}
<|endoftext|>


<commit_before>use std::fs::File;
use std::io::{BufReader, BufRead};
use std::path::Path;
use std::collections::HashMap;
use ::error::{ProcError, ProcFile, ProcOper};
use ::{TaskId, MemSize};

#[derive(Debug)]
pub struct PidStatus {
    pub name: String,
    pub tgid: TaskId,
    pub pid: TaskId,
    pub ppid: TaskId,
    pub tracerpid: TaskId,
    // uid: Real, Effective, Saved, Filesystem
    pub uid: (u32, u32, u32, u32),
    // gid: Real, Effective, Saved, Filesystem
    pub gid: (u32, u32, u32, u32),
    pub fdsize: u32,
    pub vmpeak: Option<MemSize>,
    pub vmsize: Option<MemSize>,
    pub vmlck: Option<MemSize>,
    pub vmpin: Option<MemSize>,
    pub vmhwm: Option<MemSize>,
    pub vmrss: Option<MemSize>,
    pub vmdata: Option<MemSize>,
    pub vmstk: Option<MemSize>,
    pub vmexe: Option<MemSize>,
    pub vmlib: Option<MemSize>,
    pub vmpte: Option<MemSize>,
    pub vmpmd: Option<MemSize>,
    pub vmswap: Option<MemSize>,
    pub threads: u32
}

macro_rules! extract_line_opt {
    ($map:expr, $key:expr, $func:expr) =>
        ($map.remove($key)
             // TODO: This should use .map instead
            .and_then($func)
        )
}

macro_rules! extract_line {
    ($map:expr, $key:expr, $func:expr) =>
        (try!(
            extract_line_opt!($map, $key, $func)
                .ok_or(ProcError::new_more(ProcOper::ParsingField, ProcFile::PidStatus, Some($key)))
        ))
}

impl PidStatus {
    // Generate PidStatus struct given a process directory
    pub fn new(pid_dir: &str) -> Result<Self, ProcError> {
        // Try opening file
        let status_file = try!(
            File::open(Path::new(pid_dir).join("status"))
                .map_err(|e| ProcError::new_err(ProcOper::Opening, ProcFile::PidStatus, e))
        );

        let lines =
            BufReader::with_capacity(4096, status_file)
                .lines()
                .map(|r|
                    match r {
                        Ok(o) => Ok(o),
                        Err(e) => Err(ProcError::new_err(ProcOper::Reading, ProcFile::PidStatus, e))
                    }
                );
        Self::parse_string(lines)
    }

    fn parse_string<I: Iterator<Item=Result<String, ProcError>>>(lines: I) -> Result<Self, ProcError> {
        let mut status: HashMap<_, _> =
            try!(
                lines.map(|r|
                    match r {
                        Ok(line) => {
                            let split = line.splitn(2, ':').collect::<Vec<&str>>();
                            match (split.get(0), split.get(1)) {
                                (Some(key), Some(value)) =>
                                    Ok((key.trim().to_owned(),
                                         value.trim().to_owned())),
                                _ => Err(ProcError::new_more(ProcOper::Parsing, ProcFile::PidStatus,
                                             Some("No colon on line")))
                            }
                        },
                        Err(e) => Err(e)
                    }
                ).collect::<Result<_, _>>());

        // It's quite important that these appear in the order that they
        // appear in the status file
        Ok(PidStatus{
            name: extract_line!(status, "Name", |s| Some(s)),
            tgid: extract_line!(status, "Tgid", |s| s.parse().ok()),
            pid: extract_line!(status, "Pid", |s| s.parse().ok()),
            ppid: extract_line!(status, "PPid", |s| s.parse().ok()),
            tracerpid: extract_line!(status, "TracerPid", |s| s.parse().ok()),
            uid : extract_line!(status, "Uid", parse_uids),
            gid : extract_line!(status, "Gid", parse_uids),
            fdsize : extract_line!(status, "FDSize", |s| s.parse().ok()),
            vmpeak: extract_line_opt!(status, "VmPeak", parse_mem),
            vmsize: extract_line_opt!(status, "VmSize", parse_mem),
            vmlck: extract_line_opt!(status, "VmLck", parse_mem),
            vmpin: extract_line_opt!(status, "VmPin", parse_mem),
            vmhwm: extract_line_opt!(status, "VmHWM", parse_mem),
            vmrss: extract_line_opt!(status, "VmRSS", parse_mem),
            vmdata: extract_line_opt!(status, "VmData", parse_mem),
            vmstk: extract_line_opt!(status, "VmStk", parse_mem),
            vmexe: extract_line_opt!(status, "VmExe", parse_mem),
            vmlib: extract_line_opt!(status, "VmLib", parse_mem),
            vmpmd: extract_line_opt!(status, "VmPMD", parse_mem),
            vmpte: extract_line_opt!(status, "VmPTE", parse_mem),
            vmswap: extract_line_opt!(status, "VmSwap", parse_mem),
            threads: extract_line!(status, "Threads", |s| s.parse().ok())
        })
    }
}

// TODO: These should return a boxed error

fn parse_uids(uid_str: String) -> Option<(u32, u32, u32, u32)> {
    let uids = match
        uid_str.split("\t")
            .filter(|s| s != &"")
            .map(|s|
                s.parse()
            ).collect::<Result<Vec<_>, _>>()
            .ok()
        {
            Some(s) => s,
            None => return None
        };
    if uids.len() != 4 {
        return None;
    }
    Some((uids[0], uids[1], uids[2], uids[3]))
}

fn parse_mem(mem_str: String) -> Option<MemSize> {
    Some(1)
}
<commit_msg>Add proper memory parsing for /proc/[pid]/status<commit_after>use std::fs::File;
use std::io::{BufReader, BufRead};
use std::path::Path;
use std::collections::HashMap;
use std::num::ParseIntError;
use std::str::FromStr;
use ::error::{ProcError, ProcFile, ProcOper};
use ::{TaskId, MemSize};

#[derive(Debug)]
pub struct PidStatus {
    pub name: String,
    pub tgid: TaskId,
    pub pid: TaskId,
    pub ppid: TaskId,
    pub tracerpid: TaskId,
    // uid: Real, Effective, Saved, Filesystem
    pub uid: (u32, u32, u32, u32),
    // gid: Real, Effective, Saved, Filesystem
    pub gid: (u32, u32, u32, u32),
    pub fdsize: u32,
    pub vmpeak: Option<MemSize>,
    pub vmsize: Option<MemSize>,
    pub vmlck: Option<MemSize>,
    pub vmpin: Option<MemSize>,
    pub vmhwm: Option<MemSize>,
    pub vmrss: Option<MemSize>,
    pub vmdata: Option<MemSize>,
    pub vmstk: Option<MemSize>,
    pub vmexe: Option<MemSize>,
    pub vmlib: Option<MemSize>,
    pub vmpte: Option<MemSize>,
    pub vmpmd: Option<MemSize>,
    pub vmswap: Option<MemSize>,
    pub threads: u32
}

// Try removing key, if it's missing throw an error.
// Then try parsing it, then returning a final value if no errors have occured.
macro_rules! extract_line {
    ($map:expr, $key:expr, $func:expr) =>
        (try!(
            $map.remove($key)
                .ok_or(ProcError::new_more(ProcOper::ParsingField, ProcFile::PidStatus,
                    Some(concat!("missing ", $key)))
                ).and_then(|s|
                    $func(s).map_err(|e|
                        ProcError::new(ProcOper::ParsingField, ProcFile::PidStatus,
                            Some(e), Some($key))
                    )
                )
        ))
}

// Similar to extract_line, except that a missing field isn't an error.
macro_rules! extract_line_opt {
    ($map:expr, $key:expr, $func:expr) =>
        (match $map.remove($key) {
            Some(s) => Some(try!(
                $func(s).map_err(|e|
                    ProcError::new(ProcOper::ParsingField, ProcFile::PidStatus,
                        Some(e), Some($key))
                )
            )),
            None => None
        })
}

impl PidStatus {
    // Generate PidStatus struct given a process directory
    pub fn new(pid_dir: &str) -> Result<Self, ProcError> {
        // Try opening file
        let status_file = try!(
            File::open(Path::new(pid_dir).join("status"))
                .map_err(|e| ProcError::new_err(ProcOper::Opening, ProcFile::PidStatus, e))
        );

        let lines =
            BufReader::with_capacity(4096, status_file)
                .lines()
                .map(|r|
                    match r {
                        Ok(o) => Ok(o),
                        Err(e) => Err(ProcError::new_err(ProcOper::Reading, ProcFile::PidStatus, e))
                    }
                );
        Self::parse_string(lines)
    }

    fn parse_string<I: Iterator<Item=Result<String, ProcError>>>(lines: I) -> Result<Self, ProcError> {
        let mut status: HashMap<_, _> =
            try!(
                lines.map(|r|
                    match r {
                        Ok(line) => {
                            let split = line.splitn(2, ':').collect::<Vec<&str>>();
                            match (split.get(0), split.get(1)) {
                                (Some(key), Some(value)) =>
                                    Ok((key.trim().to_owned(),
                                         value.trim().to_owned())),
                                _ => Err(ProcError::new_more(ProcOper::Parsing, ProcFile::PidStatus,
                                             Some("No colon on line")))
                            }
                        },
                        Err(e) => Err(e)
                    }
                ).collect::<Result<_, _>>());

        // It's quite important that these appear in the order that they
        // appear in the status file
        Ok(PidStatus{
            name: extract_line!(status, "Name", |s| Ok(s) as Result<String, ProcError>),
            tgid: extract_line!(status, "Tgid", parse_any),
            pid: extract_line!(status, "Pid", parse_any),
            ppid: extract_line!(status, "PPid", parse_any),
            tracerpid: extract_line!(status, "TracerPid", parse_any),
            uid : extract_line!(status, "Uid", parse_uids),
            gid : extract_line!(status, "Gid", parse_uids),
            fdsize : extract_line!(status, "FDSize", parse_any),
            vmpeak: extract_line_opt!(status, "VmPeak", parse_mem),
            vmsize: extract_line_opt!(status, "VmSize", parse_mem),
            vmlck: extract_line_opt!(status, "VmLck", parse_mem),
            vmpin: extract_line_opt!(status, "VmPin", parse_mem),
            vmhwm: extract_line_opt!(status, "VmHWM", parse_mem),
            vmrss: extract_line_opt!(status, "VmRSS", parse_mem),
            vmdata: extract_line_opt!(status, "VmData", parse_mem),
            vmstk: extract_line_opt!(status, "VmStk", parse_mem),
            vmexe: extract_line_opt!(status, "VmExe", parse_mem),
            vmlib: extract_line_opt!(status, "VmLib", parse_mem),
            vmpmd: extract_line_opt!(status, "VmPMD", parse_mem),
            vmpte: extract_line_opt!(status, "VmPTE", parse_mem),
            vmswap: extract_line_opt!(status, "VmSwap", parse_mem),
            threads: extract_line!(status, "Threads", parse_any)
        })
    }
}

// Parse anything that's parsable (type checker didn't like simple closures).
fn parse_any<N: FromStr>(str: String) -> Result<N, N::Err> {
    str.parse()
}

fn parse_uids(uid_str: String) -> Result<(u32, u32, u32, u32), ProcError> {
    let uids = try!(
        uid_str.split("\t")
            .filter(|s| s != &"")
            .map(|s|
                s.parse()
            ).collect::<Result<Vec<_>, _>>()
            .map_err(|e|
                ProcError::new(ProcOper::ParsingField, ProcFile::PidStatus,
                    Some(e), Some("parsing uid"))
            )

    );
    if uids.len() != 4 {
        return Err(ProcError::new_more(ProcOper::ParsingField,
            ProcFile::PidStatus, Some("missing uids")));
    }
    Ok((uids[0], uids[1], uids[2], uids[3]))
}

fn parse_mem(mem_str: String) -> Result<MemSize, ParseIntError> {
    mem_str.trim_right_matches(" kB")
        .parse::<MemSize>()
        .map(|n| n * 1024)
}
<|endoftext|>


<commit_before>//! Tracks minimal sets of mutually incomparable elements of a partial order.

use std::fmt::Debug;
use std::default::Default;
use std::cmp::Ordering;

use progress::CountMap;

/// A set of mutually incomparable elements.
#[derive(Default, Clone, Debug)]
pub struct Antichain<T> {
    elements: Vec<T>
}

impl<T: PartialOrd+Eq+Copy+Debug> Antichain<T> {
    /// Updates the `Antichain` if the element is not greater than or equal to some present element.
    ///
    /// Returns true if element is added to the set
    pub fn insert(&mut self, element: T) -> bool {
        // bail if any element exceeds the candidate
        if self.elements.iter().any(|x| element.ge(x)) {
            return false;
        }
        let mut removed = 0;
        for index in 0..self.elements.len() {
            let new_index = index - removed;
            if element.lt(&self.elements[new_index]) {
                self.elements.swap_remove(new_index);
                removed += 1;
            }
        }

        self.elements.push(element);
        true
    }

    /// Creates a new empty `Antichain`.
    pub fn new() -> Antichain<T> { Antichain { elements: Vec::new() } }

    /// Creates a new singleton `Antichain`.
    pub fn from_elem(element: T) -> Antichain<T> { Antichain { elements: vec![element] } }

    /// Reveals the elements in the `Antichain`.
    pub fn elements(&self) -> &[T] { &self.elements[..] }
}

/// An antichain based on a multiset whose elements frequencies can be updated.
///
/// In particular, unlike `Antichain`, element frequencies can be decremented, removing elements in
/// the antichain and revealing other elements that were previously obscured.
/// As part of this, the `MutableAntichain` must maintain the frequences for all elements, not just
/// those in the antichain at the moment.
#[derive(Default, Debug, Clone)]
pub struct MutableAntichain<T:Eq> {
    occurrences:    CountMap<T>,    // occurrence count of each time
    precedents:     Vec<(T, i64)>,  // counts number of distinct times in occurences strictly less than element
    elements:       Vec<T>,         // the set of times with precedent count == 0
}

impl<T: PartialOrd+Eq+Clone+Debug+'static> MutableAntichain<T> {
    /// Creates a new empty `MutableAntichain`.
    pub fn new() -> MutableAntichain<T> {
        MutableAntichain {
            occurrences:    Default::default(),
            precedents:     Default::default(),
            elements:       Vec::new(),
        }
    }

    /// Removes all elements from the antichain.
    pub fn clear(&mut self) {
        self.occurrences.clear();
        self.precedents.clear();
        self.elements.clear();
    }

    /// Reveals the element in the `MutableAntichain`.
    pub fn elements(&self) -> &[T] { &self.elements }

    /// Creates a new singleton `MutableAntichain`.
    pub fn new_bottom(bottom: T) -> MutableAntichain<T> {
        let mut result = MutableAntichain::new();
        result.update_weight(&bottom, 1, &mut Default::default());
        result
    }

    /// Returns true if there are no elements in the `MutableAntichain`.
    pub fn empty(&self) -> bool { self.elements.is_empty() }

    /// Returns true if any item in the `MutableAntichain` is strictly less than the argument.
    #[inline]
    pub fn lt(&self, time: &T) -> bool {
        self.elements.iter().any(|x| x.lt(time))
    }

    /// Returns true if any item in the `MutableAntichain` is less than or equal to the argument.
    #[inline]
    pub fn le(&self, time: &T) -> bool {
        self.elements.iter().any(|x| x.le(time))
    }

    /// Returns the number of times an element exists in the set.
    #[inline]
    pub fn count(&self, time: &T) -> Option<i64> {
        self.occurrences.iter().filter(|x| time.eq(&x.0)).next().map(|i| i.1)
    }

    // TODO : Four different versions of basically the same code. Fix that!
    // TODO : Should this drain updates through to the CM? Check out uses!
    /// Incorporates `updates` into the antichain, pushing frontier changes into `results`.
    pub fn update_into_cm(&mut self, updates: &CountMap<T>, results: &mut CountMap<T>) -> () {
        self.update_iter_and(updates.iter().cloned(), |time, val| { results.update(time, val); });
    }

    /// Performs a single update to the antichain, pushing frontier changes into `results`.
    pub fn update_weight(&mut self, elem: &T, delta: i64, results: &mut CountMap<T>) -> () {
        self.update_and(elem, delta, |time, delta| { results.update(time, delta); });
    }

    /// Applies a single update to the antichain.
    #[inline] pub fn update(&mut self, elem: &T, delta: i64) { self.update_and(elem, delta, |_,_| {}); }

    /// Applies updates to the antichain and applies `action` to each frontier change.
    //#[inline(always)]
    pub fn update_iter_and<I: Iterator<Item = (T, i64)>,
                           A: FnMut(&T, i64) -> ()>(&mut self, updates: I, mut action: A) -> () {
        for (ref elem, delta) in updates {
            self.update_and(elem, delta, |t,d| action(t,d));
        }
    }

    /// Tests the size of the antichain against a threshould.
    ///
    /// This is a diagnostic method in place to observe when antichains become surprisingly large.
    /// If either `self.occurrences` or `self.precedents` exceeds the threshold, they are listed.
    #[inline]
    pub fn test_size(&self, threshold: usize, name: &str) {
        if self.occurrences.len() > threshold {
            println!("{}:\toccurrences:\tlen() = {}", name, self.occurrences.len());
            // for &(ref key, val) in self.occurrences.elements().iter() { println!("{}: \toccurrence: {:?} : {:?}", name, key, val); }
            // panic!();
        }
        if self.precedents.len() > threshold {
            println!("{}: precedents:\tlen() = {}", name, self.precedents.len());
        }
    }

    /// Applies an update to the antichain and takes an action on any frontier changes.
    #[inline]
    pub fn update_and<A: FnMut(&T, i64)->()>(&mut self, elem: &T, delta: i64, mut action: A) -> () {

        if delta != 0 {
            // self.test_size(100, "???");
            let new_value = self.occurrences.update(elem, delta);
            let old_value = new_value - delta;

            // if the value went from non-positive to positive we need to update self.precedents
            if old_value <= 0 && new_value > 0 {
                let mut preceded_by = 0;

                // maintain precedent counts relative to the set
                for &mut (ref key, ref mut val) in &mut self.precedents {
                    if let Some(comparison) = elem.partial_cmp(key) {
                        match comparison {
                            Ordering::Less    => {
                                if *val == 0 {
                                    self.elements.retain(|x| x != key);
                                    action(key, -1);
                                }
                                *val += 1;
                            },
                            Ordering::Equal   => { panic!("surprising!"); },
                            Ordering::Greater => { preceded_by += 1; },
                        }
                    }
                }

                // insert count always; maybe put in elements
                self.precedents.push((elem.clone(), preceded_by));
                if preceded_by == 0 {
                    self.elements.push(elem.clone());
                    action(elem, 1);
                }
            }

            // if the value went from positive to non-positive we need to update self.precedents.
            if old_value > 0 && new_value <= 0 {
                // maintain precedent counts relative to the set
                for &mut (ref key, ref mut val) in &mut self.precedents {
                    if elem < key {
                        *val -= 1;
                        if *val == 0 {
                            self.elements.push(key.clone());
                            action(key, 1);
                        }
                    }
                }

                if let Some(position) = self.elements.iter().position(|x| x == elem) {
                    action(elem, -1);
                    self.elements.swap_remove(position);
                }

                self.precedents.retain(|x| &x.0 != elem);
            }
        }
    }
}
<commit_msg>removed constraints for antichain<commit_after>//! Tracks minimal sets of mutually incomparable elements of a partial order.

use std::fmt::Debug;
use std::default::Default;
use std::cmp::Ordering;

use progress::CountMap;

/// A set of mutually incomparable elements.
#[derive(Default, Clone, Debug)]
pub struct Antichain<T> {
    elements: Vec<T>
}

impl<T: PartialOrd> Antichain<T> {
    /// Updates the `Antichain` if the element is not greater than or equal to some present element.
    ///
    /// Returns true if element is added to the set
    pub fn insert(&mut self, element: T) -> bool {
        // bail if any element exceeds the candidate
        if self.elements.iter().any(|x| element.ge(x)) {
            return false;
        }
        let mut removed = 0;
        for index in 0..self.elements.len() {
            let new_index = index - removed;
            if element.lt(&self.elements[new_index]) {
                self.elements.swap_remove(new_index);
                removed += 1;
            }
        }

        self.elements.push(element);
        true
    }

    /// Creates a new empty `Antichain`.
    pub fn new() -> Antichain<T> { Antichain { elements: Vec::new() } }

    /// Creates a new singleton `Antichain`.
    pub fn from_elem(element: T) -> Antichain<T> { Antichain { elements: vec![element] } }

    /// Reveals the elements in the `Antichain`.
    pub fn elements(&self) -> &[T] { &self.elements[..] }
}

/// An antichain based on a multiset whose elements frequencies can be updated.
///
/// In particular, unlike `Antichain`, element frequencies can be decremented, removing elements in
/// the antichain and revealing other elements that were previously obscured.
/// As part of this, the `MutableAntichain` must maintain the frequences for all elements, not just
/// those in the antichain at the moment.
#[derive(Default, Debug, Clone)]
pub struct MutableAntichain<T:Eq> {
    occurrences:    CountMap<T>,    // occurrence count of each time
    precedents:     Vec<(T, i64)>,  // counts number of distinct times in occurences strictly less than element
    elements:       Vec<T>,         // the set of times with precedent count == 0
}

impl<T: PartialOrd+Eq+Clone+Debug+'static> MutableAntichain<T> {
    /// Creates a new empty `MutableAntichain`.
    pub fn new() -> MutableAntichain<T> {
        MutableAntichain {
            occurrences:    Default::default(),
            precedents:     Default::default(),
            elements:       Vec::new(),
        }
    }

    /// Removes all elements from the antichain.
    pub fn clear(&mut self) {
        self.occurrences.clear();
        self.precedents.clear();
        self.elements.clear();
    }

    /// Reveals the element in the `MutableAntichain`.
    pub fn elements(&self) -> &[T] { &self.elements }

    /// Creates a new singleton `MutableAntichain`.
    pub fn new_bottom(bottom: T) -> MutableAntichain<T> {
        let mut result = MutableAntichain::new();
        result.update_weight(&bottom, 1, &mut Default::default());
        result
    }

    /// Returns true if there are no elements in the `MutableAntichain`.
    pub fn empty(&self) -> bool { self.elements.is_empty() }

    /// Returns true if any item in the `MutableAntichain` is strictly less than the argument.
    #[inline]
    pub fn lt(&self, time: &T) -> bool {
        self.elements.iter().any(|x| x.lt(time))
    }

    /// Returns true if any item in the `MutableAntichain` is less than or equal to the argument.
    #[inline]
    pub fn le(&self, time: &T) -> bool {
        self.elements.iter().any(|x| x.le(time))
    }

    /// Returns the number of times an element exists in the set.
    #[inline]
    pub fn count(&self, time: &T) -> Option<i64> {
        self.occurrences.iter().filter(|x| time.eq(&x.0)).next().map(|i| i.1)
    }

    // TODO : Four different versions of basically the same code. Fix that!
    // TODO : Should this drain updates through to the CM? Check out uses!
    /// Incorporates `updates` into the antichain, pushing frontier changes into `results`.
    pub fn update_into_cm(&mut self, updates: &CountMap<T>, results: &mut CountMap<T>) -> () {
        self.update_iter_and(updates.iter().cloned(), |time, val| { results.update(time, val); });
    }

    /// Performs a single update to the antichain, pushing frontier changes into `results`.
    pub fn update_weight(&mut self, elem: &T, delta: i64, results: &mut CountMap<T>) -> () {
        self.update_and(elem, delta, |time, delta| { results.update(time, delta); });
    }

    /// Applies a single update to the antichain.
    #[inline] pub fn update(&mut self, elem: &T, delta: i64) { self.update_and(elem, delta, |_,_| {}); }

    /// Applies updates to the antichain and applies `action` to each frontier change.
    //#[inline(always)]
    pub fn update_iter_and<I: Iterator<Item = (T, i64)>,
                           A: FnMut(&T, i64) -> ()>(&mut self, updates: I, mut action: A) -> () {
        for (ref elem, delta) in updates {
            self.update_and(elem, delta, |t,d| action(t,d));
        }
    }

    /// Tests the size of the antichain against a threshould.
    ///
    /// This is a diagnostic method in place to observe when antichains become surprisingly large.
    /// If either `self.occurrences` or `self.precedents` exceeds the threshold, they are listed.
    #[inline]
    pub fn test_size(&self, threshold: usize, name: &str) {
        if self.occurrences.len() > threshold {
            println!("{}:\toccurrences:\tlen() = {}", name, self.occurrences.len());
            // for &(ref key, val) in self.occurrences.elements().iter() { println!("{}: \toccurrence: {:?} : {:?}", name, key, val); }
            // panic!();
        }
        if self.precedents.len() > threshold {
            println!("{}: precedents:\tlen() = {}", name, self.precedents.len());
        }
    }

    /// Applies an update to the antichain and takes an action on any frontier changes.
    #[inline]
    pub fn update_and<A: FnMut(&T, i64)->()>(&mut self, elem: &T, delta: i64, mut action: A) -> () {

        if delta != 0 {
            // self.test_size(100, "???");
            let new_value = self.occurrences.update(elem, delta);
            let old_value = new_value - delta;

            // if the value went from non-positive to positive we need to update self.precedents
            if old_value <= 0 && new_value > 0 {
                let mut preceded_by = 0;

                // maintain precedent counts relative to the set
                for &mut (ref key, ref mut val) in &mut self.precedents {
                    if let Some(comparison) = elem.partial_cmp(key) {
                        match comparison {
                            Ordering::Less    => {
                                if *val == 0 {
                                    self.elements.retain(|x| x != key);
                                    action(key, -1);
                                }
                                *val += 1;
                            },
                            Ordering::Equal   => { panic!("surprising!"); },
                            Ordering::Greater => { preceded_by += 1; },
                        }
                    }
                }

                // insert count always; maybe put in elements
                self.precedents.push((elem.clone(), preceded_by));
                if preceded_by == 0 {
                    self.elements.push(elem.clone());
                    action(elem, 1);
                }
            }

            // if the value went from positive to non-positive we need to update self.precedents.
            if old_value > 0 && new_value <= 0 {
                // maintain precedent counts relative to the set
                for &mut (ref key, ref mut val) in &mut self.precedents {
                    if elem < key {
                        *val -= 1;
                        if *val == 0 {
                            self.elements.push(key.clone());
                            action(key, 1);
                        }
                    }
                }

                if let Some(position) = self.elements.iter().position(|x| x == elem) {
                    action(elem, -1);
                    self.elements.swap_remove(position);
                }

                self.precedents.retain(|x| &x.0 != elem);
            }
        }
    }
}
<|endoftext|>


<commit_before>use std::fs::File;
use std::io::Read;
use std::io::Write;
use std::io::{Error, ErrorKind};
use std::path::{Path, PathBuf};
use std::string::String;

const MAX_BRIGHTNESS_BASENAME: &str = "max_brightness";
const BRIGHTNESS_BASENAME: &str = "brightness";

// Defines the apparent path to the directory containing both
// the brightness and the max_brightness files.
const BRIGHTNESS_CONTAINING_DIR: &str = "/sys/class/backlight/amdgpu_bl0/";

// Reads the file named by |path| and returns the integral contents.
fn sysfs_file_to_int(path: &PathBuf) -> std::result::Result<i32, Error> {
    let mut file = File::open(path)?;
    let mut raw_contents = String::new();
    file.read_to_string(&mut raw_contents)?;

    let contents = raw_contents.trim();

    match contents.parse::<i32>() {
        Ok(value) => Ok(value),
        Err(why) => Err(Error::new(
            ErrorKind::InvalidInput,
            format!("parse failure ({:#?}): {}", path, why),
        )),
    }
}

// Stringifies and writes |value| into file named by |path|.
fn write_int(path: &PathBuf, value: i32) -> std::io::Result<()> {
    let mut file = File::create(&path)?;
    file.write_all(&value.to_string().as_bytes())?;
    file.flush()
}

// Reads the current and max brightness; returns the new target
// brightness.
fn target_brightness(current: i32, max: i32) -> Result<i32, std::io::Error> {
    let args: Vec<String> = std::env::args().collect();
    if args.len() < 2 {
        return Err(Error::new(ErrorKind::InvalidInput, "missing delta arg"));
    }
    // We expect a integral value, like ``-312'' or ``520.''
    let delta_as_string = &args[1];
    let delta = delta_as_string.parse::<i32>();

    if !delta.is_ok() {
        return Err(Error::new(
            ErrorKind::InvalidData,
            format!("bad arg: {}", delta_as_string),
        ));
    }

    let tentative = current + delta.unwrap();
    if tentative >= max {
        return Ok(max);
    } else if tentative < 0 {
        // For kicks, set the brightness all the way up if caller is
        // trying to take it below 0.
        if current == 0 {
            return Ok(max);
        }
        return Ok(0);
    }
    Ok(tentative)
}

fn main() -> Result<(), std::io::Error> {
    let base_dir = Path::new(BRIGHTNESS_CONTAINING_DIR);
    let brightness_path: PathBuf = base_dir.join(BRIGHTNESS_BASENAME);

    let current = sysfs_file_to_int(&brightness_path)?;
    let max = sysfs_file_to_int(&base_dir.join(MAX_BRIGHTNESS_BASENAME))?;

    let new = target_brightness(current, max)?;
    write_int(&brightness_path, new)
}
<commit_msg>avocado-attenborough: simplify error handling<commit_after>use std::fs::File;
use std::io::Read;
use std::io::Write;
use std::io::{Error, ErrorKind};
use std::path::{Path, PathBuf};
use std::string::String;

const MAX_BRIGHTNESS_BASENAME: &str = "max_brightness";
const BRIGHTNESS_BASENAME: &str = "brightness";

// Defines the apparent path to the directory containing both
// the brightness and the max_brightness files.
const BRIGHTNESS_CONTAINING_DIR: &str = "/sys/class/backlight/amdgpu_bl0/";

// Reads the file named by |path| and returns the integral contents.
fn sysfs_file_to_int(path: &PathBuf) -> std::result::Result<i32, Error> {
    let mut file = File::open(path)?;
    let mut raw_contents = String::new();
    file.read_to_string(&mut raw_contents)?;

    let contents = raw_contents.trim();

    match contents.parse::<i32>() {
        Ok(value) => Ok(value),
        Err(why) => Err(Error::new(
            ErrorKind::InvalidInput,
            format!("parse failure ({:#?}): {}", path, why),
        )),
    }
}

// Stringifies and writes |value| into file named by |path|.
fn write_int(path: &PathBuf, value: i32) -> std::io::Result<()> {
    let mut file = File::create(&path)?;
    file.write_all(&value.to_string().as_bytes())?;
    file.flush()
}

fn get_brightness_delta() -> i32 {
    let args: Vec<String> = std::env::args().collect();

    // We expect a integral value, like ``-312'' or ``520.''
    let delta_as_string = &args[1];
    let delta = delta_as_string.parse::<i32>();

    return delta.unwrap();
}

// Reads the current and max brightness; returns the new target
// brightness.
fn get_target_brightness(current: i32, max: i32) -> i32 {
    let tentative = current + get_brightness_delta();
    if tentative >= max {
        return max;
    } else if tentative < 0 {
        if current == 0 {
            return max;
        }
        return 0;
    }
    tentative
}

fn main() -> Result<(), std::io::Error> {
    let base_dir = Path::new(BRIGHTNESS_CONTAINING_DIR);
    let brightness_path: PathBuf = base_dir.join(BRIGHTNESS_BASENAME);

    let current = sysfs_file_to_int(&brightness_path)?;
    let max = sysfs_file_to_int(&base_dir.join(MAX_BRIGHTNESS_BASENAME))?;

    let new = get_target_brightness(current, max);
    write_int(&brightness_path, new)
}
<|endoftext|>


<commit_before>import std::option;
import std::getopts;
import std::test;
import std::fs;
import std::str;
import std::vec;
import std::task;

import std::comm;
import std::comm::port;
import std::comm::chan;
import std::comm::send;
import std::comm::recv;

import common::cx;
import common::config;
import common::mode_run_pass;
import common::mode_run_fail;
import common::mode_compile_fail;
import common::mode_pretty;
import common::mode;
import util::logv;

fn main(args: [str]) {
    let config = parse_config(args);
    log_config(config);
    run_tests(config);
}

fn parse_config(args: &[str]) -> config {
    let opts =
        [getopts::reqopt("compile-lib-path"), getopts::reqopt("run-lib-path"),
         getopts::reqopt("rustc-path"), getopts::reqopt("src-base"),
         getopts::reqopt("build-base"), getopts::reqopt("stage-id"),
         getopts::reqopt("mode"), getopts::optflag("ignored"),
         getopts::optopt("runtool"), getopts::optopt("rustcflags"),
         getopts::optflag("verbose")];

    check (vec::is_not_empty(args));
    let args_ = vec::tail(args);
    let match =
        alt getopts::getopts(args_, opts) {
          getopts::success(m) { m }
          getopts::failure(f) { fail getopts::fail_str(f) }
        };

    ret {compile_lib_path: getopts::opt_str(match, "compile-lib-path"),
         run_lib_path: getopts::opt_str(match, "run-lib-path"),
         rustc_path: getopts::opt_str(match, "rustc-path"),
         src_base: getopts::opt_str(match, "src-base"),
         build_base: getopts::opt_str(match, "build-base"),
         stage_id: getopts::opt_str(match, "stage-id"),
         mode: str_mode(getopts::opt_str(match, "mode")),
         run_ignored: getopts::opt_present(match, "ignored"),
         filter:
             if vec::len(match.free) > 0u {
                 option::some(match.free[0])
             } else { option::none },
         runtool: getopts::opt_maybe_str(match, "runtool"),
         rustcflags: getopts::opt_maybe_str(match, "rustcflags"),
         verbose: getopts::opt_present(match, "verbose")};
}

fn log_config(config: &config) {
    let c = config;
    logv(c, #fmt["configuration:"]);
    logv(c, #fmt["compile_lib_path: %s", config.compile_lib_path]);
    logv(c, #fmt["run_lib_path: %s", config.run_lib_path]);
    logv(c, #fmt["rustc_path: %s", config.rustc_path]);
    logv(c, #fmt["src_base: %s", config.src_base]);
    logv(c, #fmt["build_base: %s", config.build_base]);
    logv(c, #fmt["stage_id: %s", config.stage_id]);
    logv(c, #fmt["mode: %s", mode_str(config.mode)]);
    logv(c, #fmt["run_ignored: %b", config.run_ignored]);
    logv(c, #fmt["filter: %s", opt_str(config.filter)]);
    logv(c, #fmt["runtool: %s", opt_str(config.runtool)]);
    logv(c, #fmt["rustcflags: %s", opt_str(config.rustcflags)]);
    logv(c, #fmt["verbose: %b", config.verbose]);
    logv(c, #fmt["\n"]);
}

fn opt_str(maybestr: option::t<str>) -> str {
    alt maybestr { option::some(s) { s } option::none. { "(none)" } }
}

fn str_opt(maybestr: &str) -> option::t<str> {
    if maybestr != "(none)" { option::some(maybestr) } else { option::none }
}

fn str_mode(s: &str) -> mode {
    alt s {
      "compile-fail" { mode_compile_fail }
      "run-fail" { mode_run_fail }
      "run-pass" { mode_run_pass }
      "pretty" { mode_pretty }
      _ { fail "invalid mode" }
    }
}

fn mode_str(mode: mode) -> str {
    alt mode {
      mode_compile_fail. { "compile-fail" }
      mode_run_fail. { "run-fail" }
      mode_run_pass. { "run-pass" }
      mode_pretty. { "pretty" }
    }
}

fn run_tests(config: &config) {
    let opts = test_opts(config);
    let cx = {config: config, procsrv: procsrv::mk()};
    let tests = make_tests(cx);
    test::run_tests_console_(opts, tests.tests, tests.to_task);
    procsrv::close(cx.procsrv);
}

fn test_opts(config: &config) -> test::test_opts {
    {filter:
         alt config.filter {
           option::some(s) { option::some(s) }
           option::none. { option::none }
         },
     run_ignored: config.run_ignored}
}

type tests_and_conv_fn =
    {tests: [test::test_desc], to_task: fn(&fn()) -> test::joinable};

fn make_tests(cx: &cx) -> tests_and_conv_fn {
    log #fmt["making tests from %s", cx.config.src_base];
    let configport = port::<[u8]>();
    let tests = [];
    for file: str in fs::list_dir(cx.config.src_base) {
        let file = file;
        log #fmt["inspecting file %s", file];
        if is_test(cx.config, file) {
            tests += [make_test(cx, file, configport)];
        }
    }
    ret {tests: tests, to_task: bind closure_to_task(cx, configport, _)};
}

fn is_test(config: &config, testfile: &str) -> bool {
    // Pretty-printer does not work with .rc files yet
    let valid_extensions =
        alt config.mode { mode_pretty. { [".rs"] } _ { [".rc", ".rs"] } };
    let invalid_prefixes = [".", "#", "~"];
    let name = fs::basename(testfile);

    let valid = false;

    for ext in valid_extensions {
        if str::ends_with(name, ext) { valid = true }
    }

    for pre in invalid_prefixes {
        if str::starts_with(name, pre) { valid = false }
    }

    ret valid;
}

fn make_test(cx: &cx, testfile: &str, configport: &port<[u8]>) ->
   test::test_desc {
    {name: make_test_name(cx.config, testfile),
     fn: make_test_closure(testfile, chan(configport)),
     ignore: header::is_test_ignored(cx.config, testfile)}
}

fn make_test_name(config: &config, testfile: &str) -> str {
    #fmt["[%s] %s", mode_str(config.mode), testfile]
}

/*
So this is kind of crappy:

A test is just defined as a function, as you might expect, but tests have to
run in their own tasks. Unfortunately, if your test needs dynamic data then it
needs to be a closure, and transferring closures across tasks without
committing a host of memory management transgressions is just impossible.

To get around this, the standard test runner allows you the opportunity do
your own conversion from a test function to a task. It gives you your function
and you give it back a task.

So that's what we're going to do. Here's where it gets stupid. To get the
the data out of the test function we are going to run the test function,
which will do nothing but send the data for that test to a port we've set
up. Then we'll spawn that data into another task and return the task.
Really convoluted. Need to think up of a better definition for tests.
*/

fn make_test_closure(testfile: &str, configchan: chan<[u8]>) ->
   test::test_fn {
    bind send_config(testfile, configchan)
}

fn send_config(testfile: str, configchan: chan<[u8]>) {
    send(configchan, str::bytes(testfile));
}

/*
FIXME: Good god forgive me.

So actually shuttling structural data across tasks isn't possible at this
time, but we can send strings! Sadly, I need the whole config record, in the
test task so, instead of fixing the mechanism in the compiler I'm going to
break up the config record and pass everything individually to the spawned
function.
*/

fn closure_to_task(cx: cx, configport: port<[u8]>, testfn: &fn()) ->
   test::joinable {
    testfn();
    let testfile = recv(configport);

    let compile_lib_path = cx.config.compile_lib_path;
    let run_lib_path = cx.config.run_lib_path;
    let rustc_path = cx.config.rustc_path;
    let src_base = cx.config.src_base;
    let build_base = cx.config.build_base;
    let stage_id = cx.config.stage_id;
    let mode = mode_str(cx.config.mode);
    let run_ignored = cx.config.run_ignored;
    let filter = opt_str(cx.config.filter);
    let runtool = opt_str(cx.config.runtool);
    let rustcflags = opt_str(cx.config.rustcflags);
    let verbose = cx.config.verbose;
    let chan = cx.procsrv.chan;

    let testthunk =
        bind run_test_task(compile_lib_path, run_lib_path, rustc_path,
                           src_base, build_base, stage_id, mode, run_ignored,
                           filter, runtool, rustcflags, verbose, chan,
                           testfile);
    ret task::spawn_joinable(testthunk);
}

fn run_test_task(compile_lib_path: -str, run_lib_path: -str, rustc_path: -str,
                 src_base: -str, build_base: -str, stage_id: -str, mode: -str,
                 run_ignored: -bool, opt_filter: -str, opt_runtool: -str,
                 opt_rustcflags: -str, verbose: -bool,
                 procsrv_chan: -procsrv::reqchan, testfile: -[u8]) {

    test::configure_test_task();

    let config =
        {compile_lib_path: compile_lib_path,
         run_lib_path: run_lib_path,
         rustc_path: rustc_path,
         src_base: src_base,
         build_base: build_base,
         stage_id: stage_id,
         mode: str_mode(mode),
         run_ignored: run_ignored,
         filter: str_opt(opt_filter),
         runtool: str_opt(opt_runtool),
         rustcflags: str_opt(opt_rustcflags),
         verbose: verbose};

    let procsrv = procsrv::from_chan(procsrv_chan);

    let cx = {config: config, procsrv: procsrv};

    runtest::run(cx, testfile);
}

// Local Variables:
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
// End:
<commit_msg>Actually fail the process when compiletest has a test failure<commit_after>import std::option;
import std::getopts;
import std::test;
import std::fs;
import std::str;
import std::vec;
import std::task;

import std::comm;
import std::comm::port;
import std::comm::chan;
import std::comm::send;
import std::comm::recv;

import common::cx;
import common::config;
import common::mode_run_pass;
import common::mode_run_fail;
import common::mode_compile_fail;
import common::mode_pretty;
import common::mode;
import util::logv;

fn main(args: [str]) {
    let config = parse_config(args);
    log_config(config);
    run_tests(config);
}

fn parse_config(args: &[str]) -> config {
    let opts =
        [getopts::reqopt("compile-lib-path"), getopts::reqopt("run-lib-path"),
         getopts::reqopt("rustc-path"), getopts::reqopt("src-base"),
         getopts::reqopt("build-base"), getopts::reqopt("stage-id"),
         getopts::reqopt("mode"), getopts::optflag("ignored"),
         getopts::optopt("runtool"), getopts::optopt("rustcflags"),
         getopts::optflag("verbose")];

    check (vec::is_not_empty(args));
    let args_ = vec::tail(args);
    let match =
        alt getopts::getopts(args_, opts) {
          getopts::success(m) { m }
          getopts::failure(f) { fail getopts::fail_str(f) }
        };

    ret {compile_lib_path: getopts::opt_str(match, "compile-lib-path"),
         run_lib_path: getopts::opt_str(match, "run-lib-path"),
         rustc_path: getopts::opt_str(match, "rustc-path"),
         src_base: getopts::opt_str(match, "src-base"),
         build_base: getopts::opt_str(match, "build-base"),
         stage_id: getopts::opt_str(match, "stage-id"),
         mode: str_mode(getopts::opt_str(match, "mode")),
         run_ignored: getopts::opt_present(match, "ignored"),
         filter:
             if vec::len(match.free) > 0u {
                 option::some(match.free[0])
             } else { option::none },
         runtool: getopts::opt_maybe_str(match, "runtool"),
         rustcflags: getopts::opt_maybe_str(match, "rustcflags"),
         verbose: getopts::opt_present(match, "verbose")};
}

fn log_config(config: &config) {
    let c = config;
    logv(c, #fmt["configuration:"]);
    logv(c, #fmt["compile_lib_path: %s", config.compile_lib_path]);
    logv(c, #fmt["run_lib_path: %s", config.run_lib_path]);
    logv(c, #fmt["rustc_path: %s", config.rustc_path]);
    logv(c, #fmt["src_base: %s", config.src_base]);
    logv(c, #fmt["build_base: %s", config.build_base]);
    logv(c, #fmt["stage_id: %s", config.stage_id]);
    logv(c, #fmt["mode: %s", mode_str(config.mode)]);
    logv(c, #fmt["run_ignored: %b", config.run_ignored]);
    logv(c, #fmt["filter: %s", opt_str(config.filter)]);
    logv(c, #fmt["runtool: %s", opt_str(config.runtool)]);
    logv(c, #fmt["rustcflags: %s", opt_str(config.rustcflags)]);
    logv(c, #fmt["verbose: %b", config.verbose]);
    logv(c, #fmt["\n"]);
}

fn opt_str(maybestr: option::t<str>) -> str {
    alt maybestr { option::some(s) { s } option::none. { "(none)" } }
}

fn str_opt(maybestr: &str) -> option::t<str> {
    if maybestr != "(none)" { option::some(maybestr) } else { option::none }
}

fn str_mode(s: &str) -> mode {
    alt s {
      "compile-fail" { mode_compile_fail }
      "run-fail" { mode_run_fail }
      "run-pass" { mode_run_pass }
      "pretty" { mode_pretty }
      _ { fail "invalid mode" }
    }
}

fn mode_str(mode: mode) -> str {
    alt mode {
      mode_compile_fail. { "compile-fail" }
      mode_run_fail. { "run-fail" }
      mode_run_pass. { "run-pass" }
      mode_pretty. { "pretty" }
    }
}

fn run_tests(config: &config) {
    let opts = test_opts(config);
    let cx = {config: config, procsrv: procsrv::mk()};
    let tests = make_tests(cx);
    let res = test::run_tests_console_(opts, tests.tests, tests.to_task);
    procsrv::close(cx.procsrv);
    if !res { fail "Some tests failed"; }
}

fn test_opts(config: &config) -> test::test_opts {
    {filter:
         alt config.filter {
           option::some(s) { option::some(s) }
           option::none. { option::none }
         },
     run_ignored: config.run_ignored}
}

type tests_and_conv_fn =
    {tests: [test::test_desc], to_task: fn(&fn()) -> test::joinable};

fn make_tests(cx: &cx) -> tests_and_conv_fn {
    log #fmt["making tests from %s", cx.config.src_base];
    let configport = port::<[u8]>();
    let tests = [];
    for file: str in fs::list_dir(cx.config.src_base) {
        let file = file;
        log #fmt["inspecting file %s", file];
        if is_test(cx.config, file) {
            tests += [make_test(cx, file, configport)];
        }
    }
    ret {tests: tests, to_task: bind closure_to_task(cx, configport, _)};
}

fn is_test(config: &config, testfile: &str) -> bool {
    // Pretty-printer does not work with .rc files yet
    let valid_extensions =
        alt config.mode { mode_pretty. { [".rs"] } _ { [".rc", ".rs"] } };
    let invalid_prefixes = [".", "#", "~"];
    let name = fs::basename(testfile);

    let valid = false;

    for ext in valid_extensions {
        if str::ends_with(name, ext) { valid = true }
    }

    for pre in invalid_prefixes {
        if str::starts_with(name, pre) { valid = false }
    }

    ret valid;
}

fn make_test(cx: &cx, testfile: &str, configport: &port<[u8]>) ->
   test::test_desc {
    {name: make_test_name(cx.config, testfile),
     fn: make_test_closure(testfile, chan(configport)),
     ignore: header::is_test_ignored(cx.config, testfile)}
}

fn make_test_name(config: &config, testfile: &str) -> str {
    #fmt["[%s] %s", mode_str(config.mode), testfile]
}

/*
So this is kind of crappy:

A test is just defined as a function, as you might expect, but tests have to
run in their own tasks. Unfortunately, if your test needs dynamic data then it
needs to be a closure, and transferring closures across tasks without
committing a host of memory management transgressions is just impossible.

To get around this, the standard test runner allows you the opportunity do
your own conversion from a test function to a task. It gives you your function
and you give it back a task.

So that's what we're going to do. Here's where it gets stupid. To get the
the data out of the test function we are going to run the test function,
which will do nothing but send the data for that test to a port we've set
up. Then we'll spawn that data into another task and return the task.
Really convoluted. Need to think up of a better definition for tests.
*/

fn make_test_closure(testfile: &str, configchan: chan<[u8]>) ->
   test::test_fn {
    bind send_config(testfile, configchan)
}

fn send_config(testfile: str, configchan: chan<[u8]>) {
    send(configchan, str::bytes(testfile));
}

/*
FIXME: Good god forgive me.

So actually shuttling structural data across tasks isn't possible at this
time, but we can send strings! Sadly, I need the whole config record, in the
test task so, instead of fixing the mechanism in the compiler I'm going to
break up the config record and pass everything individually to the spawned
function.
*/

fn closure_to_task(cx: cx, configport: port<[u8]>, testfn: &fn()) ->
   test::joinable {
    testfn();
    let testfile = recv(configport);

    let compile_lib_path = cx.config.compile_lib_path;
    let run_lib_path = cx.config.run_lib_path;
    let rustc_path = cx.config.rustc_path;
    let src_base = cx.config.src_base;
    let build_base = cx.config.build_base;
    let stage_id = cx.config.stage_id;
    let mode = mode_str(cx.config.mode);
    let run_ignored = cx.config.run_ignored;
    let filter = opt_str(cx.config.filter);
    let runtool = opt_str(cx.config.runtool);
    let rustcflags = opt_str(cx.config.rustcflags);
    let verbose = cx.config.verbose;
    let chan = cx.procsrv.chan;

    let testthunk =
        bind run_test_task(compile_lib_path, run_lib_path, rustc_path,
                           src_base, build_base, stage_id, mode, run_ignored,
                           filter, runtool, rustcflags, verbose, chan,
                           testfile);
    ret task::spawn_joinable(testthunk);
}

fn run_test_task(compile_lib_path: -str, run_lib_path: -str, rustc_path: -str,
                 src_base: -str, build_base: -str, stage_id: -str, mode: -str,
                 run_ignored: -bool, opt_filter: -str, opt_runtool: -str,
                 opt_rustcflags: -str, verbose: -bool,
                 procsrv_chan: -procsrv::reqchan, testfile: -[u8]) {

    test::configure_test_task();

    let config =
        {compile_lib_path: compile_lib_path,
         run_lib_path: run_lib_path,
         rustc_path: rustc_path,
         src_base: src_base,
         build_base: build_base,
         stage_id: stage_id,
         mode: str_mode(mode),
         run_ignored: run_ignored,
         filter: str_opt(opt_filter),
         runtool: str_opt(opt_runtool),
         rustcflags: str_opt(opt_rustcflags),
         verbose: verbose};

    let procsrv = procsrv::from_chan(procsrv_chan);

    let cx = {config: config, procsrv: procsrv};

    runtest::run(cx, testfile);
}

// Local Variables:
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
// End:
<|endoftext|>