<commit_before>// Copyright 2015 Ilkka Rauta
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

extern crate glfw;

extern crate htgl;

use glfw::Context;

use htgl::{VertexAttributeType,
    RenderOption,
    ShaderType,
    PrimitiveMode,
    SimpleUniformTypeFloat};

#[allow(dead_code)]
#[repr(packed)]
struct Vec3 {
    x: f32,
    y: f32,
    z: f32,
}

#[allow(dead_code)]
#[repr(packed)]
struct Rgba {
    r: u8,
    g: u8,
    b: u8,
    a: u8,
}

#[allow(dead_code)]
#[repr(packed)]
struct Vertex {
    position: Vec3,
    color: Rgba
}

impl Vertex {
    fn new(x: f32, y: f32, z: f32, r: u8, g: u8, b: u8, a: u8) -> Vertex {
        Vertex { position: Vec3 { x: x, y: y, z: z }, color: Rgba { r: r, g: g, b: b, a: a } }
    }
}

static VS_SOURCE: &'static str = "
#version 330 core

layout(location = 0) in vec3 position;
layout(location = 1) in vec4 color;

uniform float scale;

out vec4 v_color;

void main() {
    gl_Position.xyz = position * scale;
    gl_Position.w = 1.0;
    v_color = color;
}
";

static FS_SOURCE: &'static str = "
#version 330 core

in vec4 v_color;
out vec3 color;

void main() {
    color = v_color.rgb;
}
";

fn main() {
    let mut glfw = glfw::init(glfw::FAIL_ON_ERRORS).unwrap();

    glfw.window_hint(glfw::WindowHint::ContextVersion(3, 3));
    glfw.window_hint(glfw::WindowHint::OpenglProfile(glfw::OpenGlProfileHint::Core));
    glfw.window_hint(glfw::WindowHint::OpenglForwardCompat(true));

    // Create a windowed mode window and its OpenGL context
    let (mut window, events) = glfw.create_window(300, 300, "Hello this is window", glfw::WindowMode::Windowed)
        .expect("Failed to create GLFW window.");

    window.set_key_polling(true);
    window.make_current();

    htgl::load_with(|s| window.get_proc_address(s));

    let mut ctx = htgl::Context::new();
    println!("{:?}", ctx.get_info());
    ctx.renderer().set_option(RenderOption::ClearColor(1f32, 1f32, 1f32, 1f32));
    ctx.renderer().set_option(RenderOption::DepthTest(false));
    ctx.renderer().set_option(RenderOption::CullingEnabled(true));
    let vbo = ctx.new_buffer();
    let vertices = [
        Vertex::new(-0.5f32, -0.5f32, 0f32, 255, 0, 0, 0),
        Vertex::new(0.5f32, -0.5f32, 0f32, 0, 255, 0, 0),
        Vertex::new(0f32, 0.5f32, 0f32, 0, 0, 255, 0),
        ];
    ctx.edit_vertex_buffer(&vbo).data(&vertices);
    let ibo = ctx.new_buffer();
    let vao = ctx.new_vertex_array_simple(&[(3, VertexAttributeType::Float, false), (4, VertexAttributeType::UnsignedByte, true)], vbo, Some(ibo));
    if let Some(mut editor) = ctx.edit_index_buffer(&vao) {
        let indices = [0u16, 1u16, 2u16];
        editor.data(&indices);
    }
    let vs = ctx.new_shader(ShaderType::VertexShader, VS_SOURCE);
    if !ctx.shader_info(&vs).get_compile_status() {
        panic!(ctx.shader_info(&vs).get_info_log())
    }
    let fs = ctx.new_shader(ShaderType::FragmentShader, FS_SOURCE);
    if !ctx.shader_info(&fs).get_compile_status() {
        panic!(ctx.shader_info(&fs).get_info_log())
    }
    let program = ctx.new_program(&[vs, fs]);
    if !ctx.program_info(&program).get_link_status() {
        panic!(ctx.program_info(&program).get_info_log())
    }

    {
        let program_editor = ctx.edit_program(&program);
        let program_info = program_editor.program_info();
        let uniform_info = program_info.get_uniform_info();
        let scale_location = uniform_info.get_global_uniform("scale").map(|u| u.location).unwrap_or(-1);
        program_editor.uniform_f32(scale_location, 1, SimpleUniformTypeFloat::Uniform1f, &[1.5]);
        for uniform in uniform_info.globals.iter() {
            println!("{:?}", uniform);
        }
        for block in uniform_info.blocks.iter() {
            println!("InterfaceBlock {{ name: {}, index: {}, data_size: {} }}", block.name, block.index, block.data_size);
            for uniform in block.uniforms.iter() {
                println!("    {:?}", uniform);
            }
        }
        for attribute in program_info.get_attribute_info().attributes.iter() {
            println!("{:?}", attribute)
        }
    }

    while !window.should_close() {
        glfw.poll_events();
        for (_, event) in glfw::flush_messages(&events) {
            handle_window_event(&mut window, event);
        }

        let mut renderer = ctx.renderer();
        renderer.clear();
        renderer.use_vertex_array(&vao);
        renderer.use_program(&program);
        renderer.draw_elements_u16(PrimitiveMode::Triangles, 3, 0);

        window.swap_buffers();
        // break;
    }
}

fn handle_window_event(window: &mut glfw::Window, event: glfw::WindowEvent) {
    match event {
        glfw::WindowEvent::Key(glfw::Key::Escape, _, glfw::Action::Press, _) => {
            window.set_should_close(true)
        }
        _ => {}
    }
}
<commit_msg>Make demo app compile against current glfw-rs.<commit_after>// Copyright 2015 Ilkka Rauta
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

extern crate glfw;

extern crate htgl;

use glfw::Context;

use htgl::{VertexAttributeType,
    RenderOption,
    ShaderType,
    PrimitiveMode,
    SimpleUniformTypeFloat};

#[allow(dead_code)]
#[repr(packed)]
struct Vec3 {
    x: f32,
    y: f32,
    z: f32,
}

#[allow(dead_code)]
#[repr(packed)]
struct Rgba {
    r: u8,
    g: u8,
    b: u8,
    a: u8,
}

#[allow(dead_code)]
#[repr(packed)]
struct Vertex {
    position: Vec3,
    color: Rgba
}

impl Vertex {
    fn new(x: f32, y: f32, z: f32, r: u8, g: u8, b: u8, a: u8) -> Vertex {
        Vertex { position: Vec3 { x: x, y: y, z: z }, color: Rgba { r: r, g: g, b: b, a: a } }
    }
}

static VS_SOURCE: &'static str = "
#version 330 core

layout(location = 0) in vec3 position;
layout(location = 1) in vec4 color;

uniform float scale;

out vec4 v_color;

void main() {
    gl_Position.xyz = position * scale;
    gl_Position.w = 1.0;
    v_color = color;
}
";

static FS_SOURCE: &'static str = "
#version 330 core

in vec4 v_color;
out vec3 color;

void main() {
    color = v_color.rgb;
}
";

fn main() {
    let mut glfw = glfw::init(glfw::FAIL_ON_ERRORS).unwrap();

    glfw.window_hint(glfw::WindowHint::ContextVersion(3, 3));
    glfw.window_hint(glfw::WindowHint::OpenGlProfile(glfw::OpenGlProfileHint::Core));
    glfw.window_hint(glfw::WindowHint::OpenGlForwardCompat(true));

    // Create a windowed mode window and its OpenGL context
    let (mut window, events) = glfw.create_window(300, 300, "Hello this is window", glfw::WindowMode::Windowed)
        .expect("Failed to create GLFW window.");

    window.set_key_polling(true);
    window.make_current();

    htgl::load_with(|s| window.get_proc_address(s));

    let mut ctx = htgl::Context::new();
    println!("{:?}", ctx.get_info());
    ctx.renderer().set_option(RenderOption::ClearColor(1f32, 1f32, 1f32, 1f32));
    ctx.renderer().set_option(RenderOption::DepthTest(false));
    ctx.renderer().set_option(RenderOption::CullingEnabled(true));
    let vbo = ctx.new_buffer();
    let vertices = [
        Vertex::new(-0.5f32, -0.5f32, 0f32, 255, 0, 0, 0),
        Vertex::new(0.5f32, -0.5f32, 0f32, 0, 255, 0, 0),
        Vertex::new(0f32, 0.5f32, 0f32, 0, 0, 255, 0),
        ];
    ctx.edit_vertex_buffer(&vbo).data(&vertices);
    let ibo = ctx.new_buffer();
    let vao = ctx.new_vertex_array_simple(&[(3, VertexAttributeType::Float, false), (4, VertexAttributeType::UnsignedByte, true)], vbo, Some(ibo));
    if let Some(mut editor) = ctx.edit_index_buffer(&vao) {
        let indices = [0u16, 1u16, 2u16];
        editor.data(&indices);
    }
    let vs = ctx.new_shader(ShaderType::VertexShader, VS_SOURCE);
    if !ctx.shader_info(&vs).get_compile_status() {
        panic!(ctx.shader_info(&vs).get_info_log())
    }
    let fs = ctx.new_shader(ShaderType::FragmentShader, FS_SOURCE);
    if !ctx.shader_info(&fs).get_compile_status() {
        panic!(ctx.shader_info(&fs).get_info_log())
    }
    let program = ctx.new_program(&[vs, fs]);
    if !ctx.program_info(&program).get_link_status() {
        panic!(ctx.program_info(&program).get_info_log())
    }

    {
        let program_editor = ctx.edit_program(&program);
        let program_info = program_editor.program_info();
        let uniform_info = program_info.get_uniform_info();
        let scale_location = uniform_info.get_global_uniform("scale").map(|u| u.location).unwrap_or(-1);
        program_editor.uniform_f32(scale_location, 1, SimpleUniformTypeFloat::Uniform1f, &[1.5]);
        for uniform in uniform_info.globals.iter() {
            println!("{:?}", uniform);
        }
        for block in uniform_info.blocks.iter() {
            println!("InterfaceBlock {{ name: {}, index: {}, data_size: {} }}", block.name, block.index, block.data_size);
            for uniform in block.uniforms.iter() {
                println!("    {:?}", uniform);
            }
        }
        for attribute in program_info.get_attribute_info().attributes.iter() {
            println!("{:?}", attribute)
        }
    }

    while !window.should_close() {
        glfw.poll_events();
        for (_, event) in glfw::flush_messages(&events) {
            handle_window_event(&mut window, event);
        }

        let mut renderer = ctx.renderer();
        renderer.clear();
        renderer.use_vertex_array(&vao);
        renderer.use_program(&program);
        renderer.draw_elements_u16(PrimitiveMode::Triangles, 3, 0);

        window.swap_buffers();
        // break;
    }
}

fn handle_window_event(window: &mut glfw::Window, event: glfw::WindowEvent) {
    match event {
        glfw::WindowEvent::Key(glfw::Key::Escape, _, glfw::Action::Press, _) => {
            window.set_should_close(true)
        }
        _ => {}
    }
}
<|endoftext|>


<commit_before>extern crate bf;
extern crate clap;

use std::io::{self, Read, Write};
use std::fs::File;
use std::process::exit;

use clap::{Arg, App};

use bf::ast;
use bf::rle_ast;
use bf::flat;
use bf::peephole;

#[cfg(feature = "jit")]
use bf::jit;

use bf::traits::Interpretable;

const VERSION: &'static str = "0.1.0";

#[derive(Debug, Clone)]
struct Options {
    program_text:  Vec<u8>,
    memory_size:   Option<usize>,
    compiler_pass: Pass,
}

#[derive(Debug, Clone, Copy)]
enum Pass {
    Ast,
    Rle,
    Flat,
    Peep,
    #[cfg(feature = "jit")]
    Jit,
}

fn main() {
    // Process command-line options:
    let options = get_options();

    // Parse the program to AST:
    let program = parse(&options);

    match options.compiler_pass {
        Pass::Ast => {
            interpret(&*program, &options);
        }

        Pass::Rle => {
            let program = rle_ast::compile(&program);
            interpret(&*program, &options);
        }

        Pass::Flat => {
            let program = rle_ast::compile(&program);
            let program = flat::compile(&program);
            interpret(&*program, &options);
        }

        Pass::Peep => {
            let program = rle_ast::compile(&program);
            let program = peephole::compile(&program);
            interpret(&*program, &options);
        }

        #[cfg(feature = "jit")]
        Pass::Jit => {
            let program = rle_ast::compile(&program);
            let program = jit::compile(&program);
            interpret(&program, &options);
        }
    }
}

fn parse(options: &Options) -> Box<ast::Program> {
    ast::parse_program(&options.program_text)
        .unwrap_or_else(|e| error_exit(2, format!("Syntax error: {}.", e)))
}

fn interpret<P: Interpretable + ?Sized>(program: &P, options: &Options) {
    program.interpret_stdin(options.memory_size)
        .unwrap_or_else(|e| error_exit(3, format!("Runtime error: {}.", e)))
}

fn get_options() -> Options {
    let mut result = Options {
        program_text:  Vec::new(),
        memory_size:   None,
        compiler_pass: Pass::Flat,
    };

    let matches = build_clap_app().get_matches();

    if let Some(size) = matches.value_of("size") {
        let size = size.parse()
            .unwrap_or_else(|e| error_exit(1, format!("Could not parse memory size: {}", e)));
        result.memory_size = Some(size);
    }

    if matches.is_present("ast") {
        result.compiler_pass = Pass::Ast;
    } else if matches.is_present("rle") {
        result.compiler_pass = Pass::Rle;
    } else if matches.is_present("flat") {
        result.compiler_pass = Pass::Flat;
    } else if matches.is_present("peep") {
        result.compiler_pass = Pass::Peep;
    } else if matches.is_present("jit") {
        #[cfg(feature = "jit")]
        let _ = result.compiler_pass = Pass::Jit;
    }

    if let Some(exprs) = matches.values_of("expr") {
        for e in exprs {
            result.program_text.extend(e.as_bytes());
        }
    } else if let Some(files) = matches.values_of("INPUT") {
        for f in files {
            let mut file = File::open(f)
                .unwrap_or_else(|e| error_exit(1, format!("{}: {}", e, f)));
            file.read_to_end(&mut result.program_text)
                .unwrap_or_else(|e| error_exit(1, format!("{}: {}", e, f)));
        }
    } else {
        error_exit(1, "No program given.".to_owned());
    }

    result
}

fn build_clap_app() -> App<'static, 'static> {
    let app = App::new("bfi")
        .version(VERSION)
        .author("Jesse A. Tov <jesse.tov@gmail.com>")
        .about("A Brainfuck interpreter")
        .arg(Arg::with_name("expr")
            .short("e")
            .long("expr")
            .value_name("CODE")
            .help("BF code to execute")
            .multiple(true)
            .takes_value(true)
            .conflicts_with("INPUT"))
        .arg(Arg::with_name("INPUT")
            .help("The source file(s) to interpret")
            .multiple(true)
            .conflicts_with("expr")
            .index(1))
        .arg(Arg::with_name("size")
            .short("s")
            .long("size")
            .value_name("SIZE")
            .help("Memory size in bytes")
            .takes_value(true))
        .arg(Arg::with_name("ast")
            .long("ast")
            .help("Interpret the AST directly")
            .conflicts_with_all(&["flat", "rle", "peep", "jit"]))
        .arg(Arg::with_name("rle")
            .long("rle")
            .help("Interpret the run-length encoded AST directly")
            .conflicts_with_all(&["ast", "flat", "peep", "jit"]))
        .arg(Arg::with_name("flat")
            .long("flat")
            .help("Interpret the flattened bytecode (default)")
            .conflicts_with_all(&["ast", "rle", "peep", "jit"]))
        .arg(Arg::with_name("peep")
            .long("peep")
            .help("Interpret the peephole optimized bytecode (broken?)")
            .conflicts_with_all(&["ast", "rle", "flat", "jit"]));

    #[cfg(feature = "jit")]
    let app = app
        .arg(Arg::with_name("jit")
            .long("jit")
            .help("Use native X-64 JIT")
            .conflicts_with_all(&["ast", "rle", "flat", "peep"]));

    app
}

fn error_exit(code: i32, msg: String) -> ! {
    writeln!(io::stderr(), "{}", msg).unwrap();
    exit(code)
}

<commit_msg>Default pass now uses the peephole compiler.<commit_after>extern crate bf;
extern crate clap;

use std::io::{self, Read, Write};
use std::fs::File;
use std::process::exit;

use clap::{Arg, App};

use bf::ast;
use bf::rle_ast;
use bf::flat;
use bf::peephole;

#[cfg(feature = "jit")]
use bf::jit;

use bf::traits::Interpretable;

const VERSION: &'static str = "0.1.0";

#[derive(Debug, Clone)]
struct Options {
    program_text:  Vec<u8>,
    memory_size:   Option<usize>,
    compiler_pass: Pass,
}

#[derive(Debug, Clone, Copy)]
enum Pass {
    Ast,
    Rle,
    Flat,
    Peep,
    #[cfg(feature = "jit")]
    Jit,
}

fn main() {
    // Process command-line options:
    let options = get_options();

    // Parse the program to AST:
    let program = parse(&options);

    match options.compiler_pass {
        Pass::Ast => {
            interpret(&*program, &options);
        }

        Pass::Rle => {
            let program = rle_ast::compile(&program);
            interpret(&*program, &options);
        }

        Pass::Flat => {
            let program = rle_ast::compile(&program);
            let program = flat::compile(&program);
            interpret(&*program, &options);
        }

        Pass::Peep => {
            let program = rle_ast::compile(&program);
            let program = peephole::compile(&program);
            interpret(&*program, &options);
        }

        #[cfg(feature = "jit")]
        Pass::Jit => {
            let program = rle_ast::compile(&program);
            let program = jit::compile(&program);
            interpret(&program, &options);
        }
    }
}

fn parse(options: &Options) -> Box<ast::Program> {
    ast::parse_program(&options.program_text)
        .unwrap_or_else(|e| error_exit(2, format!("Syntax error: {}.", e)))
}

fn interpret<P: Interpretable + ?Sized>(program: &P, options: &Options) {
    program.interpret_stdin(options.memory_size)
        .unwrap_or_else(|e| error_exit(3, format!("Runtime error: {}.", e)))
}

fn get_options() -> Options {
    let mut result = Options {
        program_text:  Vec::new(),
        memory_size:   None,
        compiler_pass: Pass::Peep,
    };

    let matches = build_clap_app().get_matches();

    if let Some(size) = matches.value_of("size") {
        let size = size.parse()
            .unwrap_or_else(|e| error_exit(1, format!("Could not parse memory size: {}", e)));
        result.memory_size = Some(size);
    }

    if matches.is_present("ast") {
        result.compiler_pass = Pass::Ast;
    } else if matches.is_present("rle") {
        result.compiler_pass = Pass::Rle;
    } else if matches.is_present("flat") {
        result.compiler_pass = Pass::Flat;
    } else if matches.is_present("peep") {
        result.compiler_pass = Pass::Peep;
    } else if matches.is_present("jit") {
        #[cfg(feature = "jit")]
        let _ = result.compiler_pass = Pass::Jit;
    }

    if let Some(exprs) = matches.values_of("expr") {
        for e in exprs {
            result.program_text.extend(e.as_bytes());
        }
    } else if let Some(files) = matches.values_of("INPUT") {
        for f in files {
            let mut file = File::open(f)
                .unwrap_or_else(|e| error_exit(1, format!("{}: {}", e, f)));
            file.read_to_end(&mut result.program_text)
                .unwrap_or_else(|e| error_exit(1, format!("{}: {}", e, f)));
        }
    } else {
        error_exit(1, "No program given.".to_owned());
    }

    result
}

fn build_clap_app() -> App<'static, 'static> {
    let app = App::new("bfi")
        .version(VERSION)
        .author("Jesse A. Tov <jesse.tov@gmail.com>")
        .about("A Brainfuck interpreter")
        .arg(Arg::with_name("expr")
            .short("e")
            .long("expr")
            .value_name("CODE")
            .help("BF code to execute")
            .multiple(true)
            .takes_value(true)
            .conflicts_with("INPUT"))
        .arg(Arg::with_name("INPUT")
            .help("The source file(s) to interpret")
            .multiple(true)
            .conflicts_with("expr")
            .index(1))
        .arg(Arg::with_name("size")
            .short("s")
            .long("size")
            .value_name("SIZE")
            .help("Memory size in bytes")
            .takes_value(true))
        .arg(Arg::with_name("ast")
            .long("ast")
            .help("Interpret the AST directly")
            .conflicts_with_all(&["flat", "rle", "peep", "jit"]))
        .arg(Arg::with_name("rle")
            .long("rle")
            .help("Interpret the run-length encoded AST directly")
            .conflicts_with_all(&["ast", "flat", "peep", "jit"]))
        .arg(Arg::with_name("flat")
            .long("flat")
            .help("Interpret the flattened bytecode")
            .conflicts_with_all(&["ast", "rle", "peep", "jit"]))
        .arg(Arg::with_name("peep")
            .long("peep")
            .help("Interpret the peephole optimized bytecode (default)")
            .conflicts_with_all(&["ast", "rle", "flat", "jit"]));

    #[cfg(feature = "jit")]
    let app = app
        .arg(Arg::with_name("jit")
            .long("jit")
            .help("Use native X-64 JIT")
            .conflicts_with_all(&["ast", "rle", "flat", "peep"]));

    app
}

fn error_exit(code: i32, msg: String) -> ! {
    writeln!(io::stderr(), "{}", msg).unwrap();
    exit(code)
}

<|endoftext|>


<commit_before>use clap::{self, Arg, App, SubCommand, AppSettings};
use std::io::{self, Write};
use std::process;
use std::path::PathBuf;
use std::error;
use std::result;
use std::fmt;
use mprovision as mp;

#[derive(Debug, PartialEq)]
pub enum Command {
    List(ListParams),
    Cleanup(Option<PathBuf>),
}

#[derive(Debug, Default, PartialEq)]
pub struct ListParams {
    pub filter: Option<String>,
    pub expires_in_days: Option<i64>,
    pub directory: Option<PathBuf>,
}

pub type Result = result::Result<Command, Error>;

#[derive(Debug)]
pub enum Error {
    Lib(mp::Error),
    Clap(clap::Error),
    Custom(String),
}

impl Error {
    pub fn exit(&self) -> ! {
        match *self {
            Error::Clap(ref e) => e.exit(),
            _ => {
                writeln!(&mut io::stderr(), "{}", self).unwrap();
                process::exit(1);
            }
        }
    }
}

impl error::Error for Error {
    fn description(&self) -> &str {
        match *self {
            Error::Lib(ref e) => e.description(),
            Error::Clap(ref e) => e.description(),
            Error::Custom(ref e) => e,
        }
    }

    fn cause(&self) -> Option<&error::Error> {
        match *self {
            Error::Lib(ref e) => Some(e),
            Error::Clap(ref e) => Some(e),
            Error::Custom(_) => None,
        }
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Error::Lib(ref e) => error::Error::description(e).fmt(f),
            Error::Clap(ref e) => error::Error::description(e).fmt(f),
            Error::Custom(ref e) => e.fmt(f),
        }
    }
}

impl From<mp::Error> for Error {
    fn from(e: mp::Error) -> Self {
        Error::Lib(e)
    }
}

impl From<clap::Error> for Error {
    fn from(e: clap::Error) -> Self {
        Error::Clap(e)
    }
}

pub fn parse<I, S>(args: I) -> Result
    where I: IntoIterator<Item = S>,
          S: Clone,
          ::std::ffi::OsString: From<S>
{
    let app = App::new("mprovision")
        .about("A tool that helps iOS developers to manage mobileprovision files.")
        .subcommand(SubCommand::with_name("list")
            .about("Lists provisioning profiles")
            .setting(AppSettings::DisableVersion)
            .arg(Arg::with_name("TEXT")
                .long("--filter")
                .required(false)
                .empty_values(false)
                .takes_value(true))
            .arg(Arg::with_name("DAYS")
                .long("--expires-in-days")
                .required(false)
                .empty_values(false)
                .takes_value(true))
            .arg(Arg::with_name("DIRECTORY")
                .required(false)
                .empty_values(false)
                .takes_value(true)))
        .subcommand(SubCommand::with_name("cleanup")
            .about("Removes expired provisioning profiles")
            .setting(AppSettings::DisableVersion)
            .arg(Arg::with_name("DIRECTORY")
                .required(false)
                .empty_values(false)
                .takes_value(true)));

    let matches = app.get_matches_from_safe(args)?;
    if let Some(list_matches) = matches.subcommand_matches("list") {
        let mut params = ListParams::default();
        params.filter = list_matches.value_of("TEXT").map(|text| text.to_string());
        if let Some(days) = list_matches.value_of("DAYS")
            .and_then(|days| days.parse::<i64>().ok()) {
            if days < 0 || days > 365 {
                return Err(Error::Custom("DAYS should be between 0 and 365".to_string()));
            }
            params.expires_in_days = Some(days);
        }
        params.directory = list_matches.value_of("DIRECTORY").map(|dir| dir.into());
        Ok(Command::List(params))
    } else if let Some(cleanup_matches) = matches.subcommand_matches("cleanup") {
        let directory = cleanup_matches.value_of("DIRECTORY").map(|dir| dir.into());
        Ok(Command::Cleanup(directory))
    } else {
        Err(Error::Custom("Command isn't implemented".to_string()))
    }
}

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

    #[test]
    fn list_command() {
        expect!(parse(&["mprovision", "list"]))
            .to(be_ok().value(Command::List(ListParams::default())));

        expect!(parse(&["mprovision", "list", "."])).to(be_ok().value(Command::List(ListParams {
            filter: None,
            expires_in_days: None,
            directory: Some(".".into()),
        })));

        expect!(parse(&["mprovision", "list", "--filter", "abc"]))
            .to(be_ok().value(Command::List(ListParams {
                filter: Some("abc".to_string()),
                expires_in_days: None,
                directory: None,
            })));

        expect!(parse(&["mprovision", "list", "--filter", ""])).to(be_err());

        expect!(parse(&["mprovision", "list", "--expires-in-days", "3"]))
            .to(be_ok().value(Command::List(ListParams {
                filter: None,
                expires_in_days: Some(3),
                directory: None,
            })));

        expect!(parse(&["mprovision", "list", "--expires-in-days", "-3"])).to(be_err());
        expect!(parse(&["mprovision", "list", "--expires-in-days", "366"])).to(be_err());

        expect!(parse(&["mprovision", "list", "--filter", "abc", "--expires-in-days", "3", "."]))
            .to(be_ok().value(Command::List(ListParams {
                filter: Some("abc".to_string()),
                expires_in_days: Some(3),
                directory: Some(".".into()),
            })));
    }

    #[test]
    fn cleanup_command() {
        expect!(parse(&["mprovision", "cleanup"])).to(be_ok().value(Command::Cleanup(None)));

        expect!(parse(&["mprovision", "cleanup", "."]))
            .to(be_ok().value(Command::Cleanup(Some(".".into()))));
    }
}
<commit_msg>Add ShowUuid command<commit_after>use clap::{self, Arg, App, SubCommand, AppSettings};
use std::io::{self, Write};
use std::process;
use std::path::PathBuf;
use std::error;
use std::result;
use std::fmt;
use mprovision as mp;

#[derive(Debug, PartialEq)]
pub enum Command {
    List(ListParams),
    ShowUuid(String, Option<PathBuf>),
    Cleanup(Option<PathBuf>),
}

#[derive(Debug, Default, PartialEq)]
pub struct ListParams {
    pub filter: Option<String>,
    pub expires_in_days: Option<i64>,
    pub directory: Option<PathBuf>,
}

pub type Result = result::Result<Command, Error>;

#[derive(Debug)]
pub enum Error {
    Lib(mp::Error),
    Clap(clap::Error),
    Custom(String),
}

impl Error {
    pub fn exit(&self) -> ! {
        match *self {
            Error::Clap(ref e) => e.exit(),
            _ => {
                writeln!(&mut io::stderr(), "{}", self).unwrap();
                process::exit(1);
            }
        }
    }
}

impl error::Error for Error {
    fn description(&self) -> &str {
        match *self {
            Error::Lib(ref e) => e.description(),
            Error::Clap(ref e) => e.description(),
            Error::Custom(ref e) => e,
        }
    }

    fn cause(&self) -> Option<&error::Error> {
        match *self {
            Error::Lib(ref e) => Some(e),
            Error::Clap(ref e) => Some(e),
            Error::Custom(_) => None,
        }
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Error::Lib(ref e) => error::Error::description(e).fmt(f),
            Error::Clap(ref e) => error::Error::description(e).fmt(f),
            Error::Custom(ref e) => e.fmt(f),
        }
    }
}

impl From<mp::Error> for Error {
    fn from(e: mp::Error) -> Self {
        Error::Lib(e)
    }
}

impl From<clap::Error> for Error {
    fn from(e: clap::Error) -> Self {
        Error::Clap(e)
    }
}

pub fn parse<I, S>(args: I) -> Result
    where I: IntoIterator<Item = S>,
          S: Clone,
          ::std::ffi::OsString: From<S>
{
    let app = App::new("mprovision")
        .about("A tool that helps iOS developers to manage mobileprovision files.")
        .subcommand(SubCommand::with_name("list")
            .about("Lists provisioning profiles")
            .setting(AppSettings::DisableVersion)
            .arg(Arg::with_name("TEXT")
                .long("--filter")
                .required(false)
                .empty_values(false)
                .takes_value(true))
            .arg(Arg::with_name("DAYS")
                .long("--expires-in-days")
                .required(false)
                .empty_values(false)
                .takes_value(true))
            .arg(Arg::with_name("DIRECTORY")
                .required(false)
                .empty_values(false)
                .takes_value(true)))
        .subcommand(SubCommand::with_name("cleanup")
            .about("Removes expired provisioning profiles")
            .setting(AppSettings::DisableVersion)
            .arg(Arg::with_name("DIRECTORY")
                .required(false)
                .empty_values(false)
                .takes_value(true)))
        .subcommand(SubCommand::with_name("show")
            .about("Shows details of a provisioning profile")
            .setting(AppSettings::DisableVersion)
            .arg(Arg::with_name("UUID")
                .long("--uuid")
                .required(true)
                .empty_values(false)
                .takes_value(true))
            .arg(Arg::with_name("DIRECTORY")
                .required(false)
                .empty_values(false)
                .takes_value(true)));

    let matches = app.get_matches_from_safe(args)?;
    if let Some(list_matches) = matches.subcommand_matches("list") {
        let mut params = ListParams::default();
        params.filter = list_matches.value_of("TEXT").map(|text| text.to_string());
        if let Some(days) = list_matches.value_of("DAYS")
            .and_then(|days| days.parse::<i64>().ok()) {
            if days < 0 || days > 365 {
                return Err(Error::Custom("DAYS should be between 0 and 365".to_string()));
            }
            params.expires_in_days = Some(days);
        }
        params.directory = list_matches.value_of("DIRECTORY").map(|dir| dir.into());
        Ok(Command::List(params))
    } else if let Some(show_matches) = matches.subcommand_matches("show") {
        let uuid = show_matches.value_of("UUID").map(|uuid| uuid.to_string()).unwrap();
        let directory = show_matches.value_of("DIRECTORY").map(|dir| dir.into());
        Ok(Command::ShowUuid(uuid, directory))
    } else if let Some(cleanup_matches) = matches.subcommand_matches("cleanup") {
        let directory = cleanup_matches.value_of("DIRECTORY").map(|dir| dir.into());
        Ok(Command::Cleanup(directory))
    } else {
        Err(Error::Custom("Command isn't implemented".to_string()))
    }
}

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

    #[test]
    fn list_command() {
        expect!(parse(&["mprovision", "list"]))
            .to(be_ok().value(Command::List(ListParams::default())));

        expect!(parse(&["mprovision", "list", "."])).to(be_ok().value(Command::List(ListParams {
            filter: None,
            expires_in_days: None,
            directory: Some(".".into()),
        })));

        expect!(parse(&["mprovision", "list", "--filter", "abc"]))
            .to(be_ok().value(Command::List(ListParams {
                filter: Some("abc".to_string()),
                expires_in_days: None,
                directory: None,
            })));

        expect!(parse(&["mprovision", "list", "--filter", ""])).to(be_err());

        expect!(parse(&["mprovision", "list", "--expires-in-days", "3"]))
            .to(be_ok().value(Command::List(ListParams {
                filter: None,
                expires_in_days: Some(3),
                directory: None,
            })));

        expect!(parse(&["mprovision", "list", "--expires-in-days", "-3"])).to(be_err());
        expect!(parse(&["mprovision", "list", "--expires-in-days", "366"])).to(be_err());

        expect!(parse(&["mprovision", "list", "--filter", "abc", "--expires-in-days", "3", "."]))
            .to(be_ok().value(Command::List(ListParams {
                filter: Some("abc".to_string()),
                expires_in_days: Some(3),
                directory: Some(".".into()),
            })));
    }

    #[test]
    fn show_uuid_command() {
        expect!(parse(&["mprovision", "show", "--uuid", "abcd"]))
            .to(be_ok().value(Command::ShowUuid("abcd".to_string(), None)));

        expect!(parse(&["mprovision", "show", "--uuid", "abcd", "."]))
            .to(be_ok().value(Command::ShowUuid("abcd".to_string(), Some(".".into()))));
    }

    #[test]
    fn cleanup_command() {
        expect!(parse(&["mprovision", "cleanup"])).to(be_ok().value(Command::Cleanup(None)));

        expect!(parse(&["mprovision", "cleanup", "."]))
            .to(be_ok().value(Command::Cleanup(Some(".".into()))));
    }
}
<|endoftext|>


<commit_before>use clap::{self, Arg, App, SubCommand};
use std::io::{self, Write};
use std::process;
use std::path::PathBuf;
use std::error;
use std::result;
use std::fmt;
use mprovision as mp;

#[derive(Debug, PartialEq)]
pub enum Command {
    List(ListParams),
    Cleanup(Option<PathBuf>),
}

#[derive(Debug, Default, PartialEq)]
pub struct ListParams {
    pub filter: Option<String>,
    pub expires_in_days: Option<i64>,
    pub directory: Option<PathBuf>,
}

pub type Result = result::Result<Command, Error>;

#[derive(Debug)]
pub enum Error {
    Lib(mp::Error),
    Clap(clap::Error),
    Custom(String),
}

impl Error {
    pub fn exit(&self) -> ! {
        match *self {
            Error::Clap(ref e) => e.exit(),
            _ => {
                writeln!(&mut io::stderr(), "{}", self).unwrap();
                process::exit(1);
            }
        }
    }
}

impl error::Error for Error {
    fn description(&self) -> &str {
        match *self {
            Error::Lib(ref e) => e.description(),
            Error::Clap(ref e) => e.description(),
            Error::Custom(ref e) => e,
        }
    }

    fn cause(&self) -> Option<&error::Error> {
        match *self {
            Error::Lib(ref e) => Some(e),
            Error::Clap(ref e) => Some(e),
            Error::Custom(_) => None,
        }
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Error::Lib(ref e) => error::Error::description(e).fmt(f),
            Error::Clap(ref e) => error::Error::description(e).fmt(f),
            Error::Custom(ref e) => e.fmt(f),
        }
    }
}

impl From<mp::Error> for Error {
    fn from(e: mp::Error) -> Self {
        Error::Lib(e)
    }
}

impl From<clap::Error> for Error {
    fn from(e: clap::Error) -> Self {
        Error::Clap(e)
    }
}

pub fn parse<I, S>(args: I) -> Result
    where I: IntoIterator<Item = S>,
          S: Clone,
          ::std::ffi::OsString: From<S>
{
    let app = App::new("mprovision")
        .about("A tool that helps iOS developers to manage mobileprovision files.")
        .subcommand(SubCommand::with_name("list")
            .about("Lists provisioning profiles")
            .arg(Arg::with_name("TEXT")
                .long("--filter")
                .required(false)
                .empty_values(false)
                .takes_value(true))
            .arg(Arg::with_name("DAYS")
                .long("--expires-in-days")
                .required(false)
                .empty_values(false)
                .takes_value(true))
            .arg(Arg::with_name("DIRECTORY")
                .required(false)
                .empty_values(false)
                .takes_value(true)))
        .subcommand(SubCommand::with_name("cleanup")
            .about("Removes expired provisioning profiles")
            .arg(Arg::with_name("DIRECTORY")
                .required(false)
                .empty_values(false)
                .takes_value(true)));

    let matches = app.get_matches_from_safe(args)?;
    if let Some(list_matches) = matches.subcommand_matches("list") {
        let mut params = ListParams::default();
        params.filter = list_matches.value_of("TEXT").map(|text| text.to_string());
        if let Some(days) = list_matches.value_of("DAYS")
            .and_then(|days| days.parse::<i64>().ok()) {
            if days < 0 || days > 365 {
                return Err(Error::Custom("DAYS should be between 0 and 365".to_string()));
            }
            params.expires_in_days = Some(days);
        }
        params.directory = list_matches.value_of("DIRECTORY").map(|dir| dir.into());
        Ok(Command::List(params))
    } else if let Some(cleanup_matches) = matches.subcommand_matches("cleanup") {
        let directory = cleanup_matches.value_of("DIRECTORY").map(|dir| dir.into());
        Ok(Command::Cleanup(directory))
    } else {
        Err(Error::Custom("Command isn't implemented".to_string()))
    }
}

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

    #[test]
    fn list_command() {
        expect!(parse(&["mprovision", "list"]))
            .to(be_ok().value(Command::List(ListParams::default())));

        expect!(parse(&["mprovision", "list", "."])).to(be_ok().value(Command::List(ListParams {
            filter: None,
            expires_in_days: None,
            directory: Some(".".into()),
        })));

        expect!(parse(&["mprovision", "list", "--filter", "abc"]))
            .to(be_ok().value(Command::List(ListParams {
                filter: Some("abc".to_string()),
                expires_in_days: None,
                directory: None,
            })));

        expect!(parse(&["mprovision", "list", "--filter", ""])).to(be_err());

        expect!(parse(&["mprovision", "list", "--expires-in-days", "3"]))
            .to(be_ok().value(Command::List(ListParams {
                filter: None,
                expires_in_days: Some(3),
                directory: None,
            })));

        expect!(parse(&["mprovision", "list", "--expires-in-days", "-3"])).to(be_err());
        expect!(parse(&["mprovision", "list", "--expires-in-days", "366"])).to(be_err());

        expect!(parse(&["mprovision", "list", "--filter", "abc", "--expires-in-days", "3", "."]))
            .to(be_ok().value(Command::List(ListParams {
                filter: Some("abc".to_string()),
                expires_in_days: Some(3),
                directory: Some(".".into()),
            })));
    }

    #[test]
    fn cleanup_command() {
        expect!(parse(&["mprovision", "cleanup"])).to(be_ok().value(Command::Cleanup(None)));

        expect!(parse(&["mprovision", "cleanup", "."]))
            .to(be_ok().value(Command::Cleanup(Some(".".into()))));
    }
}
<commit_msg>Disable version for subcommands<commit_after>use clap::{self, Arg, App, SubCommand, AppSettings};
use std::io::{self, Write};
use std::process;
use std::path::PathBuf;
use std::error;
use std::result;
use std::fmt;
use mprovision as mp;

#[derive(Debug, PartialEq)]
pub enum Command {
    List(ListParams),
    Cleanup(Option<PathBuf>),
}

#[derive(Debug, Default, PartialEq)]
pub struct ListParams {
    pub filter: Option<String>,
    pub expires_in_days: Option<i64>,
    pub directory: Option<PathBuf>,
}

pub type Result = result::Result<Command, Error>;

#[derive(Debug)]
pub enum Error {
    Lib(mp::Error),
    Clap(clap::Error),
    Custom(String),
}

impl Error {
    pub fn exit(&self) -> ! {
        match *self {
            Error::Clap(ref e) => e.exit(),
            _ => {
                writeln!(&mut io::stderr(), "{}", self).unwrap();
                process::exit(1);
            }
        }
    }
}

impl error::Error for Error {
    fn description(&self) -> &str {
        match *self {
            Error::Lib(ref e) => e.description(),
            Error::Clap(ref e) => e.description(),
            Error::Custom(ref e) => e,
        }
    }

    fn cause(&self) -> Option<&error::Error> {
        match *self {
            Error::Lib(ref e) => Some(e),
            Error::Clap(ref e) => Some(e),
            Error::Custom(_) => None,
        }
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Error::Lib(ref e) => error::Error::description(e).fmt(f),
            Error::Clap(ref e) => error::Error::description(e).fmt(f),
            Error::Custom(ref e) => e.fmt(f),
        }
    }
}

impl From<mp::Error> for Error {
    fn from(e: mp::Error) -> Self {
        Error::Lib(e)
    }
}

impl From<clap::Error> for Error {
    fn from(e: clap::Error) -> Self {
        Error::Clap(e)
    }
}

pub fn parse<I, S>(args: I) -> Result
    where I: IntoIterator<Item = S>,
          S: Clone,
          ::std::ffi::OsString: From<S>
{
    let app = App::new("mprovision")
        .about("A tool that helps iOS developers to manage mobileprovision files.")
        .subcommand(SubCommand::with_name("list")
            .about("Lists provisioning profiles")
            .setting(AppSettings::DisableVersion)
            .arg(Arg::with_name("TEXT")
                .long("--filter")
                .required(false)
                .empty_values(false)
                .takes_value(true))
            .arg(Arg::with_name("DAYS")
                .long("--expires-in-days")
                .required(false)
                .empty_values(false)
                .takes_value(true))
            .arg(Arg::with_name("DIRECTORY")
                .required(false)
                .empty_values(false)
                .takes_value(true)))
        .subcommand(SubCommand::with_name("cleanup")
            .about("Removes expired provisioning profiles")
            .setting(AppSettings::DisableVersion)
            .arg(Arg::with_name("DIRECTORY")
                .required(false)
                .empty_values(false)
                .takes_value(true)));

    let matches = app.get_matches_from_safe(args)?;
    if let Some(list_matches) = matches.subcommand_matches("list") {
        let mut params = ListParams::default();
        params.filter = list_matches.value_of("TEXT").map(|text| text.to_string());
        if let Some(days) = list_matches.value_of("DAYS")
            .and_then(|days| days.parse::<i64>().ok()) {
            if days < 0 || days > 365 {
                return Err(Error::Custom("DAYS should be between 0 and 365".to_string()));
            }
            params.expires_in_days = Some(days);
        }
        params.directory = list_matches.value_of("DIRECTORY").map(|dir| dir.into());
        Ok(Command::List(params))
    } else if let Some(cleanup_matches) = matches.subcommand_matches("cleanup") {
        let directory = cleanup_matches.value_of("DIRECTORY").map(|dir| dir.into());
        Ok(Command::Cleanup(directory))
    } else {
        Err(Error::Custom("Command isn't implemented".to_string()))
    }
}

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

    #[test]
    fn list_command() {
        expect!(parse(&["mprovision", "list"]))
            .to(be_ok().value(Command::List(ListParams::default())));

        expect!(parse(&["mprovision", "list", "."])).to(be_ok().value(Command::List(ListParams {
            filter: None,
            expires_in_days: None,
            directory: Some(".".into()),
        })));

        expect!(parse(&["mprovision", "list", "--filter", "abc"]))
            .to(be_ok().value(Command::List(ListParams {
                filter: Some("abc".to_string()),
                expires_in_days: None,
                directory: None,
            })));

        expect!(parse(&["mprovision", "list", "--filter", ""])).to(be_err());

        expect!(parse(&["mprovision", "list", "--expires-in-days", "3"]))
            .to(be_ok().value(Command::List(ListParams {
                filter: None,
                expires_in_days: Some(3),
                directory: None,
            })));

        expect!(parse(&["mprovision", "list", "--expires-in-days", "-3"])).to(be_err());
        expect!(parse(&["mprovision", "list", "--expires-in-days", "366"])).to(be_err());

        expect!(parse(&["mprovision", "list", "--filter", "abc", "--expires-in-days", "3", "."]))
            .to(be_ok().value(Command::List(ListParams {
                filter: Some("abc".to_string()),
                expires_in_days: Some(3),
                directory: Some(".".into()),
            })));
    }

    #[test]
    fn cleanup_command() {
        expect!(parse(&["mprovision", "cleanup"])).to(be_ok().value(Command::Cleanup(None)));

        expect!(parse(&["mprovision", "cleanup", "."]))
            .to(be_ok().value(Command::Cleanup(Some(".".into()))));
    }
}
<|endoftext|>


<commit_before>use std::fmt::{Display, Error, Formatter};

#[derive(Debug, PartialEq, Eq)]
pub enum BinOp {
    Minus,
    Plus,
    Slash,
    Star,
}

#[derive(PartialEq, Eq, PartialOrd, Ord)]
pub enum Precedence {
    Const = 100,
    Mult = 60,
    Add = 50,
}

impl BinOp {
    pub fn precedence(&self) -> Precedence {
        match *self {
            BinOp::Minus | BinOp::Plus => Precedence::Add,
            BinOp::Slash | BinOp::Star => Precedence::Mult,
        }
    }

    pub fn is_commutative(&self) -> bool {
        match *self {
            BinOp::Minus | BinOp::Slash => false,
            _ => true
        }
    }
}

impl Display for BinOp {
    fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
        match *self {
            BinOp::Minus => write!(fmt, "-"),
            BinOp::Plus => write!(fmt, "+"),
            BinOp::Slash => write!(fmt, "/"),
            BinOp::Star => write!(fmt, "*"),
        }
    }
}

#[derive(Debug)]
pub enum Exp {
    BinExp(Box<Exp>, BinOp, Box<Exp>),
    Float(f64),
    Var(String),
}

impl Exp {
    pub fn precedence(&self) -> Precedence {
        match *self {
            Exp::BinExp(_, ref o, _) => o.precedence(),
            Exp::Float(_) | Exp::Var(_) => Precedence::Const,
        }
    }
}

impl Display for Exp {
    fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
        match *self {
            Exp::BinExp(ref e1, ref o, ref e2) => {
                let prec = self.precedence();

                // Put grouping parentheses if the left subexpression has a lower-precedence
                // operator, e.g. (1 + 2) * 3
                let left = if prec > e1.precedence() {
                    format!("({})", e1)
                } else {
                    format!("{}", e1)
                };

                let right_prec = e2.precedence();

                // Put grouping parentheses around the right subexpression if it has a
                // lower-precedence operator,  __OR__ if `o` is not commutative and the precedence
                // is the same, e.g. (1 + 2) * 3 __OR__ 1 - (2 + 3)
                let right = if prec > right_prec || (!o.is_commutative() && prec == right_prec) {
                    format!("({})", e2)
                } else {
                    format!("{}", e2)
                };

                write!(fmt, "{} {} {}", left, o, right)
            }
            Exp::Float(f)   => write!(fmt, "{}", f),
            Exp::Var(ref v) => write!(fmt, "{}", v),

        }
    }
}

#[derive(Debug)]
pub enum Stmt {
    Assign(String, Exp),
    Decl(String, Exp),
}

impl Display for Stmt {
    fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
        match *self {
            Stmt::Assign(ref v, ref exp) => write!(fmt, "{} = {};\n", v, exp),
            Stmt::Decl(ref v, ref exp) => write!(fmt, "var {} = {};\n", v, exp),
        }
    }
}
<commit_msg>add `++`/`--` to ast<commit_after>use std::fmt::{Display, Error, Formatter};

#[derive(Debug, PartialEq, Eq)]
pub enum BinOp {
    Minus,
    Plus,
    Slash,
    Star,
}

#[derive(PartialEq, Eq, PartialOrd, Ord)]
pub enum Precedence {
    Const = 110,
    Inc = 100,
    Mult = 60,
    Add = 50,
}

impl BinOp {
    pub fn precedence(&self) -> Precedence {
        match *self {
            BinOp::Minus | BinOp::Plus => Precedence::Add,
            BinOp::Slash | BinOp::Star => Precedence::Mult,
        }
    }

    pub fn is_commutative(&self) -> bool {
        match *self {
            BinOp::Minus | BinOp::Slash => false,
            _ => true
        }
    }
}

impl Display for BinOp {
    fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
        match *self {
            BinOp::Minus => write!(fmt, "-"),
            BinOp::Plus => write!(fmt, "+"),
            BinOp::Slash => write!(fmt, "/"),
            BinOp::Star => write!(fmt, "*"),
        }
    }
}

#[derive(Debug)]
pub enum Exp {
    BinExp(Box<Exp>, BinOp, Box<Exp>),
    Float(f64),
    Var(String),
    PostDec(Box<Exp>),
    PostInc(Box<Exp>),
    PreDec(Box<Exp>),
    PreInc(Box<Exp>),
}

impl Exp {
    pub fn precedence(&self) -> Precedence {
        match *self {
            Exp::BinExp(_, ref o, _) => o.precedence(),
            Exp::Float(_) | Exp::Var(_) => Precedence::Const,
            Exp::PostDec(_) | Exp::PostInc(_) | Exp::PreDec(_) | Exp::PreInc(_) => Precedence::Inc,
        }
    }
}

impl Display for Exp {
    fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
        match *self {
            Exp::BinExp(ref e1, ref o, ref e2) => {
                let prec = self.precedence();

                // Put grouping parentheses if the left subexpression has a lower-precedence
                // operator, e.g. (1 + 2) * 3
                let left = if prec > e1.precedence() {
                    format!("({})", e1)
                } else {
                    format!("{}", e1)
                };

                let right_prec = e2.precedence();

                // Put grouping parentheses around the right subexpression if it has a
                // lower-precedence operator,  __OR__ if `o` is not commutative and the precedence
                // is the same, e.g. (1 + 2) * 3 __OR__ 1 - (2 + 3)
                let right = if prec > right_prec || (!o.is_commutative() && prec == right_prec) {
                    format!("({})", e2)
                } else {
                    format!("{}", e2)
                };

                write!(fmt, "{} {} {}", left, o, right)
            }
            Exp::Float(f)   => write!(fmt, "{}", f),
            Exp::Var(ref v) => write!(fmt, "{}", v),
            Exp::PostDec(ref e) => write!(fmt, "{}--", e),
            Exp::PostInc(ref e) => write!(fmt, "{}++", e),
            Exp::PreDec(ref e) => write!(fmt, "--{}", e),
            Exp::PreInc(ref e) => write!(fmt, "++{}", e),
        }
    }
}

#[derive(Debug)]
pub enum Stmt {
    Assign(String, Exp),
    Decl(String, Exp),
}

impl Display for Stmt {
    fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
        match *self {
            Stmt::Assign(ref v, ref exp) => write!(fmt, "{} = {};\n", v, exp),
            Stmt::Decl(ref v, ref exp) => write!(fmt, "var {} = {};\n", v, exp),
        }
    }
}
<|endoftext|>


<commit_before>use std::ops::Not;
use std::ops::BitAnd;
use std::ops::BitOr;

#[derive(PartialEq, Debug)]
pub enum Bit { Zero, One }

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

    #[test]
    fn eq_zero_zero_test() {
        assert!(Bit::Zero == Bit::Zero);
    }

    #[test]
    #[should_panic]
    fn eq_zero_one_test() {
        assert!(Bit::Zero == Bit::One);
    }

    #[test]
    #[should_panic]
    fn eq_one_zero_test() {
        assert!(Bit::One == Bit::Zero);
    }

    #[test]
    fn eq_one_one_test() {
        assert!(Bit::One == Bit::One);
    }

    #[test]
    #[should_panic]
    fn neq_zero_zero_test() {
        assert!(Bit::Zero != Bit::Zero);
    }

    #[test]
    fn neq_zero_one_test() {
        assert!(Bit::Zero != Bit::One);
    }

    #[test]
    fn neq_one_zero_test() {
        assert!(Bit::One != Bit::Zero);
    }

    #[test]
    #[should_panic]
    fn neq_one_one_test() {
        assert!(Bit::One != Bit::One);
    }
}

impl Bit {
    pub fn to_i32(&self) -> i32 {
        match *self {
            Bit::Zero => 0,
            Bit::One => 1
        }
    }

    pub fn to_u32(&self) -> u32 {
        match *self {
            Bit::Zero => 0u32,
            Bit::One => 1u32
        }
    }
}

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

    #[test]
    fn to_i32_zero_test() {
        assert_eq!(Bit::Zero.to_i32(), 0);
    }

    #[test]
    fn to_i32_one_test() {
        assert_eq!(Bit::One.to_i32(), 1);
    }
}

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

    #[test]
    fn to_u32_zero_test() {
        assert_eq!(Bit::Zero.to_u32(), 0);
    }

    #[test]
    fn to_u32_one_test() {
        assert_eq!(Bit::One.to_u32(), 1);
    }
}

impl Not for Bit {
    type Output = Bit;

    fn not(self) -> Bit {
        not_impl(&self)
    }
}

fn not_impl(x: &Bit) -> Bit
{
    match *x {
        Bit::Zero => Bit::One,
        Bit::One => Bit::Zero
    }
}

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

    #[test]
    fn not_zero_test() {
        assert_eq!(!Bit::Zero, Bit::One);
    }

    #[test]
    fn not_one_test() {
        assert_eq!(!Bit::One, Bit::Zero);
    }
}

impl<'a> Not for &'a Bit {
    type Output = <Bit as Not>::Output;

    fn not(self) -> <Bit as Not>::Output {
        not_impl(self)
    }
}

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

    #[test]
    fn not_ref_zero_test() {
        assert_eq!(!&Bit::Zero, Bit::One);
    }

    #[test]
    fn not_ref_one_test() {
        assert_eq!(!&Bit::One, Bit::Zero);
    }
}

impl BitAnd<Bit> for Bit {
    type Output = Bit;

    fn bitand(self, rhs: Bit) -> Bit {
        bitand_impl(&self, &rhs)
    }
}

fn bitand_impl(x: &Bit, y: &Bit) -> Bit {
    match (x, y) {
        (&Bit::Zero, &Bit::Zero) => Bit::Zero,
        (&Bit::Zero, &Bit::One) => Bit::Zero,
        (&Bit::One, &Bit::Zero) => Bit::Zero,
        (&Bit::One, &Bit::One) => Bit::One
    }
}

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

    #[test]
    fn bitwise_and_zero_zero_test() {
        assert_eq!(Bit::Zero & Bit::Zero, Bit::Zero);
    }

    #[test]
    fn bitwise_and_zero_one_test() {
        assert_eq!(Bit::Zero & Bit::One, Bit::Zero);
    }

    #[test]
    fn bitwise_and_one_zero_test() {
        assert_eq!(Bit::One & Bit::Zero, Bit::Zero);
    }

    #[test]
    fn bitwise_and_one_one_test() {
        assert_eq!(Bit::One & Bit::One, Bit::One);
    }
}

impl<'a> BitAnd<Bit> for &'a Bit {
    type Output = <Bit as BitAnd<Bit>>::Output;

    fn bitand(self, rhs: Bit) -> <Bit as BitAnd<Bit>>::Output {
        bitand_impl(self, &rhs)
    }
}

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

    #[test]
    fn bitwise_and_ref_lhs_zero_zero_test() {
        assert_eq!(&Bit::Zero & Bit::Zero, Bit::Zero);
    }

    #[test]
    fn bitwise_and_ref_lhs_zero_one_test() {
        assert_eq!(&Bit::Zero & Bit::One, Bit::Zero);
    }

    #[test]
    fn bitwise_and_ref_lhs_one_zero_test() {
        assert_eq!(&Bit::One & Bit::Zero, Bit::Zero);
    }

    #[test]
    fn bitwise_and_ref_lhs_one_one_test() {
        assert_eq!(&Bit::One & Bit::One, Bit::One);
    }
}

impl<'a> BitAnd<&'a Bit> for Bit {
    type Output = <Bit as BitAnd<Bit>>::Output;

    fn bitand(self, rhs: &'a Bit) -> <Bit as BitAnd<Bit>>::Output {
        bitand_impl(&self, rhs)
    }
}

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

    #[test]
    fn bitwise_and_ref_rhs_zero_zero_test() {
        assert_eq!(Bit::Zero & &Bit::Zero, Bit::Zero);
    }

    #[test]
    fn bitwise_and_ref_rhs_zero_one_test() {
        assert_eq!(Bit::Zero & &Bit::One, Bit::Zero);
    }

    #[test]
    fn bitwise_and_ref_rhs_one_zero_test() {
        assert_eq!(Bit::One & &Bit::Zero, Bit::Zero);
    }

    #[test]
    fn bitwise_and_ref_rhs_one_one_test() {
        assert_eq!(Bit::One & &Bit::One, Bit::One);
    }
}

impl<'a, 'b> BitAnd<&'a Bit> for &'b Bit {
    type Output = <Bit as BitAnd<Bit>>::Output;

    fn bitand(self, rhs: &'a Bit) -> <Bit as BitAnd<Bit>>::Output {
        bitand_impl(self, rhs)
    }
}

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

    #[test]
    fn bitwise_and_ref_lhs_and_rhs_zero_zero_test() {
        assert_eq!(&Bit::Zero & &Bit::Zero, Bit::Zero);
    }

    #[test]
    fn bitwise_and_ref_lhs_and_rhs_zero_one_test() {
        assert_eq!(&Bit::Zero & &Bit::One, Bit::Zero);
    }

    #[test]
    fn bitwise_and_ref_lhs_and_rhs_one_zero_test() {
        assert_eq!(&Bit::One & &Bit::Zero, Bit::Zero);
    }

    #[test]
    fn bitwise_and_ref_lhs_and_rhs_one_one_test() {
        assert_eq!(&Bit::One & &Bit::One, Bit::One);
    }
}

impl BitOr for Bit {
    type Output = Bit;

    fn bitor(self, rhs: Bit) -> Bit {
        bitor_impl(&self, &rhs)
    }
}

fn bitor_impl(x: &Bit, y: &Bit) -> Bit
{
    match (x, y) {
        (&Bit::Zero, &Bit::Zero) => Bit::Zero,
        (&Bit::Zero, &Bit::One) => Bit::One,
        (&Bit::One, &Bit::Zero) => Bit::One,
        (&Bit::One, &Bit::One) => Bit::One
    }
}

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

    #[test]
    fn bitwise_or_zero_zero_test() {
        assert_eq!(Bit::Zero | Bit::Zero, Bit::Zero);
    }

    #[test]
    fn bitwise_or_zero_one_test() {
        assert_eq!(Bit::Zero | Bit::One, Bit::One);
    }

    #[test]
    fn bitwise_or_one_zero_test() {
        assert_eq!(Bit::One | Bit::Zero, Bit::One);
    }

    #[test]
    fn bitwise_or_one_one_test() {
        assert_eq!(Bit::One | Bit::One, Bit::One);
    }
}

impl<'a> BitOr<Bit> for &'a Bit {
    type Output = <Bit as BitOr<Bit>>::Output;

    fn bitor(self, rhs: Bit) -> <Bit as BitOr<Bit>>::Output {
        bitor_impl(self, &rhs)
    }
}

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

    #[test]
    fn bitwise_or_ref_lhs_zero_zero_test() {
        assert_eq!(&Bit::Zero | Bit::Zero, Bit::Zero);
    }

    #[test]
    fn bitwise_or_ref_lhs_zero_one_test() {
        assert_eq!(&Bit::Zero | Bit::One, Bit::One);
    }

    #[test]
    fn bitwise_or_ref_lhs_one_zero_test() {
        assert_eq!(&Bit::One | Bit::Zero, Bit::One);
    }

    #[test]
    fn bitwise_or_ref_lhs_one_one_test() {
        assert_eq!(&Bit::One | Bit::One, Bit::One);
    }
}

impl<'a> BitOr<&'a Bit> for Bit {
    type Output = <Bit as BitOr<Bit>>::Output;

    fn bitor(self, rhs: &'a Bit) -> <Bit as BitOr<Bit>>::Output {
        bitor_impl(&self, rhs)
    }
}

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

    #[test]
    fn bitwise_or_ref_rhs_zero_zero_test() {
        assert_eq!(Bit::Zero | &Bit::Zero, Bit::Zero);
    }

    #[test]
    fn bitwise_or_ref_rhs_zero_one_test() {
        assert_eq!(Bit::Zero | &Bit::One, Bit::One);
    }

    #[test]
    fn bitwise_or_ref_rhs_one_zero_test() {
        assert_eq!(Bit::One | &Bit::Zero, Bit::One);
    }

    #[test]
    fn bitwise_or_ref_rhs_one_one_test() {
        assert_eq!(Bit::One | &Bit::One, Bit::One);
    }
}

impl<'a, 'b> BitOr<&'a Bit> for &'b Bit {
    type Output = <Bit as BitOr<Bit>>::Output;

    fn bitor(self, rhs: &'a Bit) -> <Bit as BitOr<Bit>>::Output {
        bitor_impl(self, rhs)
    }
}

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

    #[test]
    fn bitwise_and_ref_lhs_and_rhs_zero_zero_test() {
        assert_eq!(&Bit::Zero | &Bit::Zero, Bit::Zero);
    }

    #[test]
    fn bitwise_and_ref_lhs_and_rhs_zero_one_test() {
        assert_eq!(&Bit::Zero | &Bit::One, Bit::One);
    }

    #[test]
    fn bitwise_and_ref_lhs_and_rhs_one_zero_test() {
        assert_eq!(&Bit::One | &Bit::Zero, Bit::One);
    }

    #[test]
    fn bitwise_and_ref_lhs_and_rhs_one_one_test() {
        assert_eq!(&Bit::One | &Bit::One, Bit::One);
    }
}
<commit_msg>more test renaming<commit_after>use std::ops::Not;
use std::ops::BitAnd;
use std::ops::BitOr;

#[derive(PartialEq, Debug)]
pub enum Bit { Zero, One }

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

    #[test]
    fn eq_zero_zero_test() {
        assert!(Bit::Zero == Bit::Zero);
    }

    #[test]
    #[should_panic]
    fn eq_zero_one_test() {
        assert!(Bit::Zero == Bit::One);
    }

    #[test]
    #[should_panic]
    fn eq_one_zero_test() {
        assert!(Bit::One == Bit::Zero);
    }

    #[test]
    fn eq_one_one_test() {
        assert!(Bit::One == Bit::One);
    }

    #[test]
    #[should_panic]
    fn neq_zero_zero_test() {
        assert!(Bit::Zero != Bit::Zero);
    }

    #[test]
    fn neq_zero_one_test() {
        assert!(Bit::Zero != Bit::One);
    }

    #[test]
    fn neq_one_zero_test() {
        assert!(Bit::One != Bit::Zero);
    }

    #[test]
    #[should_panic]
    fn neq_one_one_test() {
        assert!(Bit::One != Bit::One);
    }
}

impl Bit {
    pub fn to_i32(&self) -> i32 {
        match *self {
            Bit::Zero => 0,
            Bit::One => 1
        }
    }

    pub fn to_u32(&self) -> u32 {
        match *self {
            Bit::Zero => 0u32,
            Bit::One => 1u32
        }
    }
}

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

    #[test]
    fn zero_test() {
        assert_eq!(Bit::Zero.to_i32(), 0);
    }

    #[test]
    fn one_test() {
        assert_eq!(Bit::One.to_i32(), 1);
    }
}

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

    #[test]
    fn zero_test() {
        assert_eq!(Bit::Zero.to_u32(), 0);
    }

    #[test]
    fn one_test() {
        assert_eq!(Bit::One.to_u32(), 1);
    }
}

impl Not for Bit {
    type Output = Bit;

    fn not(self) -> Bit {
        not_impl(&self)
    }
}

fn not_impl(x: &Bit) -> Bit
{
    match *x {
        Bit::Zero => Bit::One,
        Bit::One => Bit::Zero
    }
}

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

    #[test]
    fn zero_test() {
        assert_eq!(!Bit::Zero, Bit::One);
    }

    #[test]
    fn one_test() {
        assert_eq!(!Bit::One, Bit::Zero);
    }
}

impl<'a> Not for &'a Bit {
    type Output = <Bit as Not>::Output;

    fn not(self) -> <Bit as Not>::Output {
        not_impl(self)
    }
}

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

    #[test]
    fn zero_test() {
        assert_eq!(!&Bit::Zero, Bit::One);
    }

    #[test]
    fn one_test() {
        assert_eq!(!&Bit::One, Bit::Zero);
    }
}

impl BitAnd<Bit> for Bit {
    type Output = Bit;

    fn bitand(self, rhs: Bit) -> Bit {
        bitand_impl(&self, &rhs)
    }
}

fn bitand_impl(x: &Bit, y: &Bit) -> Bit {
    match (x, y) {
        (&Bit::Zero, &Bit::Zero) => Bit::Zero,
        (&Bit::Zero, &Bit::One) => Bit::Zero,
        (&Bit::One, &Bit::Zero) => Bit::Zero,
        (&Bit::One, &Bit::One) => Bit::One
    }
}

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

    #[test]
    fn zero_zero_test() {
        assert_eq!(Bit::Zero & Bit::Zero, Bit::Zero);
    }

    #[test]
    fn zero_one_test() {
        assert_eq!(Bit::Zero & Bit::One, Bit::Zero);
    }

    #[test]
    fn one_zero_test() {
        assert_eq!(Bit::One & Bit::Zero, Bit::Zero);
    }

    #[test]
    fn one_one_test() {
        assert_eq!(Bit::One & Bit::One, Bit::One);
    }
}

impl<'a> BitAnd<Bit> for &'a Bit {
    type Output = <Bit as BitAnd<Bit>>::Output;

    fn bitand(self, rhs: Bit) -> <Bit as BitAnd<Bit>>::Output {
        bitand_impl(self, &rhs)
    }
}

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

    #[test]
    fn zero_zero_test() {
        assert_eq!(&Bit::Zero & Bit::Zero, Bit::Zero);
    }

    #[test]
    fn zero_one_test() {
        assert_eq!(&Bit::Zero & Bit::One, Bit::Zero);
    }

    #[test]
    fn one_zero_test() {
        assert_eq!(&Bit::One & Bit::Zero, Bit::Zero);
    }

    #[test]
    fn one_one_test() {
        assert_eq!(&Bit::One & Bit::One, Bit::One);
    }
}

impl<'a> BitAnd<&'a Bit> for Bit {
    type Output = <Bit as BitAnd<Bit>>::Output;

    fn bitand(self, rhs: &'a Bit) -> <Bit as BitAnd<Bit>>::Output {
        bitand_impl(&self, rhs)
    }
}

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

    #[test]
    fn zero_zero_test() {
        assert_eq!(Bit::Zero & &Bit::Zero, Bit::Zero);
    }

    #[test]
    fn zero_one_test() {
        assert_eq!(Bit::Zero & &Bit::One, Bit::Zero);
    }

    #[test]
    fn one_zero_test() {
        assert_eq!(Bit::One & &Bit::Zero, Bit::Zero);
    }

    #[test]
    fn one_one_test() {
        assert_eq!(Bit::One & &Bit::One, Bit::One);
    }
}

impl<'a, 'b> BitAnd<&'a Bit> for &'b Bit {
    type Output = <Bit as BitAnd<Bit>>::Output;

    fn bitand(self, rhs: &'a Bit) -> <Bit as BitAnd<Bit>>::Output {
        bitand_impl(self, rhs)
    }
}

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

    #[test]
    fn zero_zero_test() {
        assert_eq!(&Bit::Zero & &Bit::Zero, Bit::Zero);
    }

    #[test]
    fn zero_one_test() {
        assert_eq!(&Bit::Zero & &Bit::One, Bit::Zero);
    }

    #[test]
    fn one_zero_test() {
        assert_eq!(&Bit::One & &Bit::Zero, Bit::Zero);
    }

    #[test]
    fn one_one_test() {
        assert_eq!(&Bit::One & &Bit::One, Bit::One);
    }
}

impl BitOr for Bit {
    type Output = Bit;

    fn bitor(self, rhs: Bit) -> Bit {
        bitor_impl(&self, &rhs)
    }
}

fn bitor_impl(x: &Bit, y: &Bit) -> Bit
{
    match (x, y) {
        (&Bit::Zero, &Bit::Zero) => Bit::Zero,
        (&Bit::Zero, &Bit::One) => Bit::One,
        (&Bit::One, &Bit::Zero) => Bit::One,
        (&Bit::One, &Bit::One) => Bit::One
    }
}

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

    #[test]
    fn zero_zero_test() {
        assert_eq!(Bit::Zero | Bit::Zero, Bit::Zero);
    }

    #[test]
    fn zero_one_test() {
        assert_eq!(Bit::Zero | Bit::One, Bit::One);
    }

    #[test]
    fn one_zero_test() {
        assert_eq!(Bit::One | Bit::Zero, Bit::One);
    }

    #[test]
    fn one_one_test() {
        assert_eq!(Bit::One | Bit::One, Bit::One);
    }
}

impl<'a> BitOr<Bit> for &'a Bit {
    type Output = <Bit as BitOr<Bit>>::Output;

    fn bitor(self, rhs: Bit) -> <Bit as BitOr<Bit>>::Output {
        bitor_impl(self, &rhs)
    }
}

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

    #[test]
    fn zero_zero_test() {
        assert_eq!(&Bit::Zero | Bit::Zero, Bit::Zero);
    }

    #[test]
    fn zero_one_test() {
        assert_eq!(&Bit::Zero | Bit::One, Bit::One);
    }

    #[test]
    fn one_zero_test() {
        assert_eq!(&Bit::One | Bit::Zero, Bit::One);
    }

    #[test]
    fn one_one_test() {
        assert_eq!(&Bit::One | Bit::One, Bit::One);
    }
}

impl<'a> BitOr<&'a Bit> for Bit {
    type Output = <Bit as BitOr<Bit>>::Output;

    fn bitor(self, rhs: &'a Bit) -> <Bit as BitOr<Bit>>::Output {
        bitor_impl(&self, rhs)
    }
}

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

    #[test]
    fn zero_zero_test() {
        assert_eq!(Bit::Zero | &Bit::Zero, Bit::Zero);
    }

    #[test]
    fn zero_one_test() {
        assert_eq!(Bit::Zero | &Bit::One, Bit::One);
    }

    #[test]
    fn one_zero_test() {
        assert_eq!(Bit::One | &Bit::Zero, Bit::One);
    }

    #[test]
    fn one_one_test() {
        assert_eq!(Bit::One | &Bit::One, Bit::One);
    }
}

impl<'a, 'b> BitOr<&'a Bit> for &'b Bit {
    type Output = <Bit as BitOr<Bit>>::Output;

    fn bitor(self, rhs: &'a Bit) -> <Bit as BitOr<Bit>>::Output {
        bitor_impl(self, rhs)
    }
}

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

    #[test]
    fn zero_zero_test() {
        assert_eq!(&Bit::Zero | &Bit::Zero, Bit::Zero);
    }

    #[test]
    fn zero_one_test() {
        assert_eq!(&Bit::Zero | &Bit::One, Bit::One);
    }

    #[test]
    fn one_zero_test() {
        assert_eq!(&Bit::One | &Bit::Zero, Bit::One);
    }

    #[test]
    fn one_one_test() {
        assert_eq!(&Bit::One | &Bit::One, Bit::One);
    }
}
<|endoftext|>


<commit_before>use std::collections::{HashMap, HashSet, VecDeque};
use std::iter;

use serde::de::{Deserialize, Deserializer};

use itertools::Itertools;

#[cfg(test)]
use serde_json;

/// Marker for unsanitized input.
#[derive(Debug)]
pub struct Unsanitary;

impl<'de> Deserialize<'de> for Unsanitary {
    fn deserialize<D>(_deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        Ok(Unsanitary)
    }
}

/// Marker for sanitized input.
#[derive(Debug)]
pub struct Sanitary;

/// Used to represent errors
#[derive(Debug)]
pub enum NfaError {
    UnknownState(String),
    UnknownSymbol(String),
}

/// Nondeterministic finite automata.
#[derive(Debug, Deserialize, Serialize)]
pub struct Nfa<T> {
    /// Marker to ensure that the state machine has been validated before it is used for any
    /// computations.
    #[serde(skip_serializing)]
    _sanitized: T,

    /// Start state.
    start: String,

    /// The final (accepting) states of the automata.
    final_states: HashSet<String>,

    /// The alphabet of symbols the automata accepts.
    alphabet: HashSet<String>,

    /// The nodes within the automata. Each node has mappings from alphabet symbols to sets of
    /// other states.
    nodes: HashMap<String, HashMap<String, HashSet<String>>>,
}

impl Nfa<Unsanitary> {
    /// Ensures that the NFA is valid, and that relevant invariants within the structure hold.
    pub fn check(self) -> Result<Nfa<Sanitary>, NfaError> {
        let Nfa {
            start,
            final_states,
            alphabet,
            nodes,
            ..
        } = self;

        // ensure that the start state is a valid state
        if !nodes.contains_key(&start) {
            return Err(NfaError::UnknownState(start));
        }

        // ensure that all final states are listed as actual states
        if let Some(unknown_state) = final_states
            .iter()
            .find(|&state| !nodes.contains_key(state))
        {
            return Err(NfaError::UnknownState(unknown_state.to_owned()));
        }

        // ensure that all state transitions are on valid symbols
        if let Some((unknown_symbol, _)) = nodes
            .iter()
            .filter_map(|(_, maps)| {
                maps.iter().find(|&(symbol, _)| !alphabet.contains(symbol))
            })
            .next()
        {
            return Err(NfaError::UnknownSymbol(unknown_symbol.to_owned()));
        }

        // ensure that all state transitions are to valid states
        if let Some(unknown_state) = nodes
            .iter()
            .filter_map(|(_, maps)| {
                maps.iter()
                    .filter_map(|(_, states)| {
                        states.iter().find(|&state| !nodes.contains_key(state))
                    })
                    .next()
            })
            .next()
        {
            return Err(NfaError::UnknownState(unknown_state.to_owned()));
        }

        Ok(Nfa {
            _sanitized: Sanitary,
            start,
            final_states,
            alphabet,
            nodes,
        })
    }
}

#[test]
fn valid_nfa() {
    let input = r#"{
        "start": "1",
        "alphabet": ["a", "b"],
        "nodes": {
            "1": {
                "a": ["1", "2"],
                "b": ["1"]
            },
            "2": {
                "a": ["3"],
                "b": ["3"]
            },
            "3": {
                "a": ["1"],
                "b": ["2"]
            }
        },
        "final_states": ["3"]
    }"#;
    let unsanitary: Nfa<_> = serde_json::from_str(input).unwrap();
    unsanitary.check().unwrap();
}

#[test]
fn invalid_nfa_final_state() {
    let input = r#"{
        "start": "1",
        "alphabet": ["a", "b"],
        "nodes": {
            "1": {
                "a": ["1", "2"],
                "b": ["1"]
            },
            "2": {
                "a": ["3"],
                "b": ["3"]
            },
            "3": {
                "a": ["1"],
                "b": ["2"]
            }
        },
        "final_states": ["3", "4"]
    }"#;
    let unsanitary: Nfa<_> = serde_json::from_str(input).unwrap();
    match unsanitary.check().unwrap_err() {
        NfaError::UnknownState(err) => assert_eq!(err, "4"),
        err @ _ => panic!(err),
    }
}

#[test]
fn invalid_nfa_state_transition() {
    let input = r#"{
        "start": "1",
        "alphabet": ["a", "b"],
        "nodes": {
            "1": {
                "a": ["1", "2"],
                "b": ["1"]
            },
            "2": {
                "a": ["3"],
                "b": ["3"]
            },
            "3": {
                "a": ["1"],
                "b": ["2", "4"]
            }
        },
        "final_states": ["3"]
    }"#;
    let unsanitary: Nfa<_> = serde_json::from_str(input).unwrap();
    match unsanitary.check().unwrap_err() {
        NfaError::UnknownState(err) => assert_eq!(err, "4"),
        err @ _ => panic!(err),
    }
}

#[test]
fn invalid_nfa_alphabet() {
    let input = r#"{
        "start": "1",
        "alphabet": ["a", "b"],
        "nodes": {
            "1": {
                "a": ["1", "2"],
                "b": ["1"],
                "c": ["1"]
            },
            "2": {
                "a": ["3"],
                "b": ["3"]
            },
            "3": {
                "a": ["1"],
                "b": ["2"]
            }
        },
        "final_states": ["3"]
    }"#;
    let unsanitary: Nfa<_> = serde_json::from_str(input).unwrap();
    match unsanitary.check().unwrap_err() {
        NfaError::UnknownSymbol(err) => assert_eq!(err, "c"),
        err @ _ => panic!(err),
    }
}

#[test]
fn invalid_nfa_start_state() {
    let input = r#"{
        "start": "4",
        "alphabet": ["a", "b"],
        "nodes": {
            "1": {
                "a": ["1", "2"],
                "b": ["1"]
            },
            "2": {
                "a": ["3"],
                "b": ["3"]
            },
            "3": {
                "a": ["1"],
                "b": ["2"]
            }
        },
        "final_states": ["3"]
    }"#;
    let unsanitary: Nfa<_> = serde_json::from_str(input).unwrap();
    match unsanitary.check().unwrap_err() {
        NfaError::UnknownState(err) => assert_eq!(err, "4"),
        err @ _ => panic!(err),
    }
}

fn hash_states<'a, I>(states: I) -> String
where
    I: IntoIterator<Item = &'a String>,
{
    states
        .into_iter()
        .map(|x| x.as_str())
        .intersperse(" + ")
        .collect()
}

impl Nfa<Sanitary> {
    pub fn make_deterministic(self) -> Option<Dfa> {
        let Nfa {
            alphabet,
            start: nfa_start,
            nodes: nfa_nodes,
            final_states: nfa_final_states,
            ..
        } = self;
        let mut work = VecDeque::new();
        let mut final_states = HashSet::new();
        let mut nodes = HashMap::new();

        let start = hash_states(iter::once(&nfa_start));
        work.push_back(vec![nfa_start]);

        while let Some(node) = work.pop_front() {
            let dfa_state = hash_states(&node);
            assert!(!nodes.contains_key(&dfa_state));
            let transition_table = alphabet
                .iter()
                .map(|letter| {
                    let transition = node.iter()
                        .filter_map(|state| nfa_nodes.get(state))
                        .filter_map(|transitions| transitions.get(letter.as_str()))
                        .flatten()
                        .map(|x| x.to_owned())
                        .unique()
                        .sorted();
                    let transition_state = hash_states(&transition);
                    if transition != node && !nodes.contains_key(&transition_state) &&
                        !work.contains(&transition)
                    {
                        work.push_back(transition)
                    }
                    (letter.to_owned(), transition_state)
                })
                .collect();

            if node.iter().any(|state| nfa_final_states.contains(state)) {
                final_states.insert(dfa_state.to_owned());
            }

            nodes.insert(dfa_state, transition_table);
        }

        Some(Dfa {
            final_states,
            start,
            alphabet,
            nodes,
        })
    }
}

#[test]
fn basic_deterministic_conversion() {
    let input = r#"{
        "start": "1",
        "alphabet": ["a", "b"],
        "nodes": {
            "1": {
                "a": ["1", "2"],
                "b": ["1"]
            },
            "2": {
                "a": ["3"],
                "b": ["3"]
            },
            "3": {}
        },
        "final_states": ["3"]
        }"#;
    let unsanitary: Nfa<_> = serde_json::from_str(input).unwrap();
    let nfa = unsanitary.check().unwrap();
    let Dfa {
        final_states,
        start,
        alphabet,
        nodes,
    } = nfa.make_deterministic().unwrap();
    assert_eq!(
        final_states,
        vec!["1 + 2 + 3".into(), "1 + 3".into()]
            .into_iter()
            .collect()
    );
    assert_eq!(start, "1".to_owned());
    assert_eq!(alphabet, vec!["a".into(), "b".into()].into_iter().collect());
    assert_eq!(
        nodes,
        vec![
            (
                "1".into(),
                vec![("a".into(), "1 + 2".into()), ("b".into(), "1".into())]
                    .into_iter()
                    .collect(),
            ),
            (
                "1 + 2".into(),
                vec![
                    ("a".into(), "1 + 2 + 3".into()),
                    ("b".into(), "1 + 3".into()),
                ].into_iter()
                    .collect(),
            ),
            (
                "1 + 2 + 3".into(),
                vec![
                    ("a".into(), "1 + 2 + 3".into()),
                    ("b".into(), "1 + 3".into()),
                ].into_iter()
                    .collect(),
            ),
            (
                "1 + 3".into(),
                vec![("a".into(), "1 + 2".into()), ("b".into(), "1".into())]
                    .into_iter()
                    .collect(),
            ),
        ].into_iter()
            .collect()
    );
}

/// Deterministic finite automata.
#[derive(Debug, Serialize)]
pub struct Dfa {
    /// The final (accepting) states of the automata.
    final_states: HashSet<String>,

    /// Start state.
    start: String,

    /// The alphabet of symbols the automata accepts.
    alphabet: HashSet<String>,

    /// The nodes within the automata. Each node has mappings from alphabet symbols to transition
    /// states.
    nodes: HashMap<String, HashMap<String, String>>,
}
<commit_msg>removing Option<> from make_deterministic()<commit_after>use std::collections::{HashMap, HashSet, VecDeque};
use std::iter;

use serde::de::{Deserialize, Deserializer};

use itertools::Itertools;

#[cfg(test)]
use serde_json;

/// Marker for unsanitized input.
#[derive(Debug)]
pub struct Unsanitary;

impl<'de> Deserialize<'de> for Unsanitary {
    fn deserialize<D>(_deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        Ok(Unsanitary)
    }
}

/// Marker for sanitized input.
#[derive(Debug)]
pub struct Sanitary;

/// Used to represent errors
#[derive(Debug)]
pub enum NfaError {
    UnknownState(String),
    UnknownSymbol(String),
}

/// Nondeterministic finite automata.
#[derive(Debug, Deserialize, Serialize)]
pub struct Nfa<T> {
    /// Marker to ensure that the state machine has been validated before it is used for any
    /// computations.
    #[serde(skip_serializing)]
    _sanitized: T,

    /// Start state.
    start: String,

    /// The final (accepting) states of the automata.
    final_states: HashSet<String>,

    /// The alphabet of symbols the automata accepts.
    alphabet: HashSet<String>,

    /// The nodes within the automata. Each node has mappings from alphabet symbols to sets of
    /// other states.
    nodes: HashMap<String, HashMap<String, HashSet<String>>>,
}

impl Nfa<Unsanitary> {
    /// Ensures that the NFA is valid, and that relevant invariants within the structure hold.
    pub fn check(self) -> Result<Nfa<Sanitary>, NfaError> {
        let Nfa {
            start,
            final_states,
            alphabet,
            nodes,
            ..
        } = self;

        // ensure that the start state is a valid state
        if !nodes.contains_key(&start) {
            return Err(NfaError::UnknownState(start));
        }

        // ensure that all final states are listed as actual states
        if let Some(unknown_state) = final_states
            .iter()
            .find(|&state| !nodes.contains_key(state))
        {
            return Err(NfaError::UnknownState(unknown_state.to_owned()));
        }

        // ensure that all state transitions are on valid symbols
        if let Some((unknown_symbol, _)) = nodes
            .iter()
            .filter_map(|(_, maps)| {
                maps.iter().find(|&(symbol, _)| !alphabet.contains(symbol))
            })
            .next()
        {
            return Err(NfaError::UnknownSymbol(unknown_symbol.to_owned()));
        }

        // ensure that all state transitions are to valid states
        if let Some(unknown_state) = nodes
            .iter()
            .filter_map(|(_, maps)| {
                maps.iter()
                    .filter_map(|(_, states)| {
                        states.iter().find(|&state| !nodes.contains_key(state))
                    })
                    .next()
            })
            .next()
        {
            return Err(NfaError::UnknownState(unknown_state.to_owned()));
        }

        Ok(Nfa {
            _sanitized: Sanitary,
            start,
            final_states,
            alphabet,
            nodes,
        })
    }
}

#[test]
fn valid_nfa() {
    let input = r#"{
        "start": "1",
        "alphabet": ["a", "b"],
        "nodes": {
            "1": {
                "a": ["1", "2"],
                "b": ["1"]
            },
            "2": {
                "a": ["3"],
                "b": ["3"]
            },
            "3": {
                "a": ["1"],
                "b": ["2"]
            }
        },
        "final_states": ["3"]
    }"#;
    let unsanitary: Nfa<_> = serde_json::from_str(input).unwrap();
    unsanitary.check().unwrap();
}

#[test]
fn invalid_nfa_final_state() {
    let input = r#"{
        "start": "1",
        "alphabet": ["a", "b"],
        "nodes": {
            "1": {
                "a": ["1", "2"],
                "b": ["1"]
            },
            "2": {
                "a": ["3"],
                "b": ["3"]
            },
            "3": {
                "a": ["1"],
                "b": ["2"]
            }
        },
        "final_states": ["3", "4"]
    }"#;
    let unsanitary: Nfa<_> = serde_json::from_str(input).unwrap();
    match unsanitary.check().unwrap_err() {
        NfaError::UnknownState(err) => assert_eq!(err, "4"),
        err @ _ => panic!(err),
    }
}

#[test]
fn invalid_nfa_state_transition() {
    let input = r#"{
        "start": "1",
        "alphabet": ["a", "b"],
        "nodes": {
            "1": {
                "a": ["1", "2"],
                "b": ["1"]
            },
            "2": {
                "a": ["3"],
                "b": ["3"]
            },
            "3": {
                "a": ["1"],
                "b": ["2", "4"]
            }
        },
        "final_states": ["3"]
    }"#;
    let unsanitary: Nfa<_> = serde_json::from_str(input).unwrap();
    match unsanitary.check().unwrap_err() {
        NfaError::UnknownState(err) => assert_eq!(err, "4"),
        err @ _ => panic!(err),
    }
}

#[test]
fn invalid_nfa_alphabet() {
    let input = r#"{
        "start": "1",
        "alphabet": ["a", "b"],
        "nodes": {
            "1": {
                "a": ["1", "2"],
                "b": ["1"],
                "c": ["1"]
            },
            "2": {
                "a": ["3"],
                "b": ["3"]
            },
            "3": {
                "a": ["1"],
                "b": ["2"]
            }
        },
        "final_states": ["3"]
    }"#;
    let unsanitary: Nfa<_> = serde_json::from_str(input).unwrap();
    match unsanitary.check().unwrap_err() {
        NfaError::UnknownSymbol(err) => assert_eq!(err, "c"),
        err @ _ => panic!(err),
    }
}

#[test]
fn invalid_nfa_start_state() {
    let input = r#"{
        "start": "4",
        "alphabet": ["a", "b"],
        "nodes": {
            "1": {
                "a": ["1", "2"],
                "b": ["1"]
            },
            "2": {
                "a": ["3"],
                "b": ["3"]
            },
            "3": {
                "a": ["1"],
                "b": ["2"]
            }
        },
        "final_states": ["3"]
    }"#;
    let unsanitary: Nfa<_> = serde_json::from_str(input).unwrap();
    match unsanitary.check().unwrap_err() {
        NfaError::UnknownState(err) => assert_eq!(err, "4"),
        err @ _ => panic!(err),
    }
}

fn hash_states<'a, I>(states: I) -> String
where
    I: IntoIterator<Item = &'a String>,
{
    states
        .into_iter()
        .map(|x| x.as_str())
        .intersperse(" + ")
        .collect()
}

impl Nfa<Sanitary> {
    pub fn make_deterministic(self) -> Dfa {
        let Nfa {
            alphabet,
            start: nfa_start,
            nodes: nfa_nodes,
            final_states: nfa_final_states,
            ..
        } = self;
        let mut work = VecDeque::new();
        let mut final_states = HashSet::new();
        let mut nodes = HashMap::new();

        let start = hash_states(iter::once(&nfa_start));
        work.push_back(vec![nfa_start]);

        while let Some(node) = work.pop_front() {
            let dfa_state = hash_states(&node);
            assert!(!nodes.contains_key(&dfa_state));
            let transition_table = alphabet
                .iter()
                .map(|letter| {
                    let transition = node.iter()
                        .filter_map(|state| nfa_nodes.get(state))
                        .filter_map(|transitions| transitions.get(letter.as_str()))
                        .flatten()
                        .map(|x| x.to_owned())
                        .unique()
                        .sorted();
                    let transition_state = hash_states(&transition);
                    if transition != node && !nodes.contains_key(&transition_state) &&
                        !work.contains(&transition)
                    {
                        work.push_back(transition)
                    }
                    (letter.to_owned(), transition_state)
                })
                .collect();

            if node.iter().any(|state| nfa_final_states.contains(state)) {
                final_states.insert(dfa_state.to_owned());
            }

            nodes.insert(dfa_state, transition_table);
        }

        Dfa {
            final_states,
            start,
            alphabet,
            nodes,
        }
    }
}

#[test]
fn basic_deterministic_conversion() {
    let input = r#"{
        "start": "1",
        "alphabet": ["a", "b"],
        "nodes": {
            "1": {
                "a": ["1", "2"],
                "b": ["1"]
            },
            "2": {
                "a": ["3"],
                "b": ["3"]
            },
            "3": {}
        },
        "final_states": ["3"]
        }"#;
    let unsanitary: Nfa<_> = serde_json::from_str(input).unwrap();
    let nfa = unsanitary.check().unwrap();
    let Dfa {
        final_states,
        start,
        alphabet,
        nodes,
    } = nfa.make_deterministic();
    assert_eq!(
        final_states,
        vec!["1 + 2 + 3".into(), "1 + 3".into()]
            .into_iter()
            .collect()
    );
    assert_eq!(start, "1".to_owned());
    assert_eq!(alphabet, vec!["a".into(), "b".into()].into_iter().collect());
    assert_eq!(
        nodes,
        vec![
            (
                "1".into(),
                vec![("a".into(), "1 + 2".into()), ("b".into(), "1".into())]
                    .into_iter()
                    .collect(),
            ),
            (
                "1 + 2".into(),
                vec![
                    ("a".into(), "1 + 2 + 3".into()),
                    ("b".into(), "1 + 3".into()),
                ].into_iter()
                    .collect(),
            ),
            (
                "1 + 2 + 3".into(),
                vec![
                    ("a".into(), "1 + 2 + 3".into()),
                    ("b".into(), "1 + 3".into()),
                ].into_iter()
                    .collect(),
            ),
            (
                "1 + 3".into(),
                vec![("a".into(), "1 + 2".into()), ("b".into(), "1".into())]
                    .into_iter()
                    .collect(),
            ),
        ].into_iter()
            .collect()
    );
}

/// Deterministic finite automata.
#[derive(Debug, Serialize)]
pub struct Dfa {
    /// The final (accepting) states of the automata.
    final_states: HashSet<String>,

    /// Start state.
    start: String,

    /// The alphabet of symbols the automata accepts.
    alphabet: HashSet<String>,

    /// The nodes within the automata. Each node has mappings from alphabet symbols to transition
    /// states.
    nodes: HashMap<String, HashMap<String, String>>,
}
<|endoftext|>


<commit_before>use std::{
    path::{Path, PathBuf},
    process::Command,
};

use ra_syntax::SmolStr;
use ra_arena::{Arena, RawId, impl_arena_id};

use crate::Result;

#[derive(Debug, Clone)]
pub struct Sysroot {
    crates: Arena<SysrootCrate, SysrootCrateData>,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct SysrootCrate(RawId);
impl_arena_id!(SysrootCrate);

#[derive(Debug, Clone)]
struct SysrootCrateData {
    name: SmolStr,
    root: PathBuf,
    deps: Vec<SysrootCrate>,
}

impl Sysroot {
    pub(crate) fn std(&self) -> Option<SysrootCrate> {
        self.by_name("std")
    }

    pub(crate) fn crates<'a>(&'a self) -> impl Iterator<Item = SysrootCrate> + 'a {
        self.crates.iter().map(|(id, _data)| id)
    }

    pub(super) fn discover(cargo_toml: &Path) -> Result<Sysroot> {
        let rustc_output = Command::new("rustc")
            .current_dir(cargo_toml.parent().unwrap())
            .args(&["--print", "sysroot"])
            .output()?;
        if !rustc_output.status.success() {
            failure::bail!("failed to locate sysroot")
        }
        let stdout = String::from_utf8(rustc_output.stdout)?;
        let sysroot_path = Path::new(stdout.trim());
        let src = sysroot_path.join("lib/rustlib/src/rust/src");
        if !src.exists() {
            failure::bail!(
                "can't load standard library from sysroot\n\
                 {:?}\n\
                 try running `rustup component add rust-src`",
                src,
            );
        }

        let mut sysroot = Sysroot {
            crates: Arena::default(),
        };
        for name in SYSROOT_CRATES.trim().lines() {
            let root = src.join(format!("lib{}", name)).join("lib.rs");
            if root.exists() {
                sysroot.crates.alloc(SysrootCrateData {
                    name: name.into(),
                    root,
                    deps: Vec::new(),
                });
            }
        }
        if let Some(std) = sysroot.std() {
            for dep in STD_DEPS.trim().lines() {
                if let Some(dep) = sysroot.by_name(dep) {
                    sysroot.crates[std].deps.push(dep)
                }
            }
        }
        Ok(sysroot)
    }

    fn by_name(&self, name: &str) -> Option<SysrootCrate> {
        self.crates
            .iter()
            .find(|(_id, data)| data.name == name)
            .map(|(id, _data)| id)
    }
}

impl SysrootCrate {
    pub(crate) fn name(self, sysroot: &Sysroot) -> &SmolStr {
        &sysroot.crates[self].name
    }
    pub(crate) fn root(self, sysroot: &Sysroot) -> &Path {
        sysroot.crates[self].root.as_path()
    }
    pub(crate) fn root_dir(self, sysroot: &Sysroot) -> &Path {
        self.root(sysroot).parent().unwrap()
    }
    pub(crate) fn deps<'a>(self, sysroot: &'a Sysroot) -> impl Iterator<Item = SysrootCrate> + 'a {
        sysroot.crates[self].deps.iter().map(|&it| it)
    }
}

const SYSROOT_CRATES: &str = "
std
core
alloc
collections
libc
panic_unwind
proc_macro
rustc_unicode
std_unicode
test
alloc_jemalloc
alloc_system
compiler_builtins
getopts
panic_unwind
panic_abort
rand
term
unwind
build_helper
rustc_asan
rustc_lsan
rustc_msan
rustc_tsan
syntax";

const STD_DEPS: &str = "
alloc_jemalloc
alloc_system
core
panic_abort
rand
compiler_builtins
unwind
rustc_asan
rustc_lsan
rustc_msan
rustc_tsan
build_helper";
<commit_msg>Add alloc to std deps<commit_after>use std::{
    path::{Path, PathBuf},
    process::Command,
};

use ra_syntax::SmolStr;
use ra_arena::{Arena, RawId, impl_arena_id};

use crate::Result;

#[derive(Debug, Clone)]
pub struct Sysroot {
    crates: Arena<SysrootCrate, SysrootCrateData>,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct SysrootCrate(RawId);
impl_arena_id!(SysrootCrate);

#[derive(Debug, Clone)]
struct SysrootCrateData {
    name: SmolStr,
    root: PathBuf,
    deps: Vec<SysrootCrate>,
}

impl Sysroot {
    pub(crate) fn std(&self) -> Option<SysrootCrate> {
        self.by_name("std")
    }

    pub(crate) fn crates<'a>(&'a self) -> impl Iterator<Item = SysrootCrate> + 'a {
        self.crates.iter().map(|(id, _data)| id)
    }

    pub(super) fn discover(cargo_toml: &Path) -> Result<Sysroot> {
        let rustc_output = Command::new("rustc")
            .current_dir(cargo_toml.parent().unwrap())
            .args(&["--print", "sysroot"])
            .output()?;
        if !rustc_output.status.success() {
            failure::bail!("failed to locate sysroot")
        }
        let stdout = String::from_utf8(rustc_output.stdout)?;
        let sysroot_path = Path::new(stdout.trim());
        let src = sysroot_path.join("lib/rustlib/src/rust/src");
        if !src.exists() {
            failure::bail!(
                "can't load standard library from sysroot\n\
                 {:?}\n\
                 try running `rustup component add rust-src`",
                src,
            );
        }

        let mut sysroot = Sysroot {
            crates: Arena::default(),
        };
        for name in SYSROOT_CRATES.trim().lines() {
            let root = src.join(format!("lib{}", name)).join("lib.rs");
            if root.exists() {
                sysroot.crates.alloc(SysrootCrateData {
                    name: name.into(),
                    root,
                    deps: Vec::new(),
                });
            }
        }
        if let Some(std) = sysroot.std() {
            for dep in STD_DEPS.trim().lines() {
                if let Some(dep) = sysroot.by_name(dep) {
                    sysroot.crates[std].deps.push(dep)
                }
            }
        }
        Ok(sysroot)
    }

    fn by_name(&self, name: &str) -> Option<SysrootCrate> {
        self.crates
            .iter()
            .find(|(_id, data)| data.name == name)
            .map(|(id, _data)| id)
    }
}

impl SysrootCrate {
    pub(crate) fn name(self, sysroot: &Sysroot) -> &SmolStr {
        &sysroot.crates[self].name
    }
    pub(crate) fn root(self, sysroot: &Sysroot) -> &Path {
        sysroot.crates[self].root.as_path()
    }
    pub(crate) fn root_dir(self, sysroot: &Sysroot) -> &Path {
        self.root(sysroot).parent().unwrap()
    }
    pub(crate) fn deps<'a>(self, sysroot: &'a Sysroot) -> impl Iterator<Item = SysrootCrate> + 'a {
        sysroot.crates[self].deps.iter().map(|&it| it)
    }
}

const SYSROOT_CRATES: &str = "
std
core
alloc
collections
libc
panic_unwind
proc_macro
rustc_unicode
std_unicode
test
alloc_jemalloc
alloc_system
compiler_builtins
getopts
panic_unwind
panic_abort
rand
term
unwind
build_helper
rustc_asan
rustc_lsan
rustc_msan
rustc_tsan
syntax";

const STD_DEPS: &str = "
alloc
alloc_jemalloc
alloc_system
core
panic_abort
rand
compiler_builtins
unwind
rustc_asan
rustc_lsan
rustc_msan
rustc_tsan
build_helper";
<|endoftext|>