<commit_before>use std::iter;

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct Position {
  pub offset: usize,
  pub line: usize,
  pub column: usize,
}

impl Position {
  pub fn new(offset: usize, line: usize, column: usize) -> Self {
    Position{
      offset: offset,
      line: line,
      column: column,
    }
  }

  pub fn zero() -> Self {
    Position::new(0, 1, 1)
  }

  pub fn advance(&mut self, char: char) {
    self.offset += 1;
    if char == '\n' {
      self.line += 1;
      self.column = 1;
    } else {
      self.column += 1;
    }
  }
}

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct Lexeme<'a, T>(pub T, pub LexemeF<'a>);

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum LexemeF<'a> {
  Identifier(&'a str),

  End,
  False,
  Fun,
  True,

  Arrow,
  LeftParenthesis,
  RightParenthesis,

  Str(&'a str),
}

#[derive(Clone, Debug)]
pub struct Lexer<'a> {
  input: &'a str,
  position: Position,
  abort: bool,
}

impl<'a> Lexer<'a> {
  pub fn new(input: &'a str) -> Self {
    Lexer{input: input, position: Position::zero(), abort: false}
  }

  pub fn next_position(&self) -> Position {
    self.position
  }

  fn peek_char(&self) -> Option<char> {
    self.input.chars().next()
  }

  fn take_char(&mut self) -> Option<char> {
    let mut chars = self.input.chars();
    let result = chars.next();
    for c in result {
      self.position.advance(c);
    }
    self.input = chars.as_str();
    result
  }

  fn take_chars_while<F>(&mut self, pred: F) -> &'a str
    where F: Fn(char) -> bool {
    let terminator = (self.input.len(), '\n');
    let mut len = 0;
    for (i, c) in self.input.char_indices().chain(iter::once(terminator)) {
      len = i;
      if !pred(c) {
        break;
      }
      self.position.advance(c);
    }
    let result = unsafe { self.input.slice_unchecked(0, len) };
    self.input = unsafe { self.input.slice_unchecked(len, self.input.len()) };
    result
  }
}

impl<'a> Iterator for Lexer<'a> {
  type Item = Result<Lexeme<'a, Position>, Position>;

  fn next(&mut self) -> Option<Self::Item> {
    if self.abort {
      return None;
    }
    self.take_chars_while(is_whitespace);
    let position = self.position;
    match self.peek_char() {
      None =>
        None,
      Some('(') => {
        self.take_char();
        Some(Ok(Lexeme(position, LexemeF::LeftParenthesis)))
      },
      Some(')') => {
        self.take_char();
        Some(Ok(Lexeme(position, LexemeF::RightParenthesis)))
      },
      Some('-') => {
        self.take_char();
        if self.peek_char() == Some('>') {
          self.take_char();
          Some(Ok(Lexeme(position, LexemeF::Arrow)))
        } else {
          self.abort = true;
          Some(Err(position))
        }
      },
      Some('"') => {
        self.take_char();
        let value = self.take_chars_while(|c| c != '"');
        if self.take_char() != Some('"') {
          Some(Err(position))
        } else {
          Some(Ok(Lexeme(position, LexemeF::Str(value))))
        }
      },
      Some(c) if is_identifier_start(c) => {
        let name = self.take_chars_while(is_identifier_continue);
        Some(Ok(Lexeme(position, match name {
          "end"   => LexemeF::End,
          "false" => LexemeF::False,
          "fun"   => LexemeF::Fun,
          "true"  => LexemeF::True,
          _ => LexemeF::Identifier(name),
        })))
      },
      Some(_) => {
        self.abort = true;
        Some(Err(position))
      },
    }
  }
}

fn is_whitespace(char: char) -> bool {
  char.is_whitespace()
}

// FIXME: Switch to is_xid_start once it is stable.
fn is_identifier_start(char: char) -> bool {
  char.is_alphabetic()
  || char == '_'
}

// FIXME: Switch to is_xid_continue once it is stable.
fn is_identifier_continue(char: char) -> bool {
  is_identifier_start(char)
  || char.is_numeric()
  || char == '\''
}

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

  #[test]
  fn test_empty() {
    assert_eq!(
      Lexer::new("").collect::<Vec<_>>(),
      vec![]
    );
  }

  #[test]
  fn test_whitespace() {
    assert_eq!(
      Lexer::new(" \t\r\n").collect::<Vec<_>>(),
      vec![]
    );
  }

  #[test]
  fn test_identifier() {
    assert_eq!(
      Lexer::new("a").collect::<Vec<_>>(),
      vec![Ok(Lexeme(Position::new(0, 1, 1), LexemeF::Identifier("a")))]
    );
    assert_eq!(
      Lexer::new("ab").collect::<Vec<_>>(),
      vec![Ok(Lexeme(Position::new(0, 1, 1), LexemeF::Identifier("ab")))]
    );
    assert_eq!(
      Lexer::new("ab_c").collect::<Vec<_>>(),
      vec![Ok(Lexeme(Position::new(0, 1, 1), LexemeF::Identifier("ab_c")))]
    );
    assert_eq!(
      Lexer::new("ab_cd'").collect::<Vec<_>>(),
      vec![Ok(Lexeme(Position::new(0, 1, 1), LexemeF::Identifier("ab_cd'")))]
    );
    assert_eq!(
      Lexer::new("funend").collect::<Vec<_>>(),
      vec![Ok(Lexeme(Position::new(0, 1, 1), LexemeF::Identifier("funend")))]
    );
  }

  #[test]
  fn test_identifiers() {
    assert_eq!(
      Lexer::new("ab cd").collect::<Vec<_>>(),
      vec![
        Ok(Lexeme(Position::new(0, 1, 1), LexemeF::Identifier("ab"))),
        Ok(Lexeme(Position::new(3, 1, 4), LexemeF::Identifier("cd"))),
      ]
    );
  }

  #[test]
  fn test_keywords() {
    assert_eq!(
      Lexer::new("fun end").collect::<Vec<_>>(),
      vec![
        Ok(Lexeme(Position::new(0, 1, 1), LexemeF::Fun)),
        Ok(Lexeme(Position::new(4, 1, 5), LexemeF::End)),
      ]
    );
  }

  #[test]
  fn test_punctuation() {
    assert_eq!(
      Lexer::new("()->").collect::<Vec<_>>(),
      vec![
        Ok(Lexeme(Position::new(0, 1, 1), LexemeF::LeftParenthesis)),
        Ok(Lexeme(Position::new(1, 1, 2), LexemeF::RightParenthesis)),
        Ok(Lexeme(Position::new(2, 1, 3), LexemeF::Arrow)),
      ]
    );
  }

  #[test]
  fn test_string() {
    assert_eq!(
      Lexer::new("\"foo\"").collect::<Vec<_>>(),
      vec![Ok(Lexeme(Position::new(0, 1, 1), LexemeF::Str("foo")))]
    );
    assert_eq!(
      Lexer::new("\"foo").collect::<Vec<_>>(),
      vec![Err(Position::new(0, 1, 1))]
    );
  }
}
<commit_msg>Accept hash signs in identifiers<commit_after>use std::iter;

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct Position {
  pub offset: usize,
  pub line: usize,
  pub column: usize,
}

impl Position {
  pub fn new(offset: usize, line: usize, column: usize) -> Self {
    Position{
      offset: offset,
      line: line,
      column: column,
    }
  }

  pub fn zero() -> Self {
    Position::new(0, 1, 1)
  }

  pub fn advance(&mut self, char: char) {
    self.offset += 1;
    if char == '\n' {
      self.line += 1;
      self.column = 1;
    } else {
      self.column += 1;
    }
  }
}

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct Lexeme<'a, T>(pub T, pub LexemeF<'a>);

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum LexemeF<'a> {
  Identifier(&'a str),

  End,
  False,
  Fun,
  True,

  Arrow,
  LeftParenthesis,
  RightParenthesis,

  Str(&'a str),
}

#[derive(Clone, Debug)]
pub struct Lexer<'a> {
  input: &'a str,
  position: Position,
  abort: bool,
}

impl<'a> Lexer<'a> {
  pub fn new(input: &'a str) -> Self {
    Lexer{input: input, position: Position::zero(), abort: false}
  }

  pub fn next_position(&self) -> Position {
    self.position
  }

  fn peek_char(&self) -> Option<char> {
    self.input.chars().next()
  }

  fn take_char(&mut self) -> Option<char> {
    let mut chars = self.input.chars();
    let result = chars.next();
    for c in result {
      self.position.advance(c);
    }
    self.input = chars.as_str();
    result
  }

  fn take_chars_while<F>(&mut self, pred: F) -> &'a str
    where F: Fn(char) -> bool {
    let terminator = (self.input.len(), '\n');
    let mut len = 0;
    for (i, c) in self.input.char_indices().chain(iter::once(terminator)) {
      len = i;
      if !pred(c) {
        break;
      }
      self.position.advance(c);
    }
    let result = unsafe { self.input.slice_unchecked(0, len) };
    self.input = unsafe { self.input.slice_unchecked(len, self.input.len()) };
    result
  }
}

impl<'a> Iterator for Lexer<'a> {
  type Item = Result<Lexeme<'a, Position>, Position>;

  fn next(&mut self) -> Option<Self::Item> {
    if self.abort {
      return None;
    }
    self.take_chars_while(is_whitespace);
    let position = self.position;
    match self.peek_char() {
      None =>
        None,
      Some('(') => {
        self.take_char();
        Some(Ok(Lexeme(position, LexemeF::LeftParenthesis)))
      },
      Some(')') => {
        self.take_char();
        Some(Ok(Lexeme(position, LexemeF::RightParenthesis)))
      },
      Some('-') => {
        self.take_char();
        if self.peek_char() == Some('>') {
          self.take_char();
          Some(Ok(Lexeme(position, LexemeF::Arrow)))
        } else {
          self.abort = true;
          Some(Err(position))
        }
      },
      Some('"') => {
        self.take_char();
        let value = self.take_chars_while(|c| c != '"');
        if self.take_char() != Some('"') {
          Some(Err(position))
        } else {
          Some(Ok(Lexeme(position, LexemeF::Str(value))))
        }
      },
      Some(c) if is_identifier_start(c) => {
        let name = self.take_chars_while(is_identifier_continue);
        Some(Ok(Lexeme(position, match name {
          "end"   => LexemeF::End,
          "false" => LexemeF::False,
          "fun"   => LexemeF::Fun,
          "true"  => LexemeF::True,
          _ => LexemeF::Identifier(name),
        })))
      },
      Some(_) => {
        self.abort = true;
        Some(Err(position))
      },
    }
  }
}

fn is_whitespace(char: char) -> bool {
  char.is_whitespace()
}

// FIXME: Switch to is_xid_start once it is stable.
fn is_identifier_start(char: char) -> bool {
  char.is_alphabetic()
  || char == '_'
}

// FIXME: Switch to is_xid_continue once it is stable.
fn is_identifier_continue(char: char) -> bool {
  is_identifier_start(char)
  || char.is_numeric()
  || char == '\''
  || char == '#'
}

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

  #[test]
  fn test_empty() {
    assert_eq!(
      Lexer::new("").collect::<Vec<_>>(),
      vec![]
    );
  }

  #[test]
  fn test_whitespace() {
    assert_eq!(
      Lexer::new(" \t\r\n").collect::<Vec<_>>(),
      vec![]
    );
  }

  #[test]
  fn test_identifier() {
    assert_eq!(
      Lexer::new("a").collect::<Vec<_>>(),
      vec![Ok(Lexeme(Position::new(0, 1, 1), LexemeF::Identifier("a")))]
    );
    assert_eq!(
      Lexer::new("ab").collect::<Vec<_>>(),
      vec![Ok(Lexeme(Position::new(0, 1, 1), LexemeF::Identifier("ab")))]
    );
    assert_eq!(
      Lexer::new("ab_c").collect::<Vec<_>>(),
      vec![Ok(Lexeme(Position::new(0, 1, 1), LexemeF::Identifier("ab_c")))]
    );
    assert_eq!(
      Lexer::new("ab_cd'").collect::<Vec<_>>(),
      vec![Ok(Lexeme(Position::new(0, 1, 1), LexemeF::Identifier("ab_cd'")))]
    );
    assert_eq!(
      Lexer::new("funend").collect::<Vec<_>>(),
      vec![Ok(Lexeme(Position::new(0, 1, 1), LexemeF::Identifier("funend")))]
    );
  }

  #[test]
  fn test_identifiers() {
    assert_eq!(
      Lexer::new("ab cd").collect::<Vec<_>>(),
      vec![
        Ok(Lexeme(Position::new(0, 1, 1), LexemeF::Identifier("ab"))),
        Ok(Lexeme(Position::new(3, 1, 4), LexemeF::Identifier("cd"))),
      ]
    );
  }

  #[test]
  fn test_keywords() {
    assert_eq!(
      Lexer::new("fun end").collect::<Vec<_>>(),
      vec![
        Ok(Lexeme(Position::new(0, 1, 1), LexemeF::Fun)),
        Ok(Lexeme(Position::new(4, 1, 5), LexemeF::End)),
      ]
    );
  }

  #[test]
  fn test_punctuation() {
    assert_eq!(
      Lexer::new("()->").collect::<Vec<_>>(),
      vec![
        Ok(Lexeme(Position::new(0, 1, 1), LexemeF::LeftParenthesis)),
        Ok(Lexeme(Position::new(1, 1, 2), LexemeF::RightParenthesis)),
        Ok(Lexeme(Position::new(2, 1, 3), LexemeF::Arrow)),
      ]
    );
  }

  #[test]
  fn test_string() {
    assert_eq!(
      Lexer::new("\"foo\"").collect::<Vec<_>>(),
      vec![Ok(Lexeme(Position::new(0, 1, 1), LexemeF::Str("foo")))]
    );
    assert_eq!(
      Lexer::new("\"foo").collect::<Vec<_>>(),
      vec![Err(Position::new(0, 1, 1))]
    );
  }
}
<|endoftext|>


<commit_before>use oak_runtime::stream::*;

use getopts::Options;
use std::path::Path;

use std::io::prelude::*;
use std::fs::File;
use std::io::BufReader;

use parser::bailey;

pub fn print_usage(program: String, opts: Options) {
    let brief = format!("Usage: {} FILE [options]", program);
    print!("{}", opts.usage(&brief));
}

pub fn run(args: Vec<String>) {
    let program = args[0].clone();

    let mut opts = Options::new();

    opts.optopt("e", "", "evaluate expression", "EXP");
    opts.optflag("h", "help", "print this help menu");
    let matches = match opts.parse(&args[1..]) {
        Ok(m) => { m }
        Err(f) => { panic!(f.to_string()) }
    };

    if args.len() < 2 {
        print_usage(program, opts);
        return;
    }

    if matches.opt_present("h") {
        print_usage(program, opts);
        return;
    }

    let expression = matches.opt_str("e");
    match expression {
        Some(e) => {
            println!("{:#?}", bailey::parse_program(e.stream()).unwrap_data());
            return
        },
        None => print_usage(program, opts)
    };

    let file_path = args[1].clone();
    if Path::new(&file_path).exists() {
        let f = File::open(file_path).ok().expect("failed to open file");
        let mut reader = BufReader::new(f);
        let mut code = String::new();
        reader.read_to_string(&mut code);
        println!("{:#?}", bailey::parse_program(code.stream()).unwrap_data());
        return
    };
}
<commit_msg>Added function to check parsing state. Mainly for easing debugging of the parser.<commit_after>use oak_runtime::stream::*;
use oak_runtime::parse_state::*;
use oak_runtime::str_stream::*;

use getopts::Options;
use std::path::Path;

use std::io::prelude::*;
use std::fs::File;
use std::io::BufReader;

use parser::bailey;
use ast::PNode;

pub fn print_usage(program: String, opts: Options) {
    let brief = format!("Usage: {} FILE [options]", program);
    print!("{}", opts.usage(&brief));
}

pub fn run(args: Vec<String>) {
    let program = args[0].clone();

    let mut opts = Options::new();

    opts.optopt("e", "", "evaluate expression", "EXP");
    opts.optflag("h", "help", "print this help menu");
    let matches = match opts.parse(&args[1..]) {
        Ok(m) => { m }
        Err(f) => { panic!(f.to_string()) }
    };

    if args.len() < 2 {
        print_usage(program, opts);
        return;
    }

    if matches.opt_present("h") {
        print_usage(program, opts);
        return;
    }

    let expression = matches.opt_str("e");
    match expression {
        Some(e) => {
            println!("{:#?}", analyse_state(bailey::parse_program(e.stream())));
            return
        },
        None => print_usage(program, opts)
    };

    let file_path = args[1].clone();
    if Path::new(&file_path).exists() {
        let f = File::open(file_path).ok().expect("failed to open file");
        let mut reader = BufReader::new(f);
        let mut code = String::new();
        reader.read_to_string(&mut code);
        println!("{:#?}", analyse_state(bailey::parse_program(code.stream())));
        return
    };
}

fn analyse_state(state: ParseState<StrStream, Vec<PNode>>) {
  match state.into_result() {
    Ok((success, error)) => {
      if success.partial_read() {
        println!("Partial match: {:#?} because: {}", success.data, error);
      }
      else {
        println!("Full match: {:#?}", success.data);
      }
    }
    Err(error) => {
      println!("Error: {}", error);
    }
  }
}
<|endoftext|>


<commit_before>use clap::{App, Arg, Error};
use error;
use std::{
    ffi::OsString,
    fs::canonicalize,
    path::{PathBuf, MAIN_SEPARATOR},
    process::Command,
};

#[derive(Clone, Debug)]
pub struct Args {
    pub cmd: Vec<String>,
    pub paths: Vec<PathBuf>,
    pub filters: Vec<String>,
    pub ignores: Vec<String>,
    pub clear_screen: bool,
    pub signal: Option<String>,
    pub restart: bool,
    pub debounce: u64,
    pub debug: bool,
    pub run_initially: bool,
    pub no_shell: bool,
    pub no_vcs_ignore: bool,
    pub once: bool,
    pub poll: bool,
    pub poll_interval: u32,
}

#[cfg(target_family = "windows")]
pub fn clear_screen() {
    let _ = Command::new("cmd").arg("/c").arg("cls").status();
}

#[cfg(target_family = "unix")]
pub fn clear_screen() {
    let _ = Command::new("tput").arg("reset").status();
}

pub fn get_args() -> error::Result<Args> {
    get_args_impl(None::<&[&str]>)
}

pub fn get_args_from<I, T>(from: I) -> error::Result<Args>
where
    I: IntoIterator<Item = T>,
    T: Into<OsString> + Clone,
{
    get_args_impl(Some(from))
}

fn get_args_impl<I, T>(from: Option<I>) -> error::Result<Args>
where
    I: IntoIterator<Item = T>,
    T: Into<OsString> + Clone,
{
    let app = App::new("watchexec")
        .version(crate_version!())
        .about("Execute commands when watched files change")
        .arg(Arg::with_name("command")
                 .help("Command to execute")
                 .multiple(true)
                 .required(true))
        .arg(Arg::with_name("extensions")
                 .help("Comma-separated list of file extensions to watch (js,css,html)")
                 .short("e")
                 .long("exts")
                 .takes_value(true))
        .arg(Arg::with_name("path")
                 .help("Watch a specific directory")
                 .short("w")
                 .long("watch")
                 .number_of_values(1)
                 .multiple(true)
                 .takes_value(true))
        .arg(Arg::with_name("clear")
                 .help("Clear screen before executing command")
                 .short("c")
                 .long("clear"))
        .arg(Arg::with_name("restart")
                 .help("Restart the process if it's still running")
                 .short("r")
                 .long("restart"))
        .arg(Arg::with_name("signal")
                 .help("Send signal to process upon changes, e.g. SIGHUP")
                 .short("s")
                 .long("signal")
                 .takes_value(true)
                 .number_of_values(1)
                 .value_name("signal"))
        .arg(Arg::with_name("kill")
                 .help("Send SIGKILL to child processes (deprecated, use -s SIGKILL instead)")
                 .short("k")
                 .long("kill"))
        .arg(Arg::with_name("debounce")
                 .help("Set the timeout between detected change and command execution, defaults to 500ms")
                 .takes_value(true)
                 .value_name("milliseconds")
                 .short("d")
                 .long("debounce"))
        .arg(Arg::with_name("verbose")
                 .help("Print debugging messages to stderr")
                 .short("v")
                 .long("verbose"))
        .arg(Arg::with_name("filter")
                 .help("Ignore all modifications except those matching the pattern")
                 .short("f")
                 .long("filter")
                 .number_of_values(1)
                 .multiple(true)
                 .takes_value(true)
                 .value_name("pattern"))
        .arg(Arg::with_name("ignore")
                 .help("Ignore modifications to paths matching the pattern")
                 .short("i")
                 .long("ignore")
                 .number_of_values(1)
                 .multiple(true)
                 .takes_value(true)
                 .value_name("pattern"))
        .arg(Arg::with_name("no-vcs-ignore")
                 .help("Skip auto-loading of .gitignore files for filtering")
                 .long("no-vcs-ignore"))
        .arg(Arg::with_name("no-default-ignore")
                 .help("Skip auto-ignoring of commonly ignored globs")
                 .long("no-default-ignore"))
        .arg(Arg::with_name("postpone")
                 .help("Wait until first change to execute command")
                 .short("p")
                 .long("postpone"))
        .arg(Arg::with_name("poll")
                 .help("Forces polling mode")
                 .long("force-poll")
                 .value_name("interval"))
        .arg(Arg::with_name("no-shell")
                 .help("Do not wrap command in 'sh -c' resp. 'cmd.exe /C'")
                 .short("n")
                 .long("no-shell"))
        .arg(Arg::with_name("once").short("1").hidden(true));

    let args = match from {
        None => app.get_matches(),
        Some(i) => app.get_matches_from(i),
    };

    let cmd: Vec<String> = values_t!(args.values_of("command"), String)?;
    let str_paths = values_t!(args.values_of("path"), String).unwrap_or(vec![".".into()]);
    let mut paths = vec![];
    for path in str_paths {
        paths.push(canonicalize(&path).map_err(|e| error::Error::Canonicalization(path, e))?);
    }

    // Treat --kill as --signal SIGKILL (for compatibility with older syntax)
    let signal = if args.is_present("kill") {
        Some("SIGKILL".to_string())
    } else {
        // Convert Option<&str> to Option<String>
        args.value_of("signal").map(str::to_string)
    };

    let mut filters = values_t!(args.values_of("filter"), String).unwrap_or(Vec::new());

    if let Some(extensions) = args.values_of("extensions") {
        for exts in extensions {
            filters.extend(
                exts.split(',')
                    .filter(|ext| !ext.is_empty())
                    .map(|ext| format!("*.{}", ext.replace(".", ""))),
            );
        }
    }

    let mut ignores = vec![];
    let default_ignores = vec![
        format!("**{}.DS_Store", MAIN_SEPARATOR),
        String::from("*.py[co]"),
        String::from("#*#"),
        String::from(".#*"),
        String::from(".*.sw?"),
        String::from(".*.sw?x"),
        format!("**{}.git{}**", MAIN_SEPARATOR, MAIN_SEPARATOR),
        format!("**{}.hg{}**", MAIN_SEPARATOR, MAIN_SEPARATOR),
        format!("**{}.svn{}**", MAIN_SEPARATOR, MAIN_SEPARATOR),
    ];

    if args.occurrences_of("no-default-ignore") == 0 {
        ignores.extend(default_ignores)
    };
    ignores.extend(values_t!(args.values_of("ignore"), String).unwrap_or(Vec::new()));

    let poll_interval = if args.occurrences_of("poll") > 0 {
        value_t!(args.value_of("poll"), u32).unwrap_or_else(|e| e.exit())
    } else {
        1000
    };

    let debounce = if args.occurrences_of("debounce") > 0 {
        value_t!(args.value_of("debounce"), u64).unwrap_or_else(|e| e.exit())
    } else {
        500
    };

    if signal.is_some() && args.is_present("postpone") {
        // TODO: Error::argument_conflict() might be the better fit, usage was unclear, though
        Error::value_validation_auto("--postpone and --signal are mutually exclusive".to_string())
            .exit();
    }

    if signal.is_some() && args.is_present("kill") {
        // TODO: Error::argument_conflict() might be the better fit, usage was unclear, though
        Error::value_validation_auto("--kill and --signal is ambiguous.\n       Hint: Use only '--signal SIGKILL' without --kill".to_string())
            .exit();
    }

    Ok(Args {
        cmd: cmd,
        paths: paths,
        filters: filters,
        ignores: ignores,
        signal: signal,
        clear_screen: args.is_present("clear"),
        restart: args.is_present("restart"),
        debounce: debounce,
        debug: args.is_present("verbose"),
        run_initially: !args.is_present("postpone"),
        no_shell: args.is_present("no-shell"),
        no_vcs_ignore: args.is_present("no-vcs-ignore"),
        once: args.is_present("once"),
        poll: args.occurrences_of("poll") > 0,
        poll_interval: poll_interval,
    })
}
<commit_msg>[windows] Attempt to use tput to clear the screen<commit_after>use clap::{App, Arg, Error};
use error;
use std::{
    ffi::OsString,
    fs::canonicalize,
    path::{PathBuf, MAIN_SEPARATOR},
    process::Command,
};

#[derive(Clone, Debug)]
pub struct Args {
    pub cmd: Vec<String>,
    pub paths: Vec<PathBuf>,
    pub filters: Vec<String>,
    pub ignores: Vec<String>,
    pub clear_screen: bool,
    pub signal: Option<String>,
    pub restart: bool,
    pub debounce: u64,
    pub debug: bool,
    pub run_initially: bool,
    pub no_shell: bool,
    pub no_vcs_ignore: bool,
    pub once: bool,
    pub poll: bool,
    pub poll_interval: u32,
}

#[cfg(target_family = "windows")]
pub fn clear_screen() {
    let _ = Command::new("cmd")
        .arg("/c")
        .arg("tput reset || cls")
        .status();
}

#[cfg(target_family = "unix")]
pub fn clear_screen() {
    let _ = Command::new("tput").arg("reset").status();
}

pub fn get_args() -> error::Result<Args> {
    get_args_impl(None::<&[&str]>)
}

pub fn get_args_from<I, T>(from: I) -> error::Result<Args>
where
    I: IntoIterator<Item = T>,
    T: Into<OsString> + Clone,
{
    get_args_impl(Some(from))
}

fn get_args_impl<I, T>(from: Option<I>) -> error::Result<Args>
where
    I: IntoIterator<Item = T>,
    T: Into<OsString> + Clone,
{
    let app = App::new("watchexec")
        .version(crate_version!())
        .about("Execute commands when watched files change")
        .arg(Arg::with_name("command")
                 .help("Command to execute")
                 .multiple(true)
                 .required(true))
        .arg(Arg::with_name("extensions")
                 .help("Comma-separated list of file extensions to watch (js,css,html)")
                 .short("e")
                 .long("exts")
                 .takes_value(true))
        .arg(Arg::with_name("path")
                 .help("Watch a specific directory")
                 .short("w")
                 .long("watch")
                 .number_of_values(1)
                 .multiple(true)
                 .takes_value(true))
        .arg(Arg::with_name("clear")
                 .help("Clear screen before executing command")
                 .short("c")
                 .long("clear"))
        .arg(Arg::with_name("restart")
                 .help("Restart the process if it's still running")
                 .short("r")
                 .long("restart"))
        .arg(Arg::with_name("signal")
                 .help("Send signal to process upon changes, e.g. SIGHUP")
                 .short("s")
                 .long("signal")
                 .takes_value(true)
                 .number_of_values(1)
                 .value_name("signal"))
        .arg(Arg::with_name("kill")
                 .help("Send SIGKILL to child processes (deprecated, use -s SIGKILL instead)")
                 .short("k")
                 .long("kill"))
        .arg(Arg::with_name("debounce")
                 .help("Set the timeout between detected change and command execution, defaults to 500ms")
                 .takes_value(true)
                 .value_name("milliseconds")
                 .short("d")
                 .long("debounce"))
        .arg(Arg::with_name("verbose")
                 .help("Print debugging messages to stderr")
                 .short("v")
                 .long("verbose"))
        .arg(Arg::with_name("filter")
                 .help("Ignore all modifications except those matching the pattern")
                 .short("f")
                 .long("filter")
                 .number_of_values(1)
                 .multiple(true)
                 .takes_value(true)
                 .value_name("pattern"))
        .arg(Arg::with_name("ignore")
                 .help("Ignore modifications to paths matching the pattern")
                 .short("i")
                 .long("ignore")
                 .number_of_values(1)
                 .multiple(true)
                 .takes_value(true)
                 .value_name("pattern"))
        .arg(Arg::with_name("no-vcs-ignore")
                 .help("Skip auto-loading of .gitignore files for filtering")
                 .long("no-vcs-ignore"))
        .arg(Arg::with_name("no-default-ignore")
                 .help("Skip auto-ignoring of commonly ignored globs")
                 .long("no-default-ignore"))
        .arg(Arg::with_name("postpone")
                 .help("Wait until first change to execute command")
                 .short("p")
                 .long("postpone"))
        .arg(Arg::with_name("poll")
                 .help("Forces polling mode")
                 .long("force-poll")
                 .value_name("interval"))
        .arg(Arg::with_name("no-shell")
                 .help("Do not wrap command in 'sh -c' resp. 'cmd.exe /C'")
                 .short("n")
                 .long("no-shell"))
        .arg(Arg::with_name("once").short("1").hidden(true));

    let args = match from {
        None => app.get_matches(),
        Some(i) => app.get_matches_from(i),
    };

    let cmd: Vec<String> = values_t!(args.values_of("command"), String)?;
    let str_paths = values_t!(args.values_of("path"), String).unwrap_or(vec![".".into()]);
    let mut paths = vec![];
    for path in str_paths {
        paths.push(canonicalize(&path).map_err(|e| error::Error::Canonicalization(path, e))?);
    }

    // Treat --kill as --signal SIGKILL (for compatibility with older syntax)
    let signal = if args.is_present("kill") {
        Some("SIGKILL".to_string())
    } else {
        // Convert Option<&str> to Option<String>
        args.value_of("signal").map(str::to_string)
    };

    let mut filters = values_t!(args.values_of("filter"), String).unwrap_or(Vec::new());

    if let Some(extensions) = args.values_of("extensions") {
        for exts in extensions {
            filters.extend(
                exts.split(',')
                    .filter(|ext| !ext.is_empty())
                    .map(|ext| format!("*.{}", ext.replace(".", ""))),
            );
        }
    }

    let mut ignores = vec![];
    let default_ignores = vec![
        format!("**{}.DS_Store", MAIN_SEPARATOR),
        String::from("*.py[co]"),
        String::from("#*#"),
        String::from(".#*"),
        String::from(".*.sw?"),
        String::from(".*.sw?x"),
        format!("**{}.git{}**", MAIN_SEPARATOR, MAIN_SEPARATOR),
        format!("**{}.hg{}**", MAIN_SEPARATOR, MAIN_SEPARATOR),
        format!("**{}.svn{}**", MAIN_SEPARATOR, MAIN_SEPARATOR),
    ];

    if args.occurrences_of("no-default-ignore") == 0 {
        ignores.extend(default_ignores)
    };
    ignores.extend(values_t!(args.values_of("ignore"), String).unwrap_or(Vec::new()));

    let poll_interval = if args.occurrences_of("poll") > 0 {
        value_t!(args.value_of("poll"), u32).unwrap_or_else(|e| e.exit())
    } else {
        1000
    };

    let debounce = if args.occurrences_of("debounce") > 0 {
        value_t!(args.value_of("debounce"), u64).unwrap_or_else(|e| e.exit())
    } else {
        500
    };

    if signal.is_some() && args.is_present("postpone") {
        // TODO: Error::argument_conflict() might be the better fit, usage was unclear, though
        Error::value_validation_auto("--postpone and --signal are mutually exclusive".to_string())
            .exit();
    }

    if signal.is_some() && args.is_present("kill") {
        // TODO: Error::argument_conflict() might be the better fit, usage was unclear, though
        Error::value_validation_auto("--kill and --signal is ambiguous.\n       Hint: Use only '--signal SIGKILL' without --kill".to_string())
            .exit();
    }

    Ok(Args {
        cmd: cmd,
        paths: paths,
        filters: filters,
        ignores: ignores,
        signal: signal,
        clear_screen: args.is_present("clear"),
        restart: args.is_present("restart"),
        debounce: debounce,
        debug: args.is_present("verbose"),
        run_initially: !args.is_present("postpone"),
        no_shell: args.is_present("no-shell"),
        no_vcs_ignore: args.is_present("no-vcs-ignore"),
        once: args.is_present("once"),
        poll: args.occurrences_of("poll") > 0,
        poll_interval: poll_interval,
    })
}
<|endoftext|>


<commit_before>// The MIT License (MIT)
//
// Copyright (c) 2022 Ryan Fowler
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// copies of the Software, and to permit persons to whom the Software is
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

use clap::Parser;
use num_format::{Locale, ToFormattedString};
use serde::Serialize;
use std::path::PathBuf;
use std::str::FromStr;
use std::string::ToString;
use tabled::{
    object::{Columns, Rows},
    style::Border,
    Alignment, Modify, Style, Table, Tabled,
};

use crate::lang;

#[derive(Debug)]
pub enum Format {
    Json,
    Table,
}

impl FromStr for Format {
    type Err = String;
    fn from_str(format: &str) -> Result<Self, Self::Err> {
        match format {
            "json" => Ok(Format::Json),
            "table" => Ok(Format::Table),
            _ => Err(format.to_string()),
        }
    }
}

/// Count lines of code.
#[derive(Debug, Parser)]
#[clap(version, about)]
pub struct Args {
    /// Output format.
    #[clap(short = 'o', long = "output", default_value = "table")]
    pub format: Format,

    /// Show timing information.
    #[clap(short, long)]
    pub timing: bool,

    /// Directory or file to scan.
    #[clap(default_value = ".")]
    pub path: PathBuf,
}

#[derive(Debug, Serialize)]
pub struct Output {
    pub languages: Vec<LangOut>,
    pub total_num_files: u64,
    pub total_num_lines: u64,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub elapsed_ms: Option<u64>,
}

#[derive(Debug, Serialize)]
pub struct LangOut {
    pub language: lang::Language,
    pub num_files: u64,
    pub num_lines: u64,
}

pub fn get_options() -> Args {
    Args::parse()
}

pub fn write_output(out: &Output, format: Format) {
    match format {
        Format::Json => write_json_pretty(out),
        Format::Table => write_table(out),
    }
}

fn write_json_pretty(out: &Output) {
    println!("{}", serde_json::to_string_pretty(&out).unwrap());
}

#[derive(Tabled)]
struct Row {
    #[tabled(rename = "Language")]
    language: String,
    #[tabled(rename = "Files")]
    files: String,
    #[tabled(rename = "Lines")]
    lines: String,
}

fn write_table(out: &Output) {
    let mut data = Vec::new();
    for lang in &out.languages {
        data.push(Row {
            language: lang.language.as_str().to_string(),
            files: lang.num_files.to_formatted_string(&Locale::en),
            lines: lang.num_lines.to_formatted_string(&Locale::en),
        });
    }

    if out.languages.len() != 1 {
        data.push(Row {
            language: "Total".to_string(),
            files: out.total_num_files.to_formatted_string(&Locale::en),
            lines: out.total_num_lines.to_formatted_string(&Locale::en),
        });
    }

    let mut table = Table::new(&data)
        .with(Style::psql())
        .with(Modify::new(Columns::first()).with(Alignment::left()))
        .with(Modify::new(Columns::new(1..=2)).with(Alignment::right()))
        .with(Modify::new(Rows::first()).with(Alignment::left()));

    if out.languages.len() != 1 {
        table = table.with(Modify::new(Rows::last()).with(Border::default().top('-')));
    }

    println!("{}", table);

    if let Some(elapsed_ms) = out.elapsed_ms {
        println!("Took: {}ms", elapsed_ms);
    }
}
<commit_msg>Improve documentation for the format flag<commit_after>// The MIT License (MIT)
//
// Copyright (c) 2022 Ryan Fowler
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// copies of the Software, and to permit persons to whom the Software is
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

use clap::Parser;
use num_format::{Locale, ToFormattedString};
use serde::Serialize;
use std::path::PathBuf;
use std::str::FromStr;
use std::string::ToString;
use tabled::{
    object::{Columns, Rows},
    style::Border,
    Alignment, Modify, Style, Table, Tabled,
};

use crate::lang;

#[derive(Debug)]
pub enum Format {
    Json,
    Table,
}

impl FromStr for Format {
    type Err = String;
    fn from_str(format: &str) -> Result<Self, Self::Err> {
        match format {
            "json" => Ok(Format::Json),
            "table" => Ok(Format::Table),
            _ => Err(format.to_string()),
        }
    }
}

/// Count lines of code.
#[derive(Debug, Parser)]
#[clap(version, about)]
pub struct Args {
    /// Output format ("table" or "json").
    #[clap(short = 'o', long = "output", default_value = "table")]
    pub format: Format,

    /// Show timing information.
    #[clap(short, long)]
    pub timing: bool,

    /// Directory or file to scan.
    #[clap(default_value = ".")]
    pub path: PathBuf,
}

#[derive(Debug, Serialize)]
pub struct Output {
    pub languages: Vec<LangOut>,
    pub total_num_files: u64,
    pub total_num_lines: u64,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub elapsed_ms: Option<u64>,
}

#[derive(Debug, Serialize)]
pub struct LangOut {
    pub language: lang::Language,
    pub num_files: u64,
    pub num_lines: u64,
}

pub fn get_options() -> Args {
    Args::parse()
}

pub fn write_output(out: &Output, format: Format) {
    match format {
        Format::Json => write_json_pretty(out),
        Format::Table => write_table(out),
    }
}

fn write_json_pretty(out: &Output) {
    println!("{}", serde_json::to_string_pretty(&out).unwrap());
}

#[derive(Tabled)]
struct Row {
    #[tabled(rename = "Language")]
    language: String,
    #[tabled(rename = "Files")]
    files: String,
    #[tabled(rename = "Lines")]
    lines: String,
}

fn write_table(out: &Output) {
    let mut data = Vec::new();
    for lang in &out.languages {
        data.push(Row {
            language: lang.language.as_str().to_string(),
            files: lang.num_files.to_formatted_string(&Locale::en),
            lines: lang.num_lines.to_formatted_string(&Locale::en),
        });
    }

    if out.languages.len() != 1 {
        data.push(Row {
            language: "Total".to_string(),
            files: out.total_num_files.to_formatted_string(&Locale::en),
            lines: out.total_num_lines.to_formatted_string(&Locale::en),
        });
    }

    let mut table = Table::new(&data)
        .with(Style::psql())
        .with(Modify::new(Columns::first()).with(Alignment::left()))
        .with(Modify::new(Columns::new(1..=2)).with(Alignment::right()))
        .with(Modify::new(Rows::first()).with(Alignment::left()));

    if out.languages.len() != 1 {
        table = table.with(Modify::new(Rows::last()).with(Border::default().top('-')));
    }

    println!("{}", table);

    if let Some(elapsed_ms) = out.elapsed_ms {
        println!("Took: {}ms", elapsed_ms);
    }
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate glium;

#[macro_use]
extern crate lazy_static;

extern crate euclid;
extern crate image;
extern crate palette;

pub mod colors; // TODO
mod console;
pub mod font; // TODO
mod mvp;
mod pixset;
mod renderer;
mod shaders;
mod units;
mod utils;
mod vertex;

pub use console::Console;
pub use console::Tile;
pub use console::VertexBuffer;
pub use mvp::model_view_projection;
pub use pixset::{Pix, Pixset};
pub use renderer::Renderer;
pub use units::ScreenPoint2D;
pub use utils::indices;
pub use utils::mat4_id;
pub use utils::ortho_projection;
pub use utils::read_bytes;
pub use utils::read_file;
pub use utils::read_png_to_texture;

#[cfg(test)]
mod tests {
    #[test]
    fn it_works() {}
}
<commit_msg>chore(lib): make units public for now<commit_after>#[macro_use]
extern crate glium;

#[macro_use]
extern crate lazy_static;

extern crate euclid;
extern crate image;
extern crate palette;

pub mod colors; // TODO
mod console;
pub mod font; // TODO
mod mvp;
mod pixset;
mod renderer;
mod shaders;
pub mod units; // TODO
mod utils;
mod vertex;

pub use console::Console;
pub use console::Tile;
pub use console::VertexBuffer;
pub use mvp::model_view_projection;
pub use pixset::{Pix, Pixset};
pub use renderer::Renderer;
pub use units::ScreenPoint2D;
pub use utils::indices;
pub use utils::mat4_id;
pub use utils::ortho_projection;
pub use utils::read_bytes;
pub use utils::read_file;
pub use utils::read_png_to_texture;

#[cfg(test)]
mod tests {
    #[test]
    fn it_works() {}
}
<|endoftext|>


<commit_before>use codespan_reporting::diagnostic::{Diagnostic, Label};
use codespan_reporting::files::{SimpleFile, SimpleFiles};
use codespan_reporting::term::{termcolor::Color, Config, DisplayStyle, Styles};

mod support;

use self::support::TestData;

lazy_static::lazy_static! {
    static ref TEST_CONFIG: Config = Config {
        // Always use blue so tests are consistent across platforms
        styles: Styles::with_blue(Color::Blue),
        ..Config::default()
    };
}

macro_rules! test_emit {
    (rich_color) => {
        #[test]
        fn rich_color() {
            let config = Config {
                display_style: DisplayStyle::Rich,
                ..TEST_CONFIG.clone()
            };

            insta::assert_snapshot!("rich_color", TEST_DATA.emit_color(&config));
        }
    };
    (short_color) => {
        #[test]
        fn short_color() {
            let config = Config {
                display_style: DisplayStyle::Short,
                ..TEST_CONFIG.clone()
            };

            insta::assert_snapshot!("short_color", TEST_DATA.emit_color(&config));
        }
    };
    (rich_no_color) => {
        #[test]
        fn rich_no_color() {
            let config = Config {
                display_style: DisplayStyle::Rich,
                ..TEST_CONFIG.clone()
            };

            insta::assert_snapshot!("rich_no_color", TEST_DATA.emit_no_color(&config));
        }
    };
    (short_no_color) => {
        #[test]
        fn short_no_color() {
            let config = Config {
                display_style: DisplayStyle::Short,
                ..TEST_CONFIG.clone()
            };

            insta::assert_snapshot!("short_no_color", TEST_DATA.emit_no_color(&config));
        }
    };
}

mod empty {
    use super::*;

    lazy_static::lazy_static! {
        static ref TEST_DATA: TestData<'static, SimpleFiles<&'static str, &'static str>> = {
            let files = SimpleFiles::new();

            let diagnostics = vec![
                Diagnostic::bug(),
                Diagnostic::error(),
                Diagnostic::warning(),
                Diagnostic::note(),
                Diagnostic::help(),
                Diagnostic::bug(),
            ];

            TestData { files, diagnostics }
        };
    }

    test_emit!(rich_color);
    test_emit!(short_color);
    test_emit!(rich_no_color);
    test_emit!(short_no_color);
}

mod message {
    use super::*;

    lazy_static::lazy_static! {
        static ref TEST_DATA: TestData<'static, SimpleFiles<&'static str, &'static str>> = {
            let files = SimpleFiles::new();

            let diagnostics = vec![
                Diagnostic::error().with_message("a message"),
                Diagnostic::warning().with_message("a message"),
                Diagnostic::note().with_message("a message"),
                Diagnostic::help().with_message("a message"),
            ];

            TestData { files, diagnostics }
        };
    }

    test_emit!(rich_color);
    test_emit!(short_color);
    test_emit!(rich_no_color);
    test_emit!(short_no_color);
}

mod message_and_notes {
    use super::*;

    lazy_static::lazy_static! {
        static ref TEST_DATA: TestData<'static, SimpleFiles<&'static str, &'static str>> = {
            let files = SimpleFiles::new();

            let diagnostics = vec![
                Diagnostic::error().with_message("a message").with_notes(vec!["a note".to_owned()]),
                Diagnostic::warning().with_message("a message").with_notes(vec!["a note".to_owned()]),
                Diagnostic::note().with_message("a message").with_notes(vec!["a note".to_owned()]),
                Diagnostic::help().with_message("a message").with_notes(vec!["a note".to_owned()]),
            ];

            TestData { files, diagnostics }
        };
    }

    test_emit!(rich_color);
    test_emit!(short_color);
    test_emit!(rich_no_color);
    test_emit!(short_no_color);
}

mod empty_spans {
    use super::*;

    lazy_static::lazy_static! {
        static ref TEST_DATA: TestData<'static, SimpleFile<&'static str, &'static str>> = {
            let file = SimpleFile::new("hello", "Hello world!\nBye world!");
            let eof = file.source().len();

            let diagnostics = vec![
                Diagnostic::note()
                    .with_message("middle")
                    .with_labels(vec![Label::primary((), 6..6).with_message("middle")]),
                Diagnostic::note()
                    .with_message("end of line")
                    .with_labels(vec![Label::primary((), 12..12).with_message("end of line")]),
                Diagnostic::note()
                    .with_message("end of file")
                    .with_labels(vec![Label::primary((), eof..eof).with_message("end of file")]),
            ];

            TestData { files: file, diagnostics }
        };
    }

    test_emit!(rich_color);
    test_emit!(short_color);
    test_emit!(rich_no_color);
    test_emit!(short_no_color);
}

mod multifile {
    use super::*;

    lazy_static::lazy_static! {
        static ref TEST_DATA: TestData<'static, SimpleFiles<&'static str, String>> = {
            let mut files = SimpleFiles::new();

            let file_id1 = files.add(
                "Data/Nat.fun",
                unindent::unindent(
                    "
                        module Data.Nat where

                        data Nat : Type where
                            zero : Nat
                            succ : Nat → Nat

                        {-# BUILTIN NATRAL Nat #-}

                        infixl 6 _+_ _-_

                        _+_ : Nat → Nat → Nat
                        zero    + n₂ = n₂
                        succ n₁ + n₂ = succ (n₁ + n₂)

                        _-_ : Nat → Nat → Nat
                        n₁      - zero    = n₁
                        zero    - succ n₂ = zero
                        succ n₁ - succ n₂ = n₁ - n₂
                    ",
                ),
            );

            let file_id2 = files.add(
                "Test.fun",
                unindent::unindent(
                    r#"
                        module Test where

                        _ : Nat
                        _ = 123 + "hello"
                    "#,
                ),
            );

            let diagnostics = vec![
                // Unknown builtin error
                Diagnostic::error()
                    .with_message("unknown builtin: `NATRAL`")
                    .with_labels(vec![Label::primary(file_id1, 96..102).with_message("unknown builtin")])
                    .with_notes(vec![
                        "there is a builtin with a similar name: `NATURAL`".to_owned(),
                    ]),
                // Unused parameter warning
                Diagnostic::warning()
                    .with_message("unused parameter pattern: `n₂`")
                    .with_labels(vec![Label::primary(file_id1, 285..289).with_message("unused parameter")])
                    .with_notes(vec!["consider using a wildcard pattern: `_`".to_owned()]),
                // Unexpected type error
                Diagnostic::error()
                    .with_message("unexpected type in application of `_+_`")
                    .with_code("E0001")
                    .with_labels(vec![
                        Label::primary(file_id2, 37..44).with_message("expected `Nat`, found `String`"),
                        Label::secondary(file_id1, 130..155).with_message("based on the definition of `_+_`"),
                    ]),
            ];

            TestData { files, diagnostics }
        };
    }

    test_emit!(rich_color);
    test_emit!(short_color);
    test_emit!(rich_no_color);
    test_emit!(short_no_color);
}

mod fizz_buzz {
    use super::*;

    lazy_static::lazy_static! {
        static ref TEST_DATA: TestData<'static, SimpleFiles<&'static str, String>> = {
            let mut files = SimpleFiles::new();

            let file_id = files.add(
                "FizzBuzz.fun",
                unindent::unindent(
                    r#"
                        module FizzBuzz where

                        fizz₁ : Nat → String
                        fizz₁ num = case (mod num 5) (mod num 3) of
                            0 0 => "FizzBuzz"
                            0 _ => "Fizz"
                            _ 0 => "Buzz"
                            _ _ => num

                        fizz₂ : Nat → String
                        fizz₂ num =
                            case (mod num 5) (mod num 3) of
                                0 0 => "FizzBuzz"
                                0 _ => "Fizz"
                                _ 0 => "Buzz"
                                _ _ => num
                    "#,
                ),
            );

            let diagnostics = vec![
                // Incompatible match clause error
                Diagnostic::error()
                    .with_message("`case` clauses have incompatible types")
                    .with_code("E0308")
                    .with_labels(vec![
                        Label::primary(file_id, 163..166).with_message("expected `String`, found `Nat`"),
                        Label::secondary(file_id, 62..166).with_message("`case` clauses have incompatible types"),
                        Label::secondary(file_id, 41..47).with_message("expected type `String` found here"),
                    ])
                    .with_notes(vec![unindent::unindent(
                        "
                            expected type `String`
                               found type `Nat`
                        ",
                    )]),
                // Incompatible match clause error
                Diagnostic::error()
                    .with_message("`case` clauses have incompatible types")
                    .with_code("E0308")
                    .with_labels(vec![
                        Label::primary(file_id, 328..331).with_message("expected `String`, found `Nat`"),
                        Label::secondary(file_id, 211..331).with_message("`case` clauses have incompatible types"),
                        Label::secondary(file_id, 258..268).with_message("this is found to be of type `String`"),
                        Label::secondary(file_id, 284..290).with_message("this is found to be of type `String`"),
                        Label::secondary(file_id, 306..312).with_message("this is found to be of type `String`"),
                        Label::secondary(file_id, 186..192).with_message("expected type `String` found here"),
                    ])
                    .with_notes(vec![unindent::unindent(
                        "
                            expected type `String`
                               found type `Nat`
                        ",
                    )]),
            ];

            TestData { files, diagnostics }
        };
    }

    test_emit!(rich_color);
    test_emit!(short_color);
    test_emit!(rich_no_color);
    test_emit!(short_no_color);
}

mod tabbed {
    use super::*;

    lazy_static::lazy_static! {
        static ref TEST_DATA: TestData<'static, SimpleFiles<&'static str, String>> = {
            let mut files = SimpleFiles::new();

            let file_id = files.add(
                "tabbed",
                [
                    "Entity:",
                    "\tArmament:",
                    "\t\tWeapon: DogJaw",
                    "\t\tReloadingCondition:\tattack-cooldown",
                    "\tFoo: Bar",
                ]
                .join("\n"),
            );

            let diagnostics = vec![
                Diagnostic::warning()
                    .with_message("unknown weapon `DogJaw`")
                    .with_labels(vec![Label::primary(file_id, 29..35).with_message("the weapon")]),
                Diagnostic::warning()
                    .with_message("unknown condition `attack-cooldown`")
                    .with_labels(vec![Label::primary(file_id, 58..73).with_message("the condition")]),
                Diagnostic::warning()
                    .with_message("unknown field `Foo`")
                    .with_labels(vec![Label::primary(file_id, 75..78).with_message("the field")]),
            ];

            TestData { files, diagnostics }
        };
    }

    #[test]
    fn tab_width_default_no_color() {
        let config = TEST_CONFIG.clone();

        insta::assert_snapshot!(
            "tab_width_default_no_color",
            TEST_DATA.emit_no_color(&config)
        );
    }

    #[test]
    fn tab_width_3_no_color() {
        let config = Config {
            tab_width: 3,
            ..TEST_CONFIG.clone()
        };

        insta::assert_snapshot!("tab_width_3_no_color", TEST_DATA.emit_no_color(&config));
    }

    #[test]
    fn tab_width_6_no_color() {
        let config = Config {
            tab_width: 6,
            ..TEST_CONFIG.clone()
        };

        insta::assert_snapshot!("tab_width_6_no_color", TEST_DATA.emit_no_color(&config));
    }
}
<commit_msg>Use the automatic name in snapshot tests<commit_after>use codespan_reporting::diagnostic::{Diagnostic, Label};
use codespan_reporting::files::{SimpleFile, SimpleFiles};
use codespan_reporting::term::{termcolor::Color, Config, DisplayStyle, Styles};

mod support;

use self::support::TestData;

lazy_static::lazy_static! {
    static ref TEST_CONFIG: Config = Config {
        // Always use blue so tests are consistent across platforms
        styles: Styles::with_blue(Color::Blue),
        ..Config::default()
    };
}

macro_rules! test_emit {
    (rich_color) => {
        #[test]
        fn rich_color() {
            let config = Config {
                display_style: DisplayStyle::Rich,
                ..TEST_CONFIG.clone()
            };

            insta::assert_snapshot!(TEST_DATA.emit_color(&config));
        }
    };
    (short_color) => {
        #[test]
        fn short_color() {
            let config = Config {
                display_style: DisplayStyle::Short,
                ..TEST_CONFIG.clone()
            };

            insta::assert_snapshot!(TEST_DATA.emit_color(&config));
        }
    };
    (rich_no_color) => {
        #[test]
        fn rich_no_color() {
            let config = Config {
                display_style: DisplayStyle::Rich,
                ..TEST_CONFIG.clone()
            };

            insta::assert_snapshot!(TEST_DATA.emit_no_color(&config));
        }
    };
    (short_no_color) => {
        #[test]
        fn short_no_color() {
            let config = Config {
                display_style: DisplayStyle::Short,
                ..TEST_CONFIG.clone()
            };

            insta::assert_snapshot!(TEST_DATA.emit_no_color(&config));
        }
    };
}

mod empty {
    use super::*;

    lazy_static::lazy_static! {
        static ref TEST_DATA: TestData<'static, SimpleFiles<&'static str, &'static str>> = {
            let files = SimpleFiles::new();

            let diagnostics = vec![
                Diagnostic::bug(),
                Diagnostic::error(),
                Diagnostic::warning(),
                Diagnostic::note(),
                Diagnostic::help(),
                Diagnostic::bug(),
            ];

            TestData { files, diagnostics }
        };
    }

    test_emit!(rich_color);
    test_emit!(short_color);
    test_emit!(rich_no_color);
    test_emit!(short_no_color);
}

mod message {
    use super::*;

    lazy_static::lazy_static! {
        static ref TEST_DATA: TestData<'static, SimpleFiles<&'static str, &'static str>> = {
            let files = SimpleFiles::new();

            let diagnostics = vec![
                Diagnostic::error().with_message("a message"),
                Diagnostic::warning().with_message("a message"),
                Diagnostic::note().with_message("a message"),
                Diagnostic::help().with_message("a message"),
            ];

            TestData { files, diagnostics }
        };
    }

    test_emit!(rich_color);
    test_emit!(short_color);
    test_emit!(rich_no_color);
    test_emit!(short_no_color);
}

mod message_and_notes {
    use super::*;

    lazy_static::lazy_static! {
        static ref TEST_DATA: TestData<'static, SimpleFiles<&'static str, &'static str>> = {
            let files = SimpleFiles::new();

            let diagnostics = vec![
                Diagnostic::error().with_message("a message").with_notes(vec!["a note".to_owned()]),
                Diagnostic::warning().with_message("a message").with_notes(vec!["a note".to_owned()]),
                Diagnostic::note().with_message("a message").with_notes(vec!["a note".to_owned()]),
                Diagnostic::help().with_message("a message").with_notes(vec!["a note".to_owned()]),
            ];

            TestData { files, diagnostics }
        };
    }

    test_emit!(rich_color);
    test_emit!(short_color);
    test_emit!(rich_no_color);
    test_emit!(short_no_color);
}

mod empty_spans {
    use super::*;

    lazy_static::lazy_static! {
        static ref TEST_DATA: TestData<'static, SimpleFile<&'static str, &'static str>> = {
            let file = SimpleFile::new("hello", "Hello world!\nBye world!");
            let eof = file.source().len();

            let diagnostics = vec![
                Diagnostic::note()
                    .with_message("middle")
                    .with_labels(vec![Label::primary((), 6..6).with_message("middle")]),
                Diagnostic::note()
                    .with_message("end of line")
                    .with_labels(vec![Label::primary((), 12..12).with_message("end of line")]),
                Diagnostic::note()
                    .with_message("end of file")
                    .with_labels(vec![Label::primary((), eof..eof).with_message("end of file")]),
            ];

            TestData { files: file, diagnostics }
        };
    }

    test_emit!(rich_color);
    test_emit!(short_color);
    test_emit!(rich_no_color);
    test_emit!(short_no_color);
}

mod multifile {
    use super::*;

    lazy_static::lazy_static! {
        static ref TEST_DATA: TestData<'static, SimpleFiles<&'static str, String>> = {
            let mut files = SimpleFiles::new();

            let file_id1 = files.add(
                "Data/Nat.fun",
                unindent::unindent(
                    "
                        module Data.Nat where

                        data Nat : Type where
                            zero : Nat
                            succ : Nat → Nat

                        {-# BUILTIN NATRAL Nat #-}

                        infixl 6 _+_ _-_

                        _+_ : Nat → Nat → Nat
                        zero    + n₂ = n₂
                        succ n₁ + n₂ = succ (n₁ + n₂)

                        _-_ : Nat → Nat → Nat
                        n₁      - zero    = n₁
                        zero    - succ n₂ = zero
                        succ n₁ - succ n₂ = n₁ - n₂
                    ",
                ),
            );

            let file_id2 = files.add(
                "Test.fun",
                unindent::unindent(
                    r#"
                        module Test where

                        _ : Nat
                        _ = 123 + "hello"
                    "#,
                ),
            );

            let diagnostics = vec![
                // Unknown builtin error
                Diagnostic::error()
                    .with_message("unknown builtin: `NATRAL`")
                    .with_labels(vec![Label::primary(file_id1, 96..102).with_message("unknown builtin")])
                    .with_notes(vec![
                        "there is a builtin with a similar name: `NATURAL`".to_owned(),
                    ]),
                // Unused parameter warning
                Diagnostic::warning()
                    .with_message("unused parameter pattern: `n₂`")
                    .with_labels(vec![Label::primary(file_id1, 285..289).with_message("unused parameter")])
                    .with_notes(vec!["consider using a wildcard pattern: `_`".to_owned()]),
                // Unexpected type error
                Diagnostic::error()
                    .with_message("unexpected type in application of `_+_`")
                    .with_code("E0001")
                    .with_labels(vec![
                        Label::primary(file_id2, 37..44).with_message("expected `Nat`, found `String`"),
                        Label::secondary(file_id1, 130..155).with_message("based on the definition of `_+_`"),
                    ]),
            ];

            TestData { files, diagnostics }
        };
    }

    test_emit!(rich_color);
    test_emit!(short_color);
    test_emit!(rich_no_color);
    test_emit!(short_no_color);
}

mod fizz_buzz {
    use super::*;

    lazy_static::lazy_static! {
        static ref TEST_DATA: TestData<'static, SimpleFiles<&'static str, String>> = {
            let mut files = SimpleFiles::new();

            let file_id = files.add(
                "FizzBuzz.fun",
                unindent::unindent(
                    r#"
                        module FizzBuzz where

                        fizz₁ : Nat → String
                        fizz₁ num = case (mod num 5) (mod num 3) of
                            0 0 => "FizzBuzz"
                            0 _ => "Fizz"
                            _ 0 => "Buzz"
                            _ _ => num

                        fizz₂ : Nat → String
                        fizz₂ num =
                            case (mod num 5) (mod num 3) of
                                0 0 => "FizzBuzz"
                                0 _ => "Fizz"
                                _ 0 => "Buzz"
                                _ _ => num
                    "#,
                ),
            );

            let diagnostics = vec![
                // Incompatible match clause error
                Diagnostic::error()
                    .with_message("`case` clauses have incompatible types")
                    .with_code("E0308")
                    .with_labels(vec![
                        Label::primary(file_id, 163..166).with_message("expected `String`, found `Nat`"),
                        Label::secondary(file_id, 62..166).with_message("`case` clauses have incompatible types"),
                        Label::secondary(file_id, 41..47).with_message("expected type `String` found here"),
                    ])
                    .with_notes(vec![unindent::unindent(
                        "
                            expected type `String`
                               found type `Nat`
                        ",
                    )]),
                // Incompatible match clause error
                Diagnostic::error()
                    .with_message("`case` clauses have incompatible types")
                    .with_code("E0308")
                    .with_labels(vec![
                        Label::primary(file_id, 328..331).with_message("expected `String`, found `Nat`"),
                        Label::secondary(file_id, 211..331).with_message("`case` clauses have incompatible types"),
                        Label::secondary(file_id, 258..268).with_message("this is found to be of type `String`"),
                        Label::secondary(file_id, 284..290).with_message("this is found to be of type `String`"),
                        Label::secondary(file_id, 306..312).with_message("this is found to be of type `String`"),
                        Label::secondary(file_id, 186..192).with_message("expected type `String` found here"),
                    ])
                    .with_notes(vec![unindent::unindent(
                        "
                            expected type `String`
                               found type `Nat`
                        ",
                    )]),
            ];

            TestData { files, diagnostics }
        };
    }

    test_emit!(rich_color);
    test_emit!(short_color);
    test_emit!(rich_no_color);
    test_emit!(short_no_color);
}

mod tabbed {
    use super::*;

    lazy_static::lazy_static! {
        static ref TEST_DATA: TestData<'static, SimpleFiles<&'static str, String>> = {
            let mut files = SimpleFiles::new();

            let file_id = files.add(
                "tabbed",
                [
                    "Entity:",
                    "\tArmament:",
                    "\t\tWeapon: DogJaw",
                    "\t\tReloadingCondition:\tattack-cooldown",
                    "\tFoo: Bar",
                ]
                .join("\n"),
            );

            let diagnostics = vec![
                Diagnostic::warning()
                    .with_message("unknown weapon `DogJaw`")
                    .with_labels(vec![Label::primary(file_id, 29..35).with_message("the weapon")]),
                Diagnostic::warning()
                    .with_message("unknown condition `attack-cooldown`")
                    .with_labels(vec![Label::primary(file_id, 58..73).with_message("the condition")]),
                Diagnostic::warning()
                    .with_message("unknown field `Foo`")
                    .with_labels(vec![Label::primary(file_id, 75..78).with_message("the field")]),
            ];

            TestData { files, diagnostics }
        };
    }

    #[test]
    fn tab_width_default_no_color() {
        let config = TEST_CONFIG.clone();

        insta::assert_snapshot!(
            "tab_width_default_no_color",
            TEST_DATA.emit_no_color(&config)
        );
    }

    #[test]
    fn tab_width_3_no_color() {
        let config = Config {
            tab_width: 3,
            ..TEST_CONFIG.clone()
        };

        insta::assert_snapshot!(TEST_DATA.emit_no_color(&config));
    }

    #[test]
    fn tab_width_6_no_color() {
        let config = Config {
            tab_width: 6,
            ..TEST_CONFIG.clone()
        };

        insta::assert_snapshot!(TEST_DATA.emit_no_color(&config));
    }
}
<|endoftext|>


<commit_before>//! Providing the features between "full" and "derive" of syn.
//!
//! This crate provides the following two unique data structures.
//!
//! * [`syn_mid::ItemFn`] -- A function whose body is not parsed.
//!
//!   ```text
//!   fn process(n: usize) -> Result<()> { ... }
//!   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^     ^
//!   ```
//!
//! * [`syn_mid::Block`] -- A block whose body is not parsed.
//!
//!   ```text
//!   { ... }
//!   ^     ^
//!   ```
//!
//! Other data structures are the same as data structures of [syn]. These are defined in this crate
//! because they cannot be used in [syn] without "full" feature.
//!
//! # Optional features
//!
//! syn-mid in the default features aims to provide the features between "full"
//! and "derive" of [syn].
//!
//! * **`clone-impls`** — Clone impls for all syntax tree types.
//!
//! [`syn_mid::ItemFn`]: ItemFn
//! [`syn_mid::Block`]: Block
//! [syn]: https://github.com/dtolnay/syn

#![doc(html_root_url = "https://docs.rs/syn-mid/0.5.1")]
#![doc(test(
    no_crate_inject,
    attr(deny(warnings, rust_2018_idioms, single_use_lifetimes), allow(dead_code))
))]
#![forbid(unsafe_code)]
#![warn(future_incompatible, rust_2018_idioms, single_use_lifetimes, unreachable_pub)]
#![warn(clippy::all, clippy::default_trait_access)]
// mem::take and #[non_exhaustive] requires Rust 1.40, matches! requires Rust 1.42
#![allow(
    clippy::mem_replace_with_default,
    clippy::manual_non_exhaustive,
    clippy::match_like_matches_macro
)]
#![allow(clippy::eval_order_dependence, clippy::large_enum_variant)]

// Many of the code contained in this crate are copies from https://github.com/dtolnay/syn.

#[macro_use]
mod macros;

mod func;
mod pat;
mod path;

pub use crate::{
    func::{Block, FnArg, ItemFn, Receiver, Signature},
    pat::{
        FieldPat, Pat, PatIdent, PatPath, PatReference, PatStruct, PatTuple, PatTupleStruct,
        PatType, PatWild,
    },
};
<commit_msg>Update allowed lints<commit_after>//! Providing the features between "full" and "derive" of syn.
//!
//! This crate provides the following two unique data structures.
//!
//! * [`syn_mid::ItemFn`] -- A function whose body is not parsed.
//!
//!   ```text
//!   fn process(n: usize) -> Result<()> { ... }
//!   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^     ^
//!   ```
//!
//! * [`syn_mid::Block`] -- A block whose body is not parsed.
//!
//!   ```text
//!   { ... }
//!   ^     ^
//!   ```
//!
//! Other data structures are the same as data structures of [syn]. These are defined in this crate
//! because they cannot be used in [syn] without "full" feature.
//!
//! # Optional features
//!
//! syn-mid in the default features aims to provide the features between "full"
//! and "derive" of [syn].
//!
//! * **`clone-impls`** — Clone impls for all syntax tree types.
//!
//! [`syn_mid::ItemFn`]: ItemFn
//! [`syn_mid::Block`]: Block
//! [syn]: https://github.com/dtolnay/syn

#![doc(html_root_url = "https://docs.rs/syn-mid/0.5.1")]
#![doc(test(
    no_crate_inject,
    attr(deny(warnings, rust_2018_idioms, single_use_lifetimes), allow(dead_code))
))]
#![forbid(unsafe_code)]
#![warn(future_incompatible, rust_2018_idioms, single_use_lifetimes, unreachable_pub)]
#![warn(clippy::all, clippy::default_trait_access)]
// mem::take, #[non_exhaustive], and Option::{as_deref, as_deref_mut} require Rust 1.40,
// matches! requires Rust 1.42, str::{strip_prefix, strip_suffix} requires Rust 1.45
#![allow(
    clippy::mem_replace_with_default,
    clippy::manual_non_exhaustive,
    clippy::option_as_ref_deref,
    clippy::match_like_matches_macro,
    clippy::manual_strip
)]
#![allow(clippy::eval_order_dependence, clippy::large_enum_variant)]

// Many of the code contained in this crate are copies from https://github.com/dtolnay/syn.

#[macro_use]
mod macros;

mod func;
mod pat;
mod path;

pub use crate::{
    func::{Block, FnArg, ItemFn, Receiver, Signature},
    pat::{
        FieldPat, Pat, PatIdent, PatPath, PatReference, PatStruct, PatTuple, PatTupleStruct,
        PatType, PatWild,
    },
};
<|endoftext|>


<commit_before>//! Guessing of MIME types by file extension.
//!
//! Uses a static list of file-extension : MIME type mappings.
#![cfg_attr(feature = "bench", feature(test))]

extern crate mime;
extern crate phf;
extern crate unicase;

use mime::{Mime, TopLevel, SubLevel};
use unicase::UniCase;

use std::ffi::OsStr;
use std::path::Path;

include!("mime_types_generated.rs");

macro_rules! try_opt(
    ($expr:expr) => (
        match $expr {
            Some(val) => val,
            None => return None,
        }
    )
);

#[cfg(test)]
#[path = "mime_types.rs"]
mod mime_types_src;

/// Guess the MIME type of `path` by its extension (as defined by `Path::extension()`).
///
/// If `path` has no extension, or its extension has no known MIME type mapping,
/// then the MIME type is assumed to be `application/octet-stream`.
///
/// ##Note
/// **Guess** is the operative word here, as there are no guarantees that the contents of the file
/// that `path` points to match the MIME type associated with the path's extension.
///
/// Take care when processing files with assumptions based on the return value of this function.
pub fn guess_mime_type<P: AsRef<Path>>(path: P) -> Mime {
    guess_mime_type_opt(path)
        .unwrap_or_else(octet_stream)
}

/// Guess the MIME type of `path` by its extension (as defined by `Path::extension()`).
///
/// If `path` has no extension, or its extension has no known MIME type mapping,
/// then `None` is returned.
///
/// ##Note
/// **Guess** is the operative word here, as there are no guarantees that the contents of the file
/// that `path` points to match the MIME type associated with the path's extension.
///
/// Take care when processing files with assumptions based on the return value of this function.
pub fn guess_mime_type_opt<P: AsRef<Path>>(path: P) -> Option<Mime> {
    let ext = path.as_ref().extension().and_then(OsStr::to_str).unwrap_or("");

    get_mime_type_opt(ext)
}

/// Get the MIME type associated with a file extension.
///
/// If there is no association for the extension, or `ext` is empty,
/// `application/octet-stream` is returned.
pub fn get_mime_type(search_ext: &str) -> Mime {
    get_mime_type_opt(search_ext)
        .unwrap_or_else(octet_stream)
}

/// Get the MIME type associated with a file extension.
///
/// If there is no association for the extension, or `ext` is empty,
/// `None` is returned.
pub fn get_mime_type_opt(search_ext: &str) -> Option<Mime> {
    get_mime_type_str(search_ext)
        .map(|mime| mime.parse::<Mime>().unwrap())
}

/// Get the MIME type string associated with a file extension. Case-insensitive.
///
/// If `search_ext` is not already lowercase,
/// it will be converted to lowercase to facilitate the search.
///
/// Returns `None` if `search_ext` is empty or an associated extension was not found.
pub fn get_mime_type_str(search_ext: &str) -> Option<&'static str> {
    if search_ext.is_empty() { return None; }

    map_lookup_unicase(&MIME_TYPES, search_ext).cloned()
}

/// Get a list of known extensions for a given `Mime` type. Ignores parameters
/// (only searches with `<main type>/<subtype>`). Case-insensitive (for extension types).

///
/// Returns `None` if the mime is unknown.
pub fn get_extensions(mime: &Mime) -> Option<&'static [&'static str]> {
    let no_param_mime = format!("{}/{}", mime.0, mime.1);
    get_extensions_str(&no_param_mime)
}

/// Get a list of known extensions for a mime type. Ignores parameters (only searches
/// `<main type>/<subtype>`). Case-insensitive.

///
/// Returns `None` if the mime is unknown.
pub fn get_extensions_str(mut mime_str: &str) -> Option<&'static [&'static str]> {
    if let Some(sep_idx) = mime_str.find(';') {
        mime_str = &mime_str[..sep_idx];
    }

    // Need to map &&'static [..] to &'static [..] inside the Option
    map_lookup_unicase(&REV_MAPPINGS, mime_str).map(|&exts| exts)
}

/// Get the MIME type for `application/octet-stream` (generic binary stream)
pub fn octet_stream() -> Mime {
    // FIXME: use new representation when https://github.com/hyperium/mime.rs/pull/42
    // is merged.
    Mime(TopLevel::Application, SubLevel::Ext("octet-stream".into()), Vec::new())
}

fn map_lookup_unicase<'map, V>(map: &'map phf::Map<UniCase<&'static str>, V>, key: &str) -> Option<&'map V> {
    // This transmute should be safe as `get` will not store the reference with
    // the expanded lifetime. This is due to `Borrow` being overly strict and
    // can't have an impl for `&'static str` to `Borrow<&'a str>`.
    //
    // See https://github.com/rust-lang/rust/issues/28853#issuecomment-158735548
    let key = unsafe { ::std::mem::transmute::<_, &'static str>(key) };
    map.get(&UniCase(key))
}

#[cfg(test)]
mod tests {
    use mime::Mime;
    use std::ascii::AsciiExt;
    use std::path::Path;
    use super::{get_mime_type, guess_mime_type, MIME_TYPES};
    use super::{get_mime_type_opt, guess_mime_type_opt};

    #[test]
    fn test_mime_type_guessing() {
        assert_eq!(get_mime_type("gif").to_string(), "image/gif".to_string());
        assert_eq!(get_mime_type("TXT").to_string(), "text/plain".to_string());
        assert_eq!(get_mime_type("blahblah").to_string(), "application/octet-stream".to_string());

        assert_eq!(guess_mime_type(Path::new("/path/to/file.gif")).to_string(), "image/gif".to_string());
        assert_eq!(guess_mime_type("/path/to/file.gif").to_string(), "image/gif".to_string());
    }

    #[test]
    fn test_mime_type_guessing_opt() {
        assert_eq!(get_mime_type_opt("gif").unwrap().to_string(), "image/gif".to_string());
        assert_eq!(get_mime_type_opt("TXT").unwrap().to_string(), "text/plain".to_string());
        assert_eq!(get_mime_type_opt("blahblah"), None);

        assert_eq!(guess_mime_type_opt("/path/to/file.gif").unwrap().to_string(), "image/gif".to_string());
        assert_eq!(guess_mime_type_opt("/path/to/file"), None);
    }

    #[test]
    fn test_are_mime_types_parseable() {
        for (_, mime) in &MIME_TYPES {
            mime.parse::<Mime>().unwrap();
        }
    }

    // RFC: Is this test necessary anymore? --@cybergeek94, 2/1/2016
    #[test]
    fn test_are_extensions_ascii() {
        for (ext, _) in &MIME_TYPES {
            assert!(ext.is_ascii(), "Extension not ASCII: {:?}", ext);
        }
    }

    #[test]
    fn test_are_extensions_sorted() {
        use mime_types_src::MIME_TYPES;

        for (&(ext, _), &(n_ext, _)) in MIME_TYPES.iter().zip(MIME_TYPES.iter().skip(1)) {
            assert!(
                ext <= n_ext,
                "Extensions in src/mime_types should be sorted alphabetically
                in ascending order. Failed assert: {:?} <= {:?}",
                ext, n_ext
            );
        }
    }

}

#[cfg(feature = "bench")]
mod bench {
    extern crate test;

    use self::test::Bencher;

    use super::{get_mime_type_str, MIME_TYPES};

    /// WARNING: this may take a while!
    #[bench]
    fn bench_mime_str(b: &mut Bencher) {
        for (mime_ext, _) in &MIME_TYPES {
            b.iter(|| {
                get_mime_type_str(mime_ext).expect(mime_ext);
            });
        }
    }

    #[bench]
    fn bench_mime_str_uppercase(b: &mut Bencher) {
        let uppercased : Vec<_> = MIME_TYPES.into_iter().map(|(s, _)| s.to_uppercase()).collect();

        for mime_ext in &uppercased {
            b.iter(|| {
                get_mime_type_str(mime_ext).expect(mime_ext);
            });
        }
    }
}
<commit_msg>Just parse `application/octet-stream` so mime >=0.2.1 can use new SubLevel::OctetStream<commit_after>//! Guessing of MIME types by file extension.
//!
//! Uses a static list of file-extension : MIME type mappings.
#![cfg_attr(feature = "bench", feature(test))]

extern crate mime;
extern crate phf;
extern crate unicase;

use mime::{Mime, TopLevel, SubLevel};
use unicase::UniCase;

use std::ffi::OsStr;
use std::path::Path;

include!("mime_types_generated.rs");

macro_rules! try_opt(
    ($expr:expr) => (
        match $expr {
            Some(val) => val,
            None => return None,
        }
    )
);

#[cfg(test)]
#[path = "mime_types.rs"]
mod mime_types_src;

/// Guess the MIME type of `path` by its extension (as defined by `Path::extension()`).
///
/// If `path` has no extension, or its extension has no known MIME type mapping,
/// then the MIME type is assumed to be `application/octet-stream`.
///
/// ##Note
/// **Guess** is the operative word here, as there are no guarantees that the contents of the file
/// that `path` points to match the MIME type associated with the path's extension.
///
/// Take care when processing files with assumptions based on the return value of this function.
pub fn guess_mime_type<P: AsRef<Path>>(path: P) -> Mime {
    guess_mime_type_opt(path)
        .unwrap_or_else(octet_stream)
}

/// Guess the MIME type of `path` by its extension (as defined by `Path::extension()`).
///
/// If `path` has no extension, or its extension has no known MIME type mapping,
/// then `None` is returned.
///
/// ##Note
/// **Guess** is the operative word here, as there are no guarantees that the contents of the file
/// that `path` points to match the MIME type associated with the path's extension.
///
/// Take care when processing files with assumptions based on the return value of this function.
pub fn guess_mime_type_opt<P: AsRef<Path>>(path: P) -> Option<Mime> {
    let ext = path.as_ref().extension().and_then(OsStr::to_str).unwrap_or("");

    get_mime_type_opt(ext)
}

/// Get the MIME type associated with a file extension.
///
/// If there is no association for the extension, or `ext` is empty,
/// `application/octet-stream` is returned.
pub fn get_mime_type(search_ext: &str) -> Mime {
    get_mime_type_opt(search_ext)
        .unwrap_or_else(octet_stream)
}

/// Get the MIME type associated with a file extension.
///
/// If there is no association for the extension, or `ext` is empty,
/// `None` is returned.
pub fn get_mime_type_opt(search_ext: &str) -> Option<Mime> {
    get_mime_type_str(search_ext)
        .map(|mime| mime.parse::<Mime>().unwrap())
}

/// Get the MIME type string associated with a file extension. Case-insensitive.
///
/// If `search_ext` is not already lowercase,
/// it will be converted to lowercase to facilitate the search.
///
/// Returns `None` if `search_ext` is empty or an associated extension was not found.
pub fn get_mime_type_str(search_ext: &str) -> Option<&'static str> {
    if search_ext.is_empty() { return None; }

    map_lookup_unicase(&MIME_TYPES, search_ext).cloned()
}

/// Get a list of known extensions for a given `Mime` type. Ignores parameters
/// (only searches with `<main type>/<subtype>`). Case-insensitive (for extension types).

///
/// Returns `None` if the mime is unknown.
pub fn get_extensions(mime: &Mime) -> Option<&'static [&'static str]> {
    let no_param_mime = format!("{}/{}", mime.0, mime.1);
    get_extensions_str(&no_param_mime)
}

/// Get a list of known extensions for a mime type. Ignores parameters (only searches
/// `<main type>/<subtype>`). Case-insensitive.

///
/// Returns `None` if the mime is unknown.
pub fn get_extensions_str(mut mime_str: &str) -> Option<&'static [&'static str]> {
    if let Some(sep_idx) = mime_str.find(';') {
        mime_str = &mime_str[..sep_idx];
    }

    // Need to map &&'static [..] to &'static [..] inside the Option
    map_lookup_unicase(&REV_MAPPINGS, mime_str).map(|&exts| exts)
}

/// Get the MIME type for `application/octet-stream` (generic binary stream)
pub fn octet_stream() -> Mime {
    "application/octet-stream".parse().unwrap()
}

fn map_lookup_unicase<'map, V>(map: &'map phf::Map<UniCase<&'static str>, V>, key: &str) -> Option<&'map V> {
    // This transmute should be safe as `get` will not store the reference with
    // the expanded lifetime. This is due to `Borrow` being overly strict and
    // can't have an impl for `&'static str` to `Borrow<&'a str>`.
    //
    // See https://github.com/rust-lang/rust/issues/28853#issuecomment-158735548
    let key = unsafe { ::std::mem::transmute::<_, &'static str>(key) };
    map.get(&UniCase(key))
}

#[cfg(test)]
mod tests {
    use mime::Mime;
    use std::ascii::AsciiExt;
    use std::path::Path;
    use super::{get_mime_type, guess_mime_type, MIME_TYPES};
    use super::{get_mime_type_opt, guess_mime_type_opt};

    #[test]
    fn test_mime_type_guessing() {
        assert_eq!(get_mime_type("gif").to_string(), "image/gif".to_string());
        assert_eq!(get_mime_type("TXT").to_string(), "text/plain".to_string());
        assert_eq!(get_mime_type("blahblah").to_string(), "application/octet-stream".to_string());

        assert_eq!(guess_mime_type(Path::new("/path/to/file.gif")).to_string(), "image/gif".to_string());
        assert_eq!(guess_mime_type("/path/to/file.gif").to_string(), "image/gif".to_string());
    }

    #[test]
    fn test_mime_type_guessing_opt() {
        assert_eq!(get_mime_type_opt("gif").unwrap().to_string(), "image/gif".to_string());
        assert_eq!(get_mime_type_opt("TXT").unwrap().to_string(), "text/plain".to_string());
        assert_eq!(get_mime_type_opt("blahblah"), None);

        assert_eq!(guess_mime_type_opt("/path/to/file.gif").unwrap().to_string(), "image/gif".to_string());
        assert_eq!(guess_mime_type_opt("/path/to/file"), None);
    }

    #[test]
    fn test_are_mime_types_parseable() {
        for (_, mime) in &MIME_TYPES {
            mime.parse::<Mime>().unwrap();
        }
    }

    // RFC: Is this test necessary anymore? --@cybergeek94, 2/1/2016
    #[test]
    fn test_are_extensions_ascii() {
        for (ext, _) in &MIME_TYPES {
            assert!(ext.is_ascii(), "Extension not ASCII: {:?}", ext);
        }
    }

    #[test]
    fn test_are_extensions_sorted() {
        use mime_types_src::MIME_TYPES;

        for (&(ext, _), &(n_ext, _)) in MIME_TYPES.iter().zip(MIME_TYPES.iter().skip(1)) {
            assert!(
                ext <= n_ext,
                "Extensions in src/mime_types should be sorted alphabetically
                in ascending order. Failed assert: {:?} <= {:?}",
                ext, n_ext
            );
        }
    }

}

#[cfg(feature = "bench")]
mod bench {
    extern crate test;

    use self::test::Bencher;

    use super::{get_mime_type_str, MIME_TYPES};

    /// WARNING: this may take a while!
    #[bench]
    fn bench_mime_str(b: &mut Bencher) {
        for (mime_ext, _) in &MIME_TYPES {
            b.iter(|| {
                get_mime_type_str(mime_ext).expect(mime_ext);
            });
        }
    }

    #[bench]
    fn bench_mime_str_uppercase(b: &mut Bencher) {
        let uppercased : Vec<_> = MIME_TYPES.into_iter().map(|(s, _)| s.to_uppercase()).collect();

        for mime_ext in &uppercased {
            b.iter(|| {
                get_mime_type_str(mime_ext).expect(mime_ext);
            });
        }
    }
}
<|endoftext|>


<commit_before>extern crate chrono;
extern crate multirust;
extern crate semver;

use std::{error, str};
use std::ffi::OsStr;
use std::fmt::{self, Display};
use std::ops::Range;

use chrono::NaiveDate;
use multirust::Toolchain;
use semver::Version;

pub type Error = Box<error::Error>;
pub type Result<T> = std::result::Result<T, Error>;

pub trait RangeExt {
    fn is_empty(&self) -> bool;
    fn is_singleton(&self) -> bool;
}

impl RangeExt for Range<i32> {
    #[inline]
    fn is_empty(&self) -> bool {
        self.start >= self.end
    }
    #[inline]
    fn is_singleton(&self) -> bool {
        self.start + 1 == self.end
    }
}

/// Finds least item in `r` for which the `predicate` holds.
pub fn bisect<P>(mut r: Range<i32>, mut predicate: P) -> Option<i32>
    where P: FnMut(i32) -> bool
{
    if r.is_empty() {
        return None;
    }

    loop {
        if r.is_empty() {
            return match predicate(r.end) {
                true => Some(r.end),
                false => None,
            };
        }
        if r.is_singleton() {
            return if predicate(r.start) {
                Some(r.start)
            } else {
                None
            };
        }
        let mid = r.start + (r.end - r.start) / 2;
        if predicate(mid) {
            r = Range {
                start: r.start,
                end: mid,
            };
        } else {
            r = Range {
                start: mid + 1,
                end: r.end,
            };
        }
    }
}

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

    #[test]
    fn test_bisect() {
        assert_eq!(None, bisect(0..0, |x| x >= 0));
        assert_eq!(Some(50), bisect(0..100, |x| x >= 50));
        assert_eq!(None, bisect(0..100, |x| x >= 1000));
    }
}

pub enum ToolchainSpec {
    Stable(Version),
    Nightly(NaiveDate),
}
use ToolchainSpec::*;

impl str::FromStr for ToolchainSpec {
    type Err = Error;

    fn from_str(s: &str) -> Result<ToolchainSpec> {
        const NIGHTLY: &'static str = "nightly-";
        if s.starts_with(NIGHTLY) {
            return Ok(Nightly(try!(s[NIGHTLY.len()..].parse())));
        } else {

        }
        unimplemented!();
    }

}

impl From<NaiveDate> for ToolchainSpec {
    fn from(date: NaiveDate) -> ToolchainSpec {
        Nightly(date)
    }
}

impl From<Version> for ToolchainSpec {
    fn from(v: Version) -> ToolchainSpec {
        Stable(v)
    }
}

impl Display for ToolchainSpec {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Stable(ref v) => Display::fmt(v, f),
            Nightly(date) => write!(f, "nightly-{}", date),
        }
    }
}

pub struct Cmd<'a> {
    program: &'a OsStr,
    args: &'a [&'a OsStr],
}

impl<'a> Cmd<'a> {
    pub fn from(command: &'a [&'a OsStr]) -> Cmd<'a> {
        let program = command[0];
        let args = &command[1..];

        Cmd {
            program: program,
            args: args,
        }
    }

    pub fn succeeds_with<'b>(&self, toolchain: &Toolchain<'b>) -> Result<bool> {
        let mut cmd = try!(toolchain.create_command(&self.program));
        cmd.args(self.args);
        let status = try!(cmd.status());
        Ok(status.success())
    }
}
<commit_msg>Derive Clone and Debug on ToolchainSpec<commit_after>extern crate chrono;
extern crate multirust;
extern crate semver;

use std::{error, str};
use std::ffi::OsStr;
use std::fmt::{self, Display};
use std::ops::Range;

use chrono::NaiveDate;
use multirust::Toolchain;
use semver::Version;

pub type Error = Box<error::Error>;
pub type Result<T> = std::result::Result<T, Error>;

pub trait RangeExt {
    fn is_empty(&self) -> bool;
    fn is_singleton(&self) -> bool;
}

impl RangeExt for Range<i32> {
    #[inline]
    fn is_empty(&self) -> bool {
        self.start >= self.end
    }
    #[inline]
    fn is_singleton(&self) -> bool {
        self.start + 1 == self.end
    }
}

/// Finds least item in `r` for which the `predicate` holds.
pub fn bisect<P>(mut r: Range<i32>, mut predicate: P) -> Option<i32>
    where P: FnMut(i32) -> bool
{
    if r.is_empty() {
        return None;
    }

    loop {
        if r.is_empty() {
            return match predicate(r.end) {
                true => Some(r.end),
                false => None,
            };
        }
        if r.is_singleton() {
            return if predicate(r.start) {
                Some(r.start)
            } else {
                None
            };
        }
        let mid = r.start + (r.end - r.start) / 2;
        if predicate(mid) {
            r = Range {
                start: r.start,
                end: mid,
            };
        } else {
            r = Range {
                start: mid + 1,
                end: r.end,
            };
        }
    }
}

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

    #[test]
    fn test_bisect() {
        assert_eq!(None, bisect(0..0, |x| x >= 0));
        assert_eq!(Some(50), bisect(0..100, |x| x >= 50));
        assert_eq!(None, bisect(0..100, |x| x >= 1000));
    }
}

#[derive(Clone, Debug)]
pub enum ToolchainSpec {
    Stable(Version),
    Nightly(NaiveDate),
}
use ToolchainSpec::*;

impl str::FromStr for ToolchainSpec {
    type Err = Error;

    fn from_str(s: &str) -> Result<ToolchainSpec> {
        const NIGHTLY: &'static str = "nightly-";
        if s.starts_with(NIGHTLY) {
            return Ok(Nightly(try!(s[NIGHTLY.len()..].parse())));
        } else {

        }
        unimplemented!();
    }

}

impl From<NaiveDate> for ToolchainSpec {
    fn from(date: NaiveDate) -> ToolchainSpec {
        Nightly(date)
    }
}

impl From<Version> for ToolchainSpec {
    fn from(v: Version) -> ToolchainSpec {
        Stable(v)
    }
}

impl Display for ToolchainSpec {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Stable(ref v) => Display::fmt(v, f),
            Nightly(date) => write!(f, "nightly-{}", date),
        }
    }
}

pub struct Cmd<'a> {
    program: &'a OsStr,
    args: &'a [&'a OsStr],
}

impl<'a> Cmd<'a> {
    pub fn from(command: &'a [&'a OsStr]) -> Cmd<'a> {
        let program = command[0];
        let args = &command[1..];

        Cmd {
            program: program,
            args: args,
        }
    }

    pub fn succeeds_with<'b>(&self, toolchain: &Toolchain<'b>) -> Result<bool> {
        let mut cmd = try!(toolchain.create_command(&self.program));
        cmd.args(self.args);
        let status = try!(cmd.status());
        Ok(status.success())
    }
}
<|endoftext|>