<commit_before>use std::iter::Peekable;

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum PathToken {
    Root,
    Field(String),
    Index(usize),
    Star,
    StarIndex
}

fn peek<I>(chars: &mut Peekable<I>) -> Option<(usize, char)> where I: Iterator<Item = (usize, char)> {
    chars.peek().map(|tup| (tup.0.clone(), tup.1.clone()))
}

// identifier -> a-zA-Z0-9+
fn identifier<I>(ch: char, chars: &mut Peekable<I>, tokens: &mut Vec<PathToken>, path: &String)-> Result<(), String>
    where I: Iterator<Item = (usize, char)>  {
    let mut id = String::new();
    id.push(ch);
    let mut next_char = peek(chars);
    while next_char.is_some() {
        let ch = next_char.unwrap();
        if ch.1.is_alphabetic() || ch.1.is_numeric() || ch.1 == '_' || ch.1 == '-' {
            chars.next();
            id.push(ch.1);
        } else if ch.1 == '.' || ch.1 == '\'' || ch.1 == '[' {
            break;
        } else {
            return Err(format!("\"{}\" is not allowed in an identifier in path expression \"{}\" at index {}",
                ch.1, path, ch.0));
        }
        next_char = peek(chars);
    }
    tokens.push(PathToken::Field(id));
    Ok(())
}

// path_identifier -> identifier | *
fn path_identifier<I>(chars: &mut Peekable<I>, tokens: &mut Vec<PathToken>, path: &String, index: usize)-> Result<(), String>
    where I: Iterator<Item = (usize, char)>  {
    match chars.next() {
        Some(ch) => match ch.1 {
            '*' => {
                tokens.push(PathToken::Star);
                Ok(())
            },
            c if c.is_alphabetic() || c.is_numeric() => {
              identifier(c, chars, tokens, path)?;
              Ok(())
            },
            _ => Err(format!("Expected either a \"*\" or path identifier in path expression \"{}\" at index {}",
                path, ch.0))
        },
        None => Err(format!("Expected a path after \".\" in path expression \"{}\" at index {}",
            path, index))
    }
}

// string_path -> [^']+
fn string_path<I>(chars: &mut Peekable<I>, tokens: &mut Vec<PathToken>, path: &String, index: usize)-> Result<(), String>
    where I: Iterator<Item = (usize, char)>  {
    let mut id = String::new();
    let mut next_char = peek(chars);
    if next_char.is_some() {
        chars.next();
        let mut ch = next_char.unwrap();
        next_char = peek(chars);
        while ch.1 != '\'' && next_char.is_some() {
            id.push(ch.1);
            chars.next();
            ch = next_char.unwrap();
            next_char = peek(chars);
        }
        if ch.1 == '\'' {
            if id.is_empty() {
                Err(format!("Empty strings are not allowed in path expression \"{}\" at index {}", path, ch.0))
            } else {
                tokens.push(PathToken::Field(id));
                Ok(())
            }
        } else {
            Err(format!("Unterminated string in path expression \"{}\" at index {}", path, ch.0))
        }
    } else {
        Err(format!("Unterminated string in path expression \"{}\" at index {}", path, index))
    }
}

// index_path -> [0-9]+
fn index_path<I>(chars: &mut Peekable<I>, tokens: &mut Vec<PathToken>, path: &String)-> Result<(), String>
    where I: Iterator<Item = (usize, char)>  {
    let mut id = String::new();
    let mut next_char = chars.next();
    id.push(next_char.unwrap().1);
    next_char = peek(chars);
    while next_char.is_some() {
        let ch = next_char.unwrap();
        if ch.1.is_numeric() {
            id.push(ch.1);
            chars.next();
        } else {
            break;
        }
        next_char = peek(chars);
    }

    if let Some(ch) = next_char {
        if ch.1 != ']' {
            return Err(format!("Indexes can only consist of numbers or a \"*\", found \"{}\" instead in path expression \"{}\" at index {}",
                ch.1, path, ch.0))
        }
    }

    tokens.push(PathToken::Index(id.parse().unwrap()));
    Ok(())
}

// bracket_path -> (string_path | index | *) ]
fn bracket_path<I>(chars: &mut Peekable<I>, tokens: &mut Vec<PathToken>, path: &String, index: usize)-> Result<(), String>
    where I: Iterator<Item = (usize, char)>  {
    let mut ch = peek(chars);
    match ch {
        Some(c) => {
            if c.1 == '\'' {
              chars.next();
              string_path(chars, tokens, path, c.0)?
            } else if c.1.is_numeric() {
              index_path(chars, tokens, path)?
            } else if c.1 == '*' {
                chars.next();
                tokens.push(PathToken::StarIndex);
            } else if c.1 == ']' {
                return Err(format!("Empty bracket expressions are not allowed in path expression \"{}\" at index {}",
                    path, c.0));
            } else {
                return Err(format!("Indexes can only consist of numbers or a \"*\", found \"{}\" instead in path expression \"{}\" at index {}",
                    c.1, path, c.0));
            };
            ch = peek(chars);
            match ch {
                Some(c) => if c.1 != ']' {
                    Err(format!("Unterminated brackets, found \"{}\" instead of \"]\" in path expression \"{}\" at index {}",
                        c.1, path, c.0))
                } else {
                    chars.next();
                    Ok(())
                },
                None => Err(format!("Unterminated brackets in path expression \"{}\" at index {}",
                    path, path.len() - 1))
            }
        },
        None => Err(format!("Expected a \"'\" (single qoute) or a digit in path expression \"{}\" after index {}",
            path, index))
    }
}

// path_exp -> (dot-path | bracket-path)*
fn path_exp<I>(chars: &mut Peekable<I>, tokens: &mut Vec<PathToken>, path: &String)-> Result<(), String>
    where I: Iterator<Item = (usize, char)> {
    let mut next_char = chars.next();
    while next_char.is_some() {
        let ch = next_char.unwrap();
        match ch.1 {
            '.' => path_identifier(chars, tokens, path, ch.0)?,
            '[' => bracket_path(chars, tokens, path, ch.0)?,
            _ => return Err(format!("Expected a \".\" or \"[\" instead of \"{}\" in path expression \"{}\" at index {}",
                ch.1, path, ch.0))
        }
        next_char = chars.next();
    }
    Ok(())
}

pub fn parse_path_exp(path: String) -> Result<Vec<PathToken>, String> {
    let mut tokens = vec![];

    // parse_path_exp -> $ path_exp | empty
    let mut chars = path.chars().enumerate().peekable();
    match chars.next() {
      Some(ch) => {
        match ch.1 {
          '$' => {
            tokens.push(PathToken::Root);
            path_exp(&mut chars, &mut tokens, &path)?;
            Ok(tokens)
          },
          c if c.is_alphabetic() || c.is_numeric() => {
            tokens.push(PathToken::Root);
            identifier(c, &mut chars, &mut tokens, &path)?;
            path_exp(&mut chars, &mut tokens, &path)?;
            Ok(tokens)
          },
          _ => Err(format ! ("Path expression \"{}\" does not start with a root marker \"$\"", path))
        }
      },
      None => Ok(tokens)
    }
}

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

    #[test]
    fn parse_path_exp_handles_empty_string() {
        expect!(parse_path_exp(s!(""))).to(be_ok().value(vec![]));
    }

    #[test]
    fn parse_path_exp_handles_root() {
        expect!(parse_path_exp(s!("$"))).to(be_ok().value(vec![PathToken::Root]));
    }

    #[test]
    fn parse_path_exp_handles_missing_root() {
        expect!(parse_path_exp(s!("adsjhaskjdh")))
          .to(be_ok().value(vec![PathToken::Root, PathToken::Field(s!("adsjhaskjdh"))]));
    }

    #[test]
    fn parse_path_exp_handles_missing_path() {
        expect!(parse_path_exp(s!("$adsjhaskjdh"))).to(
            be_err().value(s!("Expected a \".\" or \"[\" instead of \"a\" in path expression \"$adsjhaskjdh\" at index 1")));
    }

    #[test]
    fn parse_path_exp_handles_missing_path_name() {
        expect!(parse_path_exp(s!("$."))).to(
            be_err().value(s!("Expected a path after \".\" in path expression \"$.\" at index 1")));
        expect!(parse_path_exp(s!("$.a.b.c."))).to(
            be_err().value(s!("Expected a path after \".\" in path expression \"$.a.b.c.\" at index 7")));
    }

    #[test]
    fn parse_path_exp_handles_invalid_identifiers() {
        expect!(parse_path_exp(s!("$.abc!"))).to(
            be_err().value(s!("\"!\" is not allowed in an identifier in path expression \"$.abc!\" at index 5")));
        expect!(parse_path_exp(s!("$.a.b.c.}"))).to(
            be_err().value(s!("Expected either a \"*\" or path identifier in path expression \"$.a.b.c.}\" at index 8")));
    }

    #[test]
    fn parse_path_exp_with_simple_identifiers() {
      expect!(parse_path_exp(s!("$.a"))).to(
          be_ok().value(vec![PathToken::Root, PathToken::Field(s!("a"))]));
      expect!(parse_path_exp(s!("$.a.b.c"))).to(
          be_ok().value(vec![PathToken::Root, PathToken::Field(s!("a")), PathToken::Field(s!("b")),
          PathToken::Field(s!("c"))]));
      expect!(parse_path_exp(s!("a.b.c"))).to(
        be_ok().value(vec![PathToken::Root, PathToken::Field(s!("a")), PathToken::Field(s!("b")),
                           PathToken::Field(s!("c"))]));
    }

    #[test]
    fn parse_path_exp_handles_underscores_and_dashes() {
        expect!(parse_path_exp(s!("$.user_id.user-id"))).to(
            be_ok().value(vec![PathToken::Root, PathToken::Field(s!("user_id")),
                               PathToken::Field(s!("user-id"))])
        );
    }

    #[test]
    fn parse_path_exp_with_star_instead_of_identifiers() {
        expect!(parse_path_exp(s!("$.*"))).to(
            be_ok().value(vec![PathToken::Root, PathToken::Star]));
        expect!(parse_path_exp(s!("$.a.*.c"))).to(
            be_ok().value(vec![PathToken::Root, PathToken::Field(s!("a")), PathToken::Star,
            PathToken::Field(s!("c"))]));
    }

    #[test]
    fn parse_path_exp_with_bracket_notation() {
        expect!(parse_path_exp(s!("$['val1']"))).to(
            be_ok().value(vec![PathToken::Root, PathToken::Field(s!("val1"))]));
        expect!(parse_path_exp(s!("$.a['val@1.'].c"))).to(
            be_ok().value(vec![PathToken::Root, PathToken::Field(s!("a")), PathToken::Field(s!("val@1.")),
            PathToken::Field(s!("c"))]));
        expect!(parse_path_exp(s!("$.a[1].c"))).to(
            be_ok().value(vec![PathToken::Root, PathToken::Field(s!("a")), PathToken::Index(1),
            PathToken::Field(s!("c"))]));
        expect!(parse_path_exp(s!("$.a[*].c"))).to(
            be_ok().value(vec![PathToken::Root, PathToken::Field(s!("a")), PathToken::StarIndex,
            PathToken::Field(s!("c"))]));
    }

    #[test]
    fn parse_path_exp_with_invalid_bracket_notation() {
        expect!(parse_path_exp(s!("$["))).to(
            be_err().value(s!("Expected a \"'\" (single qoute) or a digit in path expression \"$[\" after index 1")));
        expect!(parse_path_exp(s!("$['"))).to(
            be_err().value(s!("Unterminated string in path expression \"$['\" at index 2")));
        expect!(parse_path_exp(s!("$['Unterminated string"))).to(
            be_err().value(s!("Unterminated string in path expression \"$['Unterminated string\" at index 21")));
        expect!(parse_path_exp(s!("$['']"))).to(
            be_err().value(s!("Empty strings are not allowed in path expression \"$['']\" at index 3")));
        expect!(parse_path_exp(s!("$['test'.b.c"))).to(
            be_err().value(s!("Unterminated brackets, found \".\" instead of \"]\" in path expression \"$['test'.b.c\" at index 8")));
        expect!(parse_path_exp(s!("$['test'"))).to(
            be_err().value(s!("Unterminated brackets in path expression \"$['test'\" at index 7")));
        expect!(parse_path_exp(s!("$['test']b.c"))).to(
            be_err().value(s!("Expected a \".\" or \"[\" instead of \"b\" in path expression \"$[\'test\']b.c\" at index 9")));
    }

    #[test]
    fn parse_path_exp_with_invalid_bracket_index_notation() {
        expect!(parse_path_exp(s!("$[dhghh]"))).to(
            be_err().value(s!("Indexes can only consist of numbers or a \"*\", found \"d\" instead in path expression \"$[dhghh]\" at index 2")));
        expect!(parse_path_exp(s!("$[12abc]"))).to(
            be_err().value(s!("Indexes can only consist of numbers or a \"*\", found \"a\" instead in path expression \"$[12abc]\" at index 4")));
        expect!(parse_path_exp(s!("$[]"))).to(
            be_err().value(s!("Empty bracket expressions are not allowed in path expression \"$[]\" at index 2")));
        expect!(parse_path_exp(s!("$[-1]"))).to(
            be_err().value(s!("Indexes can only consist of numbers or a \"*\", found \"-\" instead in path expression \"$[-1]\" at index 2")));
    }
}
<commit_msg>fix: handle path expressions that start with an underscore<commit_after>use std::iter::Peekable;

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum PathToken {
    Root,
    Field(String),
    Index(usize),
    Star,
    StarIndex
}

fn peek<I>(chars: &mut Peekable<I>) -> Option<(usize, char)> where I: Iterator<Item = (usize, char)> {
    chars.peek().map(|tup| (tup.0.clone(), tup.1.clone()))
}

fn is_identifier_char(ch: char) -> bool {
    ch.is_alphabetic() || ch.is_numeric() || ch == '_' || ch == '-'
}

// identifier -> a-zA-Z0-9+
fn identifier<I>(ch: char, chars: &mut Peekable<I>, tokens: &mut Vec<PathToken>, path: &String)-> Result<(), String>
    where I: Iterator<Item = (usize, char)>  {
    let mut id = String::new();
    id.push(ch);
    let mut next_char = peek(chars);
    while next_char.is_some() {
        let ch = next_char.unwrap();
        if is_identifier_char(ch.1) {
            chars.next();
            id.push(ch.1);
        } else if ch.1 == '.' || ch.1 == '\'' || ch.1 == '[' {
            break;
        } else {
            return Err(format!("\"{}\" is not allowed in an identifier in path expression \"{}\" at index {}",
                ch.1, path, ch.0));
        }
        next_char = peek(chars);
    }
    tokens.push(PathToken::Field(id));
    Ok(())
}

// path_identifier -> identifier | *
fn path_identifier<I>(chars: &mut Peekable<I>, tokens: &mut Vec<PathToken>, path: &String, index: usize) -> Result<(), String>
    where I: Iterator<Item = (usize, char)>  {
    match chars.next() {
        Some(ch) => match ch.1 {
            '*' => {
                tokens.push(PathToken::Star);
                Ok(())
            },
            c if is_identifier_char(c) => {
              identifier(c, chars, tokens, path)?;
              Ok(())
            },
            _ => Err(format!("Expected either a \"*\" or path identifier in path expression \"{}\" at index {}",
                path, ch.0))
        },
        None => Err(format!("Expected a path after \".\" in path expression \"{}\" at index {}",
            path, index))
    }
}

// string_path -> [^']+
fn string_path<I>(chars: &mut Peekable<I>, tokens: &mut Vec<PathToken>, path: &String, index: usize) -> Result<(), String>
    where I: Iterator<Item = (usize, char)>  {
    let mut id = String::new();
    let mut next_char = peek(chars);
    if next_char.is_some() {
        chars.next();
        let mut ch = next_char.unwrap();
        next_char = peek(chars);
        while ch.1 != '\'' && next_char.is_some() {
            id.push(ch.1);
            chars.next();
            ch = next_char.unwrap();
            next_char = peek(chars);
        }
        if ch.1 == '\'' {
            if id.is_empty() {
                Err(format!("Empty strings are not allowed in path expression \"{}\" at index {}", path, ch.0))
            } else {
                tokens.push(PathToken::Field(id));
                Ok(())
            }
        } else {
            Err(format!("Unterminated string in path expression \"{}\" at index {}", path, ch.0))
        }
    } else {
        Err(format!("Unterminated string in path expression \"{}\" at index {}", path, index))
    }
}

// index_path -> [0-9]+
fn index_path<I>(chars: &mut Peekable<I>, tokens: &mut Vec<PathToken>, path: &String) -> Result<(), String>
    where I: Iterator<Item = (usize, char)>  {
    let mut id = String::new();
    let mut next_char = chars.next();
    id.push(next_char.unwrap().1);
    next_char = peek(chars);
    while next_char.is_some() {
        let ch = next_char.unwrap();
        if ch.1.is_numeric() {
            id.push(ch.1);
            chars.next();
        } else {
            break;
        }
        next_char = peek(chars);
    }

    if let Some(ch) = next_char {
        if ch.1 != ']' {
            return Err(format!("Indexes can only consist of numbers or a \"*\", found \"{}\" instead in path expression \"{}\" at index {}",
                ch.1, path, ch.0))
        }
    }

    tokens.push(PathToken::Index(id.parse().unwrap()));
    Ok(())
}

// bracket_path -> (string_path | index | *) ]
fn bracket_path<I>(chars: &mut Peekable<I>, tokens: &mut Vec<PathToken>, path: &String, index: usize) -> Result<(), String>
    where I: Iterator<Item = (usize, char)>  {
    let mut ch = peek(chars);
    match ch {
        Some(c) => {
            if c.1 == '\'' {
              chars.next();
              string_path(chars, tokens, path, c.0)?
            } else if c.1.is_numeric() {
              index_path(chars, tokens, path)?
            } else if c.1 == '*' {
                chars.next();
                tokens.push(PathToken::StarIndex);
            } else if c.1 == ']' {
                return Err(format!("Empty bracket expressions are not allowed in path expression \"{}\" at index {}",
                    path, c.0));
            } else {
                return Err(format!("Indexes can only consist of numbers or a \"*\", found \"{}\" instead in path expression \"{}\" at index {}",
                    c.1, path, c.0));
            };
            ch = peek(chars);
            match ch {
                Some(c) => if c.1 != ']' {
                    Err(format!("Unterminated brackets, found \"{}\" instead of \"]\" in path expression \"{}\" at index {}",
                        c.1, path, c.0))
                } else {
                    chars.next();
                    Ok(())
                },
                None => Err(format!("Unterminated brackets in path expression \"{}\" at index {}",
                    path, path.len() - 1))
            }
        },
        None => Err(format!("Expected a \"'\" (single qoute) or a digit in path expression \"{}\" after index {}",
            path, index))
    }
}

// path_exp -> (dot-path | bracket-path)*
fn path_exp<I>(chars: &mut Peekable<I>, tokens: &mut Vec<PathToken>, path: &String) -> Result<(), String>
    where I: Iterator<Item = (usize, char)> {
    let mut next_char = chars.next();
    while next_char.is_some() {
        let ch = next_char.unwrap();
        match ch.1 {
            '.' => path_identifier(chars, tokens, path, ch.0)?,
            '[' => bracket_path(chars, tokens, path, ch.0)?,
            _ => return Err(format!("Expected a \".\" or \"[\" instead of \"{}\" in path expression \"{}\" at index {}",
                ch.1, path, ch.0))
        }
        next_char = chars.next();
    }
    Ok(())
}

pub fn parse_path_exp(path: String) -> Result<Vec<PathToken>, String> {
    let mut tokens = vec![];

    // parse_path_exp -> $ path_exp | empty
    let mut chars = path.chars().enumerate().peekable();
    match chars.next() {
      Some(ch) => {
        match ch.1 {
          '$' => {
            tokens.push(PathToken::Root);
            path_exp(&mut chars, &mut tokens, &path)?;
            Ok(tokens)
          },
          c if c.is_alphabetic() || c.is_numeric() => {
            tokens.push(PathToken::Root);
            identifier(c, &mut chars, &mut tokens, &path)?;
            path_exp(&mut chars, &mut tokens, &path)?;
            Ok(tokens)
          },
          _ => Err(format ! ("Path expression \"{}\" does not start with a root marker \"$\"", path))
        }
      },
      None => Ok(tokens)
    }
}

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

    #[test]
    fn parse_path_exp_handles_empty_string() {
        expect!(parse_path_exp(s!(""))).to(be_ok().value(vec![]));
    }

    #[test]
    fn parse_path_exp_handles_root() {
        expect!(parse_path_exp(s!("$"))).to(be_ok().value(vec![PathToken::Root]));
    }

    #[test]
    fn parse_path_exp_handles_missing_root() {
        expect!(parse_path_exp(s!("adsjhaskjdh")))
          .to(be_ok().value(vec![PathToken::Root, PathToken::Field(s!("adsjhaskjdh"))]));
    }

    #[test]
    fn parse_path_exp_handles_missing_path() {
        expect!(parse_path_exp(s!("$adsjhaskjdh"))).to(
            be_err().value(s!("Expected a \".\" or \"[\" instead of \"a\" in path expression \"$adsjhaskjdh\" at index 1")));
    }

    #[test]
    fn parse_path_exp_handles_missing_path_name() {
        expect!(parse_path_exp(s!("$."))).to(
            be_err().value(s!("Expected a path after \".\" in path expression \"$.\" at index 1")));
        expect!(parse_path_exp(s!("$.a.b.c."))).to(
            be_err().value(s!("Expected a path after \".\" in path expression \"$.a.b.c.\" at index 7")));
    }

    #[test]
    fn parse_path_exp_handles_invalid_identifiers() {
        expect!(parse_path_exp(s!("$.abc!"))).to(
            be_err().value(s!("\"!\" is not allowed in an identifier in path expression \"$.abc!\" at index 5")));
        expect!(parse_path_exp(s!("$.a.b.c.}"))).to(
            be_err().value(s!("Expected either a \"*\" or path identifier in path expression \"$.a.b.c.}\" at index 8")));
    }

    #[test]
    fn parse_path_exp_with_simple_identifiers() {
      expect!(parse_path_exp(s!("$.a"))).to(
          be_ok().value(vec![PathToken::Root, PathToken::Field(s!("a"))]));
      expect!(parse_path_exp(s!("$.a.b.c"))).to(
          be_ok().value(vec![PathToken::Root, PathToken::Field(s!("a")), PathToken::Field(s!("b")),
          PathToken::Field(s!("c"))]));
      expect!(parse_path_exp(s!("a.b.c"))).to(
        be_ok().value(vec![PathToken::Root, PathToken::Field(s!("a")), PathToken::Field(s!("b")),
                           PathToken::Field(s!("c"))]));
    }

    #[test]
    fn parse_path_exp_handles_underscores_and_dashes() {
        expect!(parse_path_exp(s!("$.user_id.user-id"))).to(
            be_ok().value(vec![PathToken::Root, PathToken::Field(s!("user_id")),
                               PathToken::Field(s!("user-id"))])
        );
        expect!(parse_path_exp(s!("$._id"))).to(
            be_ok().value(vec![PathToken::Root, PathToken::Field(s!("_id"))])
        );
    }

    #[test]
    fn parse_path_exp_with_star_instead_of_identifiers() {
        expect!(parse_path_exp(s!("$.*"))).to(
            be_ok().value(vec![PathToken::Root, PathToken::Star]));
        expect!(parse_path_exp(s!("$.a.*.c"))).to(
            be_ok().value(vec![PathToken::Root, PathToken::Field(s!("a")), PathToken::Star,
            PathToken::Field(s!("c"))]));
    }

    #[test]
    fn parse_path_exp_with_bracket_notation() {
        expect!(parse_path_exp(s!("$['val1']"))).to(
            be_ok().value(vec![PathToken::Root, PathToken::Field(s!("val1"))]));
        expect!(parse_path_exp(s!("$.a['val@1.'].c"))).to(
            be_ok().value(vec![PathToken::Root, PathToken::Field(s!("a")), PathToken::Field(s!("val@1.")),
            PathToken::Field(s!("c"))]));
        expect!(parse_path_exp(s!("$.a[1].c"))).to(
            be_ok().value(vec![PathToken::Root, PathToken::Field(s!("a")), PathToken::Index(1),
            PathToken::Field(s!("c"))]));
        expect!(parse_path_exp(s!("$.a[*].c"))).to(
            be_ok().value(vec![PathToken::Root, PathToken::Field(s!("a")), PathToken::StarIndex,
            PathToken::Field(s!("c"))]));
    }

    #[test]
    fn parse_path_exp_with_invalid_bracket_notation() {
        expect!(parse_path_exp(s!("$["))).to(
            be_err().value(s!("Expected a \"'\" (single qoute) or a digit in path expression \"$[\" after index 1")));
        expect!(parse_path_exp(s!("$['"))).to(
            be_err().value(s!("Unterminated string in path expression \"$['\" at index 2")));
        expect!(parse_path_exp(s!("$['Unterminated string"))).to(
            be_err().value(s!("Unterminated string in path expression \"$['Unterminated string\" at index 21")));
        expect!(parse_path_exp(s!("$['']"))).to(
            be_err().value(s!("Empty strings are not allowed in path expression \"$['']\" at index 3")));
        expect!(parse_path_exp(s!("$['test'.b.c"))).to(
            be_err().value(s!("Unterminated brackets, found \".\" instead of \"]\" in path expression \"$['test'.b.c\" at index 8")));
        expect!(parse_path_exp(s!("$['test'"))).to(
            be_err().value(s!("Unterminated brackets in path expression \"$['test'\" at index 7")));
        expect!(parse_path_exp(s!("$['test']b.c"))).to(
            be_err().value(s!("Expected a \".\" or \"[\" instead of \"b\" in path expression \"$[\'test\']b.c\" at index 9")));
    }

    #[test]
    fn parse_path_exp_with_invalid_bracket_index_notation() {
        expect!(parse_path_exp(s!("$[dhghh]"))).to(
            be_err().value(s!("Indexes can only consist of numbers or a \"*\", found \"d\" instead in path expression \"$[dhghh]\" at index 2")));
        expect!(parse_path_exp(s!("$[12abc]"))).to(
            be_err().value(s!("Indexes can only consist of numbers or a \"*\", found \"a\" instead in path expression \"$[12abc]\" at index 4")));
        expect!(parse_path_exp(s!("$[]"))).to(
            be_err().value(s!("Empty bracket expressions are not allowed in path expression \"$[]\" at index 2")));
        expect!(parse_path_exp(s!("$[-1]"))).to(
            be_err().value(s!("Indexes can only consist of numbers or a \"*\", found \"-\" instead in path expression \"$[-1]\" at index 2")));
    }
}
<|endoftext|>


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

use ansi_term::{ANSIGenericString, Style};
use ansi_term::Colour::*;
use bytes::Bytes;
use maplit::*;
use serde_json::{json, Value};

use pact_matching::{match_message, Mismatch};
use pact_matching::models::{Interaction, Request, Response};
use pact_matching::models::HttpPart;
use pact_matching::models::message::Message;
use pact_models::bodies::OptionalBody;

use crate::{MismatchResult, ProviderInfo, VerificationOptions};
use crate::callback_executors::RequestFilterExecutor;
use crate::provider_client::{make_provider_request, provider_client_error_to_string};

pub async fn verify_message_from_provider<F: RequestFilterExecutor>(
  provider: &ProviderInfo,
  interaction: &Box<dyn Interaction + Send>,
  options: &VerificationOptions<F>,
  client: &reqwest::Client,
  _: &HashMap<&str, Value>
) -> Result<Option<String>, MismatchResult> {
  let mut request_body = json!({
    "description": interaction.description()
  });
  if !interaction.provider_states().is_empty() {
    if let Some(map) = request_body.as_object_mut() {
      map.insert("providerStates".into(), Value::Array(interaction.provider_states().iter()
        .map(|ps| ps.to_json()).collect()));
    }
  }
  let message_request = Request {
    method: "POST".into(),
    body: OptionalBody::Present(Bytes::from(request_body.to_string()), Some("application/json".into())),
    headers: Some(hashmap! {
        "Content-Type".to_string() => vec!["application/json".to_string()]
    }),
    .. Request::default()
  };
  match make_provider_request(provider, &message_request, options, client).await {
    Ok(ref actual_response) => {
      let metadata = extract_metadata(actual_response);
      let actual = Message {
        contents: actual_response.body.clone(),
        metadata: metadata,
        .. Message::default()
      };
      log::debug!("actual message = {:?}", actual);
      let mismatches = match_message(interaction, &actual.boxed());
      if mismatches.is_empty() {
        Ok(interaction.id().clone())
      } else {
        Err(MismatchResult::Mismatches {
          mismatches,
          expected: interaction.boxed(),
          actual: actual.boxed(),
          interaction_id: interaction.id().clone()
        })
      }
    },
    Err(err) => {
      Err(MismatchResult::Error(provider_client_error_to_string(err), interaction.id().clone()))
    }
  }
}

pub fn display_message_result(
  interaction: &Message,
  match_result: &Result<Option<String>, MismatchResult>) {
  match match_result {
    Ok(_) => {
      display_result(Green.paint("OK"),
        interaction.metadata.iter()
          .map(|(k, v)| (k.clone(), serde_json::to_string(&v.clone()).unwrap_or_default(), Green.paint("OK"))).collect());
    },
    Err(ref err) => match *err {
      MismatchResult::Error(ref err_des, _) => {
        println!("      {}", Red.paint(format!("Request Failed - {}", err_des)));
      },
      MismatchResult::Mismatches { ref mismatches, .. } => {
        let metadata_results = interaction.metadata.iter().map(|(k, v)| {
          (k.clone(), serde_json::to_string(&v.clone()).unwrap_or_default(), if mismatches.iter().any(|m| {
            match *m {
              Mismatch::MetadataMismatch { ref key, .. } => k == key,
              _ => false
            }
          }) {
            Red.paint("FAILED")
          } else {
            Green.paint("OK")
          })
        }).collect();
        let body_result = if mismatches.iter().any(|m| m.mismatch_type() == "BodyMismatch" ||
          m.mismatch_type() == "BodyTypeMismatch") {
          Red.paint("FAILED")
        } else {
          Green.paint("OK")
        };

        display_result(body_result, metadata_results);
      }
    }
  }
}

fn display_result(body_result: ANSIGenericString<str>, metadata_result: Vec<(String, String, ANSIGenericString<str>)>) {
  println!("    generates a message which");
  if !metadata_result.is_empty() {
    println!("      includes metadata");
    for (key, value, result) in metadata_result {
      println!("        \"{}\" with value {} ({})", Style::new().bold().paint(key),
        Style::new().bold().paint(value), result);
    }
  }
  println!("      has a matching body ({})", body_result);
}

fn extract_metadata(actual_response: &Response) -> HashMap<String, Value> {
  let mut default = hashmap!{
    "contentType".to_string() => Value::String(actual_response.lookup_content_type().unwrap_or_default()),
  };

  actual_response.headers.clone().unwrap_or_default().iter().for_each(|(k,v)| {
    if k == "pact_message_metadata" {
      let json: String = v.first().unwrap_or(&"".to_string()).to_string();
      log::trace!("found raw metadata from headers: {:?}", json);

      let decoded = base64::decode(&json.as_str()).unwrap_or_default();
      log::trace!("have base64 decoded headers: {:?}", decoded);

      let metadata: HashMap<String, Value> = serde_json::from_slice(&decoded).unwrap_or_default();
      log::trace!("have JSON metadata from headers: {:?}", metadata);

      for (k, v) in metadata {
        default.insert(k, v);
      }
    }
  });

  default
}<commit_msg>chore: tests for extract_headers<commit_after>use std::collections::HashMap;

use ansi_term::{ANSIGenericString, Style};
use ansi_term::Colour::*;
use bytes::Bytes;
use maplit::*;
use serde_json::{json, Value};

use pact_matching::{match_message, Mismatch};
use pact_matching::models::{Interaction, Request, Response};
use pact_matching::models::HttpPart;
use pact_matching::models::message::Message;
use pact_models::bodies::OptionalBody;

use crate::{MismatchResult, ProviderInfo, VerificationOptions};
use crate::callback_executors::RequestFilterExecutor;
use crate::provider_client::{make_provider_request, provider_client_error_to_string};

pub async fn verify_message_from_provider<F: RequestFilterExecutor>(
  provider: &ProviderInfo,
  interaction: &Box<dyn Interaction + Send>,
  options: &VerificationOptions<F>,
  client: &reqwest::Client,
  _: &HashMap<&str, Value>
) -> Result<Option<String>, MismatchResult> {
  let mut request_body = json!({
    "description": interaction.description()
  });
  if !interaction.provider_states().is_empty() {
    if let Some(map) = request_body.as_object_mut() {
      map.insert("providerStates".into(), Value::Array(interaction.provider_states().iter()
        .map(|ps| ps.to_json()).collect()));
    }
  }
  let message_request = Request {
    method: "POST".into(),
    body: OptionalBody::Present(Bytes::from(request_body.to_string()), Some("application/json".into())),
    headers: Some(hashmap! {
        "Content-Type".to_string() => vec!["application/json".to_string()]
    }),
    .. Request::default()
  };
  match make_provider_request(provider, &message_request, options, client).await {
    Ok(ref actual_response) => {
      let metadata = extract_metadata(actual_response);
      let actual = Message {
        contents: actual_response.body.clone(),
        metadata: metadata,
        .. Message::default()
      };
      log::debug!("actual message = {:?}", actual);
      let mismatches = match_message(interaction, &actual.boxed());
      if mismatches.is_empty() {
        Ok(interaction.id().clone())
      } else {
        Err(MismatchResult::Mismatches {
          mismatches,
          expected: interaction.boxed(),
          actual: actual.boxed(),
          interaction_id: interaction.id().clone()
        })
      }
    },
    Err(err) => {
      Err(MismatchResult::Error(provider_client_error_to_string(err), interaction.id().clone()))
    }
  }
}

pub fn display_message_result(
  interaction: &Message,
  match_result: &Result<Option<String>, MismatchResult>) {
  match match_result {
    Ok(_) => {
      display_result(Green.paint("OK"),
        interaction.metadata.iter()
          .map(|(k, v)| (k.clone(), serde_json::to_string(&v.clone()).unwrap_or_default(), Green.paint("OK"))).collect());
    },
    Err(ref err) => match *err {
      MismatchResult::Error(ref err_des, _) => {
        println!("      {}", Red.paint(format!("Request Failed - {}", err_des)));
      },
      MismatchResult::Mismatches { ref mismatches, .. } => {
        let metadata_results = interaction.metadata.iter().map(|(k, v)| {
          (k.clone(), serde_json::to_string(&v.clone()).unwrap_or_default(), if mismatches.iter().any(|m| {
            match *m {
              Mismatch::MetadataMismatch { ref key, .. } => k == key,
              _ => false
            }
          }) {
            Red.paint("FAILED")
          } else {
            Green.paint("OK")
          })
        }).collect();
        let body_result = if mismatches.iter().any(|m| m.mismatch_type() == "BodyMismatch" ||
          m.mismatch_type() == "BodyTypeMismatch") {
          Red.paint("FAILED")
        } else {
          Green.paint("OK")
        };

        display_result(body_result, metadata_results);
      }
    }
  }
}

fn display_result(body_result: ANSIGenericString<str>, metadata_result: Vec<(String, String, ANSIGenericString<str>)>) {
  println!("    generates a message which");
  if !metadata_result.is_empty() {
    println!("      includes metadata");
    for (key, value, result) in metadata_result {
      println!("        \"{}\" with value {} ({})", Style::new().bold().paint(key),
        Style::new().bold().paint(value), result);
    }
  }
  println!("      has a matching body ({})", body_result);
}

fn extract_metadata(actual_response: &Response) -> HashMap<String, Value> {
  let content_type = "contentType".to_string();

  let mut default = hashmap!{
    content_type => Value::String(actual_response.lookup_content_type().unwrap_or_default()),
  };

  actual_response.headers.clone().unwrap_or_default().iter().for_each(|(k,v)| {
    if k == "pact_message_metadata" {
      let json: String = v.first().unwrap_or(&"".to_string()).to_string();
      log::trace!("found raw metadata from headers: {:?}", json);

      let decoded = base64::decode(&json.as_str()).unwrap_or_default();
      log::trace!("have base64 decoded headers: {:?}", decoded);

      let metadata: HashMap<String, Value> = serde_json::from_slice(&decoded).unwrap_or_default();
      log::trace!("have JSON metadata from headers: {:?}", metadata);

      for (k, v) in metadata {
        default.insert(k, v);
      }
    }
  });

  default
}

#[cfg(test)]
mod tests {
  use expectest::prelude::*;
  use pact_models::generators::{Generators};
  use pact_models::matchingrules::{MatchingRules};

  use super::*;

    #[test]
    fn extract_metadata_default() {
      let response = Response {
        status: 200,
        headers: Some(hashmap! {
          "content-type".into() => vec!["application/json".into()],
        }),
        body: OptionalBody::default(),
        generators: Generators{
          categories: hashmap!()
        },
        matching_rules: MatchingRules {
          rules: hashmap!()
        }
      };
      let expected = hashmap! {
        "contentType".to_string() => Value::String("application/json".to_string())
      };

      expect(extract_metadata(&response)).to(be_eq(expected));
    }

    #[test]
    fn extract_metadata_from_base64_header() {
      let response = Response {
        status: 200,
        headers: Some(hashmap! {
          "content-type".into() => vec!["application/json".into()],
          // must convert lowercase here, because the http framework actually lowercases this for us
          "PACT_MESSAGE_METADATA".to_lowercase().into() => vec!["ewogICJDb250ZW50LVR5cGUiOiAiYXBwbGljYXRpb24vanNvbiIsCiAgInRvcGljIjogImJheiIsCiAgIm51bWJlciI6IDI3LAogICJjb21wbGV4IjogewogICAgImZvbyI6ICJiYXIiCiAgfQp9Cg==".into()],
        }),
        body: OptionalBody::default(),
        generators: Generators{
          categories: hashmap!()
        },
        matching_rules: MatchingRules {
          rules: hashmap!()
        }
      };
      let expected = hashmap! {
        "contentType".to_string() => Value::String("application/json".to_string()), // From actual HTTP response header
        "Content-Type".to_string() => Value::String("application/json".to_string()), // From metadata header
        "complex".to_string() => json!({"foo": "bar"}),
        "topic".to_string() => Value::String("baz".into()),
        "number".to_string() => json!(27),
      };

      expect(extract_metadata(&response)).to(be_eq(expected));
    }
}
<|endoftext|>


<commit_before><commit_msg>Rustup to rustc 1.28.0-nightly (952f344cd 2018-05-18)<commit_after><|endoftext|>


<commit_before><commit_msg>Update nonce handling for mysql_common 0.28<commit_after><|endoftext|>


<commit_before><commit_msg>[collection] removing unnecessary information<commit_after><|endoftext|>


<commit_before>
use std::io::prelude::*;
use std::fs::File;

use std::ops::Add;

extern crate regex;
use regex::Regex;

// North = +y
// East = +X
#[derive(Debug, Copy, Clone)]
struct Vec2i {
    x: i32,
    y: i32,
}
impl Vec2i {
    fn new (x: i32, y:i32) -> Vec2i {
        Vec2i{x:x, y:y}
    }
    // Component-wise multiplication
    fn mult(v1: Vec2i, scalar: i32) -> Vec2i {
        let x = v1.x*scalar;
        let y = v1.y*scalar;
        Vec2i{x:x, y: y}
    }

    // Distance from origin
    fn manhattan_magnitude(&self) -> i32 {
        &self.x + &self.y
    }
}
impl Add for Vec2i {
    type Output = Vec2i;
    fn add(self, other: Vec2i) -> Vec2i {
        Vec2i { x: self.x + other.x, y: self.y + other.y }
    }
}

#[derive(Debug, Copy, Clone)]
struct Pose {
    direction: Vec2i,
    position: Vec2i,
}

#[derive(Debug, Copy, Clone)]
enum Turn {
    Right,
    Left
}

// Given a input direction vector (x,y), rotate θ° CCW using the rotation matrix:
// R = [cos θ, -sin θ]
//     [sin θ,  cos θ]
//
// Left: (90°) = [0 -1][x]
//               [1  0][y]
// Right: (-90°) = [ 0 1][x]
//                 [-1 0][y]
fn rotated(input: Vec2i, turn: Turn) -> Vec2i {
    match turn {
        Turn::Left => Vec2i::new(-input.y, input.x),
        Turn::Right => Vec2i::new(input.y, -input.x),
    }
}

// (direction, position)
fn accumulate(current: Pose, turn: Turn, dist: i32) -> Pose {

    let new_direction= rotated(current.direction, turn);
    let new_position = current.position + Vec2i::mult(new_direction, dist);
    Pose{direction: new_direction, position: new_position}
}

fn as_direction(input: Option<&str>) -> Turn {
    match input {
        Some("R") => Turn::Right,
        _ => Turn::Left
    }
}

fn main() {
    let re = Regex::new(r"([RL])([:digit:]+)").unwrap();

    let mut pose = Pose{position: Vec2i::new(0,0), direction:  Vec2i::new(0,1)};

    let mut input_string = String::new();
    let mut file = File::open("input.txt").unwrap();
    let _ = file.read_to_string(&mut input_string);

    for cap in re.captures_iter(&input_string) {
        let turn = as_direction(cap.at(1));
        let dist: i32 = cap.at(2).unwrap().parse().unwrap();
        pose = accumulate(pose, turn, dist);
    }
    println!("Stopped at {:?}, distance {}", pose, pose.position.manhattan_magnitude());
}

//-----------------------------------------------------------------------------

#[test]
fn test1() {
    let re = Regex::new(r"([RL])([:digit:]+)").unwrap();

    // Test 1
    let mut pose = Pose{position: Vec2i::new(0,0), direction:  Vec2i::new(0,1)};
    for cap in re.captures_iter("R2, L3") {
        let turn = as_direction(cap.at(1));
        let dist: i32 = cap.at(2).unwrap().parse().unwrap();
        pose = accumulate(pose, turn, dist);
    }
    println!("{:?}", pose);
    assert!(pose.position.x == 2);
    assert!(pose.position.y == 3);
    assert!(pose.position.manhattan_magnitude() == 5);
}

#[test]
fn test2() {
    let re = Regex::new(r"([RL])([:digit:]+)").unwrap();

    // Test 1
    let mut pose = Pose{position: Vec2i::new(0,0), direction:  Vec2i::new(0,1)};
    for cap in re.captures_iter("R2, R2, R2") {
        let turn = as_direction(cap.at(1));
        let dist: i32 = cap.at(2).unwrap().parse().unwrap();
        pose = accumulate(pose, turn, dist);
    }
    println!("{:?}", pose);
    assert!(pose.position.x == 0);
    assert!(pose.position.y == -2);
}

#[test]
fn test3() {
    let re = Regex::new(r"([RL])([:digit:]+)").unwrap();

    // Test 1
    let mut pose = Pose{position: Vec2i::new(0,0), direction:  Vec2i::new(0,1)};
    for cap in re.captures_iter("R5, L5, R5, R3") {
        let turn = as_direction(cap.at(1));
        let dist: i32 = cap.at(2).unwrap().parse().unwrap();
        pose = accumulate(pose, turn, dist);
    }
    println!("{:?}", pose);
    assert!(pose.position.manhattan_magnitude() == 12);
}
<commit_msg>Day 1, part 2.<commit_after>
use std::io::prelude::*;
use std::fs::File;

use std::ops::Add;
use std::collections::HashSet;

extern crate regex;
use regex::Regex;

// North = +y
// East = +X
#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq)]
struct Vec2i {
    x: i32,
    y: i32,
}
impl Vec2i {
    fn new (x: i32, y:i32) -> Vec2i {
        Vec2i{x:x, y:y}
    }
    // Component-wise multiplication
    fn mult(v1: Vec2i, scalar: i32) -> Vec2i {
        let x = v1.x*scalar;
        let y = v1.y*scalar;
        Vec2i{x:x, y: y}
    }

    // Distance from origin
    fn manhattan_magnitude(&self) -> i32 {
        &self.x.abs() + &self.y.abs()
    }
}
impl Add for Vec2i {
    type Output = Vec2i;
    fn add(self, other: Vec2i) -> Vec2i {
        Vec2i { x: self.x + other.x, y: self.y + other.y }
    }
}

#[derive(Debug, Copy, Clone)]
struct Pose {
    direction: Vec2i,
    position: Vec2i,
}

#[derive(Debug, Copy, Clone)]
enum Turn {
    Right,
    Left,
    Straight
}

// Given a input direction vector (x,y), rotate θ° CCW using the rotation matrix:
// R = [cos θ, -sin θ]
//     [sin θ,  cos θ]
//
// Left: (90°) = [0 -1][x]
//               [1  0][y]
// Right: (-90°) = [ 0 1][x]
//                 [-1 0][y]
fn rotated(input: Vec2i, turn: Turn) -> Vec2i {
    match turn {
        Turn::Left => Vec2i::new(-input.y, input.x),
        Turn::Right => Vec2i::new(input.y, -input.x),
        Turn::Straight => input,
    }
}

// (direction, position)
fn accumulate(current: Pose, turn: Turn, dist: i32) -> Pose {

    let new_direction= rotated(current.direction, turn);
    let new_position = current.position + Vec2i::mult(new_direction, dist);
    Pose{direction: new_direction, position: new_position}
}

fn as_direction(input: Option<&str>) -> Turn {
    match input {
        Some("R") => Turn::Right,
        _ => Turn::Left
    }
}

fn get_final_pose(input_string: &str) -> Pose {
    let re = Regex::new(r"([RL])([:digit:]+)").unwrap();
    let mut pose = Pose{position: Vec2i::new(0,0), direction:  Vec2i::new(0,1)};
    for cap in re.captures_iter(&input_string) {
        let turn = as_direction(cap.at(1));
        let dist: i32 = cap.at(2).unwrap().parse().unwrap();
        pose = accumulate(pose, turn, dist);
    }
    pose
}

fn get_first_crossing(input_string: &str) -> Pose {
    let re = Regex::new(r"([RL])([:digit:]+)").unwrap();
    let mut pose = Pose{position: Vec2i::new(0,0), direction:  Vec2i::new(0,1)};
    let mut history = HashSet::new();

    'outer: for cap in re.captures_iter(&input_string) {
        let turn = as_direction(cap.at(1));
        let dist: i32 = cap.at(2).unwrap().parse().unwrap();

        // Rotate without moving
        pose.direction = rotated(pose.direction, turn);

        // Accumulate <dist> steps of size 1
        for _ in 0..dist {
            pose = accumulate(pose, Turn::Straight, 1);

            // Keep a history of positions
            if history.contains(&pose.position) {
                break 'outer;
            }
            history.insert(pose.position);
        }
    }
    pose
}

fn main() {

    let mut input_string = String::new();
    let mut file = File::open("input.txt").unwrap();
    let _ = file.read_to_string(&mut input_string);

    // Part 1
    {
        let pose = get_final_pose(&input_string);
        println!("Part 1: stopped at {:?}, distance {}",
                 pose.position, pose.position.manhattan_magnitude());
    }

    // Part 2
    {
        let pose = get_first_crossing(&input_string);
        println!("Part 2: stopped at {:?}, distance {}",
                 pose.position, pose.position.manhattan_magnitude()); // 257 is too high. 105 is too low.
    }
}

//-----------------------------------------------------------------------------

#[test]
fn test1() {
    let pose = get_final_pose("R2, L3");
    //println!("{:?}", pose);
    assert!(pose.position.x == 2);
    assert!(pose.position.y == 3);
    assert!(pose.position.manhattan_magnitude() == 5);
}

#[test]
fn test2() {
    let pose = get_final_pose("R2, R2, R2");
    //println!("{:?}", pose);
    assert!(pose.position.x == 0);
    assert!(pose.position.y == -2);
}

#[test]
fn test3() {
    let pose = get_final_pose("R5, L5, R5, R3");
    //println!("{:?}", pose);
    assert!(pose.position.manhattan_magnitude() == 12);
}

#[test]
fn revisit1() {
    let pose = get_first_crossing("R8, R4, R4, R8");

    println!("revisit1: stopped at {:?}, distance {}",
             pose.position, pose.position.manhattan_magnitude()); // 257 is too high.
    assert!(pose.position.manhattan_magnitude() == 4);
}<|endoftext|>


<commit_before>/*
--- Day 4: Security Through Obscurity ---

Finally, you come across an information kiosk with a list of rooms. Of course, the list is encrypted and full of decoy data, but the instructions to decode the list are barely hidden nearby. Better remove the decoy data first.

Each room consists of an encrypted name (lowercase letters separated by dashes) followed by a dash, a sector ID, and a checksum in square brackets.

A room is real (not a decoy) if the checksum is the five most common letters in the encrypted name, in order, with ties broken by alphabetization. For example:

aaaaa-bbb-z-y-x-123[abxyz] is a real room because the most common letters are a (5), b (3), and then a tie between x, y, and z, which are listed alphabetically.
a-b-c-d-e-f-g-h-987[abcde] is a real room because although the letters are all tied (1 of each), the first five are listed alphabetically.
not-a-real-room-404[oarel] is a real room.
totally-real-room-200[decoy] is not.
Of the real rooms from the list above, the sum of their sector IDs is 1514.

What is the sum of the sector IDs of the real rooms?

*/

extern crate regex;
#[macro_use] extern crate lazy_static;
use regex::Regex;
use std::io::prelude::*;
use std::fs::File;

#[derive(Debug)]
struct Room {
    name : String,
    sector_id : isize,
    checksum: String,
}

impl Room {
    fn from_line(line : &str) -> Room {

        lazy_static! {
            // compile regex only once
            static ref REGEX : Regex = Regex::new(r"^((([a-zA-Z0-9]+\-)+)([0-9]+)\[(.+)\])$").unwrap();
        }

        // find matches in line
        let cap = REGEX.captures(line).unwrap();

        // grab the parts we need
        let name : String     = cap.get(2).unwrap().as_str().to_string();
        let sector_id : isize = cap.get(4).unwrap().as_str().parse().unwrap();
        let checksum : String = cap.get(5).unwrap().as_str().to_string();

        // and create a Room struct off of it
        Room { name: name, sector_id: sector_id, checksum: checksum }
    }

    // does checksum match input?
    fn is_valid(&self) -> bool {
        // remove the dashes
        let undashed_name : String = self.name.replace('-', "");

        // grab the chars as an array
        let mut chars_as_vec : Vec<char> = undashed_name.chars().collect();

        // sort the array alphabetically
        chars_as_vec.sort();

        // dedupe the array
        chars_as_vec.dedup();

        // create tuples with the character and numer of occurances in original string
        let mut char_and_count : Vec<(char, usize)> = chars_as_vec.iter()
            .map(|c| { (*c, undashed_name.matches(*c).count()) })
            .collect();

        // ordering.. magic :)
        char_and_count.sort_by(|a, b| {
            // if a.1 (number of occurances) equal..
            if a.1 == b.1 {
                // matching; compare using the char alphabetically
                return b.0.cmp(&a.0)
            }
            // not matching; how do they compare?
            a.1.cmp(&b.1)
        });

        // since the order is backwards, lets reverse
        char_and_count.reverse();

        // truncate to first five chars
        char_and_count.truncate(5);

        // grab the chars and concatenate as string
        let checksum_as_vec : Vec<char> = char_and_count.iter().map(|c| c.0).collect();
        let checksum : String = checksum_as_vec.clone().into_iter().collect();

        // a room is valid once the two checksums match
        return checksum == self.checksum
    }
}

fn main() {
    let mut f = File::open("input.txt").unwrap();
    let mut input = String::new();
    f.read_to_string(&mut input).ok();

    let rooms : Vec<Room> = input.lines().map(|line| {Room::from_line(line)}).collect();

    // grab the valid rooms, and sum the sector id using fold
    let sum = rooms.iter()
        .filter(|r| {r.is_valid()})
        .fold(0, |sum, r| { sum + r.sector_id});

    // print the answer 🎉
    println!("sum: {}", sum);
}
<commit_msg>Solve part B for day 4<commit_after>/*
--- Day 4: Security Through Obscurity ---

Finally, you come across an information kiosk with a list of rooms. Of course, the list is encrypted and full of decoy data, but the instructions to decode the list are barely hidden nearby. Better remove the decoy data first.

Each room consists of an encrypted name (lowercase letters separated by dashes) followed by a dash, a sector ID, and a checksum in square brackets.

A room is real (not a decoy) if the checksum is the five most common letters in the encrypted name, in order, with ties broken by alphabetization. For example:

aaaaa-bbb-z-y-x-123[abxyz] is a real room because the most common letters are a (5), b (3), and then a tie between x, y, and z, which are listed alphabetically.
a-b-c-d-e-f-g-h-987[abcde] is a real room because although the letters are all tied (1 of each), the first five are listed alphabetically.
not-a-real-room-404[oarel] is a real room.
totally-real-room-200[decoy] is not.
Of the real rooms from the list above, the sum of their sector IDs is 1514.

What is the sum of the sector IDs of the real rooms?

--- Part Two ---

With all the decoy data out of the way, it's time to decrypt this list and get moving.

The room names are encrypted by a state-of-the-art shift cipher, which is nearly unbreakable without the right software. However, the information kiosk designers at Easter Bunny HQ were not expecting to deal with a master cryptographer like yourself.

To decrypt a room name, rotate each letter forward through the alphabet a number of times equal to the room's sector ID. A becomes B, B becomes C, Z becomes A, and so on. Dashes become spaces.

For example, the real name for qzmt-zixmtkozy-ivhz-343 is very encrypted name.

What is the sector ID of the room where North Pole objects are stored?

*/

extern crate regex;
#[macro_use] extern crate lazy_static;
use regex::Regex;
use std::io::prelude::*;
use std::fs::File;

#[derive(Debug)]
struct Room {
    name : String,
    sector_id : isize,
    checksum: String,
}

impl Room {
    fn from_line(line : &str) -> Room {

        lazy_static! {
            // compile regex only once
            static ref REGEX : Regex = Regex::new(r"^((([a-zA-Z0-9]+\-)+)([0-9]+)\[(.+)\])$").unwrap();
        }

        // find matches in line
        let cap = REGEX.captures(line).unwrap();

        // grab the parts we need
        let name : String     = cap.get(2).unwrap().as_str().to_string();
        let sector_id : isize = cap.get(4).unwrap().as_str().parse().unwrap();
        let checksum : String = cap.get(5).unwrap().as_str().to_string();

        // and create a Room struct off of it
        Room { name: name, sector_id: sector_id, checksum: checksum }
    }

    // does checksum match input?
    fn is_valid(&self) -> bool {
        // remove the dashes
        let undashed_name : String = self.name.replace('-', "");

        // grab the chars as an array
        let mut chars_as_vec : Vec<char> = undashed_name.chars().collect();

        // sort the array alphabetically
        chars_as_vec.sort();

        // dedupe the array
        chars_as_vec.dedup();

        // create tuples with the character and numer of occurances in original string
        let mut char_and_count : Vec<(char, usize)> = chars_as_vec.iter()
            .map(|c| { (*c, undashed_name.matches(*c).count()) })
            .collect();

        // ordering.. magic :)
        char_and_count.sort_by(|a, b| {
            // if a.1 (number of occurances) equal..
            if a.1 == b.1 {
                // matching; compare using the char alphabetically
                return b.0.cmp(&a.0)
            }
            // not matching; how do they compare?
            a.1.cmp(&b.1)
        });

        // since the order is backwards, lets reverse
        char_and_count.reverse();

        // truncate to first five chars
        char_and_count.truncate(5);

        // grab the chars and concatenate as string
        let checksum_as_vec : Vec<char> = char_and_count.iter().map(|c| c.0).collect();
        let checksum : String = checksum_as_vec.clone().into_iter().collect();

        // a room is valid once the two checksums match
        return checksum == self.checksum
    }

    fn decipher(&self) -> String {
        let undashed_name = self.name.replace('-', " ");
        rot(undashed_name, self.sector_id as usize)
    }
}

// basic ceasar cipher implementation
fn rot(input : String, rotations : usize) -> String {
    let alphabet  = "abcdefghijklmnopqrstuvwxyz".to_string();

    input.chars()
        .map(|c| {
            if c == ' ' { return c; }
            let new_index = alphabet.find(c).unwrap() + rotations;
            alphabet.chars().cycle().nth(new_index).unwrap()
        })
        .collect()

        //  ^ have you seen this cycle thing? 😯
}

fn main() {
    let mut f = File::open("input.txt").unwrap();
    let mut input = String::new();
    f.read_to_string(&mut input).ok();

    let rooms : Vec<Room> = input.lines().map(|line| {Room::from_line(line)}).collect();

    // grab the valid rooms
    let valid_rooms : Vec<&Room> = rooms.iter()
        .filter(|r| {r.is_valid()})
        .collect();

    // and sum the sector id using fold
    let sum = valid_rooms.iter().fold(0, |sum, r| { sum + r.sector_id});

    // for part B we're looking for a room where north pole objects are stored
    let deciphered_names : Vec<(isize, String)> = valid_rooms.iter()
        .map(|r| (r.sector_id, r.decipher() ) )
        .collect();

    // print what can be found where 💪
    for (sector_id, name) in deciphered_names {
        println!("{}can be found in sector{}", name, sector_id);
    }

    // print the answer 🎉
    println!("sum: {}", sum);
}

#[test]
fn test_rot() {
    let res = rot("mec".to_string(), 10);
    assert_eq!(res, "wom".to_string())
}

#[test]
fn test_rot_for_realsies(){
    let res = rot("qzmt zixmtkozy ivhz".to_string(), 343);
    assert_eq!(res, "very encrypted name".to_string());
}
<|endoftext|>


<commit_before>use crate::syntax::namespace::Namespace;
use crate::syntax::report::Errors;
use crate::syntax::Atom::{self, *};
use crate::syntax::{Derive, Doc, ForeignName};
use proc_macro2::{Ident, TokenStream};
use quote::ToTokens;
use syn::parse::{ParseStream, Parser as _};
use syn::{Attribute, Error, LitStr, Path, Result, Token};

// Intended usage:
//
//     let mut doc = Doc::new();
//     let mut cxx_name = None;
//     let mut rust_name = None;
//     /* ... */
//     let attrs = attrs::parse(
//         cx,
//         item.attrs,
//         attrs::Parser {
//             doc: Some(&mut doc),
//             cxx_name: Some(&mut cxx_name),
//             rust_name: Some(&mut rust_name),
//             /* ... */
//             ..Default::default()
//         },
//     );
//
#[derive(Default)]
pub struct Parser<'a> {
    pub doc: Option<&'a mut Doc>,
    pub derives: Option<&'a mut Vec<Derive>>,
    pub repr: Option<&'a mut Option<Atom>>,
    pub namespace: Option<&'a mut Namespace>,
    pub cxx_name: Option<&'a mut Option<ForeignName>>,
    pub rust_name: Option<&'a mut Option<Ident>>,

    // Suppress clippy needless_update lint ("struct update has no effect, all
    // the fields in the struct have already been specified") when preemptively
    // writing `..Default::default()`.
    pub(crate) _more: (),
}

pub fn parse(cx: &mut Errors, attrs: Vec<Attribute>, mut parser: Parser) -> OtherAttrs {
    let mut passthrough_attrs = Vec::new();
    for attr in attrs {
        if attr.path.is_ident("doc") {
            match parse_doc_attribute.parse2(attr.tokens.clone()) {
                Ok(lit) => {
                    if let Some(doc) = &mut parser.doc {
                        doc.push(lit);
                        continue;
                    }
                }
                Err(err) => {
                    cx.push(err);
                    break;
                }
            }
        } else if attr.path.is_ident("derive") {
            match attr.parse_args_with(|attr: ParseStream| parse_derive_attribute(cx, attr)) {
                Ok(attr) => {
                    if let Some(derives) = &mut parser.derives {
                        derives.extend(attr);
                        continue;
                    }
                }
                Err(err) => {
                    cx.push(err);
                    break;
                }
            }
        } else if attr.path.is_ident("repr") {
            match attr.parse_args_with(parse_repr_attribute) {
                Ok(attr) => {
                    if let Some(repr) = &mut parser.repr {
                        **repr = Some(attr);
                        continue;
                    }
                }
                Err(err) => {
                    cx.push(err);
                    break;
                }
            }
        } else if attr.path.is_ident("namespace") {
            match parse_namespace_attribute.parse2(attr.tokens.clone()) {
                Ok(attr) => {
                    if let Some(namespace) = &mut parser.namespace {
                        **namespace = attr;
                        continue;
                    }
                }
                Err(err) => {
                    cx.push(err);
                    break;
                }
            }
        } else if attr.path.is_ident("cxx_name") {
            match parse_cxx_name_attribute.parse2(attr.tokens.clone()) {
                Ok(attr) => {
                    if let Some(cxx_name) = &mut parser.cxx_name {
                        **cxx_name = Some(attr);
                        continue;
                    }
                }
                Err(err) => {
                    cx.push(err);
                    break;
                }
            }
        } else if attr.path.is_ident("rust_name") {
            match parse_rust_name_attribute.parse2(attr.tokens.clone()) {
                Ok(attr) => {
                    if let Some(rust_name) = &mut parser.rust_name {
                        **rust_name = Some(attr);
                        continue;
                    }
                }
                Err(err) => {
                    cx.push(err);
                    break;
                }
            }
        } else if attr.path.is_ident("allow")
            || attr.path.is_ident("warn")
            || attr.path.is_ident("deny")
            || attr.path.is_ident("forbid")
            || attr.path.is_ident("deprecated")
            || attr.path.is_ident("must_use")
        {
            // https://doc.rust-lang.org/reference/attributes/diagnostics.html
            passthrough_attrs.push(attr);
            continue;
        } else if attr.path.segments.len() > 1 {
            let tool = &attr.path.segments.first().unwrap().ident;
            if tool == "rustfmt" {
                // Skip, rustfmt only needs to find it in the pre-expansion source file.
                continue;
            } else if tool == "clippy" {
                passthrough_attrs.push(attr);
                continue;
            }
        }
        cx.error(attr, "unsupported attribute");
        break;
    }
    OtherAttrs(passthrough_attrs)
}

fn parse_doc_attribute(input: ParseStream) -> Result<LitStr> {
    input.parse::<Token![=]>()?;
    let lit: LitStr = input.parse()?;
    Ok(lit)
}

fn parse_derive_attribute(cx: &mut Errors, input: ParseStream) -> Result<Vec<Derive>> {
    let paths = input.parse_terminated::<Path, Token![,]>(Path::parse_mod_style)?;

    let mut derives = Vec::new();
    for path in paths {
        if let Some(ident) = path.get_ident() {
            if let Some(derive) = Derive::from(ident) {
                derives.push(derive);
                continue;
            }
        }
        cx.error(path, "unsupported derive");
    }
    Ok(derives)
}

fn parse_repr_attribute(input: ParseStream) -> Result<Atom> {
    let begin = input.cursor();
    let ident: Ident = input.parse()?;
    if let Some(atom) = Atom::from(&ident) {
        match atom {
            U8 | U16 | U32 | U64 | Usize | I8 | I16 | I32 | I64 | Isize if input.is_empty() => {
                return Ok(atom);
            }
            _ => {}
        }
    }
    Err(Error::new_spanned(
        begin.token_stream(),
        "unrecognized repr",
    ))
}

fn parse_namespace_attribute(input: ParseStream) -> Result<Namespace> {
    input.parse::<Token![=]>()?;
    let namespace = input.parse::<Namespace>()?;
    Ok(namespace)
}

fn parse_cxx_name_attribute(input: ParseStream) -> Result<ForeignName> {
    input.parse::<Token![=]>()?;
    if input.peek(LitStr) {
        let lit: LitStr = input.parse()?;
        ForeignName::parse(&lit.value(), lit.span())
    } else {
        let ident: Ident = input.parse()?;
        ForeignName::parse(&ident.to_string(), ident.span())
    }
}

fn parse_rust_name_attribute(input: ParseStream) -> Result<Ident> {
    input.parse::<Token![=]>()?;
    if input.peek(LitStr) {
        let lit: LitStr = input.parse()?;
        lit.parse()
    } else {
        input.parse()
    }
}

pub struct OtherAttrs(Vec<Attribute>);

impl OtherAttrs {
    pub fn none() -> Self {
        OtherAttrs(Vec::new())
    }
}

impl ToTokens for OtherAttrs {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        for attr in &self.0 {
            attr.to_tokens(tokens);
        }
    }
}
<commit_msg>Support inner attributes inside bridge<commit_after>use crate::syntax::namespace::Namespace;
use crate::syntax::report::Errors;
use crate::syntax::Atom::{self, *};
use crate::syntax::{Derive, Doc, ForeignName};
use proc_macro2::{Ident, TokenStream};
use quote::ToTokens;
use syn::parse::{ParseStream, Parser as _};
use syn::{Attribute, Error, LitStr, Path, Result, Token};

// Intended usage:
//
//     let mut doc = Doc::new();
//     let mut cxx_name = None;
//     let mut rust_name = None;
//     /* ... */
//     let attrs = attrs::parse(
//         cx,
//         item.attrs,
//         attrs::Parser {
//             doc: Some(&mut doc),
//             cxx_name: Some(&mut cxx_name),
//             rust_name: Some(&mut rust_name),
//             /* ... */
//             ..Default::default()
//         },
//     );
//
#[derive(Default)]
pub struct Parser<'a> {
    pub doc: Option<&'a mut Doc>,
    pub derives: Option<&'a mut Vec<Derive>>,
    pub repr: Option<&'a mut Option<Atom>>,
    pub namespace: Option<&'a mut Namespace>,
    pub cxx_name: Option<&'a mut Option<ForeignName>>,
    pub rust_name: Option<&'a mut Option<Ident>>,

    // Suppress clippy needless_update lint ("struct update has no effect, all
    // the fields in the struct have already been specified") when preemptively
    // writing `..Default::default()`.
    pub(crate) _more: (),
}

pub fn parse(cx: &mut Errors, attrs: Vec<Attribute>, mut parser: Parser) -> OtherAttrs {
    let mut passthrough_attrs = Vec::new();
    for attr in attrs {
        if attr.path.is_ident("doc") {
            match parse_doc_attribute.parse2(attr.tokens.clone()) {
                Ok(lit) => {
                    if let Some(doc) = &mut parser.doc {
                        doc.push(lit);
                        continue;
                    }
                }
                Err(err) => {
                    cx.push(err);
                    break;
                }
            }
        } else if attr.path.is_ident("derive") {
            match attr.parse_args_with(|attr: ParseStream| parse_derive_attribute(cx, attr)) {
                Ok(attr) => {
                    if let Some(derives) = &mut parser.derives {
                        derives.extend(attr);
                        continue;
                    }
                }
                Err(err) => {
                    cx.push(err);
                    break;
                }
            }
        } else if attr.path.is_ident("repr") {
            match attr.parse_args_with(parse_repr_attribute) {
                Ok(attr) => {
                    if let Some(repr) = &mut parser.repr {
                        **repr = Some(attr);
                        continue;
                    }
                }
                Err(err) => {
                    cx.push(err);
                    break;
                }
            }
        } else if attr.path.is_ident("namespace") {
            match parse_namespace_attribute.parse2(attr.tokens.clone()) {
                Ok(attr) => {
                    if let Some(namespace) = &mut parser.namespace {
                        **namespace = attr;
                        continue;
                    }
                }
                Err(err) => {
                    cx.push(err);
                    break;
                }
            }
        } else if attr.path.is_ident("cxx_name") {
            match parse_cxx_name_attribute.parse2(attr.tokens.clone()) {
                Ok(attr) => {
                    if let Some(cxx_name) = &mut parser.cxx_name {
                        **cxx_name = Some(attr);
                        continue;
                    }
                }
                Err(err) => {
                    cx.push(err);
                    break;
                }
            }
        } else if attr.path.is_ident("rust_name") {
            match parse_rust_name_attribute.parse2(attr.tokens.clone()) {
                Ok(attr) => {
                    if let Some(rust_name) = &mut parser.rust_name {
                        **rust_name = Some(attr);
                        continue;
                    }
                }
                Err(err) => {
                    cx.push(err);
                    break;
                }
            }
        } else if attr.path.is_ident("allow")
            || attr.path.is_ident("warn")
            || attr.path.is_ident("deny")
            || attr.path.is_ident("forbid")
            || attr.path.is_ident("deprecated")
            || attr.path.is_ident("must_use")
        {
            // https://doc.rust-lang.org/reference/attributes/diagnostics.html
            passthrough_attrs.push(attr);
            continue;
        } else if attr.path.segments.len() > 1 {
            let tool = &attr.path.segments.first().unwrap().ident;
            if tool == "rustfmt" {
                // Skip, rustfmt only needs to find it in the pre-expansion source file.
                continue;
            } else if tool == "clippy" {
                passthrough_attrs.push(attr);
                continue;
            }
        }
        cx.error(attr, "unsupported attribute");
        break;
    }
    OtherAttrs(passthrough_attrs)
}

fn parse_doc_attribute(input: ParseStream) -> Result<LitStr> {
    input.parse::<Token![=]>()?;
    let lit: LitStr = input.parse()?;
    Ok(lit)
}

fn parse_derive_attribute(cx: &mut Errors, input: ParseStream) -> Result<Vec<Derive>> {
    let paths = input.parse_terminated::<Path, Token![,]>(Path::parse_mod_style)?;

    let mut derives = Vec::new();
    for path in paths {
        if let Some(ident) = path.get_ident() {
            if let Some(derive) = Derive::from(ident) {
                derives.push(derive);
                continue;
            }
        }
        cx.error(path, "unsupported derive");
    }
    Ok(derives)
}

fn parse_repr_attribute(input: ParseStream) -> Result<Atom> {
    let begin = input.cursor();
    let ident: Ident = input.parse()?;
    if let Some(atom) = Atom::from(&ident) {
        match atom {
            U8 | U16 | U32 | U64 | Usize | I8 | I16 | I32 | I64 | Isize if input.is_empty() => {
                return Ok(atom);
            }
            _ => {}
        }
    }
    Err(Error::new_spanned(
        begin.token_stream(),
        "unrecognized repr",
    ))
}

fn parse_namespace_attribute(input: ParseStream) -> Result<Namespace> {
    input.parse::<Token![=]>()?;
    let namespace = input.parse::<Namespace>()?;
    Ok(namespace)
}

fn parse_cxx_name_attribute(input: ParseStream) -> Result<ForeignName> {
    input.parse::<Token![=]>()?;
    if input.peek(LitStr) {
        let lit: LitStr = input.parse()?;
        ForeignName::parse(&lit.value(), lit.span())
    } else {
        let ident: Ident = input.parse()?;
        ForeignName::parse(&ident.to_string(), ident.span())
    }
}

fn parse_rust_name_attribute(input: ParseStream) -> Result<Ident> {
    input.parse::<Token![=]>()?;
    if input.peek(LitStr) {
        let lit: LitStr = input.parse()?;
        lit.parse()
    } else {
        input.parse()
    }
}

pub struct OtherAttrs(Vec<Attribute>);

impl OtherAttrs {
    pub fn none() -> Self {
        OtherAttrs(Vec::new())
    }
}

impl ToTokens for OtherAttrs {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        for attr in &self.0 {
            let Attribute {
                pound_token,
                style,
                bracket_token,
                path,
                tokens: attr_tokens,
            } = attr;
            pound_token.to_tokens(tokens);
            let _ = style; // ignore; render outer and inner attrs both as outer
            bracket_token.surround(tokens, |tokens| {
                path.to_tokens(tokens);
                attr_tokens.to_tokens(tokens);
            });
        }
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2016, Matthew O'Connor
extern crate system_zero_core;

use std::io;
use std::io::{BufRead, Write};
use system_zero_core::*;
use system_zero_core::ast::*;

const PROMPT: &'static str = "> ";

fn prompt() -> () {
  print!("{}", PROMPT);
  io::stdout().flush().unwrap()
}

type Env = Vec<Def>;

fn repl() -> io::Result<()> {
  let mut history: Vec<String> = vec![];
  let mut env = Env::new();
  prompt();
  let stdin = io::stdin();
  for line in stdin.lock().lines() {
    let line = line.unwrap();
    if line == ":env" {
      println!("{:?}", env);
    } else if line == ":history" {
      println!("{:?}", history);
    } else if line == ":quit" {
      break
    } else if line.len() > 0 {
      history.push(line.clone());
      match parse_one(&line[..]) {
        Ok(one) => {
          match one {
            One::Def(ref def) => {
              print!("{:?}", one);
              env.push(def.clone());
            }
            One::Expr(_) => print!("{:?}", one.normalize()),
          }
        }
        Err(error) => println!("Couldn't parse: {:?}", error),
      }
    }
    prompt();
  }
  Ok(())
}

fn main() {
  println!("szi - system zero interpreter");
  println!("Copyright (c) 2016, Matthew O'Connor <thegreendragon@gmail.com>");
  // Return 0 if repl returned Ok.
  std::process::exit(!repl().is_ok() as i32)
}
<commit_msg>Begin implementing Env.<commit_after>// Copyright (c) 2016, Matthew O'Connor
extern crate system_zero_core;

use std::io;
use std::io::{BufRead, Write};
use system_zero_core::*;
use system_zero_core::ast::*;

const PROMPT: &'static str = "> ";

fn prompt() -> () {
  print!("{}", PROMPT);
  io::stdout().flush().unwrap()
}

#[derive(Debug, Eq, PartialEq)]
struct Env(Vec<Def>);

impl IntoIterator for Env {
    type Item = Def;
    type IntoIter = ::std::vec::IntoIter<Def>;

    fn into_iter(self) -> Self::IntoIter {
        self.0.into_iter()
    }
}

impl Env {
  fn new() -> Env {
    Env(Vec::new())
  }

  fn get_val(self, var: &Var) -> Option<Def> {
    self.into_iter().rev().find(|&ref def| {
      match *def {
        Def::Val(ref v, _) => *v == *var,
        Def::Ty(_, _) => false,
      }
    })
  }

  fn push(&mut self, def: Def) {
    self.0.push(def)
  }
}

fn repl() -> io::Result<()> {
  let mut history: Vec<String> = vec![];
  let mut env = Env::new();
  prompt();
  let stdin = io::stdin();
  for line in stdin.lock().lines() {
    let line = line.unwrap();
    if line == ":env" {
      println!("{:?}", env);
    } else if line == ":history" {
      println!("{:?}", history);
    } else if line == ":quit" {
      break;
    } else if line.len() > 0 {
      history.push(line.clone());
      match parse_one(&line[..]) {
        Ok(one) => {
          match one {
            One::Def(ref def) => {
              print!("{:?}", one);
              env.push(def.clone());
            }
            One::Expr(_) => print!("{:?}", one.normalize()),
          }
        }
        Err(error) => println!("Couldn't parse: {:?}", error),
      }
    }
    prompt();
  }
  Ok(())
}

fn main() {
  println!("szi - system zero interpreter");
  println!("Copyright (c) 2016, Matthew O'Connor <thegreendragon@gmail.com>");
  // Return 0 if repl returned Ok.
  std::process::exit(!repl().is_ok() as i32)
}
<|endoftext|>


<commit_before>use std::fmt;

// TODO: figure out how to use failure for error (or some other pattern)
#[derive(Debug)]
pub enum Error {
    NotAnInstruction,
    LongInstruction,
}

/// FlowMeta is metadata that describes code flow for an instruction.
///
/// The metadata is packed into a single byte, with the expectation that,
///  there may be one `FlowMeta` instance for each (code) offset in a program.
/// However, the you should not be worried about the representation, just the interface.
#[derive(Debug)]
pub struct FlowMeta(u8);

impl FlowMeta {
    /// Create a new FlowMeta.
    ///
    /// Args:
    ///   - insn_length: size of instruction, or 0 if not an instruction, or 0xF if 0xF or larger.
    ///   - does_fallthrough: is there a fallthrough code flow to the next instruction?
    ///   - has_xrefs_from: are there code xrefs from this instruction (non-fallthrough)?
    pub fn new(insn_length: u8, does_fallthrough: bool, has_xrefs_from: bool) -> FlowMeta {
        let len = if insn_length >= 0xF {
            // instrunction length too long
            0x0F
        } else if insn_length == 0x0 {
            // not an instruction
            0x00
        } else {
            // good flow
            insn_length & 0x0F
        };

        let fallthrough = if does_fallthrough {
            0b0001_0000
        } else {
            0b0000_0000
        };

        let xfrom = if has_xrefs_from {
            0b0010_0000
        } else {
            0b0000_0000
        };

        FlowMeta(len | fallthrough | xfrom)
    }

    /// Fetch the cached length of the instruction.
    ///
    /// If longer than 14 bytes, then will return `Error::LongInstruction` and you'll have to decode
    ///  the instruction yourself.
    ///
    /// Errors:
    ///   - NotAnInstruction
    ///   - LongInstruction
    ///
    /// ```
    /// use matches::matches;
    /// use lancelot::flowmeta::*;
    /// assert_eq!(FlowMeta::new(0x01, true, true).get_insn_length().unwrap(),
    ///            0x01);
    /// assert!(matches!(FlowMeta::new(0x00, true, true).get_insn_length().err().unwrap(),
    ///                  Error::NotAnInstruction));
    /// assert!(matches!(FlowMeta::new(0x0F, true, true).get_insn_length().err().unwrap(),
    ///                  Error::LongInstruction));
    /// ```
    pub fn get_insn_length(&self) -> Result<u8, Error> {
        let v = self.0 & 0b0000_1111;
        match v {
            0x00 => Err(Error::NotAnInstruction),
            0x0F => Err(Error::LongInstruction),
            v @ _ => Ok(v)
        }
    }

    /// Does the instruction fallthrough?
    pub fn does_fallthrough(&self) -> bool {
        self.0 & 0b0001_0000 > 0
    }

    /// Does the instruction have flow xrefs from it?
    /// This does not include the fallthrough flow.
    pub fn has_xrefs_from(&self) -> bool {
        self.0 & 0b0010_0000 > 0
    }

    /// Does the instruction have flow xrefs to it?
    ///
    /// ```
    /// use lancelot::flowmeta::*;
    /// assert_eq!(FlowMeta::new(0x01, false, false).has_xrefs_to(), false);
    /// ```
    pub fn has_xrefs_to(&self) -> bool {
        self.0 & 0b0100_0000 > 0
    }

    /// Set the bit indicating that there are flow xrefs to this instruction.
    ///
    /// ```
    /// use lancelot::flowmeta::*;
    /// let mut m = FlowMeta::new(0x01, false, false);
    /// assert_eq!(m.has_xrefs_to(), false);
    ///
    /// m.set_xrefs_to();
    /// assert_eq!(m.has_xrefs_to(), true);
    /// ```
    pub fn set_xrefs_to(&mut self) {
        self.0 = self.0 | 0b0100_0000;
    }

    /// Unset the bit indicating that there are flow xrefs to this instruction.
    ///
    /// ```
    /// use lancelot::flowmeta::*;
    /// let mut m = FlowMeta::new(0x01, false, false);
    /// assert_eq!(m.has_xrefs_to(), false);
    ///
    /// m.set_xrefs_to();
    /// assert_eq!(m.has_xrefs_to(), true);
    ///
    /// m.unset_xrefs_to();
    /// assert_eq!(m.has_xrefs_to(), false);
    /// ```
    pub fn unset_xrefs_to(&mut self) {
        self.0 = self.0 & 0b1011_1111
    }
}

impl fmt::Display for FlowMeta {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "FlowMeta{{length: {}, fallthrough: {}, xrefs to: {}, xrefs from: {}}}",
               match self.get_insn_length() {
                   Ok(v) => format!("0x{:x}", v),
                   Err(Error::LongInstruction) => ">=0xF".to_string(),
                   Err(Error::NotAnInstruction) => "not an instruction".to_string(),
               },
               self.does_fallthrough(),
               self.has_xrefs_to(),
               self.has_xrefs_from())
    }
}
<commit_msg>flowmeta: add `.zero()` ctor<commit_after>use std::fmt;

// TODO: figure out how to use failure for error (or some other pattern)
#[derive(Debug)]
pub enum Error {
    NotAnInstruction,
    LongInstruction,
}

/// FlowMeta is metadata that describes code flow for an instruction.
///
/// The metadata is packed into a single byte, with the expectation that,
///  there may be one `FlowMeta` instance for each (code) offset in a program.
/// However, the you should not be worried about the representation, just the interface.
#[derive(Debug, Clone)]
pub struct FlowMeta(u8);

impl FlowMeta {
    pub fn zero() -> FlowMeta {
        FlowMeta(0x0)
    }

    /// Create a new FlowMeta.
    ///
    /// Args:
    ///   - insn_length: size of instruction, or 0 if not an instruction, or 0xF if 0xF or larger.
    ///   - does_fallthrough: is there a fallthrough code flow to the next instruction?
    ///   - has_xrefs_from: are there code xrefs from this instruction (non-fallthrough)?
    pub fn new(insn_length: u8, does_fallthrough: bool, has_xrefs_from: bool) -> FlowMeta {
        let len = if insn_length >= 0xF {
            // instrunction length too long
            0x0F
        } else if insn_length == 0x0 {
            // not an instruction
            0x00
        } else {
            // good flow
            insn_length & 0x0F
        };

        let fallthrough = if does_fallthrough {
            0b0001_0000
        } else {
            0b0000_0000
        };

        let xfrom = if has_xrefs_from {
            0b0010_0000
        } else {
            0b0000_0000
        };

        FlowMeta(len | fallthrough | xfrom)
    }

    /// Fetch the cached length of the instruction.
    ///
    /// If longer than 14 bytes, then will return `Error::LongInstruction` and you'll have to decode
    ///  the instruction yourself.
    ///
    /// Errors:
    ///   - NotAnInstruction
    ///   - LongInstruction
    ///
    /// ```
    /// use matches::matches;
    /// use lancelot::flowmeta::*;
    /// assert_eq!(FlowMeta::new(0x01, true, true).get_insn_length().unwrap(),
    ///            0x01);
    /// assert!(matches!(FlowMeta::new(0x00, true, true).get_insn_length().err().unwrap(),
    ///                  Error::NotAnInstruction));
    /// assert!(matches!(FlowMeta::new(0x0F, true, true).get_insn_length().err().unwrap(),
    ///                  Error::LongInstruction));
    /// ```
    pub fn get_insn_length(&self) -> Result<u8, Error> {
        let v = self.0 & 0b0000_1111;
        match v {
            0x00 => Err(Error::NotAnInstruction),
            0x0F => Err(Error::LongInstruction),
            v @ _ => Ok(v)
        }
    }

    /// Does the instruction fallthrough?
    pub fn does_fallthrough(&self) -> bool {
        self.0 & 0b0001_0000 > 0
    }

    /// Does the instruction have flow xrefs from it?
    /// This does not include the fallthrough flow.
    pub fn has_xrefs_from(&self) -> bool {
        self.0 & 0b0010_0000 > 0
    }

    /// Does the instruction have flow xrefs to it?
    ///
    /// ```
    /// use lancelot::flowmeta::*;
    /// assert_eq!(FlowMeta::new(0x01, false, false).has_xrefs_to(), false);
    /// ```
    pub fn has_xrefs_to(&self) -> bool {
        self.0 & 0b0100_0000 > 0
    }

    /// Set the bit indicating that there are flow xrefs to this instruction.
    ///
    /// ```
    /// use lancelot::flowmeta::*;
    /// let mut m = FlowMeta::new(0x01, false, false);
    /// assert_eq!(m.has_xrefs_to(), false);
    ///
    /// m.set_xrefs_to();
    /// assert_eq!(m.has_xrefs_to(), true);
    /// ```
    pub fn set_xrefs_to(&mut self) {
        self.0 = self.0 | 0b0100_0000;
    }

    /// Unset the bit indicating that there are flow xrefs to this instruction.
    ///
    /// ```
    /// use lancelot::flowmeta::*;
    /// let mut m = FlowMeta::new(0x01, false, false);
    /// assert_eq!(m.has_xrefs_to(), false);
    ///
    /// m.set_xrefs_to();
    /// assert_eq!(m.has_xrefs_to(), true);
    ///
    /// m.unset_xrefs_to();
    /// assert_eq!(m.has_xrefs_to(), false);
    /// ```
    pub fn unset_xrefs_to(&mut self) {
        self.0 = self.0 & 0b1011_1111
    }
}

impl fmt::Display for FlowMeta {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "FlowMeta{{length: {}, fallthrough: {}, xrefs to: {}, xrefs from: {}}}",
               match self.get_insn_length() {
                   Ok(v) => format!("0x{:x}", v),
                   Err(Error::LongInstruction) => ">=0xF".to_string(),
                   Err(Error::NotAnInstruction) => "not an instruction".to_string(),
               },
               self.does_fallthrough(),
               self.has_xrefs_to(),
               self.has_xrefs_from())
    }
}
<|endoftext|>


<commit_before><commit_msg>Divide drop macro for more consistent docs<commit_after><|endoftext|>


<commit_before>use std::env;
use std::process::Command;

fn main() {
    if std::env::var("DOCS_RS").is_ok() {
        // Skip everything when building docs on docs.rs
        return;
    }

    let current_dir = std::env::current_dir()
        .unwrap()
        .into_os_string()
        .into_string()
        .unwrap();
    let out_dir = env::var("OUT_DIR").unwrap();

    // Add C++ std lib
    #[cfg(target_os = "linux")]
    {
        let path = Command::new(env::var("CXX").unwrap_or_else(|_| "c++".to_string()))
            .arg("--print-file-name=libstdc++.a")
            .output()
            .expect("Failed to start")
            .stdout;
        println!(
            "cargo:rustc-link-search=native={}",
            String::from_utf8_lossy(&path)
                .trim()
                .strip_suffix("libstdc++.a")
                .expect("Failed to strip suffix"),
        );
        println!("cargo:rustc-link-lib=static=stdc++");
    }
    #[cfg(any(
        target_os = "freebsd",
        target_os = "dragonfly",
        target_os = "openbsd",
        target_os = "netbsd"
    ))]
    {
        let path = Command::new(env::var("CXX").unwrap_or_else(|_| "c++".to_string()))
            .arg("--print-file-name=libc++.a")
            .output()
            .expect("Failed to start")
            .stdout;
        println!(
            "cargo:rustc-link-search=native={}",
            String::from_utf8_lossy(&path)
                .trim()
                .strip_suffix("libc++.a")
                .expect("Failed to strip suffix"),
        );
        println!("cargo:rustc-link-lib=static=c++");
    }
    #[cfg(target_os = "macos")]
    {
        let path = Command::new("xcrun")
            .args(&[
                "--show-sdk-path"
            ])
            .output()
            .expect("Failed to start")
            .stdout;

        let libpath = format!("{}/usr/lib", String::from_utf8(path).expect("Failed to decode path").trim());
        println!("cargo:rustc-link-search={}", libpath);
        println!("cargo:rustc-cdylib-link-arg=-stdlib=libc++");
        println!("cargo:rustc-link-lib=dylib=c++");
        println!("cargo:rustc-link-lib=dylib=c++abi");
    }
    #[cfg(target_os = "windows")]
    {
        panic!("Building on Windows is not currently supported");
        // TODO: Didn't bother, feel free to PR
    }

    // The build here is a bit awkward since we can't just specify custom target directory as
    // openssl will fail to build with `make[1]: /bin/sh: Argument list too long` due to large
    // number of files. So instead we build in place, copy files to out directory and then clean
    // after ourselves
    {
        // Build
        if !Command::new("make")
            .arg("libmediasoup-worker")
            .env("PYTHONDONTWRITEBYTECODE", "1")
            .spawn()
            .expect("Failed to start")
            .wait()
            .expect("Wasn't running")
            .success()
        {
            panic!("Failed to build libmediasoup-worker")
        }

        for file in &[
            "libnetstring.a",
            "libuv.a",
            "libopenssl.a",
            "libsrtp.a",
            "libusrsctp.a",
            "libwebrtc.a",
            "libmediasoup-worker.a",
            "libabseil.a",
            #[cfg(windows)]
            "libgetopt.a",
        ] {
            std::fs::copy(
                format!("{}/out/Release/{}", current_dir, file),
                format!("{}/{}", out_dir, file),
            )
            .unwrap_or_else(|_| {
                panic!(
                    "Failed to copy static library from {}/out/Release/{} to {}/{}",
                    current_dir, file, out_dir, file
                )
            });
        }

        if env::var("KEEP_BUILD_ARTIFACTS") != Ok("1".to_string()) {
            // Clean
            if !Command::new("make")
                .arg("clean-all")
                .spawn()
                .expect("Failed to start")
                .wait()
                .expect("Wasn't running")
                .success()
            {
                panic!("Failed to clean libmediasoup-worker")
            }
        }
    }

    println!("cargo:rustc-link-lib=static=netstring");
    println!("cargo:rustc-link-lib=static=uv");
    println!("cargo:rustc-link-lib=static=openssl");
    println!("cargo:rustc-link-lib=static=srtp");
    println!("cargo:rustc-link-lib=static=usrsctp");
    println!("cargo:rustc-link-lib=static=webrtc");
    println!("cargo:rustc-link-lib=static=mediasoup-worker");
    println!("cargo:rustc-link-lib=static=abseil");
    #[cfg(windows)]
    println!("cargo:rustc-link-lib=static=getopt");
    println!("cargo:rustc-link-search=native={}", out_dir);
}
<commit_msg>fix format<commit_after>use std::env;
use std::process::Command;

fn main() {
    if std::env::var("DOCS_RS").is_ok() {
        // Skip everything when building docs on docs.rs
        return;
    }

    let current_dir = std::env::current_dir()
        .unwrap()
        .into_os_string()
        .into_string()
        .unwrap();
    let out_dir = env::var("OUT_DIR").unwrap();

    // Add C++ std lib
    #[cfg(target_os = "linux")]
    {
        let path = Command::new(env::var("CXX").unwrap_or_else(|_| "c++".to_string()))
            .arg("--print-file-name=libstdc++.a")
            .output()
            .expect("Failed to start")
            .stdout;
        println!(
            "cargo:rustc-link-search=native={}",
            String::from_utf8_lossy(&path)
                .trim()
                .strip_suffix("libstdc++.a")
                .expect("Failed to strip suffix"),
        );
        println!("cargo:rustc-link-lib=static=stdc++");
    }
    #[cfg(any(
        target_os = "freebsd",
        target_os = "dragonfly",
        target_os = "openbsd",
        target_os = "netbsd"
    ))]
    {
        let path = Command::new(env::var("CXX").unwrap_or_else(|_| "c++".to_string()))
            .arg("--print-file-name=libc++.a")
            .output()
            .expect("Failed to start")
            .stdout;
        println!(
            "cargo:rustc-link-search=native={}",
            String::from_utf8_lossy(&path)
                .trim()
                .strip_suffix("libc++.a")
                .expect("Failed to strip suffix"),
        );
        println!("cargo:rustc-link-lib=static=c++");
    }
    #[cfg(target_os = "macos")]
    {
        let path = Command::new("xcrun")
            .args(&["--show-sdk-path"])
            .output()
            .expect("Failed to start")
            .stdout;

        let libpath = format!(
            "{}/usr/lib",
            String::from_utf8(path)
                .expect("Failed to decode path")
                .trim()
        );
        println!("cargo:rustc-link-search={}", libpath);
        println!("cargo:rustc-cdylib-link-arg=-stdlib=libc++");
        println!("cargo:rustc-link-lib=dylib=c++");
        println!("cargo:rustc-link-lib=dylib=c++abi");
    }
    #[cfg(target_os = "windows")]
    {
        panic!("Building on Windows is not currently supported");
        // TODO: Didn't bother, feel free to PR
    }

    // The build here is a bit awkward since we can't just specify custom target directory as
    // openssl will fail to build with `make[1]: /bin/sh: Argument list too long` due to large
    // number of files. So instead we build in place, copy files to out directory and then clean
    // after ourselves
    {
        // Build
        if !Command::new("make")
            .arg("libmediasoup-worker")
            .env("PYTHONDONTWRITEBYTECODE", "1")
            .spawn()
            .expect("Failed to start")
            .wait()
            .expect("Wasn't running")
            .success()
        {
            panic!("Failed to build libmediasoup-worker")
        }

        for file in &[
            "libnetstring.a",
            "libuv.a",
            "libopenssl.a",
            "libsrtp.a",
            "libusrsctp.a",
            "libwebrtc.a",
            "libmediasoup-worker.a",
            "libabseil.a",
            #[cfg(windows)]
            "libgetopt.a",
        ] {
            std::fs::copy(
                format!("{}/out/Release/{}", current_dir, file),
                format!("{}/{}", out_dir, file),
            )
            .unwrap_or_else(|_| {
                panic!(
                    "Failed to copy static library from {}/out/Release/{} to {}/{}",
                    current_dir, file, out_dir, file
                )
            });
        }

        if env::var("KEEP_BUILD_ARTIFACTS") != Ok("1".to_string()) {
            // Clean
            if !Command::new("make")
                .arg("clean-all")
                .spawn()
                .expect("Failed to start")
                .wait()
                .expect("Wasn't running")
                .success()
            {
                panic!("Failed to clean libmediasoup-worker")
            }
        }
    }

    println!("cargo:rustc-link-lib=static=netstring");
    println!("cargo:rustc-link-lib=static=uv");
    println!("cargo:rustc-link-lib=static=openssl");
    println!("cargo:rustc-link-lib=static=srtp");
    println!("cargo:rustc-link-lib=static=usrsctp");
    println!("cargo:rustc-link-lib=static=webrtc");
    println!("cargo:rustc-link-lib=static=mediasoup-worker");
    println!("cargo:rustc-link-lib=static=abseil");
    #[cfg(windows)]
    println!("cargo:rustc-link-lib=static=getopt");
    println!("cargo:rustc-link-search=native={}", out_dir);
}
<|endoftext|>


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

#[cfg(not(stage1))]
#[phase(syntax)]
extern crate regex_macros;

// Dirty hack: During stage1, test dynamic regexs. For stage2, we test
// native regexs.
#[cfg(stage1)]
macro_rules! regex(
    ($re:expr) => (
        match ::regex::Regex::new($re) {
            Ok(re) => re,
            Err(err) => fail!("{}", err),
        }
    );
)

mod bench;
mod tests;

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

#[cfg(not(stage1))]
#[phase(syntax)]
extern crate regex_macros;

#[cfg(not(stage1))]
#[path = "bench.rs"]
mod native_bench;

#[cfg(not(stage1))]
#[path = "tests.rs"]
mod native_tests;

// Due to macro scoping rules, this definition only applies for the modules
// defined below. Effectively, it allows us to use the same tests for both
// native and dynamic regexes.
macro_rules! regex(
    ($re:expr) => (
        match ::regex::Regex::new($re) {
            Ok(re) => re,
            Err(err) => fail!("{}", err),
        }
    );
)

#[path = "bench.rs"]
mod dynamic_bench;
#[path = "tests.rs"]
mod dynamic_tests;

<|endoftext|>