<commit_before>#[macro_use] extern crate glium;
#[macro_use] extern crate nalgebra as na;
extern crate obj;

use std::path::Path;
use std::f32::consts::{FRAC_PI_2};

use obj::SimplePolygon;

use na::{Pnt3, Vec3, Mat3, Iso3, PerspMat3};
use na::{Eye};

const PI_4: f32 = std::f32::consts::FRAC_PI_4;

#[derive(Copy, Clone)]
struct Vert {
    position: [f32;3],
    normal: [f32;3],
    color: [f32;3],
}

implement_vertex!(Vert, position, normal, color);

fn setup_buffer(window: &glium::backend::glutin_backend::GlutinFacade) -> glium::vertex::VertexBuffer<Vert> {
    let p = Path::new("/home/hcs/code/rust/triangle/meshes/icosohedron.obj");
    let o = obj::load::<SimplePolygon>(p).unwrap();

    let green = [0f32, 1.0, 0.0];

    let size = o.position().len();
    let mut data = Vec::with_capacity(size);

    for ob in o.object_iter() {
        for g in ob.group_iter() {
            for tri in g.indices() {
                data.push(
                    Vert {
                        position: o.position()[tri[0].0],
                        normal: o.normal()[(tri[0].2).unwrap()],
                        color: green.clone(),
                    }
                );
                data.push(
                    Vert {
                        position: o.position()[tri[1].0],
                        normal: o.normal()[(tri[1].2).unwrap()],
                        color: green.clone(),
                    }
                );
                data.push(
                    Vert {
                        position: o.position()[tri[2].0],
                        normal: o.normal()[(tri[2].2).unwrap()],
                        color: green.clone(),
                    }
                );
            }
        }
    }

    glium::vertex::VertexBuffer::new(window, &data).unwrap()
}

fn setup_plane(window: &glium::backend::glutin_backend::GlutinFacade) -> glium::vertex::VertexBuffer<Vert> {
    let tr = [1f32, 1.0, 0.0];
    let tl = [-1f32, 1.0, 0.0];
    let bl = [-1f32, -1.0, 0.0];
    let br = [1f32, -1.0, 0.0];

    let n = [0f32, 0.0, 1.0];

    let white = [1f32, 1.0, 1.0];

    let data = &[
        Vert {
            position: tr,
            normal: n,
            color: white,
        },
        Vert {
            position: tl,
            normal: n,
            color: white,
        },
        Vert {
            position: bl,
            normal: n,
            color: white,
        },

        Vert {
            position: tr,
            normal: n,
            color: white,
        },
        Vert {
            position: bl,
            normal: n,
            color: white,
        },
        Vert {
            position: br,
            normal: n,
            color: white,
        },
    ];
    glium::vertex::VertexBuffer::new(window, data).unwrap()
}

fn setup_shaders(window: &glium::backend::glutin_backend::GlutinFacade) -> glium::program::Program {
    let v_shader = "
        #version 150

        uniform mat4 perspective;
        uniform mat4 mv;
        uniform mat4 m_invt;
        uniform vec3 light_dir;

        in vec3 position;
        in vec3 normal;
        in vec3 color;

        out vec3 f_color;

        void main() {
            gl_Position = perspective*mv*vec4(position, 1.0);

            vec3 mv_normal = normalize(vec3(m_invt * vec4(normal, 0.0)));
            float diffuse = 0.6*max( dot(mv_normal, light_dir), 0.1);

            f_color = diffuse*color;
        }
    ";
    let f_shader = "
        #version 150

        in vec3 f_color;

        void main() {
            gl_FragColor = vec4(f_color, 1.0);
        }
    ";
    glium::program::Program::from_source(window, v_shader, f_shader, None).unwrap()
}

fn main() {
    use glium::{DisplayBuild,Surface};

    const WIDTH: u32 = 1280;
    const HEIGHT: u32 = 720;
    const ASPECT: f32 = 1.78;

	let params = glium::DrawParameters {
		backface_culling: glium::draw_parameters::BackfaceCullingMode::CullClockwise,
		depth: glium::Depth {
			test: glium::draw_parameters::DepthTest::IfLess,
			write: true,
			.. Default::default()
		},
		.. Default::default()
	};

    let window = glium::glutin::WindowBuilder::new()
        .with_dimensions(WIDTH, HEIGHT)
        .with_depth_buffer(24)
        .with_title(format!("Hello world"))
        .build_glium()
        .unwrap();

    let triangle = setup_buffer(&window);
    let plane = setup_plane(&window);
    let program = setup_shaders(&window);
    let indices = glium::index::NoIndices(glium::index::PrimitiveType::TrianglesList);

    let persp = PerspMat3::new(ASPECT, PI_4, 1.0, 100.0).to_mat();

    let mut position = Pnt3::new(0.0f32, 1.0f32, 5.0f32);
    let dir = Vec3::new(0.0f32, 0.0f32, -1.0f32);
    let up = Vec3::new(0.0f32, 1.0f32, 0.0f32);

    let mut t = 0f32;

    let mut exit = false;
    loop {
        for event in window.poll_events() {
            use glium::glutin::Event;
			use glium::glutin::VirtualKeyCode as KC;
            match event {
                Event::Closed => (exit = true),
				Event::KeyboardInput(state, _, key) => {
                    if state == glium::glutin::ElementState::Pressed {
                        match key.unwrap() {
                            KC::Up => {position = position + Vec3::new(0f32, 0.2f32, 0f32);},
                            KC::Down => {position = position + Vec3::new(0f32, -0.2f32, 0f32);},
							KC::Left => position = position + Vec3::new(-0.2f32, 0f32, 0f32),
							KC::Right => position = position + Vec3::new(0.2f32, 0f32, 0f32),
							KC::C => position = position + Vec3::new(0f32, 0f32, -0.2f32),
							KC::T => position = position + Vec3::new(0f32, 0f32, 0.2f32),
                            KC::Space => {println!("hello");}
                            KC::Escape => {exit = true;},
                            _ => ()
                        }
                    }
				}
                _ => ()
            }
        }

        t += 0.05;

        let mut frame = window.draw();
        frame.clear_color_and_depth((0.0, 0.0, 0.0, 1.0), 1.0);

        // camera
        let view = na::inv(&Iso3::look_at_z(&position, &(position + dir), &up)).unwrap();
        let view = na::to_homogeneous(&view);
        let light_dir = [0.5, 0.866025, 0f32];

        // draw tetrahedrons
        for i in 0..10 {
            let model = Iso3::new(Vec3::new(0f32, 0.0, -5f32*i as f32), Vec3::new(0f32, 1.0, 0.0) * t);
            let model = na::to_homogeneous(&model);
            let modelview = view*model;

            let uniforms = uniform! {
                perspective: persp.as_ref().clone(),
                mv: modelview.as_ref().clone(),
                m_invt: na::transpose(&na::inv(&model).unwrap()).as_ref().clone(),
                light_dir: light_dir,
            };

            frame.draw(&triangle, &indices, &program, &uniforms,
                                &params).unwrap();
        }

        // draw plane below tetrahedrons
        let scale = 50f32;
        let plane_scale = na::to_homogeneous(&(Mat3::<f32>::new_identity(3)*scale));
        let plane_rot = na::to_homogeneous(&(Iso3::new(Vec3::new(0f32, -1.0, 0.0), Vec3::new(1f32, 0.0, 0.0)*-FRAC_PI_2)));
        let plane_model = plane_rot*plane_scale;
        let modelview = view*plane_model;

        let uniforms = uniform! {
            perspective: persp.as_ref().clone(),
            mv: modelview.as_ref().clone(),
            m_invt: na::transpose(&na::inv(&plane_model).unwrap()).as_ref().clone(),
            light_dir: light_dir,
        };
        frame.draw(&plane, &indices, &program, &uniforms,
                            &params).unwrap();

        frame.finish().unwrap();
        window.swap_buffers().unwrap();

        if exit {
            break;
        }
    }
}
<commit_msg>Slightly improve key controls<commit_after>#[macro_use] extern crate glium;
#[macro_use] extern crate nalgebra as na;
extern crate obj;

use std::path::Path;
use std::f32::consts::{FRAC_PI_2};

use obj::SimplePolygon;

use na::{Pnt3, Vec3, Mat3, Iso3, PerspMat3};
use na::{Eye};

const PI_4: f32 = std::f32::consts::FRAC_PI_4;

#[derive(Copy, Clone)]
struct Vert {
    position: [f32;3],
    normal: [f32;3],
    color: [f32;3],
}

implement_vertex!(Vert, position, normal, color);

fn setup_buffer(window: &glium::backend::glutin_backend::GlutinFacade) -> glium::vertex::VertexBuffer<Vert> {
    let p = Path::new("/home/hcs/code/rust/triangle/meshes/icosohedron.obj");
    let o = obj::load::<SimplePolygon>(p).unwrap();

    let green = [0f32, 1.0, 0.0];

    let size = o.position().len();
    let mut data = Vec::with_capacity(size);

    for ob in o.object_iter() {
        for g in ob.group_iter() {
            for tri in g.indices() {
                data.push(
                    Vert {
                        position: o.position()[tri[0].0],
                        normal: o.normal()[(tri[0].2).unwrap()],
                        color: green.clone(),
                    }
                );
                data.push(
                    Vert {
                        position: o.position()[tri[1].0],
                        normal: o.normal()[(tri[1].2).unwrap()],
                        color: green.clone(),
                    }
                );
                data.push(
                    Vert {
                        position: o.position()[tri[2].0],
                        normal: o.normal()[(tri[2].2).unwrap()],
                        color: green.clone(),
                    }
                );
            }
        }
    }

    glium::vertex::VertexBuffer::new(window, &data).unwrap()
}

fn setup_plane(window: &glium::backend::glutin_backend::GlutinFacade) -> glium::vertex::VertexBuffer<Vert> {
    let tr = [1f32, 1.0, 0.0];
    let tl = [-1f32, 1.0, 0.0];
    let bl = [-1f32, -1.0, 0.0];
    let br = [1f32, -1.0, 0.0];

    let n = [0f32, 0.0, 1.0];

    let white = [1f32, 1.0, 1.0];

    let data = &[
        Vert {
            position: tr,
            normal: n,
            color: white,
        },
        Vert {
            position: tl,
            normal: n,
            color: white,
        },
        Vert {
            position: bl,
            normal: n,
            color: white,
        },

        Vert {
            position: tr,
            normal: n,
            color: white,
        },
        Vert {
            position: bl,
            normal: n,
            color: white,
        },
        Vert {
            position: br,
            normal: n,
            color: white,
        },
    ];
    glium::vertex::VertexBuffer::new(window, data).unwrap()
}

fn setup_shaders(window: &glium::backend::glutin_backend::GlutinFacade) -> glium::program::Program {
    let v_shader = "
        #version 150

        uniform mat4 perspective;
        uniform mat4 mv;
        uniform mat4 m_invt;
        uniform vec3 light_dir;

        in vec3 position;
        in vec3 normal;
        in vec3 color;

        out vec3 f_color;

        void main() {
            gl_Position = perspective*mv*vec4(position, 1.0);

            vec3 mv_normal = normalize(vec3(m_invt * vec4(normal, 0.0)));
            float diffuse = 0.6*max( dot(mv_normal, light_dir), 0.1);

            f_color = diffuse*color;
        }
    ";
    let f_shader = "
        #version 150

        in vec3 f_color;

        void main() {
            gl_FragColor = vec4(f_color, 1.0);
        }
    ";
    glium::program::Program::from_source(window, v_shader, f_shader, None).unwrap()
}

fn main() {
    use glium::{DisplayBuild,Surface};

    const WIDTH: u32 = 1280;
    const HEIGHT: u32 = 720;
    const ASPECT: f32 = 1.78;

	let params = glium::DrawParameters {
		backface_culling: glium::draw_parameters::BackfaceCullingMode::CullClockwise,
		depth: glium::Depth {
			test: glium::draw_parameters::DepthTest::IfLess,
			write: true,
			.. Default::default()
		},
		.. Default::default()
	};

    let window = glium::glutin::WindowBuilder::new()
        .with_dimensions(WIDTH, HEIGHT)
        .with_depth_buffer(24)
        .with_title(format!("Hello world"))
        .build_glium()
        .unwrap();

    let triangle = setup_buffer(&window);
    let plane = setup_plane(&window);
    let program = setup_shaders(&window);
    let indices = glium::index::NoIndices(glium::index::PrimitiveType::TrianglesList);

    let persp = PerspMat3::new(ASPECT, PI_4, 1.0, 100.0).to_mat();

    let mut position = Pnt3::new(0.0f32, 1.0f32, 5.0f32);
    let dir = Vec3::new(0.0f32, 0.0f32, -1.0f32);
    let up = Vec3::new(0.0f32, 1.0f32, 0.0f32);

    let mut t = 0f32;

    let mut exit = false;
    loop {
        for event in window.poll_events() {
            use glium::glutin::Event;
			use glium::glutin::VirtualKeyCode as KC;
            match event {
                Event::Closed => (exit = true),
				Event::KeyboardInput(state, _, key) => {
                    if state == glium::glutin::ElementState::Pressed {
                        match key.unwrap() {
                            KC::PageUp => {position = position + Vec3::new(0f32, 0.2f32, 0f32);},
                            KC::PageDown => {position = position + Vec3::new(0f32, -0.2f32, 0f32);},
							KC::Left => position = position + Vec3::new(-0.2f32, 0f32, 0f32),
							KC::Right => position = position + Vec3::new(0.2f32, 0f32, 0f32),
							KC::Up => position = position + Vec3::new(0f32, 0f32, -0.2f32),
							KC::Down => position = position + Vec3::new(0f32, 0f32, 0.2f32),
                            KC::Space => {println!("hello");}
                            KC::Escape => {exit = true;},
                            _ => ()
                        }
                    }
				}
                _ => ()
            }
        }

        t += 0.05;

        let mut frame = window.draw();
        frame.clear_color_and_depth((0.0, 0.0, 0.0, 1.0), 1.0);

        // camera
        let view = na::inv(&Iso3::look_at_z(&position, &(position + dir), &up)).unwrap();
        let view = na::to_homogeneous(&view);
        let light_dir = [0.5, 0.866025, 0f32];

        // draw tetrahedrons
        for i in 0..10 {
            let model = Iso3::new(Vec3::new(0f32, 0.0, -5f32*i as f32), Vec3::new(0f32, 1.0, 0.0) * t);
            let model = na::to_homogeneous(&model);
            let modelview = view*model;

            let uniforms = uniform! {
                perspective: persp.as_ref().clone(),
                mv: modelview.as_ref().clone(),
                m_invt: na::transpose(&na::inv(&model).unwrap()).as_ref().clone(),
                light_dir: light_dir,
            };

            frame.draw(&triangle, &indices, &program, &uniforms,
                                &params).unwrap();
        }

        // draw plane below tetrahedrons
        let scale = 50f32;
        let plane_scale = na::to_homogeneous(&(Mat3::<f32>::new_identity(3)*scale));
        let plane_rot = na::to_homogeneous(&(Iso3::new(Vec3::new(0f32, -1.0, 0.0), Vec3::new(1f32, 0.0, 0.0)*-FRAC_PI_2)));
        let plane_model = plane_rot*plane_scale;
        let modelview = view*plane_model;

        let uniforms = uniform! {
            perspective: persp.as_ref().clone(),
            mv: modelview.as_ref().clone(),
            m_invt: na::transpose(&na::inv(&plane_model).unwrap()).as_ref().clone(),
            light_dir: light_dir,
        };
        frame.draw(&plane, &indices, &program, &uniforms,
                            &params).unwrap();

        frame.finish().unwrap();
        window.swap_buffers().unwrap();

        if exit {
            break;
        }
    }
}
<|endoftext|>


<commit_before>extern crate crypto;
extern crate flate2;
extern crate num_cpus;

mod padding;

use flate2::Compression;
use flate2::write::ZlibEncoder;

use std::env;
use std::fs;
use std::io::Write;
use std::ops;
use std::process;
use std::str;
use std::sync::mpsc;
use std::thread;
use std::u64;
use std::u8;
use crypto::digest::Digest;
use crypto::sha1;

const SHA1_BYTE_LENGTH: usize = 20;

struct HashPrefix {
    data: Vec<u8>,
    half_byte: Option<u8>,
}

impl Clone for HashPrefix {
    fn clone(&self) -> Self {
        HashPrefix {
            data: self.data.to_owned(),
            half_byte: self.half_byte.to_owned()
        }
    }
}

struct SearchParams<'a> {
    current_message: &'a str,
    desired_prefix: &'a HashPrefix,
    counter_range: ops::Range<u64>,
    extension_word_length: usize
}

struct HashMatch {
    data: Vec<u8>,
    hash: [u8; SHA1_BYTE_LENGTH],
}

struct ProcessedCommitMessage {
    full_message: Vec<u8>,
    whitespace_index: usize,
}

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

    match args.len() {
        1 => run_lucky_commit(&parse_prefix("0000000").unwrap()),
        2 => match parse_prefix(&args[1]) {
            Some(prefix) => run_lucky_commit(&prefix),
            None => print_usage_and_exit()
        },
        _ => print_usage_and_exit()
    }
}

fn print_usage_and_exit() {
    fail_with_message("Usage: lucky-commit [commit-hash-prefix]");
}

fn fail_with_message(message: &str) {
    eprintln!("{}", message);
    process::exit(1);
}

fn parse_prefix(prefix: &str) -> Option<HashPrefix> {
    let mut data = Vec::new();
    for index in 0..(prefix.len() / 2) {
        match u8::from_str_radix(&prefix[2 * index..2 * index + 2], 16) {
            Ok(value) => data.push(value),
            Err(_) => return None
        }
    }

    let parsed_prefix = HashPrefix {
        data,
        half_byte: if prefix.len() % 2 == 1 {
            match u8::from_str_radix(&prefix[prefix.len() - 1..], 16) {
                Ok(value) => Some(value << 4),
                Err(_) => return None
            }
        } else {
            None
        }
    };

    Some(parsed_prefix)
}

fn run_lucky_commit(desired_prefix: &HashPrefix) {
    let current_message_bytes = run_command("git", &["cat-file", "commit", "HEAD"]);
    let current_message = &String::from_utf8(current_message_bytes)
        .expect("Git commit contains invalid utf8");

    match find_match(current_message, desired_prefix) {
        Some(hash_match) => {
            create_git_object_file(&hash_match);
            git_reset_to_hash(&hash_match.hash);
        },
        None => fail_with_message("Failed to find a match")
    }
}

fn run_command(command: &str, args: &[&str]) -> Vec<u8> {
    let output = process::Command::new(command)
        .args(args)
        .stderr(process::Stdio::inherit())
        .output()
        .expect("Failed to run command");

    if !output.status.success() {
        process::exit(1);
    }

    output.stdout
}

fn find_match(current_message: &str, desired_prefix: &HashPrefix) -> Option<HashMatch> {
    let num_threads = num_cpus::get();
    let (shared_sender, receiver) = mpsc::channel();
    let u64_ranges = split_range(0, u64::MAX, num_threads);

    for thread_index in 0..num_threads {
        let thread_sender = mpsc::Sender::clone(&shared_sender);
        let thread_message_copy = current_message.to_owned();
        let thread_prefix_copy = desired_prefix.clone();
        let counter_range_copy = u64_ranges[thread_index].clone();
        thread::spawn(move || {
            let thread_params = SearchParams {
                current_message: &thread_message_copy,
                desired_prefix: &thread_prefix_copy,
                counter_range: counter_range_copy,
                extension_word_length: 8
            };
            match thread_sender.send(iterate_for_match(&thread_params)) {
                /*
                 * If an error occurs when sending, then the receiver has already received
                 * a match from another thread, so ignore the error.
                 */
                Ok(_) => (),
                Err(_) => ()
            }
        });
    }

    for _ in 0..num_threads {
        let result = receiver.recv().unwrap();
        if result.is_some() {
            return result;
        }
    }

    None
}

fn split_range(min: u64, max: u64, num_segments: usize) -> Vec<ops::Range<u64>> {
    let segment_size = (max - min) / (num_segments as u64);

    let mut segments = Vec::new();
    let mut last_range_end = 0;
    for _ in 0..num_segments {
        segments.push(last_range_end..last_range_end + segment_size);
        last_range_end += segment_size;
    }

    segments
}

fn iterate_for_match(params: &SearchParams) -> Option<HashMatch> {
    let desired_prefix = params.desired_prefix;
    let extension_length = params.extension_word_length * 8;
    let processed_message = process_commit_message(
        params.current_message,
        extension_length
    );

    let mut hash_data = processed_message.full_message;
    let whitespace_index = processed_message.whitespace_index;

    let mut sha1_hash = sha1::Sha1::new();
    let mut hash_result: [u8; SHA1_BYTE_LENGTH] = [0; SHA1_BYTE_LENGTH];

    for counter in params.counter_range.clone() {
        let mut whitespace_word_index = 0;
        while whitespace_word_index < extension_length {
            let start_index = whitespace_index + whitespace_word_index;
            let padding_index = (counter >> whitespace_word_index) as u8 as usize;
            &mut hash_data[start_index..start_index + 8].copy_from_slice(&padding::PADDING_LIST[padding_index]);
            whitespace_word_index += 8;
        }

        sha1_hash.input(&hash_data);
        sha1_hash.result(&mut hash_result);

        if matches_desired_prefix(&hash_result, desired_prefix) {
            return Some(HashMatch {
                data: hash_data,
                hash: hash_result,
            })
        }

        sha1_hash.reset();
    }

    None
}

fn process_commit_message(original_message: &str, extension_length: usize) -> ProcessedCommitMessage {
    let mut message_object: Vec<u8> = format!("commit {}\x00", original_message.len() + extension_length)
        .into_bytes();

    let commit_split_index = get_commit_message_split_index(original_message);
    let whitespace_index = commit_split_index + message_object.len();

    for character in original_message[..commit_split_index].as_bytes() {
        message_object.push(*character);
    }

    for _ in 0..extension_length {
        message_object.push(padding::SPACE);
    }

    for character in original_message[commit_split_index..].as_bytes() {
        message_object.push(*character);
    }

    ProcessedCommitMessage {
        full_message: message_object,
        whitespace_index,
    }
}

fn get_commit_message_split_index(message: &str) -> usize {
    /*
     * If the commit has a GPG signature (detected by the presence of "gpgsig " at the start
     * of the fifth line), then add the padding whitespace immediately after the text "gpgsig ".
     * Otherwise, add the padding whitespace right before the end of the commit message.
     *
     * If a signature is present, modifying the commit message would make the signature invalid.
     */
    let mut current_line_index = 0;
    const SIGNATURE_MARKER: &str = "gpgsig ";
    for (index, character) in message.chars().enumerate() {
        if current_line_index == 4 {
            if message[index..].starts_with(SIGNATURE_MARKER) {
                return index + SIGNATURE_MARKER.len();
            } else {
                return message.len() - 1;
            }
        }
        if character == '\n' {
            current_line_index += 1;
        }
    }

    message.len() - 1
}

fn matches_desired_prefix(hash: &[u8; SHA1_BYTE_LENGTH], prefix: &HashPrefix) -> bool {
    prefix.data == &hash[..prefix.data.len()] && match prefix.half_byte {
        Some(half_byte) => (hash[prefix.data.len()] & 0xf0) == half_byte,
        None => true,
    }
}

fn create_git_object_file(search_result: &HashMatch) {
    let compressed_object = zlib_compress(&search_result.data);
    let dir_path = format!(".git/objects/{:02x}", search_result.hash[0]);
    let file_path = format!(
        "{}/{}",
        dir_path,
        to_hex_string(&search_result.hash[1..])
    );

    fs::DirBuilder::new()
        .recursive(true)
        .create(dir_path)
        .expect("Failed to create git object directory");

    fs::File::create(file_path)
        .expect("Failed to open git object file")
        .write_all(&compressed_object)
        .expect("Failed to write git object file");
}

fn zlib_compress(data: &[u8]) -> Vec<u8> {
    let mut encoder = ZlibEncoder::new(Vec::new(), Compression::default());
    encoder.write(data).expect("zlib compression failed");
    encoder.finish().expect("zlib compression failed")
}

fn git_reset_to_hash(hash: &[u8; SHA1_BYTE_LENGTH]) {
    run_command("git", &["reset", &to_hex_string(hash)]);
}

fn to_hex_string(hash: &[u8]) -> String {
    hash.iter().map(|byte| format!("{:02x}", *byte)).collect::<String>()
}
<commit_msg>Refactor thread spawning logic<commit_after>extern crate crypto;
extern crate flate2;
extern crate num_cpus;

mod padding;

use flate2::Compression;
use flate2::write::ZlibEncoder;

use std::env;
use std::fs;
use std::io::Write;
use std::ops;
use std::process;
use std::str;
use std::sync::mpsc;
use std::thread;
use std::u64;
use std::u8;
use crypto::digest::Digest;
use crypto::sha1;

const SHA1_BYTE_LENGTH: usize = 20;

struct HashPrefix {
    data: Vec<u8>,
    half_byte: Option<u8>,
}

impl Clone for HashPrefix {
    fn clone(&self) -> Self {
        HashPrefix {
            data: self.data.to_owned(),
            half_byte: self.half_byte.to_owned()
        }
    }
}

struct SearchParams {
    current_message: String,
    desired_prefix: HashPrefix,
    counter_range: ops::Range<u64>,
    extension_word_length: usize
}

struct HashMatch {
    data: Vec<u8>,
    hash: [u8; SHA1_BYTE_LENGTH],
}

struct ProcessedCommitMessage {
    full_message: Vec<u8>,
    whitespace_index: usize,
}

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

    match args.len() {
        1 => run_lucky_commit(&parse_prefix("0000000").unwrap()),
        2 => match parse_prefix(&args[1]) {
            Some(prefix) => run_lucky_commit(&prefix),
            None => print_usage_and_exit()
        },
        _ => print_usage_and_exit()
    }
}

fn print_usage_and_exit() {
    fail_with_message("Usage: lucky-commit [commit-hash-prefix]");
}

fn fail_with_message(message: &str) {
    eprintln!("{}", message);
    process::exit(1);
}

fn parse_prefix(prefix: &str) -> Option<HashPrefix> {
    let mut data = Vec::new();
    for index in 0..(prefix.len() / 2) {
        match u8::from_str_radix(&prefix[2 * index..2 * index + 2], 16) {
            Ok(value) => data.push(value),
            Err(_) => return None
        }
    }

    let parsed_prefix = HashPrefix {
        data,
        half_byte: if prefix.len() % 2 == 1 {
            match u8::from_str_radix(&prefix[prefix.len() - 1..], 16) {
                Ok(value) => Some(value << 4),
                Err(_) => return None
            }
        } else {
            None
        }
    };

    Some(parsed_prefix)
}

fn run_lucky_commit(desired_prefix: &HashPrefix) {
    let current_message_bytes = run_command("git", &["cat-file", "commit", "HEAD"]);
    let current_message = &String::from_utf8(current_message_bytes)
        .expect("Git commit contains invalid utf8");

    match find_match(current_message, desired_prefix) {
        Some(hash_match) => {
            create_git_object_file(&hash_match);
            git_reset_to_hash(&hash_match.hash);
        },
        None => fail_with_message("Failed to find a match")
    }
}

fn run_command(command: &str, args: &[&str]) -> Vec<u8> {
    let output = process::Command::new(command)
        .args(args)
        .stderr(process::Stdio::inherit())
        .output()
        .expect("Failed to run command");

    if !output.status.success() {
        process::exit(1);
    }

    output.stdout
}

fn find_match(current_message: &str, desired_prefix: &HashPrefix) -> Option<HashMatch> {
    let num_threads = num_cpus::get();
    let (shared_sender, receiver) = mpsc::channel();
    let u64_ranges = split_range(0, u64::MAX, num_threads);

    for thread_index in 0..num_threads {
        spawn_hash_searcher(
            mpsc::Sender::clone(&shared_sender),
            SearchParams {
                current_message: current_message.to_owned(),
                desired_prefix: desired_prefix.clone(),
                counter_range: u64_ranges[thread_index].clone(),
                extension_word_length: 8
            }
        );
    }

    for _ in 0..num_threads {
        let result = receiver.recv().unwrap();
        if result.is_some() {
            return result;
        }
    }

    None
}

fn spawn_hash_searcher(result_sender: mpsc::Sender<Option<HashMatch>>, params: SearchParams) {
    thread::spawn(move || {
        match result_sender.send(iterate_for_match(&params)) {
            /*
             * If an error occurs when sending, then the receiver has already received
             * a match from another thread, so ignore the error.
             */
            Ok(_) => (),
            Err(_) => ()
        }
    });
}

fn split_range(min: u64, max: u64, num_segments: usize) -> Vec<ops::Range<u64>> {
    let segment_size = (max - min) / (num_segments as u64);

    let mut segments = Vec::new();
    let mut last_range_end = 0;
    for _ in 0..num_segments {
        segments.push(last_range_end..last_range_end + segment_size);
        last_range_end += segment_size;
    }

    segments
}

fn iterate_for_match(params: &SearchParams) -> Option<HashMatch> {
    let desired_prefix = &params.desired_prefix;
    let extension_length = params.extension_word_length * 8;
    let processed_message = process_commit_message(
        &params.current_message,
        extension_length
    );

    let mut hash_data = processed_message.full_message;
    let whitespace_index = processed_message.whitespace_index;

    let mut sha1_hash = sha1::Sha1::new();
    let mut hash_result: [u8; SHA1_BYTE_LENGTH] = [0; SHA1_BYTE_LENGTH];

    for counter in params.counter_range.clone() {
        let mut whitespace_word_index = 0;
        while whitespace_word_index < extension_length {
            let start_index = whitespace_index + whitespace_word_index;
            let padding_index = (counter >> whitespace_word_index) as u8 as usize;
            &mut hash_data[start_index..start_index + 8].copy_from_slice(&padding::PADDING_LIST[padding_index]);
            whitespace_word_index += 8;
        }

        sha1_hash.input(&hash_data);
        sha1_hash.result(&mut hash_result);

        if matches_desired_prefix(&hash_result, desired_prefix) {
            return Some(HashMatch {
                data: hash_data,
                hash: hash_result,
            })
        }

        sha1_hash.reset();
    }

    None
}

fn process_commit_message(original_message: &str, extension_length: usize) -> ProcessedCommitMessage {
    let mut message_object: Vec<u8> = format!("commit {}\x00", original_message.len() + extension_length)
        .into_bytes();

    let commit_split_index = get_commit_message_split_index(original_message);
    let whitespace_index = commit_split_index + message_object.len();

    for character in original_message[..commit_split_index].as_bytes() {
        message_object.push(*character);
    }

    for _ in 0..extension_length {
        message_object.push(padding::SPACE);
    }

    for character in original_message[commit_split_index..].as_bytes() {
        message_object.push(*character);
    }

    ProcessedCommitMessage {
        full_message: message_object,
        whitespace_index,
    }
}

fn get_commit_message_split_index(message: &str) -> usize {
    /*
     * If the commit has a GPG signature (detected by the presence of "gpgsig " at the start
     * of the fifth line), then add the padding whitespace immediately after the text "gpgsig ".
     * Otherwise, add the padding whitespace right before the end of the commit message.
     *
     * If a signature is present, modifying the commit message would make the signature invalid.
     */
    let mut current_line_index = 0;
    const SIGNATURE_MARKER: &str = "gpgsig ";
    for (index, character) in message.chars().enumerate() {
        if current_line_index == 4 {
            if message[index..].starts_with(SIGNATURE_MARKER) {
                return index + SIGNATURE_MARKER.len();
            } else {
                return message.len() - 1;
            }
        }
        if character == '\n' {
            current_line_index += 1;
        }
    }

    message.len() - 1
}

fn matches_desired_prefix(hash: &[u8; SHA1_BYTE_LENGTH], prefix: &HashPrefix) -> bool {
    prefix.data == &hash[..prefix.data.len()] && match prefix.half_byte {
        Some(half_byte) => (hash[prefix.data.len()] & 0xf0) == half_byte,
        None => true,
    }
}

fn create_git_object_file(search_result: &HashMatch) {
    let compressed_object = zlib_compress(&search_result.data);
    let dir_path = format!(".git/objects/{:02x}", search_result.hash[0]);
    let file_path = format!(
        "{}/{}",
        dir_path,
        to_hex_string(&search_result.hash[1..])
    );

    fs::DirBuilder::new()
        .recursive(true)
        .create(dir_path)
        .expect("Failed to create git object directory");

    fs::File::create(file_path)
        .expect("Failed to open git object file")
        .write_all(&compressed_object)
        .expect("Failed to write git object file");
}

fn zlib_compress(data: &[u8]) -> Vec<u8> {
    let mut encoder = ZlibEncoder::new(Vec::new(), Compression::default());
    encoder.write(data).expect("zlib compression failed");
    encoder.finish().expect("zlib compression failed")
}

fn git_reset_to_hash(hash: &[u8; SHA1_BYTE_LENGTH]) {
    run_command("git", &["reset", &to_hex_string(hash)]);
}

fn to_hex_string(hash: &[u8]) -> String {
    hash.iter().map(|byte| format!("{:02x}", *byte)).collect::<String>()
}
<|endoftext|>


<commit_before>#![cfg_attr(feature="clippy", feature(plugin))]
#![cfg_attr(feature="clippy", plugin(clippy))]

#[macro_use]
extern crate clap;
extern crate itertools;
extern crate walkdir;
extern crate toml;

use std::env;
use std::fs::File;
use std::io::Read;
use std::process::{exit, Command, Output};
use clap::{App, SubCommand, AppSettings};
use walkdir::{DirEntry, WalkDirIterator};

fn announce(banner: &str) {
    let mut line = String::new();
    for _ in 0..banner.len() {
        line.push('-');
    }
    println!("{}\n{}\n{}", line, banner, line);
}

fn print_ident(buf: Vec<u8>) {
    for line in String::from_utf8_lossy(&buf[..]).lines() {
        println!("        {}", line);
    }
}

fn report_output(output: Output) -> std::process::ExitStatus {
    if output.status.success() {
        print_ident(output.stdout);
    } else {
        print_ident(output.stderr);
    };

    // I am still not sure what is more idiomatic - the 'if' above or the 'match' below
    //
    // match output.status.success() {
    //     true => print_ident(output.stdout),
    //     false => print_ident(output.stderr),
    // }
    println!("");

    output.status
}

const CARGO: &'static str = "cargo";
const MIN_DEPTH: usize = 1;
const MAX_DEPTH: usize = 1;

fn main() {

    let matches = App::new(CARGO)
        .bin_name(CARGO)
        .version(crate_version!())
        .about("Run cargo command on multiple crates")
        .setting(AppSettings::SubcommandRequired)
        .setting(AppSettings::ArgRequiredElseHelp)
        .subcommand(SubCommand::with_name("multi")
            .version(crate_version!())
            .setting(AppSettings::ArgRequiredElseHelp)
            .setting(AppSettings::TrailingVarArg)
            .arg_from_usage("<cmd>... 'cargo command to run'"))
        .get_matches();

    let mut cargo_cmd = Command::new(CARGO);
    let mut banner = String::from("Executing ") + CARGO;

    if let Some(arg_cmd) = matches.subcommand_matches("multi")
        .and_then(|m| m.values_of("cmd")) {
        for arg in arg_cmd {
            cargo_cmd.arg(arg);
            banner = banner + " " + arg;
        }
    }

    announce(&banner);
    let is_crate = |e: &DirEntry| e.path().join("Cargo.toml").exists();
    let display_path = |p: &String| println!("{}:", p);
    let execute = |p: String| cargo_cmd.current_dir(p).output().ok();

    // First check if there is a Cargo.toml file with a workspace section in.
    let mut workspace_members = match File::open("Cargo.toml") {
        Ok(mut file) => {
            let mut toml = String::new();
            match file.read_to_string(&mut toml) {
                Ok(_) => {
                    let value: toml::Value = toml.parse().expect("Failed to parse Cargo.toml");

                    match value.lookup("workspace.members") {
                        Some(members) => {
                            Some(members.as_slice()
                                .expect("Failed to read workspace members")
                                .into_iter()
                                .map(|m| m.as_str().unwrap().to_string())
                                .collect::<Vec<_>>())
                        }
                        None => None,
                    }
                }
                Err(_) => None,
            }
        }
        Err(_) => None,
    };

    // If there was no workspace members present, add each crate directory
    // present.
    if workspace_members.is_none() {
        workspace_members = match env::current_dir() {
            Ok(cwd) => {
                Some(walkdir::WalkDir::new(cwd)
                    .min_depth(MIN_DEPTH)
                    .max_depth(MAX_DEPTH)
                    .into_iter()
                    .filter_entry(is_crate)
                    .filter_map(|e| e.ok())
                    .map(|m| m.file_name().to_string_lossy().to_string())
                    .collect::<Vec<_>>())
            }
            Err(_) => None,
        }
    }

    let failed_commands = match workspace_members {
        Some(members) => {
            members.into_iter()
                .inspect(display_path)
                .filter_map(execute)
                .map(report_output)
                .filter(|x| !x.success())
                .collect::<Vec<_>>()
        }
        None => Vec::new(),
    };

    // If there are any failed commands, return the error code of the
    // first of them.
    if failed_commands.len() > 0 {
        exit(failed_commands[0].code().unwrap());
    }
}
<commit_msg>Switch to using PathBuf instead of a string.<commit_after>#![cfg_attr(feature="clippy", feature(plugin))]
#![cfg_attr(feature="clippy", plugin(clippy))]

#[macro_use]
extern crate clap;
extern crate itertools;
extern crate walkdir;
extern crate toml;

use std::env;
use std::fs::File;
use std::io::Read;
use std::path::PathBuf;
use std::process::{exit, Command, Output};
use clap::{App, SubCommand, AppSettings};
use walkdir::{DirEntry, WalkDirIterator};

fn announce(banner: &str) {
    let mut line = String::new();
    for _ in 0..banner.len() {
        line.push('-');
    }
    println!("{}\n{}\n{}", line, banner, line);
}

fn print_ident(buf: Vec<u8>) {
    for line in String::from_utf8_lossy(&buf[..]).lines() {
        println!("        {}", line);
    }
}

fn report_output(output: Output) -> std::process::ExitStatus {
    if output.status.success() {
        print_ident(output.stdout);
    } else {
        print_ident(output.stderr);
    };

    // I am still not sure what is more idiomatic - the 'if' above or the 'match' below
    //
    // match output.status.success() {
    //     true => print_ident(output.stdout),
    //     false => print_ident(output.stderr),
    // }
    println!("");

    output.status
}

const CARGO: &'static str = "cargo";
const MIN_DEPTH: usize = 1;
const MAX_DEPTH: usize = 1;

fn main() {

    let matches = App::new(CARGO)
                      .bin_name(CARGO)
                      .version(crate_version!())
                      .about("Run cargo command on multiple crates")
                      .setting(AppSettings::SubcommandRequired)
                      .setting(AppSettings::ArgRequiredElseHelp)
                      .subcommand(SubCommand::with_name("multi")
                                     .version(crate_version!())
                                     .setting(AppSettings::ArgRequiredElseHelp)
                                     .setting(AppSettings::TrailingVarArg)
                                     .arg_from_usage("<cmd>... 'cargo command to run'"))
                      .get_matches();

    let mut cargo_cmd = Command::new(CARGO);
    let mut banner = String::from("Executing ") + CARGO;

    if let Some(arg_cmd) = matches.subcommand_matches("multi")
                                  .and_then(|m| m.values_of("cmd")) {
        for arg in arg_cmd {
            cargo_cmd.arg(arg);
            banner = banner + " " + arg;
        }
    }

    announce(&banner);
    let is_crate = |e: &DirEntry| e.path().join("Cargo.toml").exists();
    let display_path = |p: &PathBuf| println!("{}:", p.to_string_lossy());
    let execute = |p: PathBuf| cargo_cmd.current_dir(p).output().ok();

    // First check if there is a Cargo.toml file with a workspace section in.
    let mut workspace_members = match File::open("Cargo.toml") {
        Ok(mut file) => {
            let mut toml = String::new();
            match file.read_to_string(&mut toml) {
                Ok(_) => {
                    let value: toml::Value = toml.parse().expect("Failed to parse Cargo.toml");

                    match value.lookup("workspace.members") {
                        Some(members) => {
                            Some(members.as_slice()
                                        .expect("Failed to read workspace members")
                                        .into_iter()
                                        .map(|m| PathBuf::from(m.as_str().unwrap()))
                                        .collect::<Vec<_>>())
                        }
                        None => None,
                    }
                }
                Err(_) => None,
            }
        }
        Err(_) => None,
    };

    // If there was no workspace members present, add each crate directory
    // present.
    if workspace_members.is_none() {
        workspace_members = match env::current_dir() {
            Ok(cwd) => {
                Some(walkdir::WalkDir::new(cwd)
                                .min_depth(MIN_DEPTH)
                                .max_depth(MAX_DEPTH)
                                .into_iter()
                                .filter_entry(is_crate)
                                .filter_map(|e| e.ok())
                                .map(|m| m.path().to_path_buf())
                                .collect::<Vec<_>>())
            }
            Err(_) => None,
        }
    }

    let failed_commands = match workspace_members {
        Some(members) => {
            members.into_iter()
                   .inspect(display_path)
                   .filter_map(execute)
                   .map(report_output)
                   .filter(|x| !x.success())
                   .collect::<Vec<_>>()
        }
        None => Vec::new(),
    };

    // If there are any failed commands, return the error code of the
    // first of them.
    if failed_commands.len() > 0 {
        exit(failed_commands[0].code().unwrap());
    }
}
<|endoftext|>


<commit_before>extern crate lumbergh;

use std::{thread, time};

use lumbergh::run_supervisor;
use lumbergh::errors::*;

fn run_child(pid: i32) -> Result<()> {
    thread::sleep(time::Duration::from_secs(1));
    println!("Hello, Lumbergh. This is {}", pid);

    Ok(())
}

fn run() -> Result<()> {
    run_supervisor(move || run_child(0))
}

fn main() {
    if let Err(ref e) = run() {
        println!("error: {}", e);

        for e in e.iter().skip(1) {
            println!("caused by: {}", e);
        }

        // The backtrace is not always generated. Try to run this example
        // with `RUST_BACKTRACE=1`.
        if let Some(backtrace) = e.backtrace() {
            println!("backtrace: {:?}", backtrace);
        }

        ::std::process::exit(1);
    }
}

<commit_msg>Removing comment from main function<commit_after>extern crate lumbergh;

use std::{thread, time};

use lumbergh::run_supervisor;
use lumbergh::errors::*;

fn run_child(pid: i32) -> Result<()> {
    thread::sleep(time::Duration::from_secs(1));
    println!("Hello, Lumbergh. This is {}", pid);

    Ok(())
}

fn run() -> Result<()> {
    run_supervisor(move || run_child(0))
}

fn main() {
    if let Err(ref e) = run() {
        println!("error: {}", e);

        for e in e.iter().skip(1) {
            println!("caused by: {}", e);
        }

        if let Some(backtrace) = e.backtrace() {
            println!("backtrace: {:?}", backtrace);
        }

        ::std::process::exit(1);
    }
}

<|endoftext|>


<commit_before>#![feature(plugin)]
#![plugin(oak)]

extern crate oak_runtime;
use oak_runtime::*;

grammar! awesome {
  #![show_api]

  program = declaration_list /
            statement_list /
            expression_list

  declaration_list
    = declaration (terminator declaration)*

  expression_list
    = expression (terminator expression)*

  statement_list
    = statement (terminator statement)*

  declaration
    = class_declaration > to_class_declaration
    / method_declaration > to_method_declaration

  class_declaration
    = kw_class spacing identifier class_body

  class_body
    = lbracket spacing method_declaration* rbracket

  method_declaration
    = kw_def spacing identifier lparen params_list? rparen block

  block
    = lbracket spacing (statement / expression)+ spacing rbracket

  statement
    = if_statement > to_if_statement
    / while_statement > to_while_statement

  if_statement
    = kw_if expression block

  while_statement
    = kw_while expression block

  assignment_expression
    = identifier bind_op expression

  expression
    = spacing term (term_op term)* > fold_left

  call
    = identifier lparen arg_list? rparen

  arg_list
    = (expression ("," spacing expression)*)

  params_list
    = (identifier ("," spacing identifier)*)

  term
    = exponent (factor_op exponent)* > fold_left

  exponent
    = (factor exponent_op)* factor > fold_right

  factor
    = call > call_expr
    / number > number_expr
    / assignment_expression > assign_expr
    / identifier > variable_expr
    / lparen expression rparen

  term_op
    = add_op > add_bin_op
    / sub_op > sub_bin_op

  factor_op
    = mul_op > mul_bin_op
    / div_op > div_bin_op

  exponent_op = exp_op > exp_bin_op

  identifier = !digit !keyword ident_char+ spacing > to_string
  ident_char = ["a-zA-Z0-9_"]

  digit = ["0-9"]
  number = digit+ spacing > to_number
  spacing = [" \r\t"]* -> ()

  kw_def = "def"
  kw_class = "class"
  kw_true = "true"
  kw_false = "false"
  kw_nil = "nil"
  kw_if = "if"
  kw_while = "while"
  kw_tail = !ident_char spacing

  keyword
    = kw_def
    / kw_class
    / kw_true
    / kw_false
    / kw_nil
    / kw_tail

  bind_op = "=" spacing
  add_op = "+" spacing
  sub_op = "-" spacing
  mul_op = "*" spacing
  div_op = "/" spacing
  exp_op = "^" spacing
  lparen = "(" spacing
  rparen = ")" spacing
  lbracket = "{"
  rbracket = "}"

  terminator = ";" / "\n"

  use std::str::FromStr;
  use self::Expression::*;
  use self::BinOp::*;

  pub type PExpr = Box<Expression>;

  #[derive(Debug)]
  pub enum Expression {
    Variable(String),
    Number(u32),
    BinaryExpr(BinOp, PExpr, PExpr),
    Call(String, Option<(PExpr, Vec<PExpr>)>),
    AssingExpr(String, PExpr),
    ClassDecl(String),
    MethodDecl(String, Option<(String, Vec<String>)>, Vec<PExpr>),
    IfStatement(PExpr, Vec<PExpr>),
    WhileStatement(PExpr, Vec<PExpr>)
  }

  #[derive(Debug)]
  pub enum BinOp {
    Add, Sub, Mul, Div, Exp
  }

  fn to_number(raw_text: Vec<char>) -> u32 {
    u32::from_str(&*to_string(raw_text)).unwrap()
  }

  fn number_expr(value: u32) -> PExpr {
    Box::new(Number(value))
  }

  fn variable_expr(ident: String) -> PExpr {
    Box::new(Variable(ident))
  }

  fn to_string(raw_text: Vec<char>) -> String {
    raw_text.into_iter().collect()
  }

  fn fold_left(head: PExpr, rest: Vec<(BinOp, PExpr)>) -> PExpr {
    rest.into_iter().fold(head,
      |accu, (op, expr)| Box::new(BinaryExpr(op, accu, expr)))
  }

  fn fold_right(front: Vec<(PExpr, BinOp)>, last: PExpr) -> PExpr {
    front.into_iter().rev().fold(last,
      |accu, (expr, op)| Box::new(BinaryExpr(op, expr, accu)))
  }

  fn add_bin_op() -> BinOp { Add }
  fn sub_bin_op() -> BinOp { Sub }
  fn mul_bin_op() -> BinOp { Mul }
  fn div_bin_op() -> BinOp { Div }
  fn exp_bin_op() -> BinOp { Exp }

  fn call_expr(method: String, args: Option<(PExpr, Vec<PExpr>)>) -> PExpr {
      Box::new(Call(method, args))
  }

  fn assign_expr(variable: String, exp: PExpr) -> PExpr {
      Box::new(AssingExpr(variable, exp))
  }

  fn to_class_declaration(foo: String, bar: Vec<(String, Option<(String, Vec<String>)>, Vec<PExpr>)>) -> PExpr {
      Box::new(ClassDecl(foo))
  }

  fn to_method_declaration(name: String, params: Option<(String, Vec<String>)>, body: Vec<PExpr>) -> PExpr {
      Box::new(MethodDecl(name, params, body))
  }

  fn to_if_statement(condition: PExpr, block: Vec<PExpr>) -> PExpr {
      Box::new(IfStatement(condition, block))
  }

  fn to_while_statement(condition: PExpr,  block: Vec<PExpr>) -> PExpr {
      Box::new(WhileStatement(condition, block))
  }
}

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

fn main() {
  let program1 = "def foo(a, b) { if 1 { 1 + 1 } }";
  println!("{:?}", awesome::parse_program(program1.stream()));
  //analyse_state(awesome::parse_program(program1.stream()));
}
<commit_msg>Some renaming and added basic support for conditional expressions.<commit_after>#![feature(plugin)]
#![plugin(oak)]

extern crate oak_runtime;
use oak_runtime::*;

grammar! awesome {
  #![show_api]

  program = declaration_list /
            statement_list /
            expression_list

  declaration_list
    = declaration (terminator declaration)*

  expression_list
    = expression (terminator expression)*

  statement_list
    = statement (terminator statement)*

  declaration
    = class_declaration > to_class_declaration
    / method_declaration > to_method_declaration

  class_declaration
    = kw_class spacing identifier class_body

  class_body
    = lbracket spacing method_declaration* rbracket

  method_declaration
    = kw_def spacing identifier lparen params_list? rparen block

  block
    = lbracket spacing (statement / expression)+ spacing rbracket

  statement
    = if_statement > to_if_statement
    / while_statement > to_while_statement

  if_statement
    = kw_if expression block

  while_statement
    = kw_while expression block

  assignment_expression
    = identifier bind_op expression

  expression
    = spacing cond_expr (cond_expr_op cond_expr)* > fold_left

  cond_expr
    = spacing add_expr (add_expr_op add_expr)* > fold_left

  add_expr
    = spacing mult_expr (mult_expr_op mult_expr)* > fold_left

  mult_expr
    = primary_expr

  primary_expr
    = call > call_expr
    / number > number_expr
    / assignment_expression > assign_expr
    / identifier > variable_expr
    / lparen expression rparen

    call
      = identifier lparen arg_list? rparen

    arg_list
     = (expression ("," spacing expression)*)

    params_list
      = (identifier ("," spacing identifier)*)

    add_expr_op
        = add_op > add_bin_op
        / sub_op > sub_bin_op

    mult_expr_op
        = mul_op > mul_bin_op
        / div_op > div_bin_op

    cond_expr_op
       = lt_op > lt_bin_op /
       lte_op > lte_bin_op /
       gt_op > gt_bin_op /
       gte_op > gte_bin_op /
       ne_op > ne_bin_op /
       eq_op > eq_bin_op /
       andand_op > and_bin_op /
       oror_op > or_bin_op

  identifier = !digit !keyword ident_char+ spacing > to_string
  ident_char = ["a-zA-Z0-9_"]

  digit = ["0-9"]
  number = digit+ spacing > to_number
  spacing = [" \r\t"]* -> ()

  kw_def = "def"
  kw_class = "class"
  kw_true = "true"
  kw_false = "false"
  kw_nil = "nil"
  kw_if = "if"
  kw_while = "while"
  kw_tail = !ident_char spacing

  keyword
    = kw_def
    / kw_class
    / kw_true
    / kw_false
    / kw_nil
    / kw_tail

  bind_op = "=" spacing
  add_op = "+" spacing
  sub_op = "-" spacing
  mul_op = "*" spacing
  div_op = "/" spacing
  lt_op = "<" spacing
  lte_op = "<=" spacing
  gt_op = ">" spacing
  gte_op = ">=" spacing
  ne_op = "!=" spacing
  eq_op = "==" spacing
  andand_op  = "&&" spacing
  oror_op = "||" spacing
  lparen = "(" spacing
  rparen = ")" spacing
  lbracket = "{"
  rbracket = "}"

  terminator = ";" / "\n"

  use std::str::FromStr;
  use self::Expression::*;
  use self::BinOp::*;

  pub type PExpr = Box<Expression>;

  #[derive(Debug)]
  pub enum Expression {
    Variable(String),
    Number(u32),
    BinaryExpr(BinOp, PExpr, PExpr),
    Call(String, Option<(PExpr, Vec<PExpr>)>),
    AssingExpr(String, PExpr),
    ClassDecl(String),
    MethodDecl(String, Option<(String, Vec<String>)>, Vec<PExpr>),
    IfStatement(PExpr, Vec<PExpr>),
    WhileStatement(PExpr, Vec<PExpr>)
  }

  #[derive(Debug)]
  pub enum BinOp {
    Add, Sub, Mul, Div, Lt, Lte, Gt, Gte, Ne, Eq, And, Or
  }

  fn to_number(raw_text: Vec<char>) -> u32 {
    u32::from_str(&*to_string(raw_text)).unwrap()
  }

  fn number_expr(value: u32) -> PExpr {
    Box::new(Number(value))
  }

  fn variable_expr(ident: String) -> PExpr {
    Box::new(Variable(ident))
  }

  fn to_string(raw_text: Vec<char>) -> String {
    raw_text.into_iter().collect()
  }

  fn fold_left(head: PExpr, rest: Vec<(BinOp, PExpr)>) -> PExpr {
    rest.into_iter().fold(head,
      |accu, (op, expr)| Box::new(BinaryExpr(op, accu, expr)))
  }

  fn fold_right(front: Vec<(PExpr, BinOp)>, last: PExpr) -> PExpr {
    front.into_iter().rev().fold(last,
      |accu, (expr, op)| Box::new(BinaryExpr(op, expr, accu)))
  }

  fn add_bin_op() -> BinOp { Add }
  fn sub_bin_op() -> BinOp { Sub }
  fn mul_bin_op() -> BinOp { Mul }
  fn div_bin_op() -> BinOp { Div }
  fn lt_bin_op() -> BinOp { Lt }
  fn lte_bin_op() -> BinOp { Lte }
  fn gt_bin_op() -> BinOp { Gt }
  fn gte_bin_op() -> BinOp { Gte }
  fn ne_bin_op() -> BinOp { Ne }
  fn eq_bin_op() -> BinOp { Eq }
  fn and_bin_op() -> BinOp { And }
  fn or_bin_op() -> BinOp { Or }

  fn call_expr(method: String, args: Option<(PExpr, Vec<PExpr>)>) -> PExpr {
      Box::new(Call(method, args))
  }

  fn assign_expr(variable: String, exp: PExpr) -> PExpr {
      Box::new(AssingExpr(variable, exp))
  }

  fn to_class_declaration(foo: String, bar: Vec<(String, Option<(String, Vec<String>)>, Vec<PExpr>)>) -> PExpr {
      Box::new(ClassDecl(foo))
  }

  fn to_method_declaration(name: String, params: Option<(String, Vec<String>)>, body: Vec<PExpr>) -> PExpr {
      Box::new(MethodDecl(name, params, body))
  }

  fn to_if_statement(condition: PExpr, block: Vec<PExpr>) -> PExpr {
      Box::new(IfStatement(condition, block))
  }

  fn to_while_statement(condition: PExpr,  block: Vec<PExpr>) -> PExpr {
      Box::new(WhileStatement(condition, block))
  }
}

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

fn main() {
    let program1 = "if a > 3 { a = 1 + (2 + 2) }";
    println!("{:?}", awesome::parse_program(program1.stream()));
    // analyse_state(awesome::parse_program(program1.stream()));
}
<|endoftext|>


<commit_before>#![feature(box_patterns,box_syntax)]
#![feature(scheme)]
#![feature(compile)]
#![feature(collections)]
#![feature(convert)]

extern crate rustc_serialize;
extern crate docopt;
extern crate regex;

extern crate seax_svm as svm;
extern crate seax_scheme as scheme;

#[macro_use]
extern crate log;

use docopt::Docopt;
use regex::Regex;

use std::io;
use std::io::{Write, Read, BufRead,BufReader};
use std::error::Error;
use std::fs::File;
use std::path::{PathBuf, Path};
use std::convert::AsRef;

static USAGE: &'static str = "
Usage:
    seax repl [-vd]
    seax [-vd] <file>

Options:
    -v, --verbose   Enable verbose mode
    -d, --debug     Enable debug mode
";

#[derive(RustcDecodable)]
struct Args {
    cmd_repl: bool,
    arg_file: String,
    flag_verbose: bool,
    flag_debug: bool,
}

mod loggers;

fn main() {
    let args: Args = Docopt::new(USAGE)
                .and_then(|d| d.decode())
                .unwrap_or_else(|e| e.exit());

    let ext_re = Regex::new(r".+?(?P<ext>\.[^.]*$|$)").unwrap();

    if args.flag_verbose {
        let _ = log::set_logger(|max_log_level| {
            max_log_level.set(log::LogLevelFilter::Debug);
            Box::new(loggers::DebugLogger)
        });
    } else {
        let _ = log::set_logger(|max_log_level| {
            max_log_level.set(log::LogLevelFilter::Info);
            Box::new(loggers::DefaultLogger)
        });
    };

    if args.cmd_repl {
        let stdin = BufReader::new(io::stdin());
        let mut stdout = io::stdout();

        print!("scheme> ");
        let _ = stdout.flush();

        for line in stdin.lines() {
            match line.map_err(|error| String::from_str(error.description()) )
                .and_then(  |ref code| scheme::compile(code) )
                .and_then(  |program | svm::eval_program(program, args.flag_debug) ) {
                    Ok(result)  => println!(">> {:?}",result),
                    Err(why)    => error!("{}", why)
                };
            print!("scheme> ");
            let _ = stdout.flush();
        }
    } else {
        match ext_re
            .captures(args.arg_file.as_ref())
            .and_then(|c| c.name("ext")) {
            Some(".scm")   => { // interpret scheme
                debug!("Interpreting Scheme file {}", args.arg_file);
                let path = PathBuf::from(args.arg_file.as_str());
                match File::open(&path)
                    .map_err(|error    | String::from_str(error.description()) )
                    .and_then(|mut file| {
                        let mut s = String::new();
                        file.read_to_string(&mut s).map(|_| s)
                            .map_err(|error| String::from_str(error.description()) ) })
                    .and_then(  |ref code| scheme::compile(code) )
                    .and_then(  |program | svm::eval_program(program, args.flag_debug) ) {
                        Ok(result)  => println!(">> {:?}",result),
                        Err(why)    => error!("{}", why)
                };
            },
            _              => {
                debug!("Executing binary {}", args.arg_file);
                unimplemented!()
            } // bin, figure out
        }
    }
}
<commit_msg>Remove unused import<commit_after>#![feature(box_patterns,box_syntax)]
#![feature(scheme)]
#![feature(compile)]
#![feature(collections)]
#![feature(convert)]

extern crate rustc_serialize;
extern crate docopt;
extern crate regex;

extern crate seax_svm as svm;
extern crate seax_scheme as scheme;

#[macro_use]
extern crate log;

use docopt::Docopt;
use regex::Regex;

use std::io;
use std::io::{Write, Read, BufRead,BufReader};
use std::error::Error;
use std::fs::File;
use std::path::PathBuf;
use std::convert::AsRef;

static USAGE: &'static str = "
Usage:
    seax repl [-vd]
    seax [-vd] <file>

Options:
    -v, --verbose   Enable verbose mode
    -d, --debug     Enable debug mode
";

#[derive(RustcDecodable)]
struct Args {
    cmd_repl: bool,
    arg_file: String,
    flag_verbose: bool,
    flag_debug: bool,
}

mod loggers;

fn main() {
    let args: Args = Docopt::new(USAGE)
                .and_then(|d| d.decode())
                .unwrap_or_else(|e| e.exit());

    let ext_re = Regex::new(r".+?(?P<ext>\.[^.]*$|$)").unwrap();

    if args.flag_verbose {
        let _ = log::set_logger(|max_log_level| {
            max_log_level.set(log::LogLevelFilter::Debug);
            Box::new(loggers::DebugLogger)
        });
    } else {
        let _ = log::set_logger(|max_log_level| {
            max_log_level.set(log::LogLevelFilter::Info);
            Box::new(loggers::DefaultLogger)
        });
    };

    if args.cmd_repl {
        let stdin = BufReader::new(io::stdin());
        let mut stdout = io::stdout();

        print!("scheme> ");
        let _ = stdout.flush();

        for line in stdin.lines() {
            match line.map_err(|error| String::from_str(error.description()) )
                .and_then(  |ref code| scheme::compile(code) )
                .and_then(  |program | svm::eval_program(program, args.flag_debug) ) {
                    Ok(result)  => println!(">> {:?}",result),
                    Err(why)    => error!("{}", why)
                };
            print!("scheme> ");
            let _ = stdout.flush();
        }
    } else {
        match ext_re
            .captures(args.arg_file.as_ref())
            .and_then(|c| c.name("ext")) {
            Some(".scm")   => { // interpret scheme
                debug!("Interpreting Scheme file {}", args.arg_file);
                let path = PathBuf::from(args.arg_file.as_str());
                match File::open(&path)
                    .map_err(|error    | String::from_str(error.description()) )
                    .and_then(|mut file| {
                        let mut s = String::new();
                        file.read_to_string(&mut s).map(|_| s)
                            .map_err(|error| String::from_str(error.description()) ) })
                    .and_then(  |ref code| scheme::compile(code) )
                    .and_then(  |program | svm::eval_program(program, args.flag_debug) ) {
                        Ok(result)  => println!(">> {:?}",result),
                        Err(why)    => error!("{}", why)
                };
            },
            _              => {
                debug!("Executing binary {}", args.arg_file);
                unimplemented!()
            } // bin, figure out
        }
    }
}
<|endoftext|>


<commit_before>fn main() {
    println!("Hello, world!");
}

// -------------
// Blow is Reserved for the matrix multiply

// x, y
struct Coordinate(usize, usize);

// In the stencil and the image, the first coordinate is x, the second is y.
fn compute_pixel(stencil: Vec<Vec<f32>>, coordinate: Coordinate, image_ref: &Vec<Vec<f32>>) {
    let stencil_size = (stencil.len(), stencil[0].len());

    let extracted_matrix = extract_matrix(image_ref, coordinate, stencil_size);

    for i in 0..stencil_size.0 {
        for j in 0..stencil_size.1 {
            for k in 0..stencil_size.1 {

            }
        }
    }
}

// Extracts the pixels values for a given stencil dimensions from an image
fn extract_matrix(image_ref: &Vec<Vec<f32>>, coordinate: Coordinate, dimensions: (usize, usize)){

    let delta_x = (dimensions.0 - 1)/2 as usize;
    let delta_y = (dimensions.1 - 1)/2 as usize;

    let mut extracted_matrix = Vec::with_capacity(dimensions.0);

    for (i, x) in ((coordinate.0 - delta_x)..(coordinate.0 + delta_x)).enumerate() {
        extracted_matrix.push(Vec::with_capacity(dimensions.1));
        for y in (coordinate.1 - delta_y)..(coordinate.1 + delta_y) {
            if x < 0 || y < 0 || x > image_ref.len() || y > image_ref[0].len() {
                extracted_matrix[i].push(0);
            } else {
                extracted_matrix[i].push(image_ref[x][y]);
            }
        }
    }
    extracted_matrix;
}

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

    #[test]
    fn it_works() {
        assert_eq!(4, add_two(2));
    }
}
<commit_msg>Finished with both convolution and matrix extraction, both tested<commit_after>// x, y
pub struct Coordinate(isize, isize);

fn main() {

    let image: Vec<Vec<f32>>= vec!(
        vec!(1.0,3.0,4.0,5.0,6.0,3.0,4.0,5.0,6.0),
        vec!(1.0,3.0,4.0,5.0,6.0,3.0,4.0,5.0,6.0),
        vec!(1.0,3.0,4.0,5.0,6.0,3.0,4.0,5.0,6.0),
        vec!(1.0,3.0,4.0,5.0,6.0,3.0,4.0,5.0,6.0),
        vec!(1.0,3.0,4.0,5.0,6.0,3.0,4.0,5.0,6.0),
        vec!(1.0,3.0,4.0,5.0,6.0,3.0,4.0,5.0,6.0),
        vec!(1.0,3.0,4.0,5.0,6.0,3.0,4.0,5.0,6.0),
        vec!(1.0,3.0,4.0,5.0,6.0,3.0,4.0,5.0,6.0),
        vec!(1.0,3.0,4.0,5.0,6.0,3.0,4.0,5.0,6.0)
    );

    let stencil: Vec<Vec<f32>> = vec!(
        vec!(1.0,3.0,4.0),
        vec!(1.0,3.0,4.0),
        vec!(1.0,3.0,4.0)
    );

    println!("Pixel value computed: {}", compute_pixel(stencil, Coordinate(0,0), &image));
}

// In the stencil and the image, the first coordinate is x, the second is y.
// This is computing the convolution between the stencil and the extracted matrix from the
// image
pub fn compute_pixel(stencil: Vec<Vec<f32>>, coordinate: Coordinate, image_ref: &Vec<Vec<f32>>) -> f32 {
    let stencil_size = (stencil.len(), stencil[0].len());

    let extracted_matrix = extract_matrix(image_ref, coordinate, stencil_size);

    let mut result: f32 = 0.0;

    for i in 0..stencil_size.0 {
        for j in 0..stencil_size.1 {
            result += extracted_matrix[i][j] * stencil[i][j];
        }
    }
    result
}

// Extracts the pixels values for a given stencil dimensions from an image
pub fn extract_matrix(image_ref: &Vec<Vec<f32>>, coordinate: Coordinate, dimensions: (usize, usize))
-> Vec<Vec<f32>>{

    let delta_x = ((dimensions.0 - 1) as f32 / 2.0) as isize;
    let delta_y = ((dimensions.1 - 1) as f32 / 2.0) as isize;

    let mut extracted_matrix: Vec<Vec<f32>> = Vec::with_capacity(dimensions.0);

    for (i, x) in ((coordinate.0 - delta_x)..(coordinate.0 + delta_x + 1)).enumerate() {
        extracted_matrix.push(Vec::with_capacity(dimensions.1));
        for y in (coordinate.1 - delta_y)..(coordinate.1 + delta_y + 1) {
            if x < 0 || y < 0 || x > (image_ref.len() as isize) || (y > image_ref[0].len() as isize) {
                extracted_matrix[i].push(0.0);
            } else {
                extracted_matrix[i].push(image_ref[x as usize][y as usize]);
            }
        }
    }

    extracted_matrix
}

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

    #[test]
    fn matrix_regular_extraction() {
        let image: Vec<Vec<f32>>= vec!(
            vec!(1.0,3.0,4.0,5.0,6.0,3.0,4.0,5.0,6.0),
            vec!(1.0,3.0,4.0,5.0,6.0,3.0,4.0,5.0,6.0),
            vec!(1.0,3.0,4.0,5.0,6.0,3.0,4.0,5.0,6.0),
            vec!(1.0,3.0,4.0,5.0,6.0,3.0,4.0,5.0,6.0),
            vec!(1.0,3.0,4.0,5.0,6.0,3.0,4.0,5.0,6.0),
            vec!(1.0,3.0,4.0,5.0,6.0,3.0,4.0,5.0,6.0),
            vec!(1.0,3.0,4.0,5.0,6.0,3.0,4.0,5.0,6.0),
            vec!(1.0,3.0,4.0,5.0,6.0,3.0,4.0,5.0,6.0),
            vec!(1.0,3.0,4.0,5.0,6.0,3.0,4.0,5.0,6.0)
        );

        let stencil: Vec<Vec<f32>> = vec!(
            vec!(1.0,3.0,4.0),
            vec!(1.0,3.0,4.0),
            vec!(1.0,3.0,4.0)
        );
        let stencil_size = (stencil.len(), stencil[0].len());
        let coord = Coordinate(1, 1);
        let extracted = extract_matrix(&image, coord, stencil_size);

        assert_eq!(extracted.len(), 3);
        assert_eq!(extracted[0].len(), 3);
        assert_eq!(extracted[0][0], 1.0);
        assert_eq!(extracted[0][1], 3.0);
        assert_eq!(extracted[1][0], 1.0);
        assert_eq!(extracted[1][1], 3.0);
        assert_eq!(extracted[2][2], 4.0);
    }

    #[test]
    fn compute_pixel_test() {
        let image: Vec<Vec<f32>>= vec!(
            vec!(1.0,3.0,4.0,5.0,6.0,3.0,4.0,5.0,6.0),
            vec!(1.0,3.0,4.0,5.0,6.0,3.0,4.0,5.0,6.0),
            vec!(1.0,3.0,4.0,5.0,6.0,3.0,4.0,5.0,6.0),
            vec!(1.0,3.0,4.0,5.0,6.0,3.0,4.0,5.0,6.0),
            vec!(1.0,3.0,4.0,5.0,6.0,3.0,4.0,5.0,6.0),
            vec!(1.0,3.0,4.0,5.0,6.0,3.0,4.0,5.0,6.0),
            vec!(1.0,3.0,4.0,5.0,6.0,3.0,4.0,5.0,6.0),
            vec!(1.0,3.0,4.0,5.0,6.0,3.0,4.0,5.0,6.0),
            vec!(1.0,3.0,4.0,5.0,6.0,3.0,4.0,5.0,6.0)
        );

        let stencil: Vec<Vec<f32>> = vec!(
            vec!(1.0,3.0,4.0),
            vec!(1.0,3.0,4.0),
            vec!(1.0,3.0,4.0)
        );
        let coord = Coordinate(1, 1);
        let pixel_value = compute_pixel(stencil, coord, &image);

        assert_eq!(pixel_value, 78.0);
    }
}
<|endoftext|>


<commit_before>#![feature(rustc_private, plugin, old_io, path, fs, io, env, old_path)]
#![plugin(docopt_macros)]

extern crate cargo;
extern crate docopt;
extern crate graphviz;
extern crate "rustc-serialize" as rustc_serialize;

use std::borrow::{Cow, IntoCow};
use cargo::core::{Resolve, SourceId, PackageId};
use graphviz as dot;
use std::env;
use std::fs::File;
use std::io::Write;
use std::path::Path as NewPath;

docopt!(Flags, "
Generate a graph of package dependencies in graphviz format

Usage: cargo dot [options]
       cargo dot --help

Options:
    -h, --help         Show this message
    -V, --version      Print version info and exit
    --lock-file=FILE   Specify location of input file, default \"Cargo.lock\"
    --dot-file=FILE    Output to file, default prints to stdout
    --source-labels    Use sources for the label instead of package names
");

fn main() {
    let flags: Flags = Flags::docopt()
                             // cargo passes the exe name first, so we skip it
                             .argv(env::args().skip(1))
                             .version(Some("0.2".to_string()))
                             .decode()
                             .unwrap_or_else(|e| e.exit());
    let lock_file  = unless_empty(flags.flag_lock_file, "Cargo.lock");
    let dot_f_flag = if flags.flag_dot_file.is_empty() { None } else { Some(flags.flag_dot_file) };
    let source_labels = flags.flag_source_labels;

    let lock_file = Path::new(&lock_file);
    let project_dir = lock_file.dir_path();
    let project_dir = std::env::current_dir().unwrap().join(&project_dir);
    let src_id = SourceId::for_path(&project_dir).unwrap();
    let resolved = cargo::ops::load_lockfile(&lock_file, &src_id).unwrap()
        .expect("Lock file not found.");

    let mut graph = Graph::with_root(resolved.root(), source_labels);
    graph.add_dependencies(&resolved);

    let mut buf: Vec<u8> = Vec::new();
    match dot_f_flag {
        None           => graph.render_to(&mut std::old_io::stdio::stdout()),
        Some(dot_file) => {
            graph.render_to(&mut buf);
            File::create(NewPath::new(&dot_file)).map(|mut f| f.write(&buf)).unwrap();
        }
    };

}

fn unless_empty(s: String, default: &str) -> String {
    if s.is_empty() {
        default.to_string()
    } else {
        s
    }
}

pub type Nd = usize;
pub type Ed = (usize, usize);
pub struct Graph<'a> {
    nodes: Vec<&'a PackageId>,
    edges: Vec<Ed>,
    source_labels: bool
}

impl<'a> Graph<'a> {
    pub fn with_root(root: &PackageId, source_labels: bool) -> Graph {
        Graph { nodes: vec![root], edges: vec![], source_labels: source_labels }
    }

    pub fn add_dependencies(&mut self, resolved: &'a Resolve) {
        for crat in resolved.iter() {
            match resolved.deps(crat) {
                Some(crate_deps) => {
                    let idl = self.find_or_add(crat);
                    for dep in crate_deps {
                        let idr = self.find_or_add(dep);
                        self.edges.push((idl, idr));
                    };
                },
                None => { }
            }
        }
    }

    fn find_or_add(&mut self, new: &'a PackageId) -> usize {
        for (i, id) in self.nodes.iter().enumerate() {
            if *id == new {
                return i
            }
        }
        self.nodes.push(new);
        self.nodes.len() - 1
    }

    pub fn render_to<W:Writer>(&'a self, output: &mut W) {
        match dot::render(self, output) {
            Ok(_) => {},
            Err(e) => panic!("error rendering graph: {}", e)
        }
    }
}

impl<'a> dot::Labeller<'a, Nd, Ed> for Graph<'a> {
    fn graph_id(&self) -> dot::Id<'a> {
        dot::Id::new(self.nodes[0].name()).unwrap_or(dot::Id::new("dependencies").unwrap())
    }
    fn node_id(&self, n: &Nd) -> dot::Id {
        // unwrap is safe because N######## is a valid graphviz id
        dot::Id::new(format!("N{}", *n)).unwrap()
    }
    fn node_label(&'a self, i: &Nd) -> dot::LabelText<'a> {
        if !self.source_labels {
            dot::LabelText::LabelStr(self.nodes[*i].name().into_cow())
        } else {
            dot::LabelText::LabelStr(self.nodes[*i].source_id().url().to_string().into_cow())
        }
    }
}

impl<'a> dot::GraphWalk<'a, Nd, Ed> for Graph<'a> {
    fn nodes(&self) -> dot::Nodes<'a,Nd> {
        (0..self.nodes.len()).collect()
    }
    fn edges(&self) -> dot::Edges<Ed> {
        Cow::Borrowed(&self.edges[..])
    }
    fn source(&self, &(s, _): &Ed) -> Nd { s }
    fn target(&self, &(_, t): &Ed) -> Nd { t }
}
<commit_msg>Just use old_io for now<commit_after>#![feature(rustc_private, plugin, old_io, env, old_path)]
#![plugin(docopt_macros)]

extern crate cargo;
extern crate docopt;
extern crate graphviz;
extern crate "rustc-serialize" as rustc_serialize;

use cargo::core::{Resolve, SourceId, PackageId};
use graphviz as dot;
use std::borrow::{Cow, IntoCow};
use std::env;
use std::old_io as io;
use std::old_io::fs::File;
use std::old_path::Path;

docopt!(Flags, "
Generate a graph of package dependencies in graphviz format

Usage: cargo dot [options]
       cargo dot --help

Options:
    -h, --help         Show this message
    -V, --version      Print version info and exit
    --lock-file=FILE   Specify location of input file, default \"Cargo.lock\"
    --dot-file=FILE    Output to file, default prints to stdout
    --source-labels    Use sources for the label instead of package names
");

fn main() {
    let flags: Flags = Flags::docopt()
                             // cargo passes the exe name first, so we skip it
                             .argv(env::args().skip(1))
                             .version(Some("0.2".to_string()))
                             .decode()
                             .unwrap_or_else(|e| e.exit());
    let lock_file  = unless_empty(flags.flag_lock_file, "Cargo.lock");
    let dot_f_flag = if flags.flag_dot_file.is_empty() { None } else { Some(flags.flag_dot_file) };
    let source_labels = flags.flag_source_labels;

    let lock_file = Path::new(&lock_file);
    let project_dir = lock_file.dir_path();
    let project_dir = std::env::current_dir().unwrap().join(&project_dir);
    let src_id = SourceId::for_path(&project_dir).unwrap();
    let resolved = cargo::ops::load_lockfile(&lock_file, &src_id).unwrap()
        .expect("Lock file not found.");

    let mut graph = Graph::with_root(resolved.root(), source_labels);
    graph.add_dependencies(&resolved);

    match dot_f_flag {
        None           => graph.render_to(&mut io::stdio::stdout()),
        Some(dot_file) => graph.render_to(&mut File::create(&Path::new(&dot_file)).unwrap())
    };

}

fn unless_empty(s: String, default: &str) -> String {
    if s.is_empty() {
        default.to_string()
    } else {
        s
    }
}

pub type Nd = usize;
pub type Ed = (usize, usize);
pub struct Graph<'a> {
    nodes: Vec<&'a PackageId>,
    edges: Vec<Ed>,
    source_labels: bool
}

impl<'a> Graph<'a> {
    pub fn with_root(root: &PackageId, source_labels: bool) -> Graph {
        Graph { nodes: vec![root], edges: vec![], source_labels: source_labels }
    }

    pub fn add_dependencies(&mut self, resolved: &'a Resolve) {
        for crat in resolved.iter() {
            match resolved.deps(crat) {
                Some(crate_deps) => {
                    let idl = self.find_or_add(crat);
                    for dep in crate_deps {
                        let idr = self.find_or_add(dep);
                        self.edges.push((idl, idr));
                    };
                },
                None => { }
            }
        }
    }

    fn find_or_add(&mut self, new: &'a PackageId) -> usize {
        for (i, id) in self.nodes.iter().enumerate() {
            if *id == new {
                return i
            }
        }
        self.nodes.push(new);
        self.nodes.len() - 1
    }

    pub fn render_to<W:Writer>(&'a self, output: &mut W) {
        match dot::render(self, output) {
            Ok(_) => {},
            Err(e) => panic!("error rendering graph: {}", e)
        }
    }
}

impl<'a> dot::Labeller<'a, Nd, Ed> for Graph<'a> {
    fn graph_id(&self) -> dot::Id<'a> {
        dot::Id::new(self.nodes[0].name()).unwrap_or(dot::Id::new("dependencies").unwrap())
    }
    fn node_id(&self, n: &Nd) -> dot::Id {
        // unwrap is safe because N######## is a valid graphviz id
        dot::Id::new(format!("N{}", *n)).unwrap()
    }
    fn node_label(&'a self, i: &Nd) -> dot::LabelText<'a> {
        if !self.source_labels {
            dot::LabelText::LabelStr(self.nodes[*i].name().into_cow())
        } else {
            dot::LabelText::LabelStr(self.nodes[*i].source_id().url().to_string().into_cow())
        }
    }
}

impl<'a> dot::GraphWalk<'a, Nd, Ed> for Graph<'a> {
    fn nodes(&self) -> dot::Nodes<'a,Nd> {
        (0..self.nodes.len()).collect()
    }
    fn edges(&self) -> dot::Edges<Ed> {
        Cow::Borrowed(&self.edges[..])
    }
    fn source(&self, &(s, _): &Ed) -> Nd { s }
    fn target(&self, &(_, t): &Ed) -> Nd { t }
}
<|endoftext|>