<commit_before>extern crate glob;
extern crate Archive;

use glob::glob;
use regex::Regex;
use Archive::*;
use std::path::Path;
use std::fs;

// Only initialize auxiliary resources once and keep them in a Importer struct
pub struct Importer <'a> {
  pub path : &'a str,
  pub complex : bool
}
impl <'a> Default for Importer <'a> {
  fn default() -> Importer <'a> {
    Importer {
      path : ".",
      complex : false
    }
  }
}

impl <'a> Importer <'a> {
  pub fn unpack(&self) -> Result<(),()> {
    try!(self.unpack_arxiv_top());
    try!(self.unpack_arxiv_months());
    Ok(())
  }
  pub fn unpack_arxiv_top(&self) -> Result<(),()> {
    println!("Greetings from unpack_arxiv_top");
    let path_str = self.path;
    let tars_path = path_str.to_string() + "/*.tar";
    for entry in glob(&tars_path).unwrap() {
      match entry {
        Ok(path) => {
          let base_name = path.file_stem().unwrap().to_str().unwrap();
          // If we wanted fine-grained control, we could infer the dir name:
          // let arxiv_name_re = Regex::new(r"arXiv_src_(\d+)_").unwrap();
          // let captures = arxiv_name_re.captures(base_name).unwrap();
          // let unpack_dirname = match captures.at(1) {
          //   Some(month) => month,
          //   None => base_name
          // };
          // --- but not for now

          // Let's open the tar file and unpack it:
          let archive_reader = Reader::new().unwrap()
            .support_filter_all()
            .support_format_all()
            .open_filename(path.to_str().unwrap(), 10240).unwrap();
          loop {
            match archive_reader.next_header() {
              Ok(e) => {
                let full_extract_path = path_str.to_string() + &e.pathname();
                match fs::metadata(full_extract_path.clone()) {
                  Ok(m) => println!("File {:?} exists, won't unpack.", e.pathname()),
                  Err(_) => {
                    println!("To unpack: {:?}", full_extract_path);
                    e.extract_to(&full_extract_path);
                  }
                }
              },
              Err(_) => { break }
            }
          }
        } ,
        Err(e) => println!("Failed: {:?}", e),
      }
    }
    Ok(())
  }
  pub fn unpack_arxiv_months(&self) -> Result<(),()> {
    println!("Greetings from unpack_arxiv_months");
    Ok(())
  }

  pub fn process(&self) -> Result<(),()> {
    println!("Greetings from the import processor");
    if self.complex {
      let unpacked_status = self.unpack();
    }
    Ok(())
  }
}<commit_msg>progress on month-level unpack for arxiv<commit_after>extern crate glob;
extern crate Archive;

use glob::glob;
use regex::Regex;
use Archive::*;
use std::path::Path;
use std::fs;

// Only initialize auxiliary resources once and keep them in a Importer struct
pub struct Importer <'a> {
  pub path : &'a str,
  pub complex : bool
}
impl <'a> Default for Importer <'a> {
  fn default() -> Importer <'a> {
    Importer {
      path : ".",
      complex : false
    }
  }
}

impl <'a> Importer <'a> {
  pub fn unpack(&self) -> Result<(),()> {
    try!(self.unpack_arxiv_top());
    try!(self.unpack_arxiv_months());
    Ok(())
  }
  pub fn unpack_arxiv_top(&self) -> Result<(),()> {
    println!("Greetings from unpack_arxiv_top");
    let path_str = self.path;
    let tars_path = path_str.to_string() + "/*.tar";
    for entry in glob(&tars_path).unwrap() {
      match entry {
        Ok(path) => {
          let base_name = path.file_stem().unwrap().to_str().unwrap();
          // If we wanted fine-grained control, we could infer the dir name:
          // let arxiv_name_re = Regex::new(r"arXiv_src_(\d+)_").unwrap();
          // let captures = arxiv_name_re.captures(base_name).unwrap();
          // let unpack_dirname = match captures.at(1) {
          //   Some(month) => month,
          //   None => base_name
          // };
          // --- but not for now

          // Let's open the tar file and unpack it:
          let archive_reader = Reader::new().unwrap()
            .support_filter_all()
            .support_format_all()
            .open_filename(path.to_str().unwrap(), 10240).unwrap();
          loop {
            match archive_reader.next_header() {
              Ok(e) => {
                let full_extract_path = path_str.to_string() + &e.pathname();
                match fs::metadata(full_extract_path.clone()) {
                  Ok(_) => println!("File {:?} exists, won't unpack.", e.pathname()),
                  Err(_) => {
                    println!("To unpack: {:?}", full_extract_path);
                    let res_code = e.extract_to(&full_extract_path);
                  }
                }
              },
              Err(_) => { break }
            }
          }
        },
        Err(e) => println!("Failed tar glob: {:?}", e),
      }
    }
    Ok(())
  }
  pub fn unpack_arxiv_months(&self) -> Result<(),()> {
    println!("Greetings from unpack_arxiv_months");
    let path_str = self.path;
    let gzs_path = path_str.to_string() + "/*/*.gz";
    for entry in glob(&gzs_path).unwrap() {
      match entry {
        Ok(path) => {
          let entry_path = path.to_str().unwrap();
          // Careful here, some of arXiv's .gz files are really plain-text TeX files (surprise!!!)
          let archive_reader_new = Reader::new().unwrap()
            .support_filter_all()
            .support_format_all()
            .open_filename(entry_path, 10240);
          match archive_reader_new {
            Err(_) => {
              println!("Simple TeX file: {:?}", entry_path);
            },
            Ok(archive_reader) => {
              loop {
                match archive_reader.next_header() {
                  Ok(e) => {
                    let full_extract_path = path_str.to_string() + &e.pathname();
                    match fs::metadata(full_extract_path.clone()) {
                      Ok(m) => println!("File {:?} exists, won't unpack.", e.pathname()),
                      Err(_) => {
                        println!("To unpack: {:?}", full_extract_path);
                        // let res_code = e.extract_to(&full_extract_path);
                      }
                    }
                  },
                  Err(_) => { break }
                }
              }
            }
          }
        },
        Err(e) => println!("Failed gz glob: {:?}", e)
      }
    }
    Ok(())
  }

  pub fn process(&self) -> Result<(),()> {
    println!("Greetings from the import processor");
    if self.complex {
      let unpacked_status = self.unpack();
    }
    Ok(())
  }
}<|endoftext|>


<commit_before>//! Basic types to build the parsers

use self::IResult::*;
use util::ErrorKind;

#[cfg(feature = "core")]
use std::prelude::v1::*;
use std::boxed::Box;

/// Contains the error that a parser can return
///
/// It can represent a linked list of errors, indicating the path taken in the parsing tree, with corresponding position in the input data.
/// It depends on P, the input position (for a &[u8] parser, it would be a &[u8]), and E, the custom error type (by default, u32)
#[derive(Debug,PartialEq,Eq,Clone)]
pub enum Err<P,E=u32>{
  /// An error code, represented by an ErrorKind, which can contain a custom error code represented by E
  Code(ErrorKind<E>),
  /// An error code, and the next error
  Node(ErrorKind<E>, Box<Err<P,E>>),
  /// An error code, and the input position
  Position(ErrorKind<E>, P),
  /// An error code, the input position and the next error
  NodePosition(ErrorKind<E>, P, Box<Err<P,E>>)
}

/// Contains information on needed data if a parser returned `Incomplete`
#[derive(Debug,PartialEq,Eq,Clone,Copy)]
pub enum Needed {
  /// needs more data, but we do not know how much
  Unknown,
  /// contains the required data size
  Size(usize)
}

/// Holds the result of parsing functions
///
/// It depends on I, the input type, O, the output type, and E, the error type (by default u32)
///
#[derive(Debug,PartialEq,Eq,Clone)]
pub enum IResult<I,O,E=u32> {
   /// indicates a correct parsing, the first field containing the rest of the unparsed data, the second field contains the parsed data
  Done(I,O),
  /// contains a Err, an enum that can indicate an error code, a position in the input, and a pointer to another error, making a list of errors in the parsing tree
  Error(Err<I,E>),
  /// Incomplete contains a Needed, an enum than can represent a known quantity of input data, or unknown
  Incomplete(Needed)
}

impl<I,O,E> IResult<I,O,E> {
  pub fn is_done(&self) -> bool {
    match *self {
      Done(_,_) => true,
      _         => false
    }
  }

  pub fn is_err(&self) -> bool {
    match *self {
      Error(_) => true,
      _        => false
    }
  }

  pub fn is_incomplete(&self) -> bool {
    match *self {
      Incomplete(_) => true,
      _             => false
    }
  }

  #[inline]
  pub fn map<N, F: FnOnce(O) -> N>(self, f: F) -> IResult<I, N, E> {
    match self {
      Done(i, o)    => Done(i, f(o)),
      Error(e)      => Error(e),
      Incomplete(n) => Incomplete(n),
    }
  }

  #[inline]
  pub fn map_err<N, F>(self, f: F) -> IResult<I, O, N>
   where F: FnOnce(Err<I, E>) -> Err<I, N> {
    match self {
      Error(e)      => Error(f(e)),
      Incomplete(n) => Incomplete(n),
      Done(i, o)    => Done(i, o),
    }
  }
}

pub trait GetInput<I> {
  fn remaining_input(&self) -> Option<I>;
}

pub trait GetOutput<O> {
  fn output(&self) -> Option<O>;
}

impl<'a,I,O,E> GetInput<&'a[I]> for IResult<&'a[I],O,E> {
  fn remaining_input(&self) -> Option<&'a[I]> {
    match *self {
      Done(ref i,_) => Some(*i),
      _             => None
    }
  }
}

impl<O,E> GetInput<()> for IResult<(),O,E> {
  fn remaining_input(&self) -> Option<()> {
    match *self {
      Done((),_) => Some(()),
      _          => None
    }
  }
}

impl<'a,O,E> GetInput<&'a str> for IResult<&'a str,O,E> {
  fn remaining_input(&self) -> Option<&'a str> {
    match *self {
      Done(ref i,_) => Some(*i),
      _          => None
    }
  }
}

impl<'a,I,O,E> GetOutput<&'a[O]> for IResult<I,&'a[O],E> {
  fn output(&self) -> Option<&'a[O]> {
    match *self {
      Done(_, ref o) => Some(*o),
      _              => None
    }
  }
}

impl<I,E> GetOutput<()> for IResult<I,(),E> {
  fn output(&self) -> Option<()> {
    match *self {
      Done(_,()) => Some(()),
      _          => None
    }
  }
}

impl<'a,I,E> GetOutput<&'a str> for IResult<I,&'a str,E> {
  fn output(&self) -> Option<&'a str> {
    match *self {
      Done(_,ref o) => Some(*o),
      _          => None
    }
  }
}

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

  #[test]
  fn iresult_map() {
    let done: IResult<&[u8], u32> = IResult::Done(&b""[..], 5);
    let error: IResult<&[u8], u32> = IResult::Error(Err::Code(ErrorKind::Tag));
    let incomplete: IResult<&[u8], u32> = IResult::Incomplete(Needed::Unknown);

    assert_eq!(done.map(|x| x * 2), IResult::Done(&b""[..], 10));
    assert_eq!(error.map(|x| x * 2), IResult::Error(Err::Code(ErrorKind::Tag)));
    assert_eq!(incomplete.map(|x| x * 2), IResult::Incomplete(Needed::Unknown));
  }

  #[test]
  fn iresult_map_err() {
    #[derive(Clone, Copy, Debug, PartialEq, Eq)]
    struct Error(u32);

    let error_kind = Err::Code(ErrorKind::Custom(Error(5)));

    let done: IResult<&[u8], u32> = IResult::Done(&b""[..], 5);
    let error: IResult<&[u8], u32> = IResult::Error(Err::Code(ErrorKind::Tag));
    let incomplete: IResult<&[u8], u32> = IResult::Incomplete(Needed::Unknown);

    assert_eq!(done.map_err(|_| error_kind.clone()), IResult::Done(&b""[..], 5));
    assert_eq!(error.map_err(|x| error_kind.clone()), IResult::Error(error_kind.clone()));
    assert_eq!(incomplete.map_err(|x| error_kind.clone()), IResult::Incomplete(Needed::Unknown));
  }
}
<commit_msg>Add documentation for `IResult::map`<commit_after>//! Basic types to build the parsers

use self::IResult::*;
use util::ErrorKind;

#[cfg(feature = "core")]
use std::prelude::v1::*;
use std::boxed::Box;

/// Contains the error that a parser can return
///
/// It can represent a linked list of errors, indicating the path taken in the parsing tree, with corresponding position in the input data.
/// It depends on P, the input position (for a &[u8] parser, it would be a &[u8]), and E, the custom error type (by default, u32)
#[derive(Debug,PartialEq,Eq,Clone)]
pub enum Err<P,E=u32>{
  /// An error code, represented by an ErrorKind, which can contain a custom error code represented by E
  Code(ErrorKind<E>),
  /// An error code, and the next error
  Node(ErrorKind<E>, Box<Err<P,E>>),
  /// An error code, and the input position
  Position(ErrorKind<E>, P),
  /// An error code, the input position and the next error
  NodePosition(ErrorKind<E>, P, Box<Err<P,E>>)
}

/// Contains information on needed data if a parser returned `Incomplete`
#[derive(Debug,PartialEq,Eq,Clone,Copy)]
pub enum Needed {
  /// needs more data, but we do not know how much
  Unknown,
  /// contains the required data size
  Size(usize)
}

/// Holds the result of parsing functions
///
/// It depends on I, the input type, O, the output type, and E, the error type (by default u32)
///
#[derive(Debug,PartialEq,Eq,Clone)]
pub enum IResult<I,O,E=u32> {
   /// indicates a correct parsing, the first field containing the rest of the unparsed data, the second field contains the parsed data
  Done(I,O),
  /// contains a Err, an enum that can indicate an error code, a position in the input, and a pointer to another error, making a list of errors in the parsing tree
  Error(Err<I,E>),
  /// Incomplete contains a Needed, an enum than can represent a known quantity of input data, or unknown
  Incomplete(Needed)
}

impl<I,O,E> IResult<I,O,E> {
  pub fn is_done(&self) -> bool {
    match *self {
      Done(_,_) => true,
      _         => false
    }
  }

  pub fn is_err(&self) -> bool {
    match *self {
      Error(_) => true,
      _        => false
    }
  }

  pub fn is_incomplete(&self) -> bool {
    match *self {
      Incomplete(_) => true,
      _             => false
    }
  }

  /// Maps a `IResult<I, O, E>` to `IResult<I, N, E>` by appling a function
  /// to a contained `Done` value, leaving `Error` and `Incomplete` value
  /// untouched.
  #[inline]
  pub fn map<N, F: FnOnce(O) -> N>(self, f: F) -> IResult<I, N, E> {
    match self {
      Done(i, o)    => Done(i, f(o)),
      Error(e)      => Error(e),
      Incomplete(n) => Incomplete(n),
    }
  }

  #[inline]
  pub fn map_err<N, F>(self, f: F) -> IResult<I, O, N>
   where F: FnOnce(Err<I, E>) -> Err<I, N> {
    match self {
      Error(e)      => Error(f(e)),
      Incomplete(n) => Incomplete(n),
      Done(i, o)    => Done(i, o),
    }
  }
}

pub trait GetInput<I> {
  fn remaining_input(&self) -> Option<I>;
}

pub trait GetOutput<O> {
  fn output(&self) -> Option<O>;
}

impl<'a,I,O,E> GetInput<&'a[I]> for IResult<&'a[I],O,E> {
  fn remaining_input(&self) -> Option<&'a[I]> {
    match *self {
      Done(ref i,_) => Some(*i),
      _             => None
    }
  }
}

impl<O,E> GetInput<()> for IResult<(),O,E> {
  fn remaining_input(&self) -> Option<()> {
    match *self {
      Done((),_) => Some(()),
      _          => None
    }
  }
}

impl<'a,O,E> GetInput<&'a str> for IResult<&'a str,O,E> {
  fn remaining_input(&self) -> Option<&'a str> {
    match *self {
      Done(ref i,_) => Some(*i),
      _          => None
    }
  }
}

impl<'a,I,O,E> GetOutput<&'a[O]> for IResult<I,&'a[O],E> {
  fn output(&self) -> Option<&'a[O]> {
    match *self {
      Done(_, ref o) => Some(*o),
      _              => None
    }
  }
}

impl<I,E> GetOutput<()> for IResult<I,(),E> {
  fn output(&self) -> Option<()> {
    match *self {
      Done(_,()) => Some(()),
      _          => None
    }
  }
}

impl<'a,I,E> GetOutput<&'a str> for IResult<I,&'a str,E> {
  fn output(&self) -> Option<&'a str> {
    match *self {
      Done(_,ref o) => Some(*o),
      _          => None
    }
  }
}

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

  #[test]
  fn iresult_map() {
    let done: IResult<&[u8], u32> = IResult::Done(&b""[..], 5);
    let error: IResult<&[u8], u32> = IResult::Error(Err::Code(ErrorKind::Tag));
    let incomplete: IResult<&[u8], u32> = IResult::Incomplete(Needed::Unknown);

    assert_eq!(done.map(|x| x * 2), IResult::Done(&b""[..], 10));
    assert_eq!(error.map(|x| x * 2), IResult::Error(Err::Code(ErrorKind::Tag)));
    assert_eq!(incomplete.map(|x| x * 2), IResult::Incomplete(Needed::Unknown));
  }

  #[test]
  fn iresult_map_err() {
    #[derive(Clone, Copy, Debug, PartialEq, Eq)]
    struct Error(u32);

    let error_kind = Err::Code(ErrorKind::Custom(Error(5)));

    let done: IResult<&[u8], u32> = IResult::Done(&b""[..], 5);
    let error: IResult<&[u8], u32> = IResult::Error(Err::Code(ErrorKind::Tag));
    let incomplete: IResult<&[u8], u32> = IResult::Incomplete(Needed::Unknown);

    assert_eq!(done.map_err(|_| error_kind.clone()), IResult::Done(&b""[..], 5));
    assert_eq!(error.map_err(|x| error_kind.clone()), IResult::Error(error_kind.clone()));
    assert_eq!(incomplete.map_err(|x| error_kind.clone()), IResult::Incomplete(Needed::Unknown));
  }
}
<|endoftext|>


<commit_before>//! See https://github.com/matklad/cargo-xtask/.
//!
//! This binary defines various auxiliary build commands, which are not
//! expressible with just `cargo`. Notably, it provides `cargo xtask codegen`
//! for code generation and `cargo xtask install` for installation of
//! rust-analyzer server and client.
//!
//! This binary is integrated into the `cargo` command line by using an alias in
//! `.cargo/config`.
mod help;

use autocfg;
use core::fmt::Write;
use core::str;
use pico_args::Arguments;
use std::{env, path::PathBuf};
use xtask::{
    codegen::{self, Mode},
    install_format_hook, run, run_clippy, run_fuzzer, run_rustfmt, Cmd, Result,
};

// Latest stable, feel free to send a PR if this lags behind.
const REQUIRED_RUST_VERSION: (usize, usize) = (1, 39);

struct InstallOpt {
    client: Option<ClientOpt>,
    server: Option<ServerOpt>,
}

enum ClientOpt {
    VsCode,
}

struct ServerOpt {
    jemalloc: bool,
}

fn main() -> Result<()> {
    let subcommand = match std::env::args_os().nth(1) {
        None => {
            eprintln!("{}", help::GLOBAL_HELP);
            return Ok(());
        }
        Some(s) => s,
    };
    let mut matches = Arguments::from_vec(std::env::args_os().skip(2).collect());
    let subcommand = &*subcommand.to_string_lossy();
    match subcommand {
        "install" => {
            if matches.contains(["-h", "--help"]) {
                eprintln!("{}", help::INSTALL_HELP);
                return Ok(());
            }
            let server = matches.contains("--server");
            let client_code = matches.contains("--client-code");
            if server && client_code {
                eprintln!("{}", help::INSTALL_RA_CONFLICT);
                return Ok(());
            }
            let jemalloc = matches.contains("--jemalloc");
            matches.finish().or_else(handle_extra_flags)?;
            let opts = InstallOpt {
                client: if server { None } else { Some(ClientOpt::VsCode) },
                server: if client_code { None } else { Some(ServerOpt { jemalloc }) },
            };
            install(opts)?
        }
        "codegen" => {
            if matches.contains(["-h", "--help"]) {
                help::print_no_param_subcommand_help(&subcommand);
                return Ok(());
            }
            codegen::generate_syntax(Mode::Overwrite)?;
            codegen::generate_parser_tests(Mode::Overwrite)?;
            codegen::generate_assists_docs(Mode::Overwrite)?;
        }
        "format" => {
            if matches.contains(["-h", "--help"]) {
                help::print_no_param_subcommand_help(&subcommand);
                return Ok(());
            }
            run_rustfmt(Mode::Overwrite)?
        }
        "format-hook" => {
            if matches.contains(["-h", "--help"]) {
                help::print_no_param_subcommand_help(&subcommand);
                return Ok(());
            }
            install_format_hook()?
        }
        "lint" => {
            if matches.contains(["-h", "--help"]) {
                help::print_no_param_subcommand_help(&subcommand);
                return Ok(());
            }
            run_clippy()?
        }
        "fuzz-tests" => {
            if matches.contains(["-h", "--help"]) {
                help::print_no_param_subcommand_help(&subcommand);
                return Ok(());
            }
            run_fuzzer()?
        }
        _ => eprintln!("{}", help::GLOBAL_HELP),
    }
    Ok(())
}

fn handle_extra_flags(e: pico_args::Error) -> Result<()> {
    if let pico_args::Error::UnusedArgsLeft(flags) = e {
        let mut invalid_flags = String::new();
        for flag in flags {
            write!(&mut invalid_flags, "{}, ", flag)?;
        }
        let (invalid_flags, _) = invalid_flags.split_at(invalid_flags.len() - 2);
        Err(format!("Invalid flags: {}", invalid_flags).into())
    } else {
        Err(e.to_string().into())
    }
}

fn install(opts: InstallOpt) -> Result<()> {
    if cfg!(target_os = "macos") {
        fix_path_for_mac()?
    }
    if let Some(server) = opts.server {
        install_server(server)?;
    }
    if let Some(client) = opts.client {
        install_client(client)?;
    }
    Ok(())
}

fn fix_path_for_mac() -> Result<()> {
    let mut vscode_path: Vec<PathBuf> = {
        const COMMON_APP_PATH: &str =
            r"/Applications/Visual Studio Code.app/Contents/Resources/app/bin";
        const ROOT_DIR: &str = "";
        let home_dir = match env::var("HOME") {
            Ok(home) => home,
            Err(e) => Err(format!("Failed getting HOME from environment with error: {}.", e))?,
        };

        [ROOT_DIR, &home_dir]
            .iter()
            .map(|dir| String::from(*dir) + COMMON_APP_PATH)
            .map(PathBuf::from)
            .filter(|path| path.exists())
            .collect()
    };

    if !vscode_path.is_empty() {
        let vars = match env::var_os("PATH") {
            Some(path) => path,
            None => Err("Could not get PATH variable from env.")?,
        };

        let mut paths = env::split_paths(&vars).collect::<Vec<_>>();
        paths.append(&mut vscode_path);
        let new_paths = env::join_paths(paths)?;
        env::set_var("PATH", &new_paths);
    }

    Ok(())
}

fn install_client(ClientOpt::VsCode: ClientOpt) -> Result<()> {
    let npm_version = Cmd {
        unix: r"npm --version",
        windows: r"cmd.exe /c npm.cmd --version",
        work_dir: "./editors/code",
    }
    .run();

    if npm_version.is_err() {
        eprintln!("\nERROR: `npm --version` failed, `npm` is required to build the VS Code plugin")
    }

    Cmd { unix: r"npm ci", windows: r"cmd.exe /c npm.cmd ci", work_dir: "./editors/code" }.run()?;
    Cmd {
        unix: r"npm run package --scripts-prepend-node-path",
        windows: r"cmd.exe /c npm.cmd run package",
        work_dir: "./editors/code",
    }
    .run()?;

    let code_binary = ["code", "code-insiders", "codium"].iter().find(|bin| {
        Cmd {
            unix: &format!("{} --version", bin),
            windows: &format!("cmd.exe /c {}.cmd --version", bin),
            work_dir: "./editors/code",
        }
        .run()
        .is_ok()
    });

    let code_binary = match code_binary {
        Some(it) => it,
        None => Err("Can't execute `code --version`. Perhaps it is not in $PATH?")?,
    };

    Cmd {
        unix: &format!(r"{} --install-extension ./ra-lsp-0.0.1.vsix --force", code_binary),
        windows: &format!(
            r"cmd.exe /c {}.cmd --install-extension ./ra-lsp-0.0.1.vsix --force",
            code_binary
        ),
        work_dir: "./editors/code",
    }
    .run()?;

    let output = Cmd {
        unix: &format!(r"{} --list-extensions", code_binary),
        windows: &format!(r"cmd.exe /c {}.cmd --list-extensions", code_binary),
        work_dir: ".",
    }
    .run_with_output()?;

    if !str::from_utf8(&output.stdout)?.contains("ra-lsp") {
        Err("Could not install the Visual Studio Code extension. \
             Please make sure you have at least NodeJS 10.x installed and try again.")?;
    }

    Ok(())
}

fn install_server(opts: ServerOpt) -> Result<()> {
    let ac = autocfg::AutoCfg::with_dir("target")?;

    let old_rust = !ac.probe_rustc_version(REQUIRED_RUST_VERSION.0, REQUIRED_RUST_VERSION.1);

    if old_rust {
        eprintln!(
            "\nWARNING: at least rust {}.{}.0 is required to compile rust-analyzer\n",
            REQUIRED_RUST_VERSION.0, REQUIRED_RUST_VERSION.1
        )
    }

    let res = if opts.jemalloc {
        run("cargo install --path crates/ra_lsp_server --locked --force --features jemalloc", ".")
    } else {
        run("cargo install --path crates/ra_lsp_server --locked --force", ".")
    };

    if res.is_err() && old_rust {
        eprintln!(
            "\nWARNING: at least rust {}.{}.0 is required to compile rust-analyzer\n",
            REQUIRED_RUST_VERSION.0, REQUIRED_RUST_VERSION.1
        )
    }

    res
}
<commit_msg>Allow usage of CARGO_TARGET_DIR env var<commit_after>//! See https://github.com/matklad/cargo-xtask/.
//!
//! This binary defines various auxiliary build commands, which are not
//! expressible with just `cargo`. Notably, it provides `cargo xtask codegen`
//! for code generation and `cargo xtask install` for installation of
//! rust-analyzer server and client.
//!
//! This binary is integrated into the `cargo` command line by using an alias in
//! `.cargo/config`.
mod help;

use autocfg;
use core::fmt::Write;
use core::str;
use pico_args::Arguments;
use std::{env, path::PathBuf};
use xtask::{
    codegen::{self, Mode},
    install_format_hook, run, run_clippy, run_fuzzer, run_rustfmt, Cmd, Result,
};

// Latest stable, feel free to send a PR if this lags behind.
const REQUIRED_RUST_VERSION: (usize, usize) = (1, 39);

struct InstallOpt {
    client: Option<ClientOpt>,
    server: Option<ServerOpt>,
}

enum ClientOpt {
    VsCode,
}

struct ServerOpt {
    jemalloc: bool,
}

fn main() -> Result<()> {
    let subcommand = match std::env::args_os().nth(1) {
        None => {
            eprintln!("{}", help::GLOBAL_HELP);
            return Ok(());
        }
        Some(s) => s,
    };
    let mut matches = Arguments::from_vec(std::env::args_os().skip(2).collect());
    let subcommand = &*subcommand.to_string_lossy();
    match subcommand {
        "install" => {
            if matches.contains(["-h", "--help"]) {
                eprintln!("{}", help::INSTALL_HELP);
                return Ok(());
            }
            let server = matches.contains("--server");
            let client_code = matches.contains("--client-code");
            if server && client_code {
                eprintln!("{}", help::INSTALL_RA_CONFLICT);
                return Ok(());
            }
            let jemalloc = matches.contains("--jemalloc");
            matches.finish().or_else(handle_extra_flags)?;
            let opts = InstallOpt {
                client: if server { None } else { Some(ClientOpt::VsCode) },
                server: if client_code { None } else { Some(ServerOpt { jemalloc }) },
            };
            install(opts)?
        }
        "codegen" => {
            if matches.contains(["-h", "--help"]) {
                help::print_no_param_subcommand_help(&subcommand);
                return Ok(());
            }
            codegen::generate_syntax(Mode::Overwrite)?;
            codegen::generate_parser_tests(Mode::Overwrite)?;
            codegen::generate_assists_docs(Mode::Overwrite)?;
        }
        "format" => {
            if matches.contains(["-h", "--help"]) {
                help::print_no_param_subcommand_help(&subcommand);
                return Ok(());
            }
            run_rustfmt(Mode::Overwrite)?
        }
        "format-hook" => {
            if matches.contains(["-h", "--help"]) {
                help::print_no_param_subcommand_help(&subcommand);
                return Ok(());
            }
            install_format_hook()?
        }
        "lint" => {
            if matches.contains(["-h", "--help"]) {
                help::print_no_param_subcommand_help(&subcommand);
                return Ok(());
            }
            run_clippy()?
        }
        "fuzz-tests" => {
            if matches.contains(["-h", "--help"]) {
                help::print_no_param_subcommand_help(&subcommand);
                return Ok(());
            }
            run_fuzzer()?
        }
        _ => eprintln!("{}", help::GLOBAL_HELP),
    }
    Ok(())
}

fn handle_extra_flags(e: pico_args::Error) -> Result<()> {
    if let pico_args::Error::UnusedArgsLeft(flags) = e {
        let mut invalid_flags = String::new();
        for flag in flags {
            write!(&mut invalid_flags, "{}, ", flag)?;
        }
        let (invalid_flags, _) = invalid_flags.split_at(invalid_flags.len() - 2);
        Err(format!("Invalid flags: {}", invalid_flags).into())
    } else {
        Err(e.to_string().into())
    }
}

fn install(opts: InstallOpt) -> Result<()> {
    if cfg!(target_os = "macos") {
        fix_path_for_mac()?
    }
    if let Some(server) = opts.server {
        install_server(server)?;
    }
    if let Some(client) = opts.client {
        install_client(client)?;
    }
    Ok(())
}

fn fix_path_for_mac() -> Result<()> {
    let mut vscode_path: Vec<PathBuf> = {
        const COMMON_APP_PATH: &str =
            r"/Applications/Visual Studio Code.app/Contents/Resources/app/bin";
        const ROOT_DIR: &str = "";
        let home_dir = match env::var("HOME") {
            Ok(home) => home,
            Err(e) => Err(format!("Failed getting HOME from environment with error: {}.", e))?,
        };

        [ROOT_DIR, &home_dir]
            .iter()
            .map(|dir| String::from(*dir) + COMMON_APP_PATH)
            .map(PathBuf::from)
            .filter(|path| path.exists())
            .collect()
    };

    if !vscode_path.is_empty() {
        let vars = match env::var_os("PATH") {
            Some(path) => path,
            None => Err("Could not get PATH variable from env.")?,
        };

        let mut paths = env::split_paths(&vars).collect::<Vec<_>>();
        paths.append(&mut vscode_path);
        let new_paths = env::join_paths(paths)?;
        env::set_var("PATH", &new_paths);
    }

    Ok(())
}

fn install_client(ClientOpt::VsCode: ClientOpt) -> Result<()> {
    let npm_version = Cmd {
        unix: r"npm --version",
        windows: r"cmd.exe /c npm.cmd --version",
        work_dir: "./editors/code",
    }
    .run();

    if npm_version.is_err() {
        eprintln!("\nERROR: `npm --version` failed, `npm` is required to build the VS Code plugin")
    }

    Cmd { unix: r"npm ci", windows: r"cmd.exe /c npm.cmd ci", work_dir: "./editors/code" }.run()?;
    Cmd {
        unix: r"npm run package --scripts-prepend-node-path",
        windows: r"cmd.exe /c npm.cmd run package",
        work_dir: "./editors/code",
    }
    .run()?;

    let code_binary = ["code", "code-insiders", "codium"].iter().find(|bin| {
        Cmd {
            unix: &format!("{} --version", bin),
            windows: &format!("cmd.exe /c {}.cmd --version", bin),
            work_dir: "./editors/code",
        }
        .run()
        .is_ok()
    });

    let code_binary = match code_binary {
        Some(it) => it,
        None => Err("Can't execute `code --version`. Perhaps it is not in $PATH?")?,
    };

    Cmd {
        unix: &format!(r"{} --install-extension ./ra-lsp-0.0.1.vsix --force", code_binary),
        windows: &format!(
            r"cmd.exe /c {}.cmd --install-extension ./ra-lsp-0.0.1.vsix --force",
            code_binary
        ),
        work_dir: "./editors/code",
    }
    .run()?;

    let output = Cmd {
        unix: &format!(r"{} --list-extensions", code_binary),
        windows: &format!(r"cmd.exe /c {}.cmd --list-extensions", code_binary),
        work_dir: ".",
    }
    .run_with_output()?;

    if !str::from_utf8(&output.stdout)?.contains("ra-lsp") {
        Err("Could not install the Visual Studio Code extension. \
             Please make sure you have at least NodeJS 10.x installed and try again.")?;
    }

    Ok(())
}

fn install_server(opts: ServerOpt) -> Result<()> {
    let target_dir = env::var_os("CARGO_TARGET_DIR").unwrap_or_else(|| "target".into());
    let ac = autocfg::AutoCfg::with_dir(target_dir)?;

    let old_rust = !ac.probe_rustc_version(REQUIRED_RUST_VERSION.0, REQUIRED_RUST_VERSION.1);

    if old_rust {
        eprintln!(
            "\nWARNING: at least rust {}.{}.0 is required to compile rust-analyzer\n",
            REQUIRED_RUST_VERSION.0, REQUIRED_RUST_VERSION.1
        )
    }

    let res = if opts.jemalloc {
        run("cargo install --path crates/ra_lsp_server --locked --force --features jemalloc", ".")
    } else {
        run("cargo install --path crates/ra_lsp_server --locked --force", ".")
    };

    if res.is_err() && old_rust {
        eprintln!(
            "\nWARNING: at least rust {}.{}.0 is required to compile rust-analyzer\n",
            REQUIRED_RUST_VERSION.0, REQUIRED_RUST_VERSION.1
        )
    }

    res
}
<|endoftext|>


<commit_before>#![allow(unused_variables)]

extern crate device_tree_source;
extern crate mktemp;
extern crate test;

use std::env::args;
use std::process::Command;
use std::path::{PathBuf, Path};

use mktemp::Temp;

use test::{test_main, TestDesc, TestDescAndFn, DynTestName, DynTestFn, ShouldPanic};

use device_tree_source::parser::{ParseResult, parse_dt};
use device_tree_source::include::{IncludeError, include_files};

fn main() {
    let args: Vec<_> = args().collect();

    let tests = Path::new("tests/dts")
                    .read_dir().expect("tests/dts directory does not exist")
                    .filter_map(|e| e.ok())
                    .filter(|e| e.file_name().to_string_lossy().ends_with(".dts"))
                    .map(|d| d.path())
                    .map(|p| {
                        TestDescAndFn {
                            desc: TestDesc {
                                name: DynTestName(format!("{}", p.display())),
                                ignore: true,
                                should_panic: ShouldPanic::No,
                            },
                            testfn: DynTestFn(Box::new(move || try_parse(p.clone()))),
                        }
                    })
                    .collect();
    test_main(&args, tests);
}

fn try_parse(file_name: PathBuf) {
    let cpp_temp_out = Temp::new_file().expect("\nCould not create temp file");
    let include_output = {
        let parent = file_name.parent().expect("\nCould not get parent directory of file");

        let mut cpp_command = Command::new("gcc");
        cpp_command.args(&["-E", "-nostdinc"])
                   .args(&["-undef", "-D__DTS__", "-x", "assembler-with-cpp"])
                   .args(&["-o", cpp_temp_out.as_ref().to_str().unwrap()])
                   .arg(file_name.to_str().unwrap())
                   .args(&["-I", parent.to_str().unwrap()]);

        let include_dir = parent.join("include/");
        if include_dir.is_dir() {
            cpp_command.args(&["-I", include_dir.to_str().unwrap()]);
        }

        // println!("{:?}", cpp_command);

        cpp_command.output().expect("\nFailed to start CPP")
    };

    if !include_output.status.success() {
        panic!("\nFailed to execute CPP. Error message is below.\n{}",
               String::from_utf8_lossy(&include_output.stderr));
    }

    let (buffer, bounds) = match include_files(&cpp_temp_out) {
        Ok(x) => x,
        Err(e) => {
            match e {
                IncludeError::IOError(err, path) => {
                    if let Some(path) = path {
                        panic!("\nIO error: {} {}", err, path.display());
                    } else {
                        panic!("\nIO error: {}", err);
                    }
                }
                IncludeError::LinemarkerInDtsi(path) =>
                    panic!("\nExtraneous linemarker found in DT include: {}",
                           path.to_string_lossy()),
                IncludeError::ParseError(_) =>
                    panic!("\nFailed to convert line to byte offset for bounds tracking."),
                IncludeError::NoBoundReturned(path) =>
                    panic!("\nNo bounds returned after parsing file: {}", path.to_string_lossy()),
            }
        }
    };

    // println!("{:#?}", bounds);

    match parse_dt(&buffer) {
        Ok(ParseResult::Complete(..)) => {},
        Ok(ParseResult::RemainingInput(boot_info, amends, rem)) => {
            // println!("Boot Info:\n{:#?}", boot_info);
            // println!("Amends:\n{:#?}", amends);
            panic!("\nInput remaining after parsing:\n{}", String::from_utf8_lossy(rem));
        }
        Err(err) => {
            // println!("{:#?}", bounds);
            // println!("{}", String::from_utf8_lossy(&buffer));
            panic!("\n{:?}", err);
        }
    }
}
<commit_msg>Prevent a double panic on failed run of CPP during tests<commit_after>#![allow(unused_variables)]

extern crate device_tree_source;
extern crate mktemp;
extern crate test;

use std::env::args;
use std::process::Command;
use std::path::{PathBuf, Path};

use mktemp::Temp;

use test::{test_main, TestDesc, TestDescAndFn, DynTestName, DynTestFn, ShouldPanic};

use device_tree_source::parser::{ParseResult, parse_dt};
use device_tree_source::include::{IncludeError, include_files};

fn main() {
    let args: Vec<_> = args().collect();

    let tests = Path::new("tests/dts")
                    .read_dir().expect("tests/dts directory does not exist")
                    .filter_map(|e| e.ok())
                    .filter(|e| e.file_name().to_string_lossy().ends_with(".dts"))
                    .map(|d| d.path())
                    .map(|p| {
                        TestDescAndFn {
                            desc: TestDesc {
                                name: DynTestName(format!("{}", p.display())),
                                ignore: true,
                                should_panic: ShouldPanic::No,
                            },
                            testfn: DynTestFn(Box::new(move || try_parse(p.clone()))),
                        }
                    })
                    .collect();
    test_main(&args, tests);
}

fn try_parse(file_name: PathBuf) {
    let mut cpp_temp_out = Temp::new_file().expect("\nCould not create temp file");
    let include_output = {
        let parent = file_name.parent().expect("\nCould not get parent directory of file");

        let mut cpp_command = Command::new("gcc");
        cpp_command.args(&["-E", "-nostdinc"])
                   .args(&["-undef", "-D__DTS__", "-x", "assembler-with-cpp"])
                   .args(&["-o", cpp_temp_out.as_ref().to_str().unwrap()])
                   .arg(file_name.to_str().unwrap())
                   .args(&["-I", parent.to_str().unwrap()]);

        let include_dir = parent.join("include/");
        if include_dir.is_dir() {
            cpp_command.args(&["-I", include_dir.to_str().unwrap()]);
        }

        // println!("{:?}", cpp_command);

        cpp_command.output().expect("\nFailed to start CPP")
    };

    if !include_output.status.success() {
        // Done to prevent a panic as the file will not have been written to
        cpp_temp_out.release();
        panic!("\nFailed to execute CPP. Error message is below.\n{}",
               String::from_utf8_lossy(&include_output.stderr));
    }

    let (buffer, bounds) = match include_files(&cpp_temp_out) {
        Ok(x) => x,
        Err(e) => {
            match e {
                IncludeError::IOError(err, path) => {
                    if let Some(path) = path {
                        panic!("\nIO error: {} {}", err, path.display());
                    } else {
                        panic!("\nIO error: {}", err);
                    }
                }
                IncludeError::LinemarkerInDtsi(path) =>
                    panic!("\nExtraneous linemarker found in DT include: {}",
                           path.to_string_lossy()),
                IncludeError::ParseError(_) =>
                    panic!("\nFailed to convert line to byte offset for bounds tracking."),
                IncludeError::NoBoundReturned(path) =>
                    panic!("\nNo bounds returned after parsing file: {}", path.to_string_lossy()),
            }
        }
    };

    // println!("{:#?}", bounds);

    match parse_dt(&buffer) {
        Ok(ParseResult::Complete(..)) => {},
        Ok(ParseResult::RemainingInput(boot_info, amends, rem)) => {
            // println!("Boot Info:\n{:#?}", boot_info);
            // println!("Amends:\n{:#?}", amends);
            panic!("\nInput remaining after parsing:\n{}", String::from_utf8_lossy(rem));
        }
        Err(err) => {
            // println!("{:#?}", bounds);
            // println!("{}", String::from_utf8_lossy(&buffer));
            panic!("\n{:?}", err);
        }
    }
}
<|endoftext|>


<commit_before>extern crate fleet;

use fleet::{Client, UnitOption, UnitStates};

#[test]
fn unit_lifecycle() {
    let client = Client::new("http://localhost:2999").unwrap();

    // Create unit

    let options = vec![
        UnitOption {
            name: "ExecStart".to_string(),
            section: "Service".to_string(),
            value: "/usr/bin/sleep 3000".to_string(),
        },
    ];

    let create_result = client.create_unit("test.service", UnitStates::Launched, options);

    assert!(create_result.is_ok(), "{}", create_result.err().unwrap());

    // Get unit

    let unit = client.get_unit("test.service").ok().unwrap();

    assert_eq!(&unit.name[..], "test.service");

    // Modify unit's desired state

    let modify_result = client.modify_unit("test.service", UnitStates::Loaded);

    assert!(modify_result.is_ok(), "{}", modify_result.err().unwrap());

    // Destroy unit

    let destroy_result = client.destroy_unit("test.service");

    assert!(destroy_result.is_ok(), "{}", destroy_result.err().unwrap());
}

#[test]
fn create_invalid_unit_missing_name() {
    let client = Client::new("http://localhost:2999").unwrap();
    let options = vec![];

    let result = client.create_unit("", UnitStates::Launched, options);
    let error = result.err().unwrap();

    assert_eq!(format!("{}", error), "404: Error in JSON response from Fleet was empty");
}

#[test]
fn create_invalid_unit_missing_options() {
    let client = Client::new("http://localhost:2999").unwrap();
    let options = vec![];

    let result = client.create_unit("optionless.service", UnitStates::Launched, options);
    let error = result.err().unwrap();

    assert_eq!(format!("{}", error), "409: unit does not exist and options field empty");
}
<commit_msg>Add retry logic for getting unit in lifecycle test.<commit_after>extern crate fleet;

use std::thread::sleep_ms;

use fleet::{Client, UnitOption, UnitStates};

#[test]
fn unit_lifecycle() {
    let client = Client::new("http://localhost:2999").unwrap();

    // Create unit

    let options = vec![
        UnitOption {
            name: "ExecStart".to_string(),
            section: "Service".to_string(),
            value: "/usr/bin/sleep 3000".to_string(),
        },
    ];

    let create_result = client.create_unit("test.service", UnitStates::Launched, options);

    assert!(create_result.is_ok(), "{}", create_result.err().unwrap());

    // Get unit

    // fleet takes a second to create the unit, so give it a few tries.
    for try in 0..5 {
        let unit = client.get_unit("test.service").ok().unwrap();

        assert_eq!(&unit.name[..], "test.service");

        match unit.machine_id {
            Some(_) => break,
            None => {
                if try == 4 {
                    panic!("test.service never launched");
                } else {
                    sleep_ms(500);
                }
            }
        }
    }

    // Modify unit's desired state

    let modify_result = client.modify_unit("test.service", UnitStates::Loaded);

    assert!(modify_result.is_ok(), "{}", modify_result.err().unwrap());

    // Destroy unit

    let destroy_result = client.destroy_unit("test.service");

    assert!(destroy_result.is_ok(), "{}", destroy_result.err().unwrap());
}

#[test]
fn create_invalid_unit_missing_name() {
    let client = Client::new("http://localhost:2999").unwrap();
    let options = vec![];

    let result = client.create_unit("", UnitStates::Launched, options);
    let error = result.err().unwrap();

    assert_eq!(format!("{}", error), "404: Error in JSON response from Fleet was empty");
}

#[test]
fn create_invalid_unit_missing_options() {
    let client = Client::new("http://localhost:2999").unwrap();
    let options = vec![];

    let result = client.create_unit("optionless.service", UnitStates::Launched, options);
    let error = result.err().unwrap();

    assert_eq!(format!("{}", error), "409: unit does not exist and options field empty");
}
<|endoftext|>


<commit_before>use clap::{AppSettings, Args, CommandFactory, Parser, Subcommand};

#[test]
fn arg_help_heading_applied() {
    #[derive(Debug, Clone, Parser)]
    struct CliOptions {
        #[clap(long, value_parser)]
        #[clap(help_heading = Some("HEADING A"))]
        should_be_in_section_a: u32,

        #[clap(long, value_parser)]
        no_section: u32,
    }

    let cmd = CliOptions::command();

    let should_be_in_section_a = if cfg!(feature = "unstable-v4") {
        cmd.get_arguments()
            .find(|a| a.get_id() == "should_be_in_section_a")
            .unwrap()
    } else {
        cmd.get_arguments()
            .find(|a| a.get_id() == "should-be-in-section-a")
            .unwrap()
    };
    assert_eq!(should_be_in_section_a.get_help_heading(), Some("HEADING A"));

    let should_be_in_section_b = if cfg!(feature = "unstable-v4") {
        cmd.get_arguments()
            .find(|a| a.get_id() == "no_section")
            .unwrap()
    } else {
        cmd.get_arguments()
            .find(|a| a.get_id() == "no-section")
            .unwrap()
    };
    assert_eq!(should_be_in_section_b.get_help_heading(), None);
}

#[test]
fn app_help_heading_applied() {
    #[derive(Debug, Clone, Parser)]
    #[clap(next_help_heading = "DEFAULT")]
    struct CliOptions {
        #[clap(long, value_parser)]
        #[clap(help_heading = Some("HEADING A"))]
        should_be_in_section_a: u32,

        #[clap(long, value_parser)]
        should_be_in_default_section: u32,
    }

    let cmd = CliOptions::command();

    let should_be_in_section_a = if cfg!(feature = "unstable-v4") {
        cmd.get_arguments()
            .find(|a| a.get_id() == "should_be_in_section_a")
            .unwrap()
    } else {
        cmd.get_arguments()
            .find(|a| a.get_id() == "should-be-in-section-a")
            .unwrap()
    };
    assert_eq!(should_be_in_section_a.get_help_heading(), Some("HEADING A"));

    let should_be_in_default_section = if cfg!(feature = "unstable-v4") {
        cmd.get_arguments()
            .find(|a| a.get_id() == "should_be_in_default_section")
            .unwrap()
    } else {
        cmd.get_arguments()
            .find(|a| a.get_id() == "should-be-in-default-section")
            .unwrap()
    };
    assert_eq!(
        should_be_in_default_section.get_help_heading(),
        Some("DEFAULT")
    );
}

#[test]
fn app_help_heading_flattened() {
    // Used to help track the cause in tests
    #![allow(clippy::enum_variant_names)]

    #[derive(Debug, Clone, Parser)]
    struct CliOptions {
        #[clap(flatten)]
        options_a: OptionsA,

        #[clap(flatten)]
        options_b: OptionsB,

        #[clap(subcommand)]
        sub_a: SubA,

        #[clap(long, value_parser)]
        should_be_in_default_section: u32,
    }

    #[derive(Debug, Clone, Args)]
    #[clap(next_help_heading = "HEADING A")]
    struct OptionsA {
        #[clap(long, value_parser)]
        should_be_in_section_a: u32,
    }

    #[derive(Debug, Clone, Args)]
    #[clap(next_help_heading = "HEADING B")]
    struct OptionsB {
        #[clap(long, value_parser)]
        should_be_in_section_b: u32,
    }

    #[derive(Debug, Clone, Subcommand)]
    enum SubA {
        #[clap(flatten)]
        SubB(SubB),
        #[clap(subcommand)]
        SubC(SubC),
        SubAOne,
        #[clap(next_help_heading = "SUB A")]
        SubATwo {
            #[clap(value_parser)]
            should_be_in_sub_a: u32,
        },
    }

    #[derive(Debug, Clone, Subcommand)]
    enum SubB {
        #[clap(next_help_heading = "SUB B")]
        SubBOne {
            #[clap(value_parser)]
            should_be_in_sub_b: u32,
        },
    }

    #[derive(Debug, Clone, Subcommand)]
    enum SubC {
        #[clap(next_help_heading = "SUB C")]
        SubCOne {
            #[clap(value_parser)]
            should_be_in_sub_c: u32,
        },
    }

    let cmd = CliOptions::command();

    let should_be_in_section_a = if cfg!(feature = "unstable-v4") {
        cmd.get_arguments()
            .find(|a| a.get_id() == "should_be_in_section_a")
            .unwrap()
    } else {
        cmd.get_arguments()
            .find(|a| a.get_id() == "should-be-in-section-a")
            .unwrap()
    };
    assert_eq!(should_be_in_section_a.get_help_heading(), Some("HEADING A"));

    let should_be_in_section_b = if cfg!(feature = "unstable-v4") {
        cmd.get_arguments()
            .find(|a| a.get_id() == "should_be_in_section_b")
            .unwrap()
    } else {
        cmd.get_arguments()
            .find(|a| a.get_id() == "should-be-in-section-b")
            .unwrap()
    };
    assert_eq!(should_be_in_section_b.get_help_heading(), Some("HEADING B"));

    let should_be_in_default_section = if cfg!(feature = "unstable-v4") {
        cmd.get_arguments()
            .find(|a| a.get_id() == "should_be_in_default_section")
            .unwrap()
    } else {
        cmd.get_arguments()
            .find(|a| a.get_id() == "should-be-in-default-section")
            .unwrap()
    };
    assert_eq!(should_be_in_default_section.get_help_heading(), None);

    let sub_a_two = cmd.find_subcommand("sub-a-two").unwrap();

    let should_be_in_sub_a = if cfg!(feature = "unstable-v4") {
        sub_a_two
            .get_arguments()
            .find(|a| a.get_id() == "should_be_in_sub_a")
            .unwrap()
    } else {
        sub_a_two
            .get_arguments()
            .find(|a| a.get_id() == "should-be-in-sub-a")
            .unwrap()
    };
    assert_eq!(should_be_in_sub_a.get_help_heading(), Some("SUB A"));

    let sub_b_one = cmd.find_subcommand("sub-b-one").unwrap();

    let should_be_in_sub_b = if cfg!(feature = "unstable-v4") {
        sub_b_one
            .get_arguments()
            .find(|a| a.get_id() == "should_be_in_sub_b")
            .unwrap()
    } else {
        sub_b_one
            .get_arguments()
            .find(|a| a.get_id() == "should-be-in-sub-b")
            .unwrap()
    };
    assert_eq!(should_be_in_sub_b.get_help_heading(), Some("SUB B"));

    let sub_c = cmd.find_subcommand("sub-c").unwrap();
    let sub_c_one = sub_c.find_subcommand("sub-c-one").unwrap();

    let should_be_in_sub_c = if cfg!(feature = "unstable-v4") {
        sub_c_one
            .get_arguments()
            .find(|a| a.get_id() == "should_be_in_sub_c")
            .unwrap()
    } else {
        sub_c_one
            .get_arguments()
            .find(|a| a.get_id() == "should-be-in-sub-c")
            .unwrap()
    };
    assert_eq!(should_be_in_sub_c.get_help_heading(), Some("SUB C"));
}

#[test]
fn flatten_field_with_help_heading() {
    #[derive(Debug, Clone, Parser)]
    struct CliOptions {
        #[clap(flatten)]
        #[clap(next_help_heading = "HEADING A")]
        options_a: OptionsA,
    }

    #[derive(Debug, Clone, Args)]
    struct OptionsA {
        #[clap(long, value_parser)]
        should_be_in_section_a: u32,
    }

    let cmd = CliOptions::command();

    let should_be_in_section_a = if cfg!(feature = "unstable-v4") {
        cmd.get_arguments()
            .find(|a| a.get_id() == "should_be_in_section_a")
            .unwrap()
    } else {
        cmd.get_arguments()
            .find(|a| a.get_id() == "should-be-in-section-a")
            .unwrap()
    };
    assert_eq!(should_be_in_section_a.get_help_heading(), Some("HEADING A"));
}

// The challenge with this test is creating an error situation not caught by `clap`'s error checking
// but by the code that `clap_derive` generates.
//
// Ultimately, the easiest way to confirm is to put a debug statement in the desired error path.
#[test]
fn derive_generated_error_has_full_context() {
    #[derive(Debug, Parser)]
    #[clap(subcommand_negates_reqs = true)]
    struct Opts {
        #[clap(long, value_parser)]
        req_str: String,

        #[clap(subcommand)]
        cmd: Option<SubCommands>,
    }

    #[derive(Debug, Parser)]
    enum SubCommands {
        Sub {
            #[clap(short, long, parse(from_occurrences))]
            verbose: u8,
        },
    }

    let result = Opts::try_parse_from(&["test", "sub"]);
    assert!(
        result.is_err(),
        "`SubcommandsNegateReqs` with non-optional `req_str` should fail: {:?}",
        result.unwrap()
    );

    if cfg!(feature = "unstable-v4") {
        let expected = r#"error: The following required argument was not provided: req_str

USAGE:
    clap --req-str <REQ_STR>
    clap <SUBCOMMAND>

For more information try --help
"#;
        assert_eq!(result.unwrap_err().to_string(), expected);
    } else {
        let expected = r#"error: The following required argument was not provided: req-str

USAGE:
    clap --req-str <REQ_STR>
    clap <SUBCOMMAND>

For more information try --help
"#;
        assert_eq!(result.unwrap_err().to_string(), expected);
    }
}

#[test]
fn derive_order_next_order() {
    static HELP: &str = "test 1.2

USAGE:
    test [OPTIONS]

OPTIONS:
        --flag-b                 first flag
        --option-b <OPTION_B>    first option
    -h, --help                   Print help information
    -V, --version                Print version information
        --flag-a                 second flag
        --option-a <OPTION_A>    second option
";

    #[derive(Parser, Debug)]
    #[clap(name = "test", version = "1.2")]
    #[clap(setting = AppSettings::DeriveDisplayOrder)]
    struct Args {
        #[clap(flatten)]
        a: A,
        #[clap(flatten)]
        b: B,
    }

    #[derive(Args, Debug)]
    #[clap(next_display_order = 10000)]
    struct A {
        /// second flag
        #[clap(long)]
        flag_a: bool,
        /// second option
        #[clap(long, value_parser)]
        option_a: Option<String>,
    }

    #[derive(Args, Debug)]
    #[clap(next_display_order = 10)]
    struct B {
        /// first flag
        #[clap(long)]
        flag_b: bool,
        /// first option
        #[clap(long, value_parser)]
        option_b: Option<String>,
    }

    use clap::CommandFactory;
    let mut cmd = Args::command();

    let mut buffer: Vec<u8> = Default::default();
    cmd.write_help(&mut buffer).unwrap();
    let help = String::from_utf8(buffer).unwrap();
    assert_eq!(help, HELP);
}

#[test]
fn derive_order_next_order_flatten() {
    static HELP: &str = "test 1.2

USAGE:
    test [OPTIONS]

OPTIONS:
        --flag-b                 first flag
        --option-b <OPTION_B>    first option
    -h, --help                   Print help information
    -V, --version                Print version information
        --flag-a                 second flag
        --option-a <OPTION_A>    second option
";

    #[derive(Parser, Debug)]
    #[clap(setting = AppSettings::DeriveDisplayOrder)]
    #[clap(name = "test", version = "1.2")]
    struct Args {
        #[clap(flatten)]
        #[clap(next_display_order = 10000)]
        a: A,
        #[clap(flatten)]
        #[clap(next_display_order = 10)]
        b: B,
    }

    #[derive(Args, Debug)]
    struct A {
        /// second flag
        #[clap(long)]
        flag_a: bool,
        /// second option
        #[clap(long, value_parser)]
        option_a: Option<String>,
    }

    #[derive(Args, Debug)]
    struct B {
        /// first flag
        #[clap(long)]
        flag_b: bool,
        /// first option
        #[clap(long, value_parser)]
        option_b: Option<String>,
    }

    use clap::CommandFactory;
    let mut cmd = Args::command();

    let mut buffer: Vec<u8> = Default::default();
    cmd.write_help(&mut buffer).unwrap();
    let help = String::from_utf8(buffer).unwrap();
    assert_eq!(help, HELP);
}

#[test]
fn derive_order_no_next_order() {
    static HELP: &str = "test 1.2

USAGE:
    test [OPTIONS]

OPTIONS:
        --flag-a                 first flag
        --flag-b                 second flag
    -h, --help                   Print help information
        --option-a <OPTION_A>    first option
        --option-b <OPTION_B>    second option
    -V, --version                Print version information
";

    #[derive(Parser, Debug)]
    #[clap(name = "test", version = "1.2")]
    #[clap(setting = AppSettings::DeriveDisplayOrder)]
    #[clap(next_display_order = None)]
    struct Args {
        #[clap(flatten)]
        a: A,
        #[clap(flatten)]
        b: B,
    }

    #[derive(Args, Debug)]
    struct A {
        /// first flag
        #[clap(long)]
        flag_a: bool,
        /// first option
        #[clap(long, value_parser)]
        option_a: Option<String>,
    }

    #[derive(Args, Debug)]
    struct B {
        /// second flag
        #[clap(long)]
        flag_b: bool,
        /// second option
        #[clap(long, value_parser)]
        option_b: Option<String>,
    }

    use clap::CommandFactory;
    let mut cmd = Args::command();

    let mut buffer: Vec<u8> = Default::default();
    cmd.write_help(&mut buffer).unwrap();
    let help = String::from_utf8(buffer).unwrap();
    assert_eq!(help, HELP);
}
<commit_msg>test(derive): Improve help output assertions<commit_after>use clap::{AppSettings, Args, CommandFactory, Parser, Subcommand};

#[test]
fn arg_help_heading_applied() {
    #[derive(Debug, Clone, Parser)]
    struct CliOptions {
        #[clap(long, value_parser)]
        #[clap(help_heading = Some("HEADING A"))]
        should_be_in_section_a: u32,

        #[clap(long, value_parser)]
        no_section: u32,
    }

    let cmd = CliOptions::command();

    let should_be_in_section_a = if cfg!(feature = "unstable-v4") {
        cmd.get_arguments()
            .find(|a| a.get_id() == "should_be_in_section_a")
            .unwrap()
    } else {
        cmd.get_arguments()
            .find(|a| a.get_id() == "should-be-in-section-a")
            .unwrap()
    };
    assert_eq!(should_be_in_section_a.get_help_heading(), Some("HEADING A"));

    let should_be_in_section_b = if cfg!(feature = "unstable-v4") {
        cmd.get_arguments()
            .find(|a| a.get_id() == "no_section")
            .unwrap()
    } else {
        cmd.get_arguments()
            .find(|a| a.get_id() == "no-section")
            .unwrap()
    };
    assert_eq!(should_be_in_section_b.get_help_heading(), None);
}

#[test]
fn app_help_heading_applied() {
    #[derive(Debug, Clone, Parser)]
    #[clap(next_help_heading = "DEFAULT")]
    struct CliOptions {
        #[clap(long, value_parser)]
        #[clap(help_heading = Some("HEADING A"))]
        should_be_in_section_a: u32,

        #[clap(long, value_parser)]
        should_be_in_default_section: u32,
    }

    let cmd = CliOptions::command();

    let should_be_in_section_a = if cfg!(feature = "unstable-v4") {
        cmd.get_arguments()
            .find(|a| a.get_id() == "should_be_in_section_a")
            .unwrap()
    } else {
        cmd.get_arguments()
            .find(|a| a.get_id() == "should-be-in-section-a")
            .unwrap()
    };
    assert_eq!(should_be_in_section_a.get_help_heading(), Some("HEADING A"));

    let should_be_in_default_section = if cfg!(feature = "unstable-v4") {
        cmd.get_arguments()
            .find(|a| a.get_id() == "should_be_in_default_section")
            .unwrap()
    } else {
        cmd.get_arguments()
            .find(|a| a.get_id() == "should-be-in-default-section")
            .unwrap()
    };
    assert_eq!(
        should_be_in_default_section.get_help_heading(),
        Some("DEFAULT")
    );
}

#[test]
fn app_help_heading_flattened() {
    // Used to help track the cause in tests
    #![allow(clippy::enum_variant_names)]

    #[derive(Debug, Clone, Parser)]
    struct CliOptions {
        #[clap(flatten)]
        options_a: OptionsA,

        #[clap(flatten)]
        options_b: OptionsB,

        #[clap(subcommand)]
        sub_a: SubA,

        #[clap(long, value_parser)]
        should_be_in_default_section: u32,
    }

    #[derive(Debug, Clone, Args)]
    #[clap(next_help_heading = "HEADING A")]
    struct OptionsA {
        #[clap(long, value_parser)]
        should_be_in_section_a: u32,
    }

    #[derive(Debug, Clone, Args)]
    #[clap(next_help_heading = "HEADING B")]
    struct OptionsB {
        #[clap(long, value_parser)]
        should_be_in_section_b: u32,
    }

    #[derive(Debug, Clone, Subcommand)]
    enum SubA {
        #[clap(flatten)]
        SubB(SubB),
        #[clap(subcommand)]
        SubC(SubC),
        SubAOne,
        #[clap(next_help_heading = "SUB A")]
        SubATwo {
            #[clap(value_parser)]
            should_be_in_sub_a: u32,
        },
    }

    #[derive(Debug, Clone, Subcommand)]
    enum SubB {
        #[clap(next_help_heading = "SUB B")]
        SubBOne {
            #[clap(value_parser)]
            should_be_in_sub_b: u32,
        },
    }

    #[derive(Debug, Clone, Subcommand)]
    enum SubC {
        #[clap(next_help_heading = "SUB C")]
        SubCOne {
            #[clap(value_parser)]
            should_be_in_sub_c: u32,
        },
    }

    let cmd = CliOptions::command();

    let should_be_in_section_a = if cfg!(feature = "unstable-v4") {
        cmd.get_arguments()
            .find(|a| a.get_id() == "should_be_in_section_a")
            .unwrap()
    } else {
        cmd.get_arguments()
            .find(|a| a.get_id() == "should-be-in-section-a")
            .unwrap()
    };
    assert_eq!(should_be_in_section_a.get_help_heading(), Some("HEADING A"));

    let should_be_in_section_b = if cfg!(feature = "unstable-v4") {
        cmd.get_arguments()
            .find(|a| a.get_id() == "should_be_in_section_b")
            .unwrap()
    } else {
        cmd.get_arguments()
            .find(|a| a.get_id() == "should-be-in-section-b")
            .unwrap()
    };
    assert_eq!(should_be_in_section_b.get_help_heading(), Some("HEADING B"));

    let should_be_in_default_section = if cfg!(feature = "unstable-v4") {
        cmd.get_arguments()
            .find(|a| a.get_id() == "should_be_in_default_section")
            .unwrap()
    } else {
        cmd.get_arguments()
            .find(|a| a.get_id() == "should-be-in-default-section")
            .unwrap()
    };
    assert_eq!(should_be_in_default_section.get_help_heading(), None);

    let sub_a_two = cmd.find_subcommand("sub-a-two").unwrap();

    let should_be_in_sub_a = if cfg!(feature = "unstable-v4") {
        sub_a_two
            .get_arguments()
            .find(|a| a.get_id() == "should_be_in_sub_a")
            .unwrap()
    } else {
        sub_a_two
            .get_arguments()
            .find(|a| a.get_id() == "should-be-in-sub-a")
            .unwrap()
    };
    assert_eq!(should_be_in_sub_a.get_help_heading(), Some("SUB A"));

    let sub_b_one = cmd.find_subcommand("sub-b-one").unwrap();

    let should_be_in_sub_b = if cfg!(feature = "unstable-v4") {
        sub_b_one
            .get_arguments()
            .find(|a| a.get_id() == "should_be_in_sub_b")
            .unwrap()
    } else {
        sub_b_one
            .get_arguments()
            .find(|a| a.get_id() == "should-be-in-sub-b")
            .unwrap()
    };
    assert_eq!(should_be_in_sub_b.get_help_heading(), Some("SUB B"));

    let sub_c = cmd.find_subcommand("sub-c").unwrap();
    let sub_c_one = sub_c.find_subcommand("sub-c-one").unwrap();

    let should_be_in_sub_c = if cfg!(feature = "unstable-v4") {
        sub_c_one
            .get_arguments()
            .find(|a| a.get_id() == "should_be_in_sub_c")
            .unwrap()
    } else {
        sub_c_one
            .get_arguments()
            .find(|a| a.get_id() == "should-be-in-sub-c")
            .unwrap()
    };
    assert_eq!(should_be_in_sub_c.get_help_heading(), Some("SUB C"));
}

#[test]
fn flatten_field_with_help_heading() {
    #[derive(Debug, Clone, Parser)]
    struct CliOptions {
        #[clap(flatten)]
        #[clap(next_help_heading = "HEADING A")]
        options_a: OptionsA,
    }

    #[derive(Debug, Clone, Args)]
    struct OptionsA {
        #[clap(long, value_parser)]
        should_be_in_section_a: u32,
    }

    let cmd = CliOptions::command();

    let should_be_in_section_a = if cfg!(feature = "unstable-v4") {
        cmd.get_arguments()
            .find(|a| a.get_id() == "should_be_in_section_a")
            .unwrap()
    } else {
        cmd.get_arguments()
            .find(|a| a.get_id() == "should-be-in-section-a")
            .unwrap()
    };
    assert_eq!(should_be_in_section_a.get_help_heading(), Some("HEADING A"));
}

// The challenge with this test is creating an error situation not caught by `clap`'s error checking
// but by the code that `clap_derive` generates.
//
// Ultimately, the easiest way to confirm is to put a debug statement in the desired error path.
#[test]
fn derive_generated_error_has_full_context() {
    #[derive(Debug, Parser)]
    #[clap(subcommand_negates_reqs = true)]
    struct Opts {
        #[clap(long, value_parser)]
        req_str: String,

        #[clap(subcommand)]
        cmd: Option<SubCommands>,
    }

    #[derive(Debug, Parser)]
    enum SubCommands {
        Sub {
            #[clap(short, long, parse(from_occurrences))]
            verbose: u8,
        },
    }

    let result = Opts::try_parse_from(&["test", "sub"]);
    assert!(
        result.is_err(),
        "`SubcommandsNegateReqs` with non-optional `req_str` should fail: {:?}",
        result.unwrap()
    );

    if cfg!(feature = "unstable-v4") {
        let expected = r#"error: The following required argument was not provided: req_str

USAGE:
    clap --req-str <REQ_STR>
    clap <SUBCOMMAND>

For more information try --help
"#;
        assert_eq!(result.unwrap_err().to_string(), expected);
    } else {
        let expected = r#"error: The following required argument was not provided: req-str

USAGE:
    clap --req-str <REQ_STR>
    clap <SUBCOMMAND>

For more information try --help
"#;
        assert_eq!(result.unwrap_err().to_string(), expected);
    }
}

#[test]
fn derive_order_next_order() {
    static HELP: &str = "test 1.2

USAGE:
    test [OPTIONS]

OPTIONS:
        --flag-b                 first flag
        --option-b <OPTION_B>    first option
    -h, --help                   Print help information
    -V, --version                Print version information
        --flag-a                 second flag
        --option-a <OPTION_A>    second option
";

    #[derive(Parser, Debug)]
    #[clap(name = "test", version = "1.2")]
    #[clap(setting = AppSettings::DeriveDisplayOrder)]
    struct Args {
        #[clap(flatten)]
        a: A,
        #[clap(flatten)]
        b: B,
    }

    #[derive(Args, Debug)]
    #[clap(next_display_order = 10000)]
    struct A {
        /// second flag
        #[clap(long)]
        flag_a: bool,
        /// second option
        #[clap(long, value_parser)]
        option_a: Option<String>,
    }

    #[derive(Args, Debug)]
    #[clap(next_display_order = 10)]
    struct B {
        /// first flag
        #[clap(long)]
        flag_b: bool,
        /// first option
        #[clap(long, value_parser)]
        option_b: Option<String>,
    }

    use clap::CommandFactory;
    let mut cmd = Args::command();

    let mut buffer: Vec<u8> = Default::default();
    cmd.write_help(&mut buffer).unwrap();
    let help = String::from_utf8(buffer).unwrap();
    snapbox::assert_eq(HELP, help);
}

#[test]
fn derive_order_next_order_flatten() {
    static HELP: &str = "test 1.2

USAGE:
    test [OPTIONS]

OPTIONS:
        --flag-b                 first flag
        --option-b <OPTION_B>    first option
    -h, --help                   Print help information
    -V, --version                Print version information
        --flag-a                 second flag
        --option-a <OPTION_A>    second option
";

    #[derive(Parser, Debug)]
    #[clap(setting = AppSettings::DeriveDisplayOrder)]
    #[clap(name = "test", version = "1.2")]
    struct Args {
        #[clap(flatten)]
        #[clap(next_display_order = 10000)]
        a: A,
        #[clap(flatten)]
        #[clap(next_display_order = 10)]
        b: B,
    }

    #[derive(Args, Debug)]
    struct A {
        /// second flag
        #[clap(long)]
        flag_a: bool,
        /// second option
        #[clap(long, value_parser)]
        option_a: Option<String>,
    }

    #[derive(Args, Debug)]
    struct B {
        /// first flag
        #[clap(long)]
        flag_b: bool,
        /// first option
        #[clap(long, value_parser)]
        option_b: Option<String>,
    }

    use clap::CommandFactory;
    let mut cmd = Args::command();

    let mut buffer: Vec<u8> = Default::default();
    cmd.write_help(&mut buffer).unwrap();
    let help = String::from_utf8(buffer).unwrap();
    snapbox::assert_eq(HELP, help);
}

#[test]
fn derive_order_no_next_order() {
    static HELP: &str = "test 1.2

USAGE:
    test [OPTIONS]

OPTIONS:
        --flag-a                 first flag
        --flag-b                 second flag
    -h, --help                   Print help information
        --option-a <OPTION_A>    first option
        --option-b <OPTION_B>    second option
    -V, --version                Print version information
";

    #[derive(Parser, Debug)]
    #[clap(name = "test", version = "1.2")]
    #[clap(setting = AppSettings::DeriveDisplayOrder)]
    #[clap(next_display_order = None)]
    struct Args {
        #[clap(flatten)]
        a: A,
        #[clap(flatten)]
        b: B,
    }

    #[derive(Args, Debug)]
    struct A {
        /// first flag
        #[clap(long)]
        flag_a: bool,
        /// first option
        #[clap(long, value_parser)]
        option_a: Option<String>,
    }

    #[derive(Args, Debug)]
    struct B {
        /// second flag
        #[clap(long)]
        flag_b: bool,
        /// second option
        #[clap(long, value_parser)]
        option_b: Option<String>,
    }

    use clap::CommandFactory;
    let mut cmd = Args::command();

    let mut buffer: Vec<u8> = Default::default();
    cmd.write_help(&mut buffer).unwrap();
    let help = String::from_utf8(buffer).unwrap();
    snapbox::assert_eq(HELP, help);
}
<|endoftext|>


<commit_before>extern crate boxx;

#[cfg(test)]
mod tests {
  extern crate boxx;
  use boxx::expr::{Expr, boxx};
  extern crate env_logger;

  #[test]
  pub fn test_if_else() {
    let _ = env_logger::init();

    assert_eq!(
      Expr::Int(34),
      boxx("if (true && false) { 32 } else if (!true && true) { 33 } else { 34 }")
    );

    assert_eq!(
      Expr::Int(32),
      boxx("if (true || false) { 32 } else if (!true && true) { 33 } else { 34 }")
    );

    assert_eq!(
      Expr::Int(30),
      boxx("if (true && false) { 32 } else { 30 }")
    );

    assert_eq!(
      Expr::Int(52),
      boxx("if (let x = 4; x > 3) { 52 } else { 30 }")
    );

    assert_eq!(
      Expr::Int(22),
      boxx("if (true) { 11 } else { 0 }; 22")
    );
  }

  #[test]
  pub fn test_func() {
    let _ = env_logger::init();

    assert_eq!(Expr::Int(2), boxx("let x = 4; fn foo() { let x = 1; x + 1 }; foo()"));
    assert_eq!(Expr::Int(6), boxx("let x = 5; fn foo() { x + 1 }; foo()"));
    assert_eq!(Expr::Int(60), boxx("fn foo() { 5 }; fn bar() { fn foo() { 6 }; foo() * 10 }; bar()"));
    assert_eq!(Expr::Int(50), boxx("fn foo() { 5 }; fn bar() { foo() * 10 }; bar()"));

    assert_eq!(Expr::Int(12), boxx("fn sum(a, b) { a + b }; sum(sum(3, 4), 5)"));
    assert_eq!(Expr::Int(12), boxx("fn tx_two(a) { 2 * a }; tx_two(tx_two(3))"));

    assert_eq!(
      Expr::Int(41),
      boxx("
        fn foo(a) {
          a < 40 ? foo(a + 3) : a
        };

        foo(20)
      ")
    );

    assert_eq!(
      Expr::Int(21),
      boxx("
        fn fib(n) {
          n == 0 ? 0 : (n == 1 ? 1 : fib(n - 1) + fib(n - 2))
        };

        fib(8)
      ")
    );

    assert_eq!(
      Expr::Int(28),
      boxx("
        fn foo(a) {
          1 + a
        };

        fn bar(b) {
          5 * b
        };

        foo(bar(4)) + 7
      ")
    );

    assert_eq!(Expr::Int(12), boxx("fn foo(a) { 1 + a }; foo(4) + 7"));
    assert_eq!(Expr::Int(12), boxx("let foo = fn(a) { 1 + a }; foo(4) + 7"));

    assert_eq!(Expr::Int(2), boxx("fn foo() { 1 + 1 }; foo()"));
    assert_eq!(Expr::Int(7), boxx("fn foo() { 1 + 3 }; foo() + 3"));
    assert_eq!(Expr::Int(9), boxx("fn foo() { 1 + 3 }; fn bar() { foo() + 1}; 4 + bar()"));

    assert_eq!(Expr::Int(2), boxx("let foo = fn() { 1 + 1 }; foo()"));
    assert_eq!(Expr::Int(7), boxx("let foo = fn() { 1 + 3 }; foo() + 3"));
    assert_eq!(Expr::Int(9), boxx("let foo = fn() { 1 + 3 }; let bar = fn() { foo() + 1}; 4 + bar()"));
    // TODO: this should be parsed as a fn call assert_eq!(Expr::Int(4), boxx("fn() { 1 + 3 }()"));
    // TODO: have better failure message when ending a fn block with a semicolon
    // assert_eq!(Expr::Int(12), boxx("fn foo(a) { 1 + a; }; foo(4) + 7"));
    // TODO: recursion
  }

  #[test]
  pub fn test_const_decl() {
    let _ = env_logger::init();
    assert_eq!(Expr::Int(3), boxx("let x = 1 + 2; x"));
    assert_eq!(Expr::Int(1), boxx("let x = 1; x"));
    assert_eq!(Expr::Int(8), boxx("let x = 5; let y = 3; let z = x + y; z"));

    assert_eq!(Expr::Int(52), boxx("let underscore_name = 51; 1 + underscore_name"));
  }

  #[test]
  pub fn test_ternary() {
    let _ = env_logger::init();
    assert_eq!(Expr::Int(1), boxx("true ? 1 : 0"));
    assert_eq!(Expr::Int(0), boxx("false ? 1 : 0"));
    assert_eq!(Expr::Int(3), boxx("(false ? 1 : 0); 1 + 2"));
    assert_eq!(Expr::Int(3), boxx("false ? 1 : 0; 1 + 2"));
    assert_eq!(Expr::Int(0), boxx("((1 + 1) > 3) ? 1 : 0"));
    assert_eq!(Expr::Int(14), boxx("((1 + 1) > 3) ? true && false : 12 + 2"));
    assert_eq!(Expr::Int(14), boxx("1 + 1 > 3 ? true && false : 12 + 2"));
    assert_eq!(
      Expr::Int(10),
      boxx(
          "(false || true) ? ((1 + 2 > 12) ? 9 : 10) : ((1 + 2 < 12) ? 6 : 7)"
       )
    );
    // same as above but without parens
    assert_eq!(
      Expr::Int(10),
      boxx(
          "false || true ? 1 + 2 > 12 ? 9 : 10 : 1 + 2 < 12 ? 6 : 7"
       )
    );

    assert_eq!(Expr::Bool(true), boxx("1 + 2 > (1 == 0 ? 5 : 1)"));

    assert_eq!(Expr::Int(-1), boxx("true;false ? 1;2 : 0;-1"));
  }

  #[test]
  pub fn test_seq() {
    let _ = env_logger::init();
    assert_eq!(Expr::Int(5), boxx("3;5"));
    assert_eq!(Expr::Int(4), boxx("let x = 3;let y = 1;x + y"));
  }

  #[test]
  pub fn test_mod() {
    assert_eq!(Expr::Int(0), boxx("1 % 1"));
    assert_eq!(Expr::Int(2), boxx("7 % 5"));
    assert_eq!(Expr::Int(3), boxx("-7 % 5"));
    assert_eq!(Expr::Int(-2), boxx("-7 % -5"));
  }

  #[test]
  pub fn test_or_and_and() {
    assert_eq!(Expr::Bool(true), boxx("true && true"));
    assert_eq!(Expr::Bool(false), boxx("false && false"));
    assert_eq!(Expr::Bool(false), boxx("true && false"));
    assert_eq!(Expr::Bool(false), boxx("false && true"));

    assert_eq!(Expr::Bool(true), boxx("true || true"));
    assert_eq!(Expr::Bool(false), boxx("false || false"));
    assert_eq!(Expr::Bool(true), boxx("true || false"));
    assert_eq!(Expr::Bool(true), boxx("false || true"));
  }

  #[test]
  pub fn test_not_and_neg() {
    let _ = env_logger::init();

    // TODO: unop negate is not sticking to term
    //assert_eq!(Expr::Bool(true), boxx("!true || true"));

    assert_eq!(Expr::Bool(true), boxx("!false"));

    assert_eq!(Expr::Bool(true), boxx("!(true == false)"));
    assert_eq!(Expr::Bool(true), boxx("!((1 == 1) == (3 <= 2))"));
    assert_eq!(Expr::Bool(false), boxx("!((1 == 1) == !(3 <= 2))"));
    assert_eq!(Expr::Bool(true), boxx("!!(!(!(true)))"));

    assert_eq!(Expr::Int(-1), boxx("-1"));
    assert_eq!(Expr::Int(-100), boxx("-(20 * 5)"));
    assert_eq!(Expr::Int(-100), boxx("-(-20 * -5)"));
    assert_eq!(Expr::Int(-100), boxx("(20 * -5)"));
    assert_eq!(Expr::Int(-100), boxx("(-20 * 5)"));
    assert_eq!(Expr::Int(100), boxx("(-20 * -5)"));
    assert_eq!(Expr::Int(100), boxx("-(20 * -5)"));
    assert_eq!(Expr::Int(100), boxx("-(-20 * 5)"));
    assert_eq!(Expr::Int(0), boxx("1 + -1"));
    assert_eq!(Expr::Int(2), boxx("1 - -1"));
    assert_eq!(Expr::Int(0), boxx("-1 - -1"));
    assert_eq!(Expr::Int(-2), boxx("-1 - 1"));
    assert_eq!(Expr::Int(-2), boxx("-1 * 2"));
    assert_eq!(Expr::Int(-2), boxx("2 * -1"));
    assert_eq!(Expr::Int(-2), boxx("-2 * 1"));
    assert_eq!(Expr::Int(-1), boxx("-(2 * 1) + 1"));
    assert_eq!(Expr::Int(1), boxx("(2 * 1) + -1"));
  }

  #[test]
  pub fn test_comparison_operators() {
    assert_eq!(Expr::Bool(true), boxx("1 == 1"));
    assert_eq!(Expr::Bool(false), boxx("1 == 2"));
    assert_eq!(Expr::Bool(false), boxx("(1 == 1) == (1 == 2)"));
    assert_eq!(Expr::Bool(true), boxx("(5 == 2) == (1 == 2)"));
    assert_eq!(Expr::Bool(true), boxx("(6 == 6) == true"));
    assert_eq!(Expr::Bool(false), boxx("1 == true"));
    assert_eq!(Expr::Bool(true), boxx("false == false"));

    assert_eq!(Expr::Bool(true), boxx("1 > 0"));
    assert_eq!(Expr::Bool(false), boxx("1 < 0"));

    assert_eq!(Expr::Bool(true), boxx("88 > 34"));
    assert_eq!(Expr::Bool(false), boxx("1 < 1"));
    assert_eq!(Expr::Bool(false), boxx("1 > 1"));

    assert_eq!(Expr::Bool(true), boxx("88 != 34"));
    assert_eq!(Expr::Bool(false), boxx("88 != 88"));
    assert_eq!(Expr::Bool(true), boxx("88 <= 88"));
    assert_eq!(Expr::Bool(true), boxx("88 >= 88"));
    assert_eq!(Expr::Bool(true), boxx("1 >= 0"));
    assert_eq!(Expr::Bool(false), boxx("1 >= 12"));

    assert_eq!(Expr::Bool(false), boxx("true != true"));
    assert_eq!(Expr::Bool(true), boxx("true != false"));
  }

  #[test]
  pub fn test_spaces() {
    assert_eq!(Expr::Int(2), boxx("1 + 1"));
    assert_eq!(Expr::Int(12), boxx(" (3+   3)* 2      "));
    assert_eq!(Expr::Int(7), boxx("1 + 3*(3 + (1 - 2))"));
  }

  #[test]
  pub fn test_eval_mult() {
    assert_eq!(Expr::Int(12), boxx("6*2"));
    assert_eq!(Expr::Int(12), boxx("(3+3)*2"));
    assert_eq!(Expr::Int(0), boxx("(3+3)*0"));
  }

  #[test]
  pub fn test_eval_div() {
    assert_eq!(Expr::Int(6), boxx("12/2"));
    //assert_eq!(Expr::Float(1.5), boxx("3/2"));
  }

  #[test]
  pub fn test_eval_addition() {
    assert_eq!(Expr::Int(3), boxx("1+2"));
    assert_eq!(Expr::Int(16), boxx("5+7+4"));
    assert_eq!(Expr::Int(-1), boxx("1-2"));
    assert_eq!(Expr::Int(-100), boxx("32-132"));
    assert_eq!(Expr::Int(-120), boxx("32-132-20"));

    assert_eq!(Expr::Int(-80), boxx("32-(132-20)"));

    assert_eq!(Expr::Int(-6), boxx("4-(7+3)"));
    assert_eq!(Expr::Int(0), boxx("4-(7-3)"));
    assert_eq!(Expr::Int(8), boxx("4+(7-3)"));
    assert_eq!(Expr::Int(8), boxx("(4+7)-3)"));
    assert_eq!(Expr::Int(0), boxx("(4-7)+3)"));
    assert_eq!(Expr::Int(14), boxx("(4+7)+3)"));

    assert_eq!(Expr::Int(2), boxx("(1-1)+(2-2)+(3-3)+((1+2)-((3-2)+1)+1)"));
    assert_eq!(Expr::Int(0), boxx("((((((((((1-1)))+1))))-1)))"));
  }
}
<commit_msg>test for reusing let rebinds value<commit_after>extern crate boxx;

#[cfg(test)]
mod tests {
  extern crate boxx;
  use boxx::expr::{Expr, boxx};
  extern crate env_logger;

  #[test]
  pub fn test_if_else() {
    let _ = env_logger::init();

    assert_eq!(
      Expr::Int(34),
      boxx("if (true && false) { 32 } else if (!true && true) { 33 } else { 34 }")
    );

    assert_eq!(
      Expr::Int(32),
      boxx("if (true || false) { 32 } else if (!true && true) { 33 } else { 34 }")
    );

    assert_eq!(
      Expr::Int(30),
      boxx("if (true && false) { 32 } else { 30 }")
    );

    assert_eq!(
      Expr::Int(52),
      boxx("if (let x = 4; x > 3) { 52 } else { 30 }")
    );

    assert_eq!(
      Expr::Int(22),
      boxx("if (true) { 11 } else { 0 }; 22")
    );
  }

  #[test]
  pub fn test_func() {
    let _ = env_logger::init();

    assert_eq!(Expr::Int(2), boxx("let x = 4; fn foo() { let x = 1; x + 1 }; foo()"));
    assert_eq!(Expr::Int(6), boxx("let x = 5; fn foo() { x + 1 }; foo()"));
    assert_eq!(Expr::Int(60), boxx("fn foo() { 5 }; fn bar() { fn foo() { 6 }; foo() * 10 }; bar()"));
    assert_eq!(Expr::Int(50), boxx("fn foo() { 5 }; fn bar() { foo() * 10 }; bar()"));

    assert_eq!(Expr::Int(12), boxx("fn sum(a, b) { a + b }; sum(sum(3, 4), 5)"));
    assert_eq!(Expr::Int(12), boxx("fn tx_two(a) { 2 * a }; tx_two(tx_two(3))"));

    assert_eq!(
      Expr::Int(41),
      boxx("
        fn foo(a) {
          a < 40 ? foo(a + 3) : a
        };

        foo(20)
      ")
    );

    assert_eq!(
      Expr::Int(21),
      boxx("
        fn fib(n) {
          n == 0 ? 0 : (n == 1 ? 1 : fib(n - 1) + fib(n - 2))
        };

        fib(8)
      ")
    );

    assert_eq!(
      Expr::Int(28),
      boxx("
        fn foo(a) {
          1 + a
        };

        fn bar(b) {
          5 * b
        };

        foo(bar(4)) + 7
      ")
    );

    assert_eq!(Expr::Int(12), boxx("fn foo(a) { 1 + a }; foo(4) + 7"));
    assert_eq!(Expr::Int(12), boxx("let foo = fn(a) { 1 + a }; foo(4) + 7"));

    assert_eq!(Expr::Int(2), boxx("fn foo() { 1 + 1 }; foo()"));
    assert_eq!(Expr::Int(7), boxx("fn foo() { 1 + 3 }; foo() + 3"));
    assert_eq!(Expr::Int(9), boxx("fn foo() { 1 + 3 }; fn bar() { foo() + 1}; 4 + bar()"));

    assert_eq!(Expr::Int(2), boxx("let foo = fn() { 1 + 1 }; foo()"));
    assert_eq!(Expr::Int(7), boxx("let foo = fn() { 1 + 3 }; foo() + 3"));
    assert_eq!(Expr::Int(9), boxx("let foo = fn() { 1 + 3 }; let bar = fn() { foo() + 1}; 4 + bar()"));
    // TODO: this should be parsed as a fn call assert_eq!(Expr::Int(4), boxx("fn() { 1 + 3 }()"));
    // TODO: have better failure message when ending a fn block with a semicolon
    // assert_eq!(Expr::Int(12), boxx("fn foo(a) { 1 + a; }; foo(4) + 7"));
    // TODO: recursion
  }

  #[test]
  pub fn test_const_decl() {
    let _ = env_logger::init();
    assert_eq!(Expr::Int(3), boxx("let x = 1 + 2; x"));
    assert_eq!(Expr::Int(1), boxx("let x = 1; x"));
    assert_eq!(Expr::Int(8), boxx("let x = 5; let y = 3; let z = x + y; z"));

    // using let keyword again re-binds value
    assert_eq!(Expr::Int(5), boxx("let x = 2; let x = 3; x + 2"));

    assert_eq!(Expr::Int(52), boxx("let underscore_name = 51; 1 + underscore_name"));
  }

  #[test]
  pub fn test_ternary() {
    let _ = env_logger::init();
    assert_eq!(Expr::Int(1), boxx("true ? 1 : 0"));
    assert_eq!(Expr::Int(0), boxx("false ? 1 : 0"));
    assert_eq!(Expr::Int(3), boxx("(false ? 1 : 0); 1 + 2"));
    assert_eq!(Expr::Int(3), boxx("false ? 1 : 0; 1 + 2"));
    assert_eq!(Expr::Int(0), boxx("((1 + 1) > 3) ? 1 : 0"));
    assert_eq!(Expr::Int(14), boxx("((1 + 1) > 3) ? true && false : 12 + 2"));
    assert_eq!(Expr::Int(14), boxx("1 + 1 > 3 ? true && false : 12 + 2"));
    assert_eq!(
      Expr::Int(10),
      boxx(
          "(false || true) ? ((1 + 2 > 12) ? 9 : 10) : ((1 + 2 < 12) ? 6 : 7)"
       )
    );
    // same as above but without parens
    assert_eq!(
      Expr::Int(10),
      boxx(
          "false || true ? 1 + 2 > 12 ? 9 : 10 : 1 + 2 < 12 ? 6 : 7"
       )
    );

    assert_eq!(Expr::Bool(true), boxx("1 + 2 > (1 == 0 ? 5 : 1)"));

    assert_eq!(Expr::Int(-1), boxx("true;false ? 1;2 : 0;-1"));
  }

  #[test]
  pub fn test_seq() {
    let _ = env_logger::init();
    assert_eq!(Expr::Int(5), boxx("3;5"));
    assert_eq!(Expr::Int(4), boxx("let x = 3;let y = 1;x + y"));
  }

  #[test]
  pub fn test_mod() {
    assert_eq!(Expr::Int(0), boxx("1 % 1"));
    assert_eq!(Expr::Int(2), boxx("7 % 5"));
    assert_eq!(Expr::Int(3), boxx("-7 % 5"));
    assert_eq!(Expr::Int(-2), boxx("-7 % -5"));
  }

  #[test]
  pub fn test_or_and_and() {
    assert_eq!(Expr::Bool(true), boxx("true && true"));
    assert_eq!(Expr::Bool(false), boxx("false && false"));
    assert_eq!(Expr::Bool(false), boxx("true && false"));
    assert_eq!(Expr::Bool(false), boxx("false && true"));

    assert_eq!(Expr::Bool(true), boxx("true || true"));
    assert_eq!(Expr::Bool(false), boxx("false || false"));
    assert_eq!(Expr::Bool(true), boxx("true || false"));
    assert_eq!(Expr::Bool(true), boxx("false || true"));
  }

  #[test]
  pub fn test_not_and_neg() {
    let _ = env_logger::init();

    // TODO: unop negate is not sticking to term
    //assert_eq!(Expr::Bool(true), boxx("!true || true"));

    assert_eq!(Expr::Bool(true), boxx("!false"));

    assert_eq!(Expr::Bool(true), boxx("!(true == false)"));
    assert_eq!(Expr::Bool(true), boxx("!((1 == 1) == (3 <= 2))"));
    assert_eq!(Expr::Bool(false), boxx("!((1 == 1) == !(3 <= 2))"));
    assert_eq!(Expr::Bool(true), boxx("!!(!(!(true)))"));

    assert_eq!(Expr::Int(-1), boxx("-1"));
    assert_eq!(Expr::Int(-100), boxx("-(20 * 5)"));
    assert_eq!(Expr::Int(-100), boxx("-(-20 * -5)"));
    assert_eq!(Expr::Int(-100), boxx("(20 * -5)"));
    assert_eq!(Expr::Int(-100), boxx("(-20 * 5)"));
    assert_eq!(Expr::Int(100), boxx("(-20 * -5)"));
    assert_eq!(Expr::Int(100), boxx("-(20 * -5)"));
    assert_eq!(Expr::Int(100), boxx("-(-20 * 5)"));
    assert_eq!(Expr::Int(0), boxx("1 + -1"));
    assert_eq!(Expr::Int(2), boxx("1 - -1"));
    assert_eq!(Expr::Int(0), boxx("-1 - -1"));
    assert_eq!(Expr::Int(-2), boxx("-1 - 1"));
    assert_eq!(Expr::Int(-2), boxx("-1 * 2"));
    assert_eq!(Expr::Int(-2), boxx("2 * -1"));
    assert_eq!(Expr::Int(-2), boxx("-2 * 1"));
    assert_eq!(Expr::Int(-1), boxx("-(2 * 1) + 1"));
    assert_eq!(Expr::Int(1), boxx("(2 * 1) + -1"));
  }

  #[test]
  pub fn test_comparison_operators() {
    assert_eq!(Expr::Bool(true), boxx("1 == 1"));
    assert_eq!(Expr::Bool(false), boxx("1 == 2"));
    assert_eq!(Expr::Bool(false), boxx("(1 == 1) == (1 == 2)"));
    assert_eq!(Expr::Bool(true), boxx("(5 == 2) == (1 == 2)"));
    assert_eq!(Expr::Bool(true), boxx("(6 == 6) == true"));
    assert_eq!(Expr::Bool(false), boxx("1 == true"));
    assert_eq!(Expr::Bool(true), boxx("false == false"));

    assert_eq!(Expr::Bool(true), boxx("1 > 0"));
    assert_eq!(Expr::Bool(false), boxx("1 < 0"));

    assert_eq!(Expr::Bool(true), boxx("88 > 34"));
    assert_eq!(Expr::Bool(false), boxx("1 < 1"));
    assert_eq!(Expr::Bool(false), boxx("1 > 1"));

    assert_eq!(Expr::Bool(true), boxx("88 != 34"));
    assert_eq!(Expr::Bool(false), boxx("88 != 88"));
    assert_eq!(Expr::Bool(true), boxx("88 <= 88"));
    assert_eq!(Expr::Bool(true), boxx("88 >= 88"));
    assert_eq!(Expr::Bool(true), boxx("1 >= 0"));
    assert_eq!(Expr::Bool(false), boxx("1 >= 12"));

    assert_eq!(Expr::Bool(false), boxx("true != true"));
    assert_eq!(Expr::Bool(true), boxx("true != false"));
  }

  #[test]
  pub fn test_spaces() {
    assert_eq!(Expr::Int(2), boxx("1 + 1"));
    assert_eq!(Expr::Int(12), boxx(" (3+   3)* 2      "));
    assert_eq!(Expr::Int(7), boxx("1 + 3*(3 + (1 - 2))"));
  }

  #[test]
  pub fn test_eval_mult() {
    assert_eq!(Expr::Int(12), boxx("6*2"));
    assert_eq!(Expr::Int(12), boxx("(3+3)*2"));
    assert_eq!(Expr::Int(0), boxx("(3+3)*0"));
  }

  #[test]
  pub fn test_eval_div() {
    assert_eq!(Expr::Int(6), boxx("12/2"));
    //assert_eq!(Expr::Float(1.5), boxx("3/2"));
  }

  #[test]
  pub fn test_eval_addition() {
    assert_eq!(Expr::Int(3), boxx("1+2"));
    assert_eq!(Expr::Int(16), boxx("5+7+4"));
    assert_eq!(Expr::Int(-1), boxx("1-2"));
    assert_eq!(Expr::Int(-100), boxx("32-132"));
    assert_eq!(Expr::Int(-120), boxx("32-132-20"));

    assert_eq!(Expr::Int(-80), boxx("32-(132-20)"));

    assert_eq!(Expr::Int(-6), boxx("4-(7+3)"));
    assert_eq!(Expr::Int(0), boxx("4-(7-3)"));
    assert_eq!(Expr::Int(8), boxx("4+(7-3)"));
    assert_eq!(Expr::Int(8), boxx("(4+7)-3)"));
    assert_eq!(Expr::Int(0), boxx("(4-7)+3)"));
    assert_eq!(Expr::Int(14), boxx("(4+7)+3)"));

    assert_eq!(Expr::Int(2), boxx("(1-1)+(2-2)+(3-3)+((1+2)-((3-2)+1)+1)"));
    assert_eq!(Expr::Int(0), boxx("((((((((((1-1)))+1))))-1)))"));
  }
}
<|endoftext|>