<commit_before>use std::ffi::OsString;
use std::fs;
use std::fs::Permissions;
use std::io;
use std::io::Stderr;
use std::io::Stdin;
use std::io::Stdout;
use std::io::Write;
use std::os::unix::fs::PermissionsExt;
use std::path::PathBuf;
use structopt::StructOpt;
use super::Error;
use super::Result;

enum Context {
    /// The path is absolute (has root).
    Absolute,
    /// The `interactive` option is present.
    Interactive,
}

#[derive(Debug, StructOpt)]
#[structopt(about = "Recursively change the mode of files or directories.")]
struct Options {
    /// Change the mode of files.
    #[structopt(short = "f", long = "file")]
    file: Option<String>,

    /// Change the mode of directories.
    #[structopt(short = "d", long = "dir")]
    dir: Option<String>,

    /// Do not overwrite any files (verbose).
    #[structopt(short = "D", long = "dry-run")]
    dry_run: bool,

    /// Prompt before overwriting each file.
    #[structopt(short = "i", long = "interactive")]
    interactive: bool,

    /// Suppress all interaction.
    #[structopt(short = "s", long = "suppress")]
    suppress: bool,

    /// Explain what's being done.
    #[structopt(short = "V", long = "verbose")]
    verbose: bool,

    /// Show this message.
    #[structopt(short = "h", long = "help")]
    help: bool,

    /// Show the version.
    #[structopt(short = "v", long = "version")]
    version: bool,

    /// The paths to be modified by this tool.
    #[structopt(name = "PATHS", parse(from_str))]
    paths: Vec<PathBuf>,
}

pub struct Chmodrt {
    options: Options,
    stderr: Stderr,
    stdout: Stdout,
    stdin: Stdin,
}

impl Chmodrt {
    /// Constructs this program from an iterable of arguments.
    pub fn from_iter<I>(iter: I) -> Result<Self>
    where
        Self: Sized,
        I: IntoIterator,
        I::Item: Into<OsString> + Clone,
    {
        return Ok(
            Self {
                options: Options::from_iter_safe(iter)?,
                stderr: io::stderr(),
                stdout: io::stdout(),
                stdin: io::stdin(),
            }
        );
    }

    /// Replaces the standard error stream for this program.
    pub fn stderr(&mut self, stderr: Stderr) -> &mut Self {
        self.stderr = stderr;
        return self;
    }

    /// Replaces the standard output stream for this program.
    pub fn stdout(&mut self, stdout: Stdout) -> &mut Self {
        self.stdout = stdout;
        return self;
    }

    /// Replaces the standard input stream for this program.
    pub fn stdin(&mut self, stdin: Stdin) -> &mut Self {
        self.stdin = stdin;
        return self;
    }

    /// Runs this program and writes all errors.
    pub fn run(&mut self) -> Result<()> {
        match self.run_inner() {
            Ok(val) => {
                return Ok(val);
            },
            Err(err) => {
                writeln!(self.stderr, "Error: {}", err)?;
                return Err(err);
            },
        }
    }

    /// Runs this program.
    fn run_inner(&mut self) -> Result<()> {
        // Write the help or version message
        if self.options.help {
            return self.help();
        }
        if self.options.version {
            return self.version();
        }

        // Check for conflicting options
        self.validate()?;

        // Loop through the paths
        return self.change();
    }

    /// Checks for conflicts in the options.
    fn validate(&self) -> Result<()> {
        return if {
            self.options.interactive && self.options.suppress ||
            self.has_file() && self.has_dir()
        } {
            Err(Error::Conflict)
        } else if {
            !self.has_file() &&
            !self.has_dir()
        } {
            Err(Error::Missing)
        } else {
            Ok(())
        };
    }

    /// Writes the help message to the standard error stream.
    fn help(&mut self) -> Result<()> {
        Options::clap().write_help(&mut self.stderr)?;
        writeln!(self.stderr, "")?;
        return Ok(());
    }

    /// Writes the version message to the standard error stream.
    fn version(&mut self) -> Result<()> {
        Options::clap().write_version(&mut self.stderr)?;
        writeln!(self.stderr, "")?;
        return Ok(());
    }

    /// Authorizes directory and file access by prompting the user and reading
    /// from the standard input stream.
    fn auth(&mut self, path: &PathBuf, context: Context) -> Result<bool> {
        // Determine the appropriate prompt
        let prompt = match context {
            Context::Absolute => "is absolute",
            Context::Interactive => "mode will be changed",
        };

        let mut input = String::new();
        loop {
            // Prompt the user and normalize the input
            write!(self.stderr, r#""{}" {} - continue? [y/n] "#, path.display(), prompt)?;
            self.stdin.read_line(&mut input)?;

            // The response must be `y` or `n`
            match input.trim().to_lowercase().as_str() {
                "n" => {
                    if self.options.verbose {
                        writeln!(self.stderr, "Skipped.")?;
                    }
                    return Ok(false);
                },
                "y" => {
                    return Ok(true);
                },
                _ => {
                    input.clear();
                },
            }
        }
    }

    /// Changes all paths provided by the user. Authorization may be requested
    /// if the `suppress` option is not present.
    fn change(&mut self) -> Result<()> {
        let mode = u32::from_str_radix(
            if self.has_file() {
                self.options.file.as_ref().unwrap()
            } else if self.has_dir() {
                self.options.dir.as_ref().unwrap()
            } else {
                ""
            },
            8,
        )?;
        for path in self.options.paths.to_owned() {
            if !self.options.suppress && path.has_root() {
                // Authorize absolute paths (optional)
                if let Ok(false) = self.auth(&path, Context::Absolute) {
                    continue;
                }
            }

            if path.is_file() {
                // The path is a file
                if self.has_file() {
                    self.change_one(&path, mode)?;
                }
            } else {
                // Try the path as a directory
                self.change_many(&path, mode)?;
            }
        }
        return Ok(());
    }

    /// Changes all entries under the given directory and writes all errors.
    fn change_many(&mut self, path: &PathBuf, mode: u32) -> Result<()> {
        return if let Err(err) = self.change_many_inner(path, mode) {
            self.write_error("Cannot access", path, &err)
        } else {
            Ok(())
        };
    }

    /// Changes all entries under the given directory.
    fn change_many_inner(&mut self, path: &PathBuf, mode: u32) -> Result<()> {
        if self.has_dir() {
            self.change_one(path, mode)?;
        }
        for entry in path.read_dir()? {
            let path = entry?.path();

            // Recurse if the entry is a directory
            if path.is_file() {
                if self.has_file() {
                    self.change_one(&path, mode)?;
                }
            } else {
                self.change_many(&path, mode)?;
            }
        }
        return Ok(());
    }

    /// Changes the mode of the given path and writes all errors.
    fn change_one(&mut self, path: &PathBuf, mode: u32) -> Result<()> {
        return if let Err(err) = self.change_one_inner(path, mode) {
            self.write_error("Cannot change permissions", path, &err)
        } else {
            Ok(())
        };
    }

    /// Changes the mode of the given path. Authorization may be requested and
    /// additional information may be written if the `interactive` and
    /// `verbose` options are present. The path will not be changed during a
    /// `dry-run`.
    fn change_one_inner(&mut self, path: &PathBuf, mode: u32) -> Result<()> {
        if self.options.interactive && !self.options.suppress {
            // Authorize every path (optional)
            if let Ok(false) = self.auth(path, Context::Interactive) {
                return Ok(());
            }
        }

        if !self.options.dry_run {
            // Change the mode of the path
            fs::set_permissions(path, Permissions::from_mode(mode))?;

            if self.options.verbose {
                // Write the results (optional)
                self.write_result("changed", path)?;
            }
        } else {
            // Perform a dry run (optional)
            self.write_result("will be changed", path)?;
        }
        return Ok(());
    }

    /// Determines if the file option is present.
    fn has_file(&self) -> bool {
        return self.options.file.is_some();
    }

    /// Determines if the directory option is present.
    fn has_dir(&self) -> bool {
        return self.options.dir.is_some();
    }

    /// Writes a path related error to the standard error stream.
    fn write_error(&mut self, msg: &str, path: &PathBuf, err: &Error) -> Result<()> {
        writeln!(self.stderr, r#"Error: {} "{}": {}"#, msg, path.display(), err)?;
        return Ok(());
    }

    /// Writes the result of an operation to the standard output stream.
    fn write_result(&mut self, msg: &str, path: &PathBuf) -> Result<()> {
        writeln!(self.stdout, r#""{}": mode {}."#, path.display(), msg)?;
        return Ok(());
    }
}
<commit_msg>Updated comment<commit_after>use std::ffi::OsString;
use std::fs;
use std::fs::Permissions;
use std::io;
use std::io::Stderr;
use std::io::Stdin;
use std::io::Stdout;
use std::io::Write;
use std::os::unix::fs::PermissionsExt;
use std::path::PathBuf;
use structopt::StructOpt;
use super::Error;
use super::Result;

enum Context {
    /// The path is absolute (has root).
    Absolute,
    /// The `interactive` option is present.
    Interactive,
}

#[derive(Debug, StructOpt)]
#[structopt(about = "Recursively change the mode of files or directories.")]
struct Options {
    /// Change the mode of files.
    #[structopt(short = "f", long = "file")]
    file: Option<String>,

    /// Change the mode of directories.
    #[structopt(short = "d", long = "dir")]
    dir: Option<String>,

    /// Do not overwrite any files (verbose).
    #[structopt(short = "D", long = "dry-run")]
    dry_run: bool,

    /// Prompt before overwriting each file.
    #[structopt(short = "i", long = "interactive")]
    interactive: bool,

    /// Suppress all interaction.
    #[structopt(short = "s", long = "suppress")]
    suppress: bool,

    /// Explain what's being done.
    #[structopt(short = "V", long = "verbose")]
    verbose: bool,

    /// Show this message.
    #[structopt(short = "h", long = "help")]
    help: bool,

    /// Show the version.
    #[structopt(short = "v", long = "version")]
    version: bool,

    /// The paths to be modified by this tool.
    #[structopt(name = "PATHS", parse(from_str))]
    paths: Vec<PathBuf>,
}

pub struct Chmodrt {
    options: Options,
    stderr: Stderr,
    stdout: Stdout,
    stdin: Stdin,
}

impl Chmodrt {
    /// Constructs this program from an iterable of arguments.
    pub fn from_iter<I>(iter: I) -> Result<Self>
    where
        Self: Sized,
        I: IntoIterator,
        I::Item: Into<OsString> + Clone,
    {
        return Ok(
            Self {
                options: Options::from_iter_safe(iter)?,
                stderr: io::stderr(),
                stdout: io::stdout(),
                stdin: io::stdin(),
            }
        );
    }

    /// Replaces the standard error stream for this program.
    pub fn stderr(&mut self, stderr: Stderr) -> &mut Self {
        self.stderr = stderr;
        return self;
    }

    /// Replaces the standard output stream for this program.
    pub fn stdout(&mut self, stdout: Stdout) -> &mut Self {
        self.stdout = stdout;
        return self;
    }

    /// Replaces the standard input stream for this program.
    pub fn stdin(&mut self, stdin: Stdin) -> &mut Self {
        self.stdin = stdin;
        return self;
    }

    /// Runs this program and writes all errors.
    pub fn run(&mut self) -> Result<()> {
        match self.run_inner() {
            Ok(val) => {
                return Ok(val);
            },
            Err(err) => {
                writeln!(self.stderr, "Error: {}", err)?;
                return Err(err);
            },
        }
    }

    /// Runs this program.
    fn run_inner(&mut self) -> Result<()> {
        // Write the help or version message
        if self.options.help {
            return self.help();
        }
        if self.options.version {
            return self.version();
        }

        // Validate the options
        self.validate()?;

        // Handle the paths
        return self.change();
    }

    /// Validates the options.
    fn validate(&self) -> Result<()> {
        return if {
            self.options.interactive && self.options.suppress ||
            self.has_file() && self.has_dir()
        } {
            Err(Error::Conflict)
        } else if {
            !self.has_file() &&
            !self.has_dir()
        } {
            Err(Error::Missing)
        } else {
            Ok(())
        };
    }

    /// Writes the help message to the standard error stream.
    fn help(&mut self) -> Result<()> {
        Options::clap().write_help(&mut self.stderr)?;
        writeln!(self.stderr, "")?;
        return Ok(());
    }

    /// Writes the version message to the standard error stream.
    fn version(&mut self) -> Result<()> {
        Options::clap().write_version(&mut self.stderr)?;
        writeln!(self.stderr, "")?;
        return Ok(());
    }

    /// Authorizes directory and file access by prompting the user and reading
    /// from the standard input stream.
    fn auth(&mut self, path: &PathBuf, context: Context) -> Result<bool> {
        // Determine the appropriate prompt
        let prompt = match context {
            Context::Absolute => "is absolute",
            Context::Interactive => "mode will be changed",
        };

        let mut input = String::new();
        loop {
            // Prompt the user and normalize the input
            write!(self.stderr, r#""{}" {} - continue? [y/n] "#, path.display(), prompt)?;
            self.stdin.read_line(&mut input)?;

            // The response must be `y` or `n`
            match input.trim().to_lowercase().as_str() {
                "n" => {
                    if self.options.verbose {
                        writeln!(self.stderr, "Skipped.")?;
                    }
                    return Ok(false);
                },
                "y" => {
                    return Ok(true);
                },
                _ => {
                    input.clear();
                },
            }
        }
    }

    /// Changes all paths provided by the user. Authorization may be requested
    /// if the `suppress` option is not present.
    fn change(&mut self) -> Result<()> {
        let mode = u32::from_str_radix(
            if self.has_file() {
                self.options.file.as_ref().unwrap()
            } else if self.has_dir() {
                self.options.dir.as_ref().unwrap()
            } else {
                ""
            },
            8,
        )?;
        for path in self.options.paths.to_owned() {
            if !self.options.suppress && path.has_root() {
                // Authorize absolute paths (optional)
                if let Ok(false) = self.auth(&path, Context::Absolute) {
                    continue;
                }
            }

            if path.is_file() {
                // The path is a file
                if self.has_file() {
                    self.change_one(&path, mode)?;
                }
            } else {
                // Try the path as a directory
                self.change_many(&path, mode)?;
            }
        }
        return Ok(());
    }

    /// Changes all entries under the given directory and writes all errors.
    fn change_many(&mut self, path: &PathBuf, mode: u32) -> Result<()> {
        return if let Err(err) = self.change_many_inner(path, mode) {
            self.write_error("Cannot access", path, &err)
        } else {
            Ok(())
        };
    }

    /// Changes all entries under the given directory.
    fn change_many_inner(&mut self, path: &PathBuf, mode: u32) -> Result<()> {
        if self.has_dir() {
            self.change_one(path, mode)?;
        }
        for entry in path.read_dir()? {
            let path = entry?.path();

            // Recurse if the entry is a directory
            if path.is_file() {
                if self.has_file() {
                    self.change_one(&path, mode)?;
                }
            } else {
                self.change_many(&path, mode)?;
            }
        }
        return Ok(());
    }

    /// Changes the mode of the given path and writes all errors.
    fn change_one(&mut self, path: &PathBuf, mode: u32) -> Result<()> {
        return if let Err(err) = self.change_one_inner(path, mode) {
            self.write_error("Cannot change permissions", path, &err)
        } else {
            Ok(())
        };
    }

    /// Changes the mode of the given path. Authorization may be requested and
    /// additional information may be written if the `interactive` and
    /// `verbose` options are present. The path will not be changed during a
    /// `dry-run`.
    fn change_one_inner(&mut self, path: &PathBuf, mode: u32) -> Result<()> {
        if self.options.interactive && !self.options.suppress {
            // Authorize every path (optional)
            if let Ok(false) = self.auth(path, Context::Interactive) {
                return Ok(());
            }
        }

        if !self.options.dry_run {
            // Change the mode of the path
            fs::set_permissions(path, Permissions::from_mode(mode))?;

            if self.options.verbose {
                // Write the results (optional)
                self.write_result("changed", path)?;
            }
        } else {
            // Perform a dry run (optional)
            self.write_result("will be changed", path)?;
        }
        return Ok(());
    }

    /// Determines if the file option is present.
    fn has_file(&self) -> bool {
        return self.options.file.is_some();
    }

    /// Determines if the directory option is present.
    fn has_dir(&self) -> bool {
        return self.options.dir.is_some();
    }

    /// Writes a path related error to the standard error stream.
    fn write_error(&mut self, msg: &str, path: &PathBuf, err: &Error) -> Result<()> {
        writeln!(self.stderr, r#"Error: {} "{}": {}"#, msg, path.display(), err)?;
        return Ok(());
    }

    /// Writes the result of an operation to the standard output stream.
    fn write_result(&mut self, msg: &str, path: &PathBuf) -> Result<()> {
        writeln!(self.stdout, r#""{}": mode {}."#, path.display(), msg)?;
        return Ok(());
    }
}
<|endoftext|>


<commit_before>extern crate rustbox;
extern crate rustc_serialize;
extern crate gag;

use std::env::args;
use std::path::Path;
use rustbox::{RustBox, Event, InputMode, InitOptions};
use rustbox::keyboard::Key;
use gag::Hold;

#[macro_use] mod signals;
mod ui;
mod buffer;
mod util;
mod segment;

use ui::view::HexEdit;

fn main() {
    let mut args = args();
    let mut edit = HexEdit::new();

    if args.len() > 1 {
        edit.open(&Path::new(&args.nth(1).unwrap()));
    }

    let hold = (Hold::stdout().unwrap(), Hold::stderr().unwrap());

    let rb = RustBox::init(InitOptions{
        buffer_stderr: false,
        input_mode: InputMode::Esc,
    }).unwrap();

    edit.resize(rb.width() as i32, rb.height() as i32);
    edit.draw(&rb);
    // tb::set_cursor(0, 0);
    rb.present();
    // tb::set_cursor(2, 0);
    loop {
        let event = rb.poll_event(false).unwrap();
        // println!("{:?}", event);
        match event {
            // This case is here, since we want to have a 'way ouy' till we fixed bugs
            Event::KeyEvent(Some(Key::Char('\u{0}'))) => break,  /** This should be Ctrl-` */
            Event::KeyEvent(Some(key)) => edit.input(key),
            Event::ResizeEvent(w, h) => { edit.resize(w, h) }
            _ => ()
        };
        rb.clear();
        edit.draw(&rb);
        rb.present();
    }
    drop(rb);
    drop(hold);
}

// fn test_main() {
//     let mut s = Segment::from_slice(&[1,2,3,4]);
//     println!("Segment {}", s);

//     s.insert_slice(0, &[5,6,7,8,9,10,11,12]);

//     s.insert_slice(0, &[100]);

//     println!("Segment {}", s);

//     println!("Slice {}", s.move_out_slice(0, 7));
//     // println!("Slice {}", );
//     println!("Segment {}", s);

//     println!("Slice {}", s.move_out_slice(0, 6));
//     // println!("Slice {}", );
//     println!("Segment {}", s);

// }
<commit_msg>Clean up really old bad test code in main<commit_after>extern crate rustbox;
extern crate rustc_serialize;
extern crate gag;

use std::env::args;
use std::path::Path;
use rustbox::{RustBox, Event, InputMode, InitOptions};
use rustbox::keyboard::Key;
use gag::Hold;

#[macro_use] mod signals;
mod ui;
mod buffer;
mod util;
mod segment;

use ui::view::HexEdit;

fn main() {
    let mut args = args();
    let mut edit = HexEdit::new();

    if args.len() > 1 {
        edit.open(&Path::new(&args.nth(1).unwrap()));
    }

    let hold = (Hold::stdout().unwrap(), Hold::stderr().unwrap());

    let rb = RustBox::init(InitOptions{
        buffer_stderr: false,
        input_mode: InputMode::Esc,
    }).unwrap();

    edit.resize(rb.width() as i32, rb.height() as i32);
    edit.draw(&rb);
    rb.present();
    loop {
        let event = rb.poll_event(false).unwrap();
        match event {
            // This case is here, since we want to have a 'way ouy' till we fixed bugs
            Event::KeyEvent(Some(Key::Char('\u{0}'))) => break,  /** This should be Ctrl-` */
            Event::KeyEvent(Some(key)) => edit.input(key),
            Event::ResizeEvent(w, h) => { edit.resize(w, h) }
            _ => ()
        };
        rb.clear();
        edit.draw(&rb);
        rb.present();
    }
    drop(rb);
    drop(hold);
}
<|endoftext|>


<commit_before>extern crate glium_graphics;
#[macro_use]
extern crate glium;
extern crate piston;
#[macro_use]
extern crate dyon;
extern crate current;
extern crate dyon_interactive;
extern crate sdl2_window;
extern crate sdl2;
extern crate sdl2_mixer;
extern crate wavefront_obj;
extern crate vecmath;

use sdl2_window::Sdl2Window;
use sdl2_mixer as mix;
use glium_graphics::{Glium2d, GliumWindow, OpenGL};
use piston::window::WindowSettings;
use piston::input::Event;
use dyon::{error, load, Module, Runtime};
use current::CurrentGuard;

use engine::{IndexBuffers, Materials, MusicTracks, ObjSets, Programs,
    SoundTracks, VertexBuffers};

mod engine;

type Window = GliumWindow<Sdl2Window>;

fn main() {
    let opengl = OpenGL::V3_2;
    let ref mut window: Window = WindowSettings::new("Dyon: Asteroids!", [512, 512])
        .opengl(opengl).samples(4).exit_on_esc(true).build().unwrap();

    let mut runtime = Runtime::new();
    let module = match load_module() {
        None => return,
        Some(m) => m
    };

    init_audio();

    let (audio, timer) = {
        let ref sdl = window.window.borrow().sdl_context;
        (sdl.audio().unwrap(), sdl.timer().unwrap())
    };

    let mut g2d = Glium2d::new(opengl, window);
    let mut e: Option<Event> = None;
    let mut target = window.draw();
    let mut materials: Materials = vec![];
    let mut obj_sets: ObjSets = vec![];
    let mut programs: Programs = vec![];
    let mut vertex_buffers: VertexBuffers = vec![];
    let mut index_buffers: IndexBuffers = vec![];
    let mut music_tracks: MusicTracks = vec![];
    let mut sound_tracks: SoundTracks = vec![];

    {
        let window_guard = CurrentGuard::new(window);
        let event_guard: CurrentGuard<Option<Event>> = CurrentGuard::new(&mut e);
        let g2d_guard = CurrentGuard::new(&mut g2d);
        let target_guard = CurrentGuard::new(&mut target);
        let materials_guard = CurrentGuard::new(&mut materials);
        let obj_sets_guard = CurrentGuard::new(&mut obj_sets);
        let programs_guard = CurrentGuard::new(&mut programs);
        let vertex_buffers_guard = CurrentGuard::new(&mut vertex_buffers);
        let index_buffers_guard = CurrentGuard::new(&mut index_buffers);
        let music_tracks_guard = CurrentGuard::new(&mut music_tracks);
        let sound_tracks_guard = CurrentGuard::new(&mut sound_tracks);
        if error(runtime.run(&module)) {
            return;
        }
        drop(sound_tracks_guard);
        drop(music_tracks_guard);
        drop(index_buffers_guard);
        drop(vertex_buffers_guard);
        drop(programs_guard);
        drop(obj_sets_guard);
        drop(materials_guard);
        drop(target_guard);
        drop(g2d_guard);
        drop(event_guard);
        drop(window_guard);
    }

    target.finish().unwrap();

    drop(timer);
    drop(audio);
    drop(window);
}

fn init_audio() {
    // Load dynamic libraries.
    // Ignore formats that are not built in.
    let _ = mix::init(
          mix::INIT_MP3
        | mix::INIT_FLAC
        | mix::INIT_MOD
        | mix::INIT_FLUIDSYNTH
        | mix::INIT_MODPLUG
        | mix::INIT_OGG
    );
    mix::open_audio(
        mix::DEFAULT_FREQUENCY,
        mix::DEFAULT_FORMAT,
        mix::DEFAULT_CHANNELS,
        1024
    ).unwrap();
    mix::allocate_channels(mix::DEFAULT_CHANNELS);
}

fn load_module() -> Option<Module> {
    use std::sync::Arc;
    use dyon_functions::*;
    use dyon_interactive::add_functions;
    use dyon::{Lt, Module, PreludeFunction, Type};

    let mut module = Module::new();
    add_functions::<Window>(&mut module);
    module.add(Arc::new("draw".into()), draw, PreludeFunction {
        lts: vec![Lt::Default],
        tys: vec![Type::array()],
        ret: Type::Void
    });
    module.add(Arc::new("next_event".into()),
        next_event, PreludeFunction {
            lts: vec![],
            tys: vec![],
            ret: Type::Bool
        });
    engine::register_obj(&mut module);
    engine::register_shader(&mut module);
    engine::register_sound(&mut module);
    if error(load("src/main.dyon", &mut module)) {
        None
    } else {
        Some(module)
    }
}

mod dyon_functions {
    use dyon::Runtime;
    use dyon_interactive::{draw_2d, NO_EVENT};
    use current::Current;
    use super::Window;

    pub fn draw(rt: &mut Runtime) -> Result<(), String> {
        use piston::input::*;
        use glium_graphics::Glium2d;
        use glium::Frame;

        let e = unsafe { &*Current::<Option<Event>>::new() };
        let g2d = unsafe { &mut *Current::<Glium2d>::new() };
        let target = unsafe { &mut *Current::<Frame>::new() };
        if let &Some(ref e) = e {
            if let Some(args) = e.render_args() {
                g2d.draw(target, args.viewport(), |c, g| {
                    draw_2d(rt, c, g)
                })
            } else {
                Ok(())
            }
        } else {
            Err(NO_EVENT.into())
        }
    }

    pub fn next_event(rt: &mut Runtime) -> Result<(), String> {
        use piston::input::*;
        use glium::Frame;

        let window = unsafe { &mut *Current::<Window>::new() };
        let e = unsafe { &mut *Current::<Option<Event>>::new() };
        let target = unsafe { &mut *Current::<Frame>::new() };
        if let Some(new_e) = window.next() {
            if new_e.after_render_args().is_some() {
                target.set_finish().unwrap();
                *target = window.draw();
            }
            *e = Some(new_e);
            rt.push(true);
        } else {
            *e = None;
            rt.push(false);
        }
        Ok(())
    }
}
<commit_msg>Fixed sound<commit_after>extern crate glium_graphics;
#[macro_use]
extern crate glium;
extern crate piston;
#[macro_use]
extern crate dyon;
extern crate current;
extern crate dyon_interactive;
extern crate sdl2_window;
extern crate sdl2;
extern crate sdl2_mixer;
extern crate wavefront_obj;
extern crate vecmath;

use sdl2_window::Sdl2Window;
use sdl2_mixer as mix;
use glium_graphics::{Glium2d, GliumWindow, OpenGL};
use piston::window::WindowSettings;
use piston::input::Event;
use dyon::{error, load, Module, Runtime};
use current::CurrentGuard;

use engine::{IndexBuffers, Materials, MusicTracks, ObjSets, Programs,
    SoundTracks, VertexBuffers};

mod engine;

type Window = GliumWindow<Sdl2Window>;

fn main() {
    let opengl = OpenGL::V3_2;
    let ref mut window: Window = WindowSettings::new("Dyon: Asteroids!", [512, 512])
        .opengl(opengl).samples(4).exit_on_esc(true).build().unwrap();

    let mut runtime = Runtime::new();
    let module = match load_module() {
        None => return,
        Some(m) => m
    };

    init_audio();

    let (audio, timer) = {
        let ref sdl = window.window.borrow().sdl_context;
        (sdl.audio().unwrap(), sdl.timer().unwrap())
    };

    let mut g2d = Glium2d::new(opengl, window);
    let mut e: Option<Event> = None;
    let mut target = window.draw();
    let mut materials: Materials = vec![];
    let mut obj_sets: ObjSets = vec![];
    let mut programs: Programs = vec![];
    let mut vertex_buffers: VertexBuffers = vec![];
    let mut index_buffers: IndexBuffers = vec![];
    let mut music_tracks: MusicTracks = vec![];
    let mut sound_tracks: SoundTracks = vec![];

    {
        let window_guard = CurrentGuard::new(window);
        let event_guard: CurrentGuard<Option<Event>> = CurrentGuard::new(&mut e);
        let g2d_guard = CurrentGuard::new(&mut g2d);
        let target_guard = CurrentGuard::new(&mut target);
        let materials_guard = CurrentGuard::new(&mut materials);
        let obj_sets_guard = CurrentGuard::new(&mut obj_sets);
        let programs_guard = CurrentGuard::new(&mut programs);
        let vertex_buffers_guard = CurrentGuard::new(&mut vertex_buffers);
        let index_buffers_guard = CurrentGuard::new(&mut index_buffers);
        let music_tracks_guard = CurrentGuard::new(&mut music_tracks);
        let sound_tracks_guard = CurrentGuard::new(&mut sound_tracks);
        if error(runtime.run(&module)) {
            return;
        }
        drop(sound_tracks_guard);
        drop(music_tracks_guard);
        drop(index_buffers_guard);
        drop(vertex_buffers_guard);
        drop(programs_guard);
        drop(obj_sets_guard);
        drop(materials_guard);
        drop(target_guard);
        drop(g2d_guard);
        drop(event_guard);
        drop(window_guard);
    }

    target.finish().unwrap();

    drop(timer);
    drop(audio);
    drop(window);
}

fn init_audio() {
    // Load dynamic libraries.
    // Ignore formats that are not built in.
    let _ = mix::init(
          mix::INIT_MP3
        | mix::INIT_FLAC
        | mix::INIT_MOD
        | mix::INIT_FLUIDSYNTH
        | mix::INIT_MODPLUG
        | mix::INIT_OGG
    );
    mix::open_audio(
        // Use cd quality to avoid noise artifacts.
        mix::DEFAULT_FREQUENCY * 2,
        mix::DEFAULT_FORMAT,
        mix::DEFAULT_CHANNELS,
        1024
    ).unwrap();
    // Allow up to 3 sounds playing at the same time.
    mix::allocate_channels(mix::DEFAULT_CHANNELS * 3);
}

fn load_module() -> Option<Module> {
    use std::sync::Arc;
    use dyon_functions::*;
    use dyon_interactive::add_functions;
    use dyon::{Lt, Module, PreludeFunction, Type};

    let mut module = Module::new();
    add_functions::<Window>(&mut module);
    module.add(Arc::new("draw".into()), draw, PreludeFunction {
        lts: vec![Lt::Default],
        tys: vec![Type::array()],
        ret: Type::Void
    });
    module.add(Arc::new("next_event".into()),
        next_event, PreludeFunction {
            lts: vec![],
            tys: vec![],
            ret: Type::Bool
        });
    engine::register_obj(&mut module);
    engine::register_shader(&mut module);
    engine::register_sound(&mut module);
    if error(load("src/main.dyon", &mut module)) {
        None
    } else {
        Some(module)
    }
}

mod dyon_functions {
    use dyon::Runtime;
    use dyon_interactive::{draw_2d, NO_EVENT};
    use current::Current;
    use super::Window;

    pub fn draw(rt: &mut Runtime) -> Result<(), String> {
        use piston::input::*;
        use glium_graphics::Glium2d;
        use glium::Frame;

        let e = unsafe { &*Current::<Option<Event>>::new() };
        let g2d = unsafe { &mut *Current::<Glium2d>::new() };
        let target = unsafe { &mut *Current::<Frame>::new() };
        if let &Some(ref e) = e {
            if let Some(args) = e.render_args() {
                g2d.draw(target, args.viewport(), |c, g| {
                    draw_2d(rt, c, g)
                })
            } else {
                Ok(())
            }
        } else {
            Err(NO_EVENT.into())
        }
    }

    pub fn next_event(rt: &mut Runtime) -> Result<(), String> {
        use piston::input::*;
        use glium::Frame;

        let window = unsafe { &mut *Current::<Window>::new() };
        let e = unsafe { &mut *Current::<Option<Event>>::new() };
        let target = unsafe { &mut *Current::<Frame>::new() };
        if let Some(new_e) = window.next() {
            if new_e.after_render_args().is_some() {
                target.set_finish().unwrap();
                *target = window.draw();
            }
            *e = Some(new_e);
            rt.push(true);
        } else {
            *e = None;
            rt.push(false);
        }
        Ok(())
    }
}
<|endoftext|>


<commit_before>extern crate bytes;
extern crate env_logger;
extern crate mio;
#[macro_use]
extern crate log;
#[macro_use]
extern crate clap;
extern crate failure;
extern crate slab;

use clap::{App, Arg};
use failure::Error;
use failure::ResultExt;
use mio::net::{TcpListener, TcpStream};
use slab::Slab;
use std::collections::VecDeque;
use std::io::{Read, Write};
use std::ops;

#[derive(Debug)]
enum MiChatCommand {
    Broadcast(String),
    NewConnection(TcpStream),
    CloseConnection,
}

struct MiChat {
    connections: Slab<EventHandler>,
}

trait RuleHandler {
    fn handle_event(&mut self, _event_loop: &mut mio::Poll, events: mio::Ready);
    fn register(&self, event_loop: &mut mio::Poll, token: mio::Token) -> Result<(), Error>;
    fn deregister(&self, event_loop: &mut mio::Poll) -> Result<(), Error>;
    fn process_rules(
        &mut self,
        event_loop: &mut mio::Poll,
        to_parent: &mut FnMut(MiChatCommand),
    ) -> Result<(), Error>;
}

impl MiChat {
    fn new() -> MiChat {
        MiChat {
            connections: Slab::new(),
        }
    }

    fn listen(&mut self, event_loop: &mut mio::Poll, listener: TcpListener) -> Result<(), Error> {
        let token = {
            let e = self.connections.vacant_entry();
            let token = mio::Token(e.key());
            let l = EventHandler::Listener(Listener::new(listener, token));
            e.insert(l);
            token
        };
        &self.connections[token.into()].register(event_loop, token)?;
        Ok(())
    }

    fn process_action(
        &mut self,
        src: usize,
        msg: MiChatCommand,
        event_loop: &mut mio::Poll,
    ) -> Result<(), Error> {
        trace!("{:p}; from {:?}; got {:?}", self, src, msg);
        match msg {
            MiChatCommand::Broadcast(s) => self.process_broadcast(s)?,
            MiChatCommand::NewConnection(socket) => {
                self.process_new_connection(event_loop, socket)?
            }
            MiChatCommand::CloseConnection => self.process_close(src, event_loop)?,
        }
        Ok(())
    }

    fn process_broadcast(&mut self, s: String) -> Result<(), Error> {
        for c in self.client_connections_mut() {
            c.enqueue(&s);
        }
        Ok(())
    }

    fn process_new_connection(
        &mut self,
        event_loop: &mut mio::Poll,
        socket: mio::tcp::TcpStream,
    ) -> Result<(), Error> {
        let token = {
            let e = self.connections.vacant_entry();
            let token = mio::Token(e.key());
            let l = EventHandler::Conn(Connection::new(socket, token));
            e.insert(l);
            token
        };
        &self.connections[token.into()].register(event_loop, token)?;
        Ok(())
    }

    fn process_close(&mut self, src: usize, event_loop: &mut mio::Poll) -> Result<(), Error> {
        debug!("Processing close: {:?}", src);
        let conn = self.connections.remove(src);
        conn.deregister(event_loop)?;
        Ok(())
    }

    fn client_connections_mut<'a>(&'a mut self) -> impl Iterator<Item = &'a mut Connection> + 'a {
        self.connections.iter_mut().filter_map(|(_, it)| match it {
            &mut EventHandler::Conn(ref mut c) => Some(c),
            _ => None,
        })
    }
}

#[derive(Debug)]
struct Connection {
    socket: TcpStream,
    sock_status: mio::Ready,
    token: mio::Token,
    read_buf: Vec<u8>,
    read_eof: bool,
    write_buf: Vec<u8>,
}

impl RuleHandler for Connection {
    fn register(&self, event_loop: &mut mio::Poll, token: mio::Token) -> Result<(), Error> {
        event_loop
            .register(
                &self.socket,
                token,
                mio::Ready::readable(),
                mio::PollOpt::edge() | mio::PollOpt::oneshot(),
            ).context("event loop register")?;
        Ok(())
    }
    fn deregister(&self, event_loop: &mut mio::Poll) -> Result<(), Error> {
        event_loop.deregister(&self.socket)?;
        Ok(())
    }

    // Event updates arrive here
    fn handle_event(&mut self, _event_loop: &mut mio::Poll, events: mio::Ready) {
        self.sock_status.insert(events);
        info!(
            "Connection::handle_event: {:?}; this time: {:?}; now: {:?}",
            self.socket.peer_addr(),
            events,
            self.sock_status
        );
    }

    // actions are processed here on down.

    fn process_rules(
        &mut self,
        event_loop: &mut mio::Poll,
        to_parent: &mut FnMut(MiChatCommand),
    ) -> Result<(), Error> {
        if self.sock_status.is_readable() {
            self.sock_status.remove(mio::Ready::readable());
            self.read()?;
        }

        self.process_buffer(to_parent);

        if self.sock_status.is_writable() {
            self.sock_status.remove(mio::Ready::writable());
            self.write()?;
        }

        if self.should_close() {
            to_parent(MiChatCommand::CloseConnection)
        } else {
            self.reregister(event_loop)?;
        }

        Ok(())
    }
}

impl Connection {
    fn new(socket: TcpStream, token: mio::Token) -> Connection {
        Connection {
            socket: socket,
            sock_status: mio::Ready::empty(),
            token: token,
            read_buf: Vec::with_capacity(1024),
            write_buf: Vec::new(),
            read_eof: false,
        }
    }
    fn process_buffer(&mut self, to_parent: &mut FnMut(MiChatCommand)) {
        let mut prev = 0;
        info!(
            "{:?}: Read buffer: {:?}",
            self.socket.peer_addr(),
            self.read_buf
        );
        for n in self.read_buf.iter().enumerate().filter_map(|(i, e)| {
            if *e == '\n' as u8 {
                Some(i)
            } else {
                None
            }
        }) {
            info!(
                "{:?}: Pos: {:?}; chunk: {:?}",
                self.socket.peer_addr(),
                n,
                &self.read_buf[prev..n]
            );
            let s = String::from_utf8_lossy(&self.read_buf[prev..n]).to_string();
            let cmd = MiChatCommand::Broadcast(s);
            info!("Send! {:?}", cmd);
            to_parent(cmd);
            prev = n + 1;
        }
        let remainder = self.read_buf[prev..].to_vec();
        info!(
            "{:?}: read Remainder: {}",
            self.socket.peer_addr(),
            remainder.len()
        );
        self.read_buf = remainder;
    }

    fn read(&mut self) -> Result<(), Error> {
        let mut abuf = vec![0; 1024];
        match self.socket.read(&mut abuf).context("Reading socket")? {
            0 => {
                info!("{:?}: EOF!", self.socket.peer_addr());
                self.read_eof = true;
                Ok(())
            }
            n => {
                info!("{:?}: Read {}bytes", self.socket.peer_addr(), n);
                self.read_buf.extend(&abuf[0..n]);
                Ok(())
            }
        }
    }

    fn write(&mut self) -> Result<(), Error> {
        let n = self
            .socket
            .write(&mut self.write_buf)
            .context("writing buffer")?;
        info!(
            "{:?}: Wrote {} of {} in buffer",
            self.socket.peer_addr(),
            n,
            self.write_buf.len()
        );
        self.write_buf = self.write_buf[n..].to_vec();
        info!(
            "{:?}: Now {:?}b",
            self.socket.peer_addr(),
            self.write_buf.len()
        );
        Ok(())
    }

    fn should_close(&self) -> bool {
        self.read_eof && self.write_buf.is_empty()
    }

    fn reregister(&mut self, event_loop: &mut mio::Poll) -> Result<(), Error> {
        let mut flags = mio::Ready::readable();
        if !self.write_buf.is_empty() {
            flags.insert(mio::Ready::writable());
        }
        info!("Registering {:?} with {:?}", self, flags);

        event_loop
            .reregister(&self.socket, self.token, flags, mio::PollOpt::oneshot())
            .context("EventLoop#reregister")?;

        Ok(())
    }

    fn enqueue(&mut self, s: &str) {
        self.write_buf.extend(s.as_bytes());
        self.write_buf.push('\n' as u8);
    }
}

#[derive(Debug)]
struct Listener {
    listener: TcpListener,
    sock_status: mio::Ready,
    token: mio::Token,
}

impl Listener {
    fn new(listener: TcpListener, token: mio::Token) -> Listener {
        Listener {
            listener: listener,
            sock_status: mio::Ready::empty(),
            token: token,
        }
    }
    fn reregister(&self, event_loop: &mut mio::Poll, token: mio::Token) -> Result<(), Error> {
        event_loop
            .reregister(
                &self.listener,
                token,
                mio::Ready::readable(),
                mio::PollOpt::edge() | mio::PollOpt::oneshot(),
            ).context("Register listener")?;
        Ok(())
    }
}

impl RuleHandler for Listener {
    fn register(&self, event_loop: &mut mio::Poll, token: mio::Token) -> Result<(), Error> {
        event_loop
            .register(
                &self.listener,
                token,
                mio::Ready::readable(),
                mio::PollOpt::edge() | mio::PollOpt::oneshot(),
            ).context("Register listener")?;
        Ok(())
    }
    fn deregister(&self, event_loop: &mut mio::Poll) -> Result<(), Error> {
        event_loop.deregister(&self.listener)?;
        Ok(())
    }

    fn handle_event(&mut self, _event_loop: &mut mio::Poll, events: mio::Ready) {
        assert!(events.is_readable());
        self.sock_status.insert(events);
        info!(
            "Listener::handle_event: {:?}; this time: {:?}; now: {:?}",
            self.listener.local_addr(),
            events,
            self.sock_status
        );
    }
    fn process_rules(
        &mut self,
        event_loop: &mut mio::Poll,
        to_parent: &mut FnMut(MiChatCommand),
    ) -> Result<(), Error> {
        if self.sock_status.is_readable() {
            info!("the listener socket is ready to accept a connection");
            self.sock_status.remove(mio::Ready::readable());
            let (socket, _client) = self.listener.accept().context("Accept listening socket")?;
            let cmd = MiChatCommand::NewConnection(socket);
            to_parent(cmd);

            self.reregister(event_loop, self.token)?;
        }
        Ok(())
    }
}

#[derive(Debug)]
enum EventHandler {
    Listener(Listener),
    Conn(Connection),
}

impl ops::Deref for EventHandler {
    type Target = RuleHandler;
    fn deref(&self) -> &Self::Target {
        match self {
            &EventHandler::Conn(ref conn) => conn,
            &EventHandler::Listener(ref listener) => listener,
        }
    }
}
impl ops::DerefMut for EventHandler {
    fn deref_mut(&mut self) -> &mut Self::Target {
        match self {
            &mut EventHandler::Conn(ref mut conn) => conn,
            &mut EventHandler::Listener(ref mut listener) => listener,
        }
    }
}

impl MiChat {
    fn ready(&mut self, event_loop: &mut mio::Poll, token: mio::Token, events: mio::Ready) {
        info!("{:?}: {:?}", token, events);
        self.connections[token.into()].handle_event(event_loop, events);
    }

    fn process(&mut self, event_loop: &mut mio::Poll) -> Result<(), Error> {
        let mut parent_actions = VecDeque::new();
        let mut failed = Vec::new();
        loop {
            let mut something_done = false;
            for (idx, conn) in self.connections.iter_mut() {
                if let Err(e) = conn.process_rules(event_loop, &mut |action| {
                    parent_actions.push_back((idx, action))
                }) {
                    failed.push(idx);
                    error!("Error on connection {:?}; Dropping: {:?}", idx, e);
                }
            }
            for idx in failed.drain(..) {
                debug!("Remove failed; {:?}", idx);
                let conn = self.connections.remove(idx);
                conn.deregister(event_loop)?;
            }
            for (src, action) in parent_actions.drain(..) {
                something_done = true;
                if let Err(e) = self.process_action(src, action, event_loop) {
                    failed.push(src);
                    error!("Error on action for {:?}; Dropping: {:?}", src, e);
                }
            }
            // If we didn't do anything, we're done.
            if !something_done {
                break;
            }
        }

        Ok(())
    }

    fn run(&mut self, event_loop: &mut mio::Poll) -> Result<(), Error> {
        loop {
            self.run_once(event_loop)?;
        }
    }
    fn run_once(&mut self, event_loop: &mut mio::Poll) -> Result<(), Error> {
        let mut events = mio::Events::with_capacity(1024);
        event_loop.poll(&mut events, None)?;
        for ev in &events {
            self.ready(event_loop, ev.token(), ev.readiness())
        }

        self.process(event_loop)
    }
}

fn main() {
    env_logger::init();
    let matches = App::new("michat")
        .arg(
            Arg::with_name("bind")
                .short("l")
                .takes_value(true)
                .required(true),
        ).get_matches();

    let address = value_t_or_exit!(matches.value_of("bind"), std::net::SocketAddr);
    let listener = TcpListener::bind(&address).expect("bind");

    let mut event_loop = mio::Poll::new().expect("Create event loop");
    let mut service = MiChat::new();

    service
        .listen(&mut event_loop, listener)
        .expect("Starting listener");
    info!("running michat listener at: {:?}", address);
    service.run(&mut event_loop).expect("run");
}
<commit_msg>Allow connection setup to fail.<commit_after>extern crate bytes;
extern crate env_logger;
extern crate mio;
#[macro_use]
extern crate log;
#[macro_use]
extern crate clap;
extern crate failure;
extern crate slab;

use clap::{App, Arg};
use failure::Error;
use failure::ResultExt;
use mio::net::{TcpListener, TcpStream};
use slab::Slab;
use std::collections::VecDeque;
use std::io::{Read, Write};
use std::ops;

#[derive(Debug)]
enum MiChatCommand {
    Broadcast(String),
    NewConnection(TcpStream),
    CloseConnection,
}

struct MiChat {
    connections: Slab<EventHandler>,
}

trait RuleHandler {
    fn handle_event(&mut self, _event_loop: &mut mio::Poll, events: mio::Ready);
    fn register(&self, event_loop: &mut mio::Poll, token: mio::Token) -> Result<(), Error>;
    fn deregister(&self, event_loop: &mut mio::Poll) -> Result<(), Error>;
    fn process_rules(
        &mut self,
        event_loop: &mut mio::Poll,
        to_parent: &mut FnMut(MiChatCommand),
    ) -> Result<(), Error>;
}

impl MiChat {
    fn new() -> MiChat {
        MiChat {
            connections: Slab::new(),
        }
    }

    fn listen(&mut self, event_loop: &mut mio::Poll, listener: TcpListener) -> Result<(), Error> {
        let token = {
            let e = self.connections.vacant_entry();
            let token = mio::Token(e.key());
            let l = EventHandler::Listener(Listener::new(listener, token));
            e.insert(l);
            token
        };
        &self.connections[token.into()].register(event_loop, token)?;
        Ok(())
    }

    fn process_action(
        &mut self,
        src: usize,
        msg: MiChatCommand,
        event_loop: &mut mio::Poll,
    ) -> Result<(), Error> {
        trace!("{:p}; from {:?}; got {:?}", self, src, msg);
        match msg {
            MiChatCommand::Broadcast(s) => self.process_broadcast(s)?,
            MiChatCommand::NewConnection(socket) => {
                self.process_new_connection(event_loop, socket)?
            }
            MiChatCommand::CloseConnection => self.process_close(src, event_loop)?,
        }
        Ok(())
    }

    fn process_broadcast(&mut self, s: String) -> Result<(), Error> {
        for c in self.client_connections_mut() {
            c.enqueue(&s);
        }
        Ok(())
    }

    fn process_new_connection(
        &mut self,
        event_loop: &mut mio::Poll,
        socket: mio::tcp::TcpStream,
    ) -> Result<(), Error> {
        let token = {
            let e = self.connections.vacant_entry();
            let token = mio::Token(e.key());
            let l = EventHandler::Conn(Connection::new(socket, token)?);
            e.insert(l);
            token
        };
        &self.connections[token.into()].register(event_loop, token)?;
        Ok(())
    }

    fn process_close(&mut self, src: usize, event_loop: &mut mio::Poll) -> Result<(), Error> {
        debug!("Processing close: {:?}", src);
        let conn = self.connections.remove(src);
        conn.deregister(event_loop)?;
        Ok(())
    }

    fn client_connections_mut<'a>(&'a mut self) -> impl Iterator<Item = &'a mut Connection> + 'a {
        self.connections.iter_mut().filter_map(|(_, it)| match it {
            &mut EventHandler::Conn(ref mut c) => Some(c),
            _ => None,
        })
    }
}

#[derive(Debug)]
struct Connection {
    socket: TcpStream,
    sock_status: mio::Ready,
    token: mio::Token,
    read_buf: Vec<u8>,
    read_eof: bool,
    write_buf: Vec<u8>,
}

impl RuleHandler for Connection {
    fn register(&self, event_loop: &mut mio::Poll, token: mio::Token) -> Result<(), Error> {
        event_loop
            .register(
                &self.socket,
                token,
                mio::Ready::readable(),
                mio::PollOpt::edge() | mio::PollOpt::oneshot(),
            ).context("event loop register")?;
        Ok(())
    }
    fn deregister(&self, event_loop: &mut mio::Poll) -> Result<(), Error> {
        event_loop.deregister(&self.socket)?;
        Ok(())
    }

    // Event updates arrive here
    fn handle_event(&mut self, _event_loop: &mut mio::Poll, events: mio::Ready) {
        self.sock_status.insert(events);
        info!(
            "Connection::handle_event: {:?}; this time: {:?}; now: {:?}",
            self.socket.peer_addr(),
            events,
            self.sock_status
        );
    }

    // actions are processed here on down.

    fn process_rules(
        &mut self,
        event_loop: &mut mio::Poll,
        to_parent: &mut FnMut(MiChatCommand),
    ) -> Result<(), Error> {
        if self.sock_status.is_readable() {
            self.sock_status.remove(mio::Ready::readable());
            self.read()?;
        }

        self.process_buffer(to_parent);

        if self.sock_status.is_writable() {
            self.sock_status.remove(mio::Ready::writable());
            self.write()?;
        }

        if self.should_close() {
            to_parent(MiChatCommand::CloseConnection)
        } else {
            self.reregister(event_loop)?;
        }

        Ok(())
    }
}

impl Connection {
    fn new(socket: TcpStream, token: mio::Token) -> Result<Connection, Error> {
        Ok(Connection {
            socket: socket,
            sock_status: mio::Ready::empty(),
            token: token,
            read_buf: Vec::with_capacity(1024),
            write_buf: Vec::new(),
            read_eof: false,
        })
    }
    fn process_buffer(&mut self, to_parent: &mut FnMut(MiChatCommand)) {
        let mut prev = 0;
        info!(
            "{:?}: Read buffer: {:?}",
            self.socket.peer_addr(),
            self.read_buf
        );
        for n in self.read_buf.iter().enumerate().filter_map(|(i, e)| {
            if *e == '\n' as u8 {
                Some(i)
            } else {
                None
            }
        }) {
            info!(
                "{:?}: Pos: {:?}; chunk: {:?}",
                self.socket.peer_addr(),
                n,
                &self.read_buf[prev..n]
            );
            let s = String::from_utf8_lossy(&self.read_buf[prev..n]).to_string();
            let cmd = MiChatCommand::Broadcast(s);
            info!("Send! {:?}", cmd);
            to_parent(cmd);
            prev = n + 1;
        }
        let remainder = self.read_buf[prev..].to_vec();
        info!(
            "{:?}: read Remainder: {}",
            self.socket.peer_addr(),
            remainder.len()
        );
        self.read_buf = remainder;
    }

    fn read(&mut self) -> Result<(), Error> {
        let mut abuf = vec![0; 1024];
        match self.socket.read(&mut abuf).context("Reading socket")? {
            0 => {
                info!("{:?}: EOF!", self.socket.peer_addr());
                self.read_eof = true;
                Ok(())
            }
            n => {
                info!("{:?}: Read {}bytes", self.socket.peer_addr(), n);
                self.read_buf.extend(&abuf[0..n]);
                Ok(())
            }
        }
    }

    fn write(&mut self) -> Result<(), Error> {
        let n = self
            .socket
            .write(&mut self.write_buf)
            .context("writing buffer")?;
        info!(
            "{:?}: Wrote {} of {} in buffer",
            self.socket.peer_addr(),
            n,
            self.write_buf.len()
        );
        self.write_buf = self.write_buf[n..].to_vec();
        info!(
            "{:?}: Now {:?}b",
            self.socket.peer_addr(),
            self.write_buf.len()
        );
        Ok(())
    }

    fn should_close(&self) -> bool {
        self.read_eof && self.write_buf.is_empty()
    }

    fn reregister(&mut self, event_loop: &mut mio::Poll) -> Result<(), Error> {
        let mut flags = mio::Ready::readable();
        if !self.write_buf.is_empty() {
            flags.insert(mio::Ready::writable());
        }
        info!("Registering {:?} with {:?}", self, flags);

        event_loop
            .reregister(&self.socket, self.token, flags, mio::PollOpt::oneshot())
            .context("EventLoop#reregister")?;

        Ok(())
    }

    fn enqueue(&mut self, s: &str) {
        self.write_buf.extend(s.as_bytes());
        self.write_buf.push('\n' as u8);
    }
}

#[derive(Debug)]
struct Listener {
    listener: TcpListener,
    sock_status: mio::Ready,
    token: mio::Token,
}

impl Listener {
    fn new(listener: TcpListener, token: mio::Token) -> Listener {
        Listener {
            listener: listener,
            sock_status: mio::Ready::empty(),
            token: token,
        }
    }
    fn reregister(&self, event_loop: &mut mio::Poll, token: mio::Token) -> Result<(), Error> {
        event_loop
            .reregister(
                &self.listener,
                token,
                mio::Ready::readable(),
                mio::PollOpt::edge() | mio::PollOpt::oneshot(),
            ).context("Register listener")?;
        Ok(())
    }
}

impl RuleHandler for Listener {
    fn register(&self, event_loop: &mut mio::Poll, token: mio::Token) -> Result<(), Error> {
        event_loop
            .register(
                &self.listener,
                token,
                mio::Ready::readable(),
                mio::PollOpt::edge() | mio::PollOpt::oneshot(),
            ).context("Register listener")?;
        Ok(())
    }
    fn deregister(&self, event_loop: &mut mio::Poll) -> Result<(), Error> {
        event_loop.deregister(&self.listener)?;
        Ok(())
    }

    fn handle_event(&mut self, _event_loop: &mut mio::Poll, events: mio::Ready) {
        assert!(events.is_readable());
        self.sock_status.insert(events);
        info!(
            "Listener::handle_event: {:?}; this time: {:?}; now: {:?}",
            self.listener.local_addr(),
            events,
            self.sock_status
        );
    }
    fn process_rules(
        &mut self,
        event_loop: &mut mio::Poll,
        to_parent: &mut FnMut(MiChatCommand),
    ) -> Result<(), Error> {
        if self.sock_status.is_readable() {
            info!("the listener socket is ready to accept a connection");
            self.sock_status.remove(mio::Ready::readable());
            let (socket, _client) = self.listener.accept().context("Accept listening socket")?;
            let cmd = MiChatCommand::NewConnection(socket);
            to_parent(cmd);

            self.reregister(event_loop, self.token)?;
        }
        Ok(())
    }
}

#[derive(Debug)]
enum EventHandler {
    Listener(Listener),
    Conn(Connection),
}

impl ops::Deref for EventHandler {
    type Target = RuleHandler;
    fn deref(&self) -> &Self::Target {
        match self {
            &EventHandler::Conn(ref conn) => conn,
            &EventHandler::Listener(ref listener) => listener,
        }
    }
}
impl ops::DerefMut for EventHandler {
    fn deref_mut(&mut self) -> &mut Self::Target {
        match self {
            &mut EventHandler::Conn(ref mut conn) => conn,
            &mut EventHandler::Listener(ref mut listener) => listener,
        }
    }
}

impl MiChat {
    fn ready(&mut self, event_loop: &mut mio::Poll, token: mio::Token, events: mio::Ready) {
        info!("{:?}: {:?}", token, events);
        self.connections[token.into()].handle_event(event_loop, events);
    }

    fn process(&mut self, event_loop: &mut mio::Poll) -> Result<(), Error> {
        let mut parent_actions = VecDeque::new();
        let mut failed = Vec::new();
        loop {
            let mut something_done = false;
            for (idx, conn) in self.connections.iter_mut() {
                if let Err(e) = conn.process_rules(event_loop, &mut |action| {
                    parent_actions.push_back((idx, action))
                }) {
                    failed.push(idx);
                    error!("Error on connection {:?}; Dropping: {:?}", idx, e);
                }
            }
            for idx in failed.drain(..) {
                debug!("Remove failed; {:?}", idx);
                let conn = self.connections.remove(idx);
                conn.deregister(event_loop)?;
            }
            for (src, action) in parent_actions.drain(..) {
                something_done = true;
                if let Err(e) = self.process_action(src, action, event_loop) {
                    failed.push(src);
                    error!("Error on action for {:?}; Dropping: {:?}", src, e);
                }
            }
            // If we didn't do anything, we're done.
            if !something_done {
                break;
            }
        }

        Ok(())
    }

    fn run(&mut self, event_loop: &mut mio::Poll) -> Result<(), Error> {
        loop {
            self.run_once(event_loop)?;
        }
    }
    fn run_once(&mut self, event_loop: &mut mio::Poll) -> Result<(), Error> {
        let mut events = mio::Events::with_capacity(1024);
        event_loop.poll(&mut events, None)?;
        for ev in &events {
            self.ready(event_loop, ev.token(), ev.readiness())
        }

        self.process(event_loop)
    }
}

fn main() {
    env_logger::init();
    let matches = App::new("michat")
        .arg(
            Arg::with_name("bind")
                .short("l")
                .takes_value(true)
                .required(true),
        ).get_matches();

    let address = value_t_or_exit!(matches.value_of("bind"), std::net::SocketAddr);
    let listener = TcpListener::bind(&address).expect("bind");

    let mut event_loop = mio::Poll::new().expect("Create event loop");
    let mut service = MiChat::new();

    service
        .listen(&mut event_loop, listener)
        .expect("Starting listener");
    info!("running michat listener at: {:?}", address);
    service.run(&mut event_loop).expect("run");
}
<|endoftext|>


<commit_before>
extern crate clap;
use clap::{Arg, App};

use std::io::{self, Read, Write};
use std::fs::File;
use std::path::{Path, PathBuf};
use std::ffi::{OsString, OsStr};
use std::error::Error;

use std::collections::HashSet;
use std::collections::HashMap;
use std::collections::hash_map::Entry;

extern crate petgraph;
use petgraph::Graph;
use petgraph::prelude::NodeIndex;
use petgraph::dot::{Dot, Config};

extern crate walkdir;
use walkdir::{DirEntry, WalkDir, WalkDirIterator};

#[macro_use]
extern crate lazy_static;
extern crate regex;

use regex::Regex;

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

#[derive(Debug)]
enum IncludeStatus {
    Relative, // e.g. <vector>
    Absolute, // e.g. /usr/include/c++/4.8/vector
    FailedLookup, // Failed to resolve an absolute file path.
}

#[derive(Debug)]
struct Include {
    path: PathBuf,
    is_system_include: bool,
    status: IncludeStatus,
}

impl Include {
    fn new_relative(name: &str, is_sys: bool) -> Include {
        Include {
            path: PathBuf::from(name),
            is_system_include: is_sys,
            status: IncludeStatus::Relative,
        }
    }

    fn as_absolute(&self, absolute_path: &Path) -> Include {
        Include {
            path: PathBuf::from(absolute_path),
            is_system_include: self.is_system_include,
            status: IncludeStatus::Absolute,
        }
    }

    fn as_failed_lookup(&self) -> Include {
        Include {
            path: PathBuf::from(&self.path),
            is_system_include: self.is_system_include,
            status: IncludeStatus::FailedLookup,
        }
    }
}

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

fn is_hidden(entry: &DirEntry) -> bool {
    entry.file_name()
        .to_str()
        .map(|s| s.starts_with("."))
        .unwrap_or(false)
}

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

// Convert a relative include path (e.g. <Windows.h>) into an absolute path.
fn find_absolute_include_path(include: &Include,
                              local_search_path: &Path,
                              search_paths: &[PathBuf])
                              -> Include {

    let local_full_path = local_search_path.join(&include.path);
    if local_full_path.exists() {
        return include.as_absolute(&local_full_path);
    }

    for search_prefix in search_paths {
        let full_path = search_prefix.join(&include.path);
        if full_path.exists() {
            return include.as_absolute(&full_path);
        }
    }
    include.as_failed_lookup()
}

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

// Return a list of #include statements found in the file
fn scan_file_for_includes(file: &Path) -> Result<Vec<Include>, io::Error> {
    let mut f = File::open(file)?;
    let mut text = String::new();
    f.read_to_string(&mut text)?;

    let mut includes = Vec::new();

    // Use a regex to search for '#include ...' lines.
    // The second (...) capture group isolates just the text, not the "" or <> symbols.
    lazy_static! {
        static ref RE: Regex = Regex::new(r##"#include ([<|"])(.*)[>|"]"##).unwrap();
    }

    // cap.at(1) is an angle brace or double quote, to determine user or system include.
    // cap.at(2) is the include file name.
    for cap in RE.captures_iter(&text) {
        let inc_symbol = cap.at(1).unwrap_or("<");
        let is_system_include = inc_symbol == "<";
        match cap.at(2) {
            Some(include_name) => {
                includes.push(Include::new_relative(include_name, is_system_include))
            }
            None => {}
        }
    }

    Ok(includes)
}

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

fn main() {
    // TODO: accept extra include paths.
    let args = App::new("IncludeGraph-rs")
        .version("0.1.0")
        .about("Generates an include graph from a tree of C++ files")
//        .arg(Arg::with_name("output_format")
//            .help("The output format to produce: PDF, DOT, PNG")
//            .value_name("OUTPUT")
//            .short("o"))
        .arg(Arg::with_name("source")
            .help("The source directory to scan")
            .value_name("PATH")
            .required(true)
            .multiple(false)
            .index(1))
        .get_matches();

    let root_dir_string = match args.value_of("source") {
        Some(path) => path,
        None => panic!("Unable to parse directory argument."),
    };

    println!("Scanning path: {}", root_dir_string);
    let root_dir = Path::new(root_dir_string);

    if !root_dir.exists() {
        println!("Unable to access directory: {}", root_dir.display());
    }

    // Create a list of default system include paths.
    let mut search_paths = Vec::new();
    search_paths.push(PathBuf::from(r"C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\include"));

    // Restrict the file extensions to search.
    let mut extensions = HashSet::new();
    extensions.insert(OsStr::new("h"));
    extensions.insert(OsStr::new("cpp"));
    extensions.insert(OsStr::new("hpp"));

    let mut input_queue: HashSet<String> = HashSet::new();

    // Collect all the files to scan
    let walker = WalkDir::new(root_dir).into_iter();
    for entry in walker.filter_entry(|e| !is_hidden(e)) {
        let entry = entry.unwrap(); // What error values could there be?
        let path = entry.path().canonicalize().unwrap();

        // If the file extension matches our set, queue full path for processing.
        match path.extension() {
            Some(ext) => {
                if extensions.contains(ext) {
                    input_queue.insert(path.to_str().unwrap().to_owned());
                }
            }
            None => {}
        }
    }

    // Graph of all the tracked files
    let mut graph = Graph::<OsString, bool>::new();
    let mut indices = HashMap::<OsString, NodeIndex>::new();

    for path_str in input_queue {
        let path = Path::new(&path_str);
        let includes_result = scan_file_for_includes(path);
        match includes_result {
            Ok(includes) => {
                let local_dir = path.parent().unwrap(); // strip the file name

                // Convert relative includes to absolute includes
                let absolute_includes: Vec<_> = includes.iter()
                    .map(|inc| find_absolute_include_path(inc, local_dir, &search_paths))
                    .collect();

                for inc in absolute_includes {

                    // Get an existing NodeIndex from the graph, on create a new node.
                    let src_str: OsString = path.as_os_str().to_owned();
                    let dst_str: OsString = inc.path.as_os_str().to_owned();

                    // These functions should work, but currently create duplicate nodes.
                    let src_node = indices.entry(src_str.clone())
                        .or_insert_with(|| graph.add_node(src_str.clone()))
                        .clone();
                    let dst_node = indices.entry(dst_str.clone())
                        .or_insert_with(|| graph.add_node(dst_str.clone()))
                        .clone();

//                    println!("Node {:?}: {:?}", src_node, src_str);
//                    println!("Node {:?}: {:?}", dst_node, dst_str);
//                    println!("Node count is now {}\n", graph.node_count());

                    graph.add_edge(src_node, dst_node, true);
                }
            }
            Err(err) => {
                println!("Unable to process file {:?}: {}", path, err);
            }
        };
    }

    //    // Print all the includes
    //    for node_idx in graph.node_indices() {
    //        for edge in graph.edges(node_idx) {
    //            let src_str = graph.index(edge.source());
    //            let dst_str = graph.index(edge.target());
    //            println!("{:?} -> {:?}", src_str, dst_str);
    //        }
    //    }

    // Write output to file graph.dot
    let out_path = Path::new("./graph.dot");
    let out_path_display = out_path.display();

    let mut dotfile = match File::create(&out_path) {
        Ok(file) => file,
        Err(why) => panic!("couldn't create {}: {}", out_path_display, why.description()),
    };

    writeln!(&mut dotfile,
             "{:?}",
             Dot::with_config(&graph, &[Config::EdgeNoLabel]));

    println!("Wrote graph to {}", out_path_display);
    println!("Run 'dot -Tpdf graph.dot > graph.pdf' to create a graph.");
}
<commit_msg>Added node formatting<commit_after>
extern crate clap;
use clap::{Arg, App};

use std::fmt;
use std::io::{self, Read, Write};
use std::fs::File;
use std::path::{Path, PathBuf};
use std::ffi::{OsString, OsStr};
use std::error::Error;

use std::collections::HashSet;
use std::collections::HashMap;

extern crate petgraph;
use petgraph::Graph;
use petgraph::prelude::NodeIndex;
use petgraph::dot::{Dot, Config};

extern crate walkdir;
use walkdir::{DirEntry, WalkDir, WalkDirIterator};

#[macro_use]
extern crate lazy_static;
extern crate regex;

use regex::Regex;

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

#[derive(Debug,Clone,PartialEq,Eq,Hash)]
struct FileNode {
    path: PathBuf, // Can use is_absolute() and is_relative() to check status.
    is_system: bool,
}

impl fmt::Display for FileNode {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{:?}", self.path.as_path()
            .file_name()
            .unwrap_or(OsStr::new("[error getting filename]")))
    }
}

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

#[derive(Debug)]
enum IncludeStatus {
    Relative, // e.g. <vector>
    Absolute, // e.g. /usr/include/c++/4.8/vector
    FailedLookup, // Failed to resolve an absolute file path.
}

#[derive(Debug)]
struct Include {
    path: PathBuf,
    is_system_include: bool,
    status: IncludeStatus,
}

impl Include {
    fn new_relative(name: &str, is_sys: bool) -> Include {
        Include {
            path: PathBuf::from(name),
            is_system_include: is_sys,
            status: IncludeStatus::Relative,
        }
    }

    fn as_absolute(&self, absolute_path: &Path) -> Include {
        Include {
            path: PathBuf::from(absolute_path),
            is_system_include: self.is_system_include,
            status: IncludeStatus::Absolute,
        }
    }

    fn as_failed_lookup(&self) -> Include {
        Include {
            path: PathBuf::from(&self.path),
            is_system_include: self.is_system_include,
            status: IncludeStatus::FailedLookup,
        }
    }
}

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

fn is_hidden(entry: &DirEntry) -> bool {
    entry.file_name()
        .to_str()
        .map(|s| s.starts_with("."))
        .unwrap_or(false)
}

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

// Convert a relative include path (e.g. <Windows.h>) into an absolute path.
fn find_absolute_include_path(include: &Include,
                              local_search_path: &Path,
                              system_search_paths: &[PathBuf])
                              -> Include {

    let local_full_path = local_search_path.join(&include.path);
    if local_full_path.exists() {
        return include.as_absolute(&local_full_path);
    }

    for search_prefix in system_search_paths {
        let full_path = search_prefix.join(&include.path);
        if full_path.exists() {
            return include.as_absolute(&full_path);
        }
    }
    include.as_failed_lookup()
}

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

// Return a list of #include statements found in the file
fn scan_file_for_includes(file: &Path) -> Result<Vec<Include>, io::Error> {
    let mut f = File::open(file)?;
    let mut text = String::new();
    f.read_to_string(&mut text)?;

    let mut includes = Vec::new();

    // Use a regex to search for '#include ...' lines.
    // The second (...) capture group isolates just the text, not the "" or <> symbols.
    lazy_static! {
        static ref RE: Regex = Regex::new(r##"#include ([<|"])(.*)[>|"]"##).unwrap();
    }

    // cap.at(1) is an angle brace or double quote, to determine user or system include.
    // cap.at(2) is the include file name.
    for cap in RE.captures_iter(&text) {
        let inc_symbol = cap.at(1).unwrap_or("<");
        let is_system_include = inc_symbol == "<";
        match cap.at(2) {
            Some(include_name) => {
                includes.push(Include::new_relative(include_name, is_system_include))
            }
            None => {}
        }
    }

    Ok(includes)
}

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

fn main() {
    // TODO: accept extra include paths.
    let args = App::new("IncludeGraph-rs")
        .version("0.1.0")
        .about("Generates an include graph from a tree of C++ files")
//        .arg(Arg::with_name("output_format")
//            .help("The output format to produce: PDF, DOT, PNG")
//            .value_name("OUTPUT")
//            .short("o"))
        .arg(Arg::with_name("source")
            .help("The source directory to scan")
            .value_name("PATH")
            .required(true)
            .multiple(false)
            .index(1))
        .get_matches();

    let root_dir_string = match args.value_of("source") {
        Some(path) => path,
        None => panic!("Unable to parse directory argument."),
    };

    println!("Scanning path: {}", root_dir_string);
    let root_dir = Path::new(root_dir_string);

    if !root_dir.exists() {
        println!("Unable to access directory: {}", root_dir.display());
    }

    // Create a list of default system include paths.
    let mut search_paths = Vec::new();
    search_paths.push(PathBuf::from(r"C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\include"));

    // Restrict the file extensions to search.
    let mut extensions = HashSet::new();
    extensions.insert(OsStr::new("h"));
    extensions.insert(OsStr::new("cpp"));
    extensions.insert(OsStr::new("hpp"));

    let mut input_queue: HashSet<String> = HashSet::new();

    // Collect all the files to scan
    let walker = WalkDir::new(root_dir).into_iter();
    for entry in walker.filter_entry(|e| !is_hidden(e)) {
        let entry = entry.unwrap(); // What error values could there be?
        let path = entry.path().canonicalize().unwrap();

        // If the file extension matches our set, queue full path for processing.
        match path.extension() {
            Some(ext) => {
                if extensions.contains(ext) {
                    input_queue.insert(path.to_str().unwrap().to_owned());
                }
            }
            None => {}
        }
    }

    // Graph of all the tracked files
    let mut graph = Graph::<FileNode, bool>::new();
    let mut indices = HashMap::<FileNode, NodeIndex>::new();

    for path_str in input_queue {
        let path = Path::new(&path_str);
        let includes_result = scan_file_for_includes(path);
        match includes_result {
            Ok(includes) => {
                let local_dir = path.parent().unwrap(); // strip the file name

                // Convert relative includes to absolute includes
                let absolute_includes: Vec<_> = includes.iter()
                    .map(|inc| find_absolute_include_path(inc, local_dir, &search_paths))
                    .collect();

                for inc in absolute_includes {

                    // Get an existing NodeIndex from the graph, on create a new node.
                    let src_node = FileNode{path: PathBuf::from(path), is_system: false};
                    let dst_node = FileNode{path: PathBuf::from(inc.path), is_system: false};

                    // These functions should work, but currently create duplicate nodes.
                    let src_node_idx = indices.entry(src_node.clone())
                        .or_insert_with(|| graph.add_node(src_node))
                        .clone();
                    let dst_node_idx = indices.entry(dst_node.clone())
                        .or_insert_with(|| graph.add_node(dst_node))
                        .clone();

                    //let src_str: OsString = path.as_os_str().to_owned();
//                    let dst_str: OsString = inc.path.as_os_str().to_owned();
//
//                    // These functions should work, but currently create duplicate nodes.
//                    let src_node_idx = indices.entry(src_str.clone())
//                        .or_insert_with(|| graph.add_node(src_str.clone()))
//                        .clone();
//                    let dst_node_idx = indices.entry(dst_str.clone())
//                        .or_insert_with(|| graph.add_node(dst_str.clone()))
//                        .clone();

//                    println!("Node {:?}: {:?}", src_node, src_str);
//                    println!("Node {:?}: {:?}", dst_node, dst_str);
//                    println!("Node count is now {}\n", graph.node_count());

                    graph.add_edge(src_node_idx, dst_node_idx, true);
                }
            }
            Err(err) => {
                println!("Unable to process file {:?}: {}", path, err);
            }
        };
    }

    //    // Print all the includes
    //    for node_idx in graph.node_indices() {
    //        for edge in graph.edges(node_idx) {
    //            let src_str = graph.index(edge.source());
    //            let dst_str = graph.index(edge.target());
    //            println!("{:?} -> {:?}", src_str, dst_str);
    //        }
    //    }

    // Write output to file graph.dot
    let out_path = Path::new("./graph.dot");
    let out_path_display = out_path.display();

    let mut dotfile = match File::create(&out_path) {
        Ok(file) => file,
        Err(why) => panic!("couldn't create {}: {}", out_path_display, why.description()),
    };

    writeln!(&mut dotfile,
             "{}",
             Dot::with_config(&graph, &[Config::EdgeNoLabel]));

    println!("Wrote graph to {}", out_path_display);
    println!("Run 'dot -Tpdf graph.dot > graph.pdf' to create a graph.");
}
<|endoftext|>


<commit_before>extern crate base64;
extern crate blake2_rfc;
extern crate humansize;
extern crate linear_map;
extern crate twox_hash;
extern crate walkdir;

// use blake2_rfc::blake2b::Blake2b;
use humansize::{FileSize, file_size_opts as options};
use linear_map::LinearMap;
use std::{env, io};
use std::io::Read;
use std::fs::File;
use std::hash::Hasher;
use std::collections::BTreeMap;
use std::path::PathBuf;
use walkdir::WalkDir;

fn copy<R: io::Read, W: Hasher>(reader: &mut R, writer: &mut W) -> io::Result<u64> {
    let mut buf = [0; 64 * 1024];
    let mut written = 0;
    loop {
        let len = match reader.read(&mut buf) {
            Ok(0) => return Ok(written),
            Ok(len) => len,
            Err(ref e) if e.kind() == io::ErrorKind::Interrupted => continue,
            Err(e) => return Err(e),
        };
        writer.write(&buf[..len]);
        written += len as u64;
    }
}

fn to_hash(path: &PathBuf) -> u64 {
    let mut file = File::open(path).unwrap();
    let mut hasher = twox_hash::XxHash::default();
    copy(&mut file, &mut hasher).unwrap();
    hasher.finish()
}

fn read_file(path: &PathBuf) -> Vec<u8> {
    let mut file = File::open(path).unwrap();
    let mut vec = Vec::new();
    file.read_to_end(&mut vec).unwrap();
    vec
}

fn partition_by<I, F, R>(paths: I, f: F) -> LinearMap<R, Vec<PathBuf>>
    where I: Iterator<Item=PathBuf>, F: Fn(&PathBuf) -> R, R: Eq
{
    let mut map = LinearMap::with_capacity(paths.size_hint().0);
    for path in paths {
        let key = f(&path);
        map.entry(key).or_insert_with(Vec::new).push(path);
    }
    map
}

fn main() {
    let path = env::args().nth(1).expect("paramater");

    let mut paths_by_size = BTreeMap::new();

    let mut total_files = 0;

    for entry in WalkDir::new(path) {
        let entry = entry.unwrap();
        if entry.file_type().is_dir() {
            continue
        }

        let file_size = entry.metadata().unwrap().len() as usize;
        paths_by_size.entry(file_size).or_insert_with(Vec::new).push(entry.path().to_owned());

        total_files += 1;
        if total_files % 10000 == 0 {
            println!("Handled {} files", total_files);
        }
    }

    println!("Handled {} files", total_files);
    println!();

    let mut total_wasted_size = 0;

    for (file_size, paths) in paths_by_size {
        if paths.len() == 1 {
            continue
        }

        let by_hash = partition_by(paths.into_iter(), to_hash);

        for (hash, paths) in by_hash {
            if paths.len() == 1 {
                continue
            }

            let by_contents = partition_by(paths.into_iter(), read_file);

            for (_, paths) in by_contents {
                if paths.len() == 1 {
                    continue
                }

                print!("Duplicate {} paths (hash: {})\n", paths.len(), hash);
                for path in &paths {
                    print!("  {}\n", path.display());
                }

                let wasted = file_size * (paths.len() - 1);
                print!(
                    " Size: {}. Wasting {}.\n",
                    file_size.file_size(options::CONVENTIONAL).unwrap(),
                    wasted.file_size(options::CONVENTIONAL).unwrap(),
                );

                total_wasted_size += wasted;
            }
        }
    }

    println!();
    println!("Total wasted size: {}", total_wasted_size.file_size(options::CONVENTIONAL).unwrap());
}
<commit_msg>Add LICENSE header<commit_after>//    Copyright 2017 Vector Creations Limited
//
//    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 base64;
extern crate blake2_rfc;
extern crate humansize;
extern crate linear_map;
extern crate twox_hash;
extern crate walkdir;

// use blake2_rfc::blake2b::Blake2b;
use humansize::{FileSize, file_size_opts as options};
use linear_map::LinearMap;
use std::{env, io};
use std::io::Read;
use std::fs::File;
use std::hash::Hasher;
use std::collections::BTreeMap;
use std::path::PathBuf;
use walkdir::WalkDir;

fn copy<R: io::Read, W: Hasher>(reader: &mut R, writer: &mut W) -> io::Result<u64> {
    let mut buf = [0; 64 * 1024];
    let mut written = 0;
    loop {
        let len = match reader.read(&mut buf) {
            Ok(0) => return Ok(written),
            Ok(len) => len,
            Err(ref e) if e.kind() == io::ErrorKind::Interrupted => continue,
            Err(e) => return Err(e),
        };
        writer.write(&buf[..len]);
        written += len as u64;
    }
}

fn to_hash(path: &PathBuf) -> u64 {
    let mut file = File::open(path).unwrap();
    let mut hasher = twox_hash::XxHash::default();
    copy(&mut file, &mut hasher).unwrap();
    hasher.finish()
}

fn read_file(path: &PathBuf) -> Vec<u8> {
    let mut file = File::open(path).unwrap();
    let mut vec = Vec::new();
    file.read_to_end(&mut vec).unwrap();
    vec
}

fn partition_by<I, F, R>(paths: I, f: F) -> LinearMap<R, Vec<PathBuf>>
    where I: Iterator<Item=PathBuf>, F: Fn(&PathBuf) -> R, R: Eq
{
    let mut map = LinearMap::with_capacity(paths.size_hint().0);
    for path in paths {
        let key = f(&path);
        map.entry(key).or_insert_with(Vec::new).push(path);
    }
    map
}

fn main() {
    let path = env::args().nth(1).expect("paramater");

    let mut paths_by_size = BTreeMap::new();

    let mut total_files = 0;

    for entry in WalkDir::new(path) {
        let entry = entry.unwrap();
        if entry.file_type().is_dir() {
            continue
        }

        let file_size = entry.metadata().unwrap().len() as usize;
        paths_by_size.entry(file_size).or_insert_with(Vec::new).push(entry.path().to_owned());

        total_files += 1;
        if total_files % 10000 == 0 {
            println!("Handled {} files", total_files);
        }
    }

    println!("Handled {} files", total_files);
    println!();

    let mut total_wasted_size = 0;

    for (file_size, paths) in paths_by_size {
        if paths.len() == 1 {
            continue
        }

        let by_hash = partition_by(paths.into_iter(), to_hash);

        for (hash, paths) in by_hash {
            if paths.len() == 1 {
                continue
            }

            let by_contents = partition_by(paths.into_iter(), read_file);

            for (_, paths) in by_contents {
                if paths.len() == 1 {
                    continue
                }

                print!("Duplicate {} paths (hash: {})\n", paths.len(), hash);
                for path in &paths {
                    print!("  {}\n", path.display());
                }

                let wasted = file_size * (paths.len() - 1);
                print!(
                    " Size: {}. Wasting {}.\n",
                    file_size.file_size(options::CONVENTIONAL).unwrap(),
                    wasted.file_size(options::CONVENTIONAL).unwrap(),
                );

                total_wasted_size += wasted;
            }
        }
    }

    println!();
    println!("Total wasted size: {}", total_wasted_size.file_size(options::CONVENTIONAL).unwrap());
}
<|endoftext|>


<commit_before>//! Muxed. A tmux project manager with no runtime dependencies.
extern crate common;
extern crate docopt;
extern crate edit;
extern crate load;
extern crate new;
extern crate snapshot;

use common::args::Args;
use docopt::Docopt;
use std::env;
use std::process::exit;

#[macro_export]
macro_rules! try_or_err (
    ($expr: expr) => ({
        match $expr {
            Ok(val) => val,
            Err(e) => {
                println!("Muxed ran in to a problem: {}", e);
                exit(1);
            }
        }
    })
);

static DISALLOWED_SHORTHAND_PROJECT_NAMES: [&str; 4] = ["new", "edit", "load", "snapshot"];

static USAGE: &str = "
Usage:
    muxed [options] <project>
    muxed edit [options] <project>
    muxed new [options] <project>
    muxed snapshot [options] <project>
    muxed load [options] <project>
    muxed (-h | --help)
    muxed (-v | --version)

Flags:
    -d                  If you want to create a muxed session without connecting to it
    -f                  Overwrite existing file if one exists
    --debug             Prints debug information while executing (project opening only)
    -h, --help          Prints help information
    -v, --version       Prints version information

Options:
    -p <project_dir>    The directory your project config files live in. Defaults to ~/.muxed/
    -t <session>        The name of the running TMUX session to codify

Args:
    <project>           The name of your project to open

Subcommands:
    edit <project>                   Edit an existing project file
    new <project>                    To create a new project file
    snapshot -t <session> <project>  Capture a running session and create a config file for it
";

/// The main execution method.
/// Currently accepts a single option. The option represents a configuration
/// file in the same naming format. Given a project file name `projectName.yml`
/// in the `~/.muxed/` directory.
///
/// # Examples
///
/// ~/.muxed/projectName.yml
///
/// ```
/// root: ~/projects/muxed/
/// windows:
///     - cargo: "cargo build"
///     - vim: "vim ."
///     - git: ""
/// ```
///
/// You can run the command:
///
/// ```
/// $ ./muxed projectName
/// ```
pub fn main() {
    let args: Args = Docopt::new(USAGE)
        .and_then(|d| d.deserialize())
        .unwrap_or_else(|e| e.exit());

    if args.flag_v {
        println!("Muxed {}", env!("CARGO_PKG_VERSION"));
        exit(0);
    };

    if args.cmd_new {
        try_or_err!(new::exec(args))
    } else if args.cmd_edit {
        try_or_err!(edit::exec(args))
    } else if args.cmd_snapshot {
        try_or_err!(snapshot::exec(args))
    } else if args.cmd_load {
        try_or_err!(load::exec(args))
    } else {
        if DISALLOWED_SHORTHAND_PROJECT_NAMES.contains(&args.arg_project.as_ref()) {
            println!(
                "Tried to call sub-command '{}' without a project. Please specify a project name.",
                args.arg_project
            );
            exit(1);
        }

        try_or_err!(load::exec(args))
    }
}
<commit_msg>Add documentation about load command<commit_after>//! Muxed. A tmux project manager with no runtime dependencies.
extern crate common;
extern crate docopt;
extern crate edit;
extern crate load;
extern crate new;
extern crate snapshot;

use common::args::Args;
use docopt::Docopt;
use std::env;
use std::process::exit;

#[macro_export]
macro_rules! try_or_err (
    ($expr: expr) => ({
        match $expr {
            Ok(val) => val,
            Err(e) => {
                println!("Muxed ran in to a problem: {}", e);
                exit(1);
            }
        }
    })
);

static DISALLOWED_SHORTHAND_PROJECT_NAMES: [&str; 4] = ["new", "edit", "load", "snapshot"];

static USAGE: &str = "
Usage:
    muxed [options] <project>
    muxed edit [options] <project>
    muxed new [options] <project>
    muxed snapshot [options] <project>
    muxed load [options] <project>
    muxed (-h | --help)
    muxed (-v | --version)

Flags:
    -d                  If you want to create a muxed session without connecting to it
    -f                  Overwrite existing file if one exists
    --debug             Prints debug information while executing (project opening only)
    -h, --help          Prints help information
    -v, --version       Prints version information

Options:
    -p <project_dir>    The directory your project config files live in. Defaults to ~/.muxed/
    -t <session>        The name of the running TMUX session to codify

Args:
    <project>           The name of your project to open

Subcommands:
    edit <project>                   Edit an existing project file
    new <project>                    To create a new project file
    snapshot -t <session> <project>  Capture a running session and create a config file for it
    load                             Load the specified project, this is the default command
";

/// The main execution method.
/// Currently accepts a single option. The option represents a configuration
/// file in the same naming format. Given a project file name `projectName.yml`
/// in the `~/.muxed/` directory.
///
/// # Examples
///
/// ~/.muxed/projectName.yml
///
/// ```
/// root: ~/projects/muxed/
/// windows:
///     - cargo: "cargo build"
///     - vim: "vim ."
///     - git: ""
/// ```
///
/// You can run the command:
///
/// ```
/// $ ./muxed projectName
/// ```
pub fn main() {
    let args: Args = Docopt::new(USAGE)
        .and_then(|d| d.deserialize())
        .unwrap_or_else(|e| e.exit());

    if args.flag_v {
        println!("Muxed {}", env!("CARGO_PKG_VERSION"));
        exit(0);
    };

    if args.cmd_new {
        try_or_err!(new::exec(args))
    } else if args.cmd_edit {
        try_or_err!(edit::exec(args))
    } else if args.cmd_snapshot {
        try_or_err!(snapshot::exec(args))
    } else if args.cmd_load {
        try_or_err!(load::exec(args))
    } else {
        if DISALLOWED_SHORTHAND_PROJECT_NAMES.contains(&args.arg_project.as_ref()) {
            println!(
                "Tried to call sub-command '{}' without a project. Please specify a project name.",
                args.arg_project
            );
            exit(1);
        }

        try_or_err!(load::exec(args))
    }
}
<|endoftext|>