<commit_before>#[macro_use]
extern crate glium;
extern crate clock_ticks;
extern crate nalgebra;
extern crate rustc_serialize;

use std::f32;
use glium::Surface;
use glium::glutin;
use glium::glutin::*;
use glium::index::PrimitiveType;
use nalgebra::Vec2;

mod settings;
use settings::*;

struct Ship {
    pub rotation         : f32,
    pub position         : Vec2<f32>,
    pub velocity         : Vec2<f32>,
    pub prev_position    : Vec2<f32>,
    pub prev_rotation    : f32,
}

struct LocalPlayer {
    ship                 : Option<Ship>,
    pub left_is_pressed  : bool,
    pub right_is_pressed : bool,
    pub up_is_pressed    : bool,
    pub left_key         : VirtualKeyCode,
    pub right_key        : VirtualKeyCode,
    pub up_key           : VirtualKeyCode,
}

impl LocalPlayer {
    fn new(left_key : VirtualKeyCode, right_key : VirtualKeyCode, up_key : VirtualKeyCode) -> Self {
        LocalPlayer {
            ship             : None,
            left_is_pressed  : false,
            right_is_pressed : false,
            up_is_pressed    : false,
            left_key         : left_key,
            right_key        : right_key,
            up_key           : up_key,
        }
    }

    fn ship(&self) -> &Option<Ship> {
        &self.ship
    }

    fn spawn(&mut self) -> Result<(), ()> {
        match self.ship {
            None => { self.ship = Some(Ship::new()); Ok(()) },
            _    => Err(()),
        }
    }

    fn on_key(&mut self, key: VirtualKeyCode, pressed: bool) -> bool {
        if      key == self.left_key  { self.left_is_pressed  = pressed; true }
        else if key == self.right_key { self.right_is_pressed = pressed; true }
        else if key == self.up_key    { self.up_is_pressed    = pressed; true }
        else { false }
    }
}

impl Ship {
    fn new() -> Ship {
        Ship {
            rotation         : 0f32,
            position         : Vec2::new(0.3f32, 0.1f32),
            velocity         : Vec2::new(0.0f32, 0.0f32),
            prev_position    : Vec2::new(0.3f32, 0.1f32),
            prev_rotation    : 0f32,
        }
    }
}

fn main() {
    use glium::DisplayBuild;

    let settings = load_settings("settings.json");

    let display = {
        let mut display_builder = glutin::WindowBuilder::new();

        if settings.fullscreen {
            display_builder = display_builder.with_fullscreen(glutin::get_primary_monitor());
        }

        display_builder.build_glium().unwrap()
    };

    let index_buffer = glium::IndexBuffer::new(&display, PrimitiveType::TrianglesList,
                                               &[0, 1, 2,
                                                 3, 4, 5u16]).unwrap();

    let program = program!(&display,
        140 => {
            vertex: "
                #version 140

                uniform mat4 matrix;

                in vec2 position;
                in vec2 global_position;
                in vec3 color;
                in float rotation;

                out vec3 vColor;

                void main() {
                    float cos_rotation = cos(rotation);
                    float sin_rotation = sin(rotation);
                    gl_Position = vec4(
                        position.x * cos_rotation - position.y * sin_rotation + global_position.x,
                        position.x * sin_rotation + position.y * cos_rotation + global_position.y,
                        0.0,
                        1.0) * matrix;
                    vColor = color;
                }
            ",

            fragment: "
                #version 140
                in vec3 vColor;
                out vec4 f_color;

                void main() {
                    f_color = vec4(vColor, 1.0);
                }
            "
        },
    ).unwrap();

    let mut accumulator = 0;
    let mut previous_clock = clock_ticks::precise_time_ns();

    let mut frames = 0;
    let mut fpses = Vec::new();

    let mut do_clear = true;

    let uniforms = uniform! {
        matrix: [
            [0.4, 0.0, 0.0, 0.0],
            [0.0, 0.5, 0.0, 0.0],
            [0.0, 0.0, 1.0, 0.0],
            [0.0, 0.0, 0.0, 1.0f32]
        ]
    };

    let mut player = LocalPlayer::new(VirtualKeyCode::Left,
                                      VirtualKeyCode::Right,
                                      VirtualKeyCode::Up);

    player.spawn().unwrap();

    loop {
        let mut target = display.draw();

        if do_clear {
            target.clear_color(0.0, 0.0, 0.0, 0.0);
        }

        let vertex_buffer = {
            #[derive(Copy, Clone)]
            struct Vertex {
                position: [f32; 2],
                color: [f32; 3],
                rotation: f32,
                global_position: [f32; 2],
            }

            implement_vertex!(Vertex, position, color, rotation, global_position);

            let mut vertices = Vec::new();

            match *player.ship() {
                None => {}
                Some(ref ship) => {
                    vertices.push(Vertex { position: [-0.05, -0.025], color: [0.3, 0.3, 0.3], rotation: ship.prev_rotation, global_position: *ship.prev_position.as_array() });
                    vertices.push(Vertex { position: [ 0.05,  0.000], color: [0.3, 0.3, 0.3], rotation: ship.prev_rotation, global_position: *ship.prev_position.as_array() });
                    vertices.push(Vertex { position: [-0.05,  0.025], color: [0.3, 0.3, 0.3], rotation: ship.prev_rotation, global_position: *ship.prev_position.as_array() });
                    vertices.push(Vertex { position: [-0.05, -0.025], color: [1.0, 1.0, 1.0], rotation: ship.rotation     , global_position: *ship.position.as_array() });
                    vertices.push(Vertex { position: [ 0.05,  0.000], color: [1.0, 1.0, 1.0], rotation: ship.rotation     , global_position: *ship.position.as_array() });
                    vertices.push(Vertex { position: [-0.05,  0.025], color: [1.0, 1.0, 1.0], rotation: ship.rotation     , global_position: *ship.position.as_array() });
                }
            };

            glium::VertexBuffer::new(&display, &vertices).unwrap()
        };

        target.draw(&vertex_buffer, &index_buffer, &program, &uniforms, &Default::default()).unwrap();

        target.finish().unwrap();

        for event in display.poll_events() {
            match event {
                glutin::Event::Closed => return,
                glutin::Event::KeyboardInput(ElementState::Pressed, _, Some(VirtualKeyCode::C)) => { do_clear = !do_clear },
                glutin::Event::KeyboardInput(ElementState::Pressed, _, Some(VirtualKeyCode::Escape)) => return,

                glutin::Event::KeyboardInput(pressed, _, Some(key)) => {
                    let is_pressed = pressed == ElementState::Pressed;
                    let handled = player.on_key(key, is_pressed);

                    if !handled && is_pressed {
                        println!("Key pressed but not handled: {:?}", key);
                    }
                },

                _ => ()
            }
        }

        let now = clock_ticks::precise_time_ns();

        if settings.print_fps {
            frames += 1;

            if previous_clock / 1_000_000_000 < now / 1_000_000_000 {
                fpses.push(frames);
                frames = 0;

                if fpses.len() == 5 {
                    for fps in fpses.iter() {
                        println!("FPS {}", fps);
                    }
                    fpses.clear();
                }
            }
        }

        accumulator += now - previous_clock;
        previous_clock = now;

        const FIXED_TIME_STAMP: u64 = 1_000_000; // = 1 millisecond
        while accumulator >= FIXED_TIME_STAMP {
            accumulator -= FIXED_TIME_STAMP;

            match *player.ship() {
                None => {}
                Some(ref mut ship) => {
                    ship.prev_position = ship.position;
                    ship.prev_rotation = ship.rotation;

                    if player.left_is_pressed {
                        ship.rotation += settings.rotation_speed;
                    }
                    if player.right_is_pressed {
                        ship.rotation -= settings.rotation_speed;
                    }
                    let acceleration = if player.up_is_pressed { settings.acceleration } else { 0f32 };
                    let direction = Vec2::new(f32::cos(ship.rotation), f32::sin(ship.rotation));
                    ship.velocity = (ship.velocity + direction * acceleration) * settings.drag;
                    ship.position = ship.position + ship.velocity;
                }
            }
        }
    }

}

<commit_msg>Two players!!!!<commit_after>#[macro_use]
extern crate glium;
extern crate clock_ticks;
extern crate nalgebra;
extern crate rustc_serialize;

use std::f32;
use glium::Surface;
use glium::glutin;
use glium::glutin::*;
use glium::index::PrimitiveType;
use nalgebra::Vec2;

mod settings;
use settings::*;

struct Ship {
    pub rotation         : f32,
    pub position         : Vec2<f32>,
    pub velocity         : Vec2<f32>,
    pub prev_position    : Vec2<f32>,
    pub prev_rotation    : f32,
}

struct LocalPlayer {
    ship                 : Option<Ship>,
    pub left_is_pressed  : bool,
    pub right_is_pressed : bool,
    pub up_is_pressed    : bool,
    pub left_key         : VirtualKeyCode,
    pub right_key        : VirtualKeyCode,
    pub up_key           : VirtualKeyCode,
}

impl LocalPlayer {
    fn new(left_key : VirtualKeyCode, right_key : VirtualKeyCode, up_key : VirtualKeyCode) -> Self {
        LocalPlayer {
            ship             : None,
            left_is_pressed  : false,
            right_is_pressed : false,
            up_is_pressed    : false,
            left_key         : left_key,
            right_key        : right_key,
            up_key           : up_key,
        }
    }

    fn ship(&self) -> &Option<Ship> {
        &self.ship
    }

    fn spawn(&mut self) -> Result<(), ()> {
        match self.ship {
            None => { self.ship = Some(Ship::new()); Ok(()) },
            _    => Err(()),
        }
    }

    fn on_key(&mut self, key: VirtualKeyCode, pressed: bool) -> bool {
        if      key == self.left_key  { self.left_is_pressed  = pressed; true }
        else if key == self.right_key { self.right_is_pressed = pressed; true }
        else if key == self.up_key    { self.up_is_pressed    = pressed; true }
        else { false }
    }
}

impl Ship {
    fn new() -> Ship {
        Ship {
            rotation         : 0f32,
            position         : Vec2::new(0.3f32, 0.1f32),
            velocity         : Vec2::new(0.0f32, 0.0f32),
            prev_position    : Vec2::new(0.3f32, 0.1f32),
            prev_rotation    : 0f32,
        }
    }
}

fn main() {
    use glium::DisplayBuild;

    let settings = load_settings("settings.json");

    let display = {
        let mut display_builder = glutin::WindowBuilder::new();

        if settings.fullscreen {
            display_builder = display_builder.with_fullscreen(glutin::get_primary_monitor());
        }

        display_builder.build_glium().unwrap()
    };

    let program = program!(&display,
        140 => {
            vertex: "
                #version 140

                uniform mat4 matrix;

                in vec2 position;
                in vec2 global_position;
                in vec3 color;
                in float rotation;

                out vec3 vColor;

                void main() {
                    float cos_rotation = cos(rotation);
                    float sin_rotation = sin(rotation);
                    gl_Position = vec4(
                        position.x * cos_rotation - position.y * sin_rotation + global_position.x,
                        position.x * sin_rotation + position.y * cos_rotation + global_position.y,
                        0.0,
                        1.0) * matrix;
                    vColor = color;
                }
            ",

            fragment: "
                #version 140
                in vec3 vColor;
                out vec4 f_color;

                void main() {
                    f_color = vec4(vColor, 1.0);
                }
            "
        },
    ).unwrap();

    let mut accumulator = 0;
    let mut previous_clock = clock_ticks::precise_time_ns();

    let mut frames = 0;
    let mut fpses = Vec::new();

    let mut do_clear = true;

    let uniforms = uniform! {
        matrix: [
            [0.4, 0.0, 0.0, 0.0],
            [0.0, 0.5, 0.0, 0.0],
            [0.0, 0.0, 1.0, 0.0],
            [0.0, 0.0, 0.0, 1.0f32]
        ]
    };

    let mut players = Vec::new();

    players.push(LocalPlayer::new(VirtualKeyCode::Left,
                                  VirtualKeyCode::Right,
                                  VirtualKeyCode::Up));

    players.push(LocalPlayer::new(VirtualKeyCode::A,
                                  VirtualKeyCode::D,
                                  VirtualKeyCode::W));

    for player in players.iter_mut() {
        player.spawn().unwrap();
    }

    loop {
        let mut target = display.draw();

        if do_clear {
            target.clear_color(0.0, 0.0, 0.0, 0.0);
        }

        let (vertex_buffer, index_buffer) = {
            #[derive(Copy, Clone)]
            struct Vertex {
                position: [f32; 2],
                color: [f32; 3],
                rotation: f32,
                global_position: [f32; 2],
            }

            implement_vertex!(Vertex, position, color, rotation, global_position);

            let mut vertices = Vec::new();
            let mut indices = Vec::new();

            for player in players.iter() {
                match *player.ship() {
                    None => {}
                    Some(ref ship) => {
                        let base_index = vertices.len() as u16;
                        for i in 0..6 {
                            indices.push(base_index + i);
                        }

                        vertices.push(Vertex { position: [-0.05, -0.025], color: [1.0, 0.3, 0.3], rotation: ship.prev_rotation, global_position: *ship.prev_position.as_array() });
                        vertices.push(Vertex { position: [ 0.05,  0.000], color: [1.0, 0.3, 0.3], rotation: ship.prev_rotation, global_position: *ship.prev_position.as_array() });
                        vertices.push(Vertex { position: [-0.05,  0.025], color: [1.0, 0.3, 0.3], rotation: ship.prev_rotation, global_position: *ship.prev_position.as_array() });
                        vertices.push(Vertex { position: [-0.05, -0.025], color: [1.0, 1.0, 1.0], rotation: ship.rotation     , global_position: *ship.position.as_array() });
                        vertices.push(Vertex { position: [ 0.05,  0.000], color: [1.0, 1.0, 1.0], rotation: ship.rotation     , global_position: *ship.position.as_array() });
                        vertices.push(Vertex { position: [-0.05,  0.025], color: [1.0, 1.0, 1.0], rotation: ship.rotation     , global_position: *ship.position.as_array() });
                    }
                };
            }

            let vertex_buffer = glium::VertexBuffer::new(&display, &vertices).unwrap();
            let index_buffer = glium::IndexBuffer::new(&display, PrimitiveType::TrianglesList, &indices).unwrap();

            (vertex_buffer, index_buffer)
        };

        target.draw(&vertex_buffer, &index_buffer, &program, &uniforms, &Default::default()).unwrap();

        target.finish().unwrap();

        for event in display.poll_events() {
            match event {
                glutin::Event::Closed => return,
                glutin::Event::KeyboardInput(ElementState::Pressed, _, Some(VirtualKeyCode::C)) => { do_clear = !do_clear },
                glutin::Event::KeyboardInput(ElementState::Pressed, _, Some(VirtualKeyCode::Escape)) => return,

                glutin::Event::KeyboardInput(pressed, _, Some(key)) => {
                    let is_pressed = pressed == ElementState::Pressed;
                    let handled = players.iter_mut().any(|player| player.on_key(key, is_pressed));

                    if !handled && is_pressed {
                        println!("Key pressed but not handled: {:?}", key);
                    }
                },

                _ => ()
            }
        }

        let now = clock_ticks::precise_time_ns();

        if settings.print_fps {
            frames += 1;

            if previous_clock / 1_000_000_000 < now / 1_000_000_000 {
                fpses.push(frames);
                frames = 0;

                if fpses.len() == 5 {
                    for fps in fpses.iter() {
                        println!("FPS {}", fps);
                    }
                    fpses.clear();
                }
            }
        }

        accumulator += now - previous_clock;
        previous_clock = now;

        const FIXED_TIME_STAMP: u64 = 1_000_000; // = 1 millisecond
        while accumulator >= FIXED_TIME_STAMP {
            accumulator -= FIXED_TIME_STAMP;

            for player in players.iter_mut() {
                match player.ship {
                    None => {}
                    Some(ref mut ship) => {
                        ship.prev_position = ship.position;
                        ship.prev_rotation = ship.rotation;

                        if player.left_is_pressed {
                            ship.rotation += settings.rotation_speed;
                        }
                        if player.right_is_pressed {
                            ship.rotation -= settings.rotation_speed;
                        }
                        let acceleration = if player.up_is_pressed { settings.acceleration } else { 0f32 };
                        let direction = Vec2::new(f32::cos(ship.rotation), f32::sin(ship.rotation));
                        ship.velocity = (ship.velocity + direction * acceleration) * settings.drag;
                        ship.position = ship.position + ship.velocity;
                    }
                }
            }
        }
    }

}

<|endoftext|>


<commit_before>#![feature(slice_patterns)]
#![feature(nll)]

extern crate getopts;

use std::fs;
use std::io;
use std::io::{Read, Write};

use getopts::Options;

mod optimizer;
mod parser;
mod runner;
mod structs;

use parser::parse;
use runner::State;
use structs::OpStream;

fn main() {
    let mut opts = Options::new();
    opts.optflag("n", "dry-run", "don't actually run");
    opts.optflag("0", "no-optimize", "don't optimize");
    opts.optflag("h", "help", "print this help menu");

    let matches = opts.parse(std::env::args().skip(1)).unwrap_or_else(|f| {
        writeln!(&mut io::stderr(), "{}", f).unwrap();
        std::process::exit(2)
    });
    if matches.opt_present("h") {
        write!(&mut io::stderr(), "{}", opts.usage("Usage: brain_rust [options] FILE... ")).unwrap();
        std::process::exit(2);
    }

    let dry_run = matches.opt_present("n");
    let no_optimize = matches.opt_present("0");

    for filename in matches.free {
        let buffer = match read_file(&filename) {
            Ok(v) => v,
            Err(e) => {
                writeln!(&mut io::stderr(), "Error while reading {}: {}", filename, e).unwrap();
                continue;
            }
        };
        let ops = match parse(&buffer[..]) {
            Ok(v) => v,
            Err(e) => {
                writeln!(&mut io::stderr(), "Error while parsing {}: {}", filename, e).unwrap();
                continue;
            }
        };
        let mut opstream = OpStream { ops };
        if !no_optimize {
            opstream.optimize();
        }
        if !dry_run {
            State::new(&mut io::stdin(), &mut io::stdout()).run(opstream.get());
        }
    }
}

fn read_file(filename: &str) -> Result<Vec<u8>, io::Error> {
    let mut buffer = Vec::new();
    fs::File::open(filename)?.read_to_end(&mut buffer)?;
    Ok(buffer)
}
<commit_msg>chore: Update to latest Rust version<commit_after>extern crate getopts;

use std::fs;
use std::io;
use std::io::{Read, Write};

use getopts::Options;

mod optimizer;
mod parser;
mod runner;
mod structs;

use parser::parse;
use runner::State;
use structs::OpStream;

fn main() {
    let mut opts = Options::new();
    opts.optflag("n", "dry-run", "don't actually run");
    opts.optflag("0", "no-optimize", "don't optimize");
    opts.optflag("h", "help", "print this help menu");

    let matches = opts.parse(std::env::args().skip(1)).unwrap_or_else(|f| {
        writeln!(&mut io::stderr(), "{}", f).unwrap();
        std::process::exit(2)
    });
    if matches.opt_present("h") {
        write!(&mut io::stderr(), "{}", opts.usage("Usage: brain_rust [options] FILE... ")).unwrap();
        std::process::exit(2);
    }

    let dry_run = matches.opt_present("n");
    let no_optimize = matches.opt_present("0");

    for filename in matches.free {
        let buffer = match read_file(&filename) {
            Ok(v) => v,
            Err(e) => {
                writeln!(&mut io::stderr(), "Error while reading {}: {}", filename, e).unwrap();
                continue;
            }
        };
        let ops = match parse(&buffer[..]) {
            Ok(v) => v,
            Err(e) => {
                writeln!(&mut io::stderr(), "Error while parsing {}: {}", filename, e).unwrap();
                continue;
            }
        };
        let mut opstream = OpStream { ops };
        if !no_optimize {
            opstream.optimize();
        }
        if !dry_run {
            State::new(&mut io::stdin(), &mut io::stdout()).run(opstream.get());
        }
    }
}

fn read_file(filename: &str) -> Result<Vec<u8>, io::Error> {
    let mut buffer = Vec::new();
    fs::File::open(filename)?.read_to_end(&mut buffer)?;
    Ok(buffer)
}
<|endoftext|>


<commit_before>#![deny(unused_must_use, unused_imports)]
#![deny(unused_parens, unused_variables, unused_mut)]
extern crate term;
extern crate git2;
extern crate libc;

use prompt_buffer::PromptBuffer;

use std::io::{
    Acceptor,
    Command,
    File,
    fs,
    Listener,
    process,
    stdio,
    timer,
    IoError,
    Timer
};
use std::time::Duration;
use std::io::fs::PathExtensions;
use std::io::net::pipe::{
    UnixStream,
    UnixListener,
};
use std::os;
use std::sync::mpsc::{self, Sender, Receiver};
use std::thread::Thread;
use std::collections::HashMap;

mod prompt_buffer;
mod git;
mod due_date;

fn get_prompt() -> PromptBuffer {
    let mut buf = PromptBuffer::new();
    buf.add_plugin(Box::new(due_date::DueDatePlugin::new()));
    buf.add_plugin(Box::new(git::GitPlugin::new()));

    buf
}

fn exe_changed() -> u64 {
    match os::self_exe_name() {
        Some(exe_path) => {
            match exe_path.stat() {
                Ok(s) => s.modified,
                _ => 0u64
            }
        },
        None => 0u64
    }
}

fn current_pid(pid_path: &Path) -> Result<i32, IoError> {
    let mut file = try!(File::open(pid_path));
    let contents = try!(file.read_to_string());

    match contents.parse() {
        Some(value) => Ok(value),
        None => Err(IoError::from_errno(libc::consts::os::posix88::ENOMSG as usize, true))
    }
}

macro_rules! sock_try {
    ($x:expr) => {
        match $x {
            Ok(v) => v,
            Err(_) => continue
        }
    };
}

struct PromptThread {
    send: Sender<()>,
    recv: Receiver<String>,
    death: Receiver<()>,
    path: Path,
    cached: String,
    alive: bool
}

impl PromptThread {
    fn new(path: Path) -> PromptThread {
        let (tx_notify, rx_notify) = mpsc::channel();
        let (tx_prompt, rx_prompt) = mpsc::channel();
        let (tx_death, rx_death) = mpsc::channel();

        let p = path.clone();
        Thread::spawn(move || {
            let mut prompt = get_prompt();
            prompt.set_path(p);
            let mut timer = Timer::new().unwrap();

            loop {
                let timeout = timer.oneshot(Duration::minutes(10));

                select! {
                    _ = rx_notify.recv() => {
                        tx_prompt.send(prompt.to_string()).unwrap();
                    },
                    _ = timeout.recv() => {
                        // Assume someone is listening for my death
                        // Otherwise it doesn't matter
                        tx_death.send(()).unwrap();
                        break;
                    }
                }
            }
        });

        PromptThread {
            send: tx_notify,
            recv: rx_prompt,
            death: rx_death,
            path: path,
            cached: get_prompt().to_string_ext(true),
            alive: true
        }
    }

    fn is_alive(&mut self) -> bool {
        if self.death.try_recv().is_ok() {
            self.alive = false;
        }

        self.alive
    }

    fn revive(&mut self) {
        let new = PromptThread::new(self.path.clone());
        self.send = new.send;
        self.recv = new.recv;
        self.death = new.death;
        self.path = new.path;
        self.cached = new.cached;
        self.alive = new.alive;
    }

    fn get(&mut self) -> String {
        if !self.is_alive() {
            self.revive();
        }

        self.send.send(()).unwrap();

        let mut timer = Timer::new().unwrap();
        let timeout = timer.oneshot(Duration::milliseconds(100));

        loop {
            let resp = self.recv.try_recv();

            if resp.is_ok() {
                // We got a good response
                let mut text = resp.unwrap();

                loop {
                    match self.recv.try_recv() {
                        Ok(t) => text = t,
                        Err(_) => break
                    }
                }

                self.cached = text;
                return self.cached.clone();
            }

            // We ran out of time!
            if timeout.try_recv().is_ok() {
                return self.cached.clone();
            }
        }
    }
}

fn main() {
    let stdout_path = Path::new("/var/log/megaprompt/current.out");
    let stderr_path = Path::new("/var/log/megaprompt/current.err");
    let pid_path = Path::new("/var/log/megaprompt/current.pid");
    let socket_path = Path::new("/tmp/megaprompt-socket");

    let args = os::args();
    if args.len() > 1 { // Daemon process
        stdio::set_stdout(Box::new(File::create(&stdout_path)));
        stdio::set_stderr(Box::new(File::create(&stderr_path)));

        let last_modified = exe_changed();
        let mut threads: HashMap<Path, PromptThread> = HashMap::new();

        if socket_path.exists() {
            fs::unlink(&socket_path).unwrap();
        }

        let stream = match UnixListener::bind(&socket_path) {
            Err(_) => panic!("Failed to bind to socket"),
            Ok(stream) => stream
        };

        for mut connection in stream.listen().incoming() {
            let c = &mut connection;

            let output = Path::new(sock_try!(c.read_to_string()));
            println!("Preparing to respond to for {}", output.display());

            let keys: Vec<Path> = threads.keys().map(|x| { x.clone() }).collect();
            for path in keys.iter() {
                if !threads.get_mut(path).unwrap().is_alive() {
                    println!("- Remove thread {}", path.display());
                    threads.remove(path);
                }
            }

            if !threads.contains_key(&output) {
                println!("+ Add thread {}", output.display());
                threads.insert(output.clone(), PromptThread::new(output.clone()));
            }

            for path in threads.keys() {
                println!("* Active thread {}", path.display());
            }

            let mut thr = threads.get_mut(&output).unwrap();

            sock_try!(write!(c, "{}", thr.get()));

            println!("");

            if last_modified != exe_changed() {
                sock_try!(write!(c, "♻  "));
                return;
            }
        }
    } else {
        let current_pid = current_pid(&pid_path).ok().unwrap_or(-1);

        match process::Process::kill(current_pid, 0) {
            Err(_) => {
                // We need to start up the daemon again
                let child = Command::new(args.get(0).as_slice()[0])
                    .arg("daemon")
                    .detached().spawn().unwrap();

                let mut f = match File::create(&pid_path) {
                    Ok(f) => f,
                    Err(_) => panic!("Unable to open pid file")
                };

                write!(&mut f, "{}", child.id()).unwrap();

                println!("Spawned child {}", child.id());

                child.forget();

                timer::sleep(Duration::milliseconds(10));
            }
            _ => {}
        }

        let mut stream = match UnixStream::connect(&socket_path) {
            Err(_) => {
                println!("Can't connect");
                get_prompt().print();
                return;
            },
            Ok(stream) => stream
        };

        write!(&mut stream, "{}", os::make_absolute(&Path::new(".")).unwrap().display()).unwrap();
        stream.close_write().unwrap();
        stream.set_read_timeout(Some(200));
        match stream.read_to_string() {
            Ok(s) => println!("{}", s),
            Err(_) => {
                println!("Response too slow");
                get_prompt().print_fast();
                return;
            }
        }
    }
}
<commit_msg>Simplify revive function<commit_after>#![deny(unused_must_use, unused_imports)]
#![deny(unused_parens, unused_variables, unused_mut)]
extern crate term;
extern crate git2;
extern crate libc;

use prompt_buffer::PromptBuffer;

use std::io::{
    Acceptor,
    Command,
    File,
    fs,
    Listener,
    process,
    stdio,
    timer,
    IoError,
    Timer
};
use std::time::Duration;
use std::io::fs::PathExtensions;
use std::io::net::pipe::{
    UnixStream,
    UnixListener,
};
use std::os;
use std::sync::mpsc::{self, Sender, Receiver};
use std::thread::Thread;
use std::collections::HashMap;

mod prompt_buffer;
mod git;
mod due_date;

fn get_prompt() -> PromptBuffer {
    let mut buf = PromptBuffer::new();
    buf.add_plugin(Box::new(due_date::DueDatePlugin::new()));
    buf.add_plugin(Box::new(git::GitPlugin::new()));

    buf
}

fn exe_changed() -> u64 {
    match os::self_exe_name() {
        Some(exe_path) => {
            match exe_path.stat() {
                Ok(s) => s.modified,
                _ => 0u64
            }
        },
        None => 0u64
    }
}

fn current_pid(pid_path: &Path) -> Result<i32, IoError> {
    let mut file = try!(File::open(pid_path));
    let contents = try!(file.read_to_string());

    match contents.parse() {
        Some(value) => Ok(value),
        None => Err(IoError::from_errno(libc::consts::os::posix88::ENOMSG as usize, true))
    }
}

macro_rules! sock_try {
    ($x:expr) => {
        match $x {
            Ok(v) => v,
            Err(_) => continue
        }
    };
}

struct PromptThread {
    send: Sender<()>,
    recv: Receiver<String>,
    death: Receiver<()>,
    path: Path,
    cached: String,
    alive: bool
}

impl PromptThread {
    fn new(path: Path) -> PromptThread {
        let (tx_notify, rx_notify) = mpsc::channel();
        let (tx_prompt, rx_prompt) = mpsc::channel();
        let (tx_death, rx_death) = mpsc::channel();

        let p = path.clone();
        Thread::spawn(move || {
            let mut prompt = get_prompt();
            prompt.set_path(p);
            let mut timer = Timer::new().unwrap();

            loop {
                let timeout = timer.oneshot(Duration::minutes(10));

                select! {
                    _ = rx_notify.recv() => {
                        tx_prompt.send(prompt.to_string()).unwrap();
                    },
                    _ = timeout.recv() => {
                        // Assume someone is listening for my death
                        // Otherwise it doesn't matter
                        tx_death.send(()).unwrap();
                        break;
                    }
                }
            }
        });

        PromptThread {
            send: tx_notify,
            recv: rx_prompt,
            death: rx_death,
            path: path,
            cached: get_prompt().to_string_ext(true),
            alive: true
        }
    }

    fn is_alive(&mut self) -> bool {
        if self.death.try_recv().is_ok() {
            self.alive = false;
        }

        self.alive
    }

    fn revive(&mut self) {
        *self = PromptThread::new(self.path.clone());
    }

    fn get(&mut self) -> String {
        if !self.is_alive() {
            self.revive();
        }

        self.send.send(()).unwrap();

        let mut timer = Timer::new().unwrap();
        let timeout = timer.oneshot(Duration::milliseconds(100));

        loop {
            let resp = self.recv.try_recv();

            if resp.is_ok() {
                // We got a good response
                let mut text = resp.unwrap();

                loop {
                    match self.recv.try_recv() {
                        Ok(t) => text = t,
                        Err(_) => break
                    }
                }

                self.cached = text;
                return self.cached.clone();
            }

            // We ran out of time!
            if timeout.try_recv().is_ok() {
                return self.cached.clone();
            }
        }
    }
}

fn main() {
    let stdout_path = Path::new("/var/log/megaprompt/current.out");
    let stderr_path = Path::new("/var/log/megaprompt/current.err");
    let pid_path = Path::new("/var/log/megaprompt/current.pid");
    let socket_path = Path::new("/tmp/megaprompt-socket");

    let args = os::args();
    if args.len() > 1 { // Daemon process
        stdio::set_stdout(Box::new(File::create(&stdout_path)));
        stdio::set_stderr(Box::new(File::create(&stderr_path)));

        let last_modified = exe_changed();
        let mut threads: HashMap<Path, PromptThread> = HashMap::new();

        if socket_path.exists() {
            fs::unlink(&socket_path).unwrap();
        }

        let stream = match UnixListener::bind(&socket_path) {
            Err(_) => panic!("Failed to bind to socket"),
            Ok(stream) => stream
        };

        for mut connection in stream.listen().incoming() {
            let c = &mut connection;

            let output = Path::new(sock_try!(c.read_to_string()));
            println!("Preparing to respond to for {}", output.display());

            let keys: Vec<Path> = threads.keys().map(|x| { x.clone() }).collect();
            for path in keys.iter() {
                if !threads.get_mut(path).unwrap().is_alive() {
                    println!("- Remove thread {}", path.display());
                    threads.remove(path);
                }
            }

            if !threads.contains_key(&output) {
                println!("+ Add thread {}", output.display());
                threads.insert(output.clone(), PromptThread::new(output.clone()));
            }

            for path in threads.keys() {
                println!("* Active thread {}", path.display());
            }

            let mut thr = threads.get_mut(&output).unwrap();

            sock_try!(write!(c, "{}", thr.get()));

            println!("");

            if last_modified != exe_changed() {
                sock_try!(write!(c, "♻  "));
                return;
            }
        }
    } else {
        let current_pid = current_pid(&pid_path).ok().unwrap_or(-1);

        match process::Process::kill(current_pid, 0) {
            Err(_) => {
                // We need to start up the daemon again
                let child = Command::new(args.get(0).as_slice()[0])
                    .arg("daemon")
                    .detached().spawn().unwrap();

                let mut f = match File::create(&pid_path) {
                    Ok(f) => f,
                    Err(_) => panic!("Unable to open pid file")
                };

                write!(&mut f, "{}", child.id()).unwrap();

                println!("Spawned child {}", child.id());

                child.forget();

                timer::sleep(Duration::milliseconds(10));
            }
            _ => {}
        }

        let mut stream = match UnixStream::connect(&socket_path) {
            Err(_) => {
                println!("Can't connect");
                get_prompt().print();
                return;
            },
            Ok(stream) => stream
        };

        write!(&mut stream, "{}", os::make_absolute(&Path::new(".")).unwrap().display()).unwrap();
        stream.close_write().unwrap();
        stream.set_read_timeout(Some(200));
        match stream.read_to_string() {
            Ok(s) => println!("{}", s),
            Err(_) => {
                println!("Response too slow");
                get_prompt().print_fast();
                return;
            }
        }
    }
}
<|endoftext|>


<commit_before>#![deny(missing_docs)]

//! Main driver application for the "dsp" library.

// PortAudio:
extern crate portaudio;
use portaudio as pa;

// Channels for buffering up samples in between threads:
extern crate chan;

// other `use` statements:
use std::thread;
use std::time;

// DSP module
// TODO: remove all these "use" statements in favor of explicitly calling them in main.rs
pub mod dsp;
use dsp::generators;
use dsp::negate_signal::NegateSignal;
use dsp::add_signals::AddSignals;
use dsp::evaluatable::Evaluatable;

// Constants:
const NUM_CHANNELS: i32 = 2;
const SAMPLE_RATE: f64 = 44100.0;
const FRAMES_PER_BUFFER: u32 = 1024;
const BUFFER_SECONDS: f64 = 0.100;  // Buffer samples for 100ms -- reduces chances of underrun
const FREQUENCY: f64 = 440.0;

fn main() {
    // Collect all our threads:
    let mut children = vec![];

    // Signal flow:
    // (signal generators + filters + combinations) --> (portaudio) --> (grapher - first 1000 samples)

    // Create a channel for (signals) --> (portaudio)
    let (send_audio, recv_audio) = chan::sync(SAMPLE_RATE as usize);

    // Create a channel for (portaudio) --> (grapher)
    let (send_points, recv_points) = chan::sync(SAMPLE_RATE as usize);

    // TODO: maybe split this out into different functions / modules

    // signal generators:
    children.push(thread::spawn(move || {
        // A-Minor chord
        let sine_generator1 = generators::Sine::new(SAMPLE_RATE, FREQUENCY * 1.0, 0.1);
        let sine_generator2 = generators::Sine::new(SAMPLE_RATE, FREQUENCY * 1.2 * 0.5, 0.1);
        let sine_generator3 = generators::Sine::new(SAMPLE_RATE, FREQUENCY * 1.5 * 0.5, 0.1);
        let add_vec: Vec<Box<Evaluatable>> = vec![Box::new(sine_generator1), Box::new(sine_generator2), Box::new(sine_generator3)];

        let mut add_signals = AddSignals::new(add_vec);
        loop {
            send_audio.send(add_signals.evaluate());
        }
    }));

    // PortAudio:
    children.push(thread::spawn(move || {
        thread::sleep(time::Duration::new(0, 1_000_000));
        run(recv_audio, send_points).unwrap()
    }));

    // Grapher:
    children.push(thread::spawn(move || {
        let mut x = vec![];
        let mut y = vec![];

        let period = SAMPLE_RATE / FREQUENCY;

        for i in 0..((period*50.0) as usize) {
            let sample = recv_points.recv().unwrap();
            x.push(i as f32);
            y.push(sample.0);
        }

        // TODO: all we need is a plotting library to plot here...

        // TODO: Just close the channel instead of constantly recv-ing here
        loop {
            recv_points.recv();
        }
    }));

    // Wait for all the child threads to finish:
    for child in children {
        let _ = child.join();
    }
}

fn run(recv_audio: chan::Receiver<(f32, f32)>, send_points: chan::Sender<(f32, f32)>) -> Result<(), pa::Error> {
    // Fire up ye olde PortAudio:
    println!("=============");
    let pa = try!(pa::PortAudio::new());
    println!("=============");

    // Set up our settings - set a buffer amount to try to reduce underruns:
    let mut settings = try!(pa.default_output_stream_settings(NUM_CHANNELS, SAMPLE_RATE, FRAMES_PER_BUFFER));
    settings.params.suggested_latency = BUFFER_SECONDS;

    // This callback function will be called by PortAudio when it needs more audio samples.
    // It may be called at interrupt level on some machines, so don't do anything that could mess
    // up the system like dynamic resource allocation or I/O. (although doing so seems to be fine on
    // my machine...?)
    //
    // The job of this callback is to fill up the buffer that PortAudio tells us to fill up.
    // Each "frame" represents one sample for each channel that we have, so we need to put a total
    // of (NUM_CHANNELS * frames) samples into the buffer.
    // The samples are "interleaved" by default, so the structure of buffer looks like:
    // [ch0_sample0, ch1_sample0, ch0_sample1, ch1_sample1, ch0_sample2, ch1_sample2, ...]
    let callback = move |pa::OutputStreamCallbackArgs { buffer, frames, .. }| {
        let mut i = 0;
        for _ in 0..frames {
            let samples = recv_audio.recv().unwrap();
            send_points.send(samples.clone());
            buffer[i]   = samples.0;
            buffer[i+1] = samples.1;
            i += 2;
        }
        pa::Continue
    };

    // Now that we have the settings and the callback function set up, we can finally open the
    // stream, through which we will actually play audio:
    let mut stream = try!(pa.open_non_blocking_stream(settings, callback));

    // And now that we have the stream, we can start playing sounds!
    try!(stream.start());

    // We're using PortAudio in non-blocking mode, so execution will fall through immedately.
    // Loop indefinitely to let audio keep playing.
    loop {
        thread::sleep(time::Duration::new(1, 0));
    }

    // We're done playing, gracefully shut down the stream:
    try!(stream.stop());
    try!(stream.close());

    Ok(())
}<commit_msg>Fix a tiny thing in a comment<commit_after>#![deny(missing_docs)]

//! Main driver application for the "dsp" library.

// PortAudio:
extern crate portaudio;
use portaudio as pa;

// Channels for buffering up samples in between threads:
extern crate chan;

// other `use` statements:
use std::thread;
use std::time;

// DSP module
// TODO: remove all these "use" statements in favor of explicitly calling them in main.rs
pub mod dsp;
use dsp::generators;
use dsp::negate_signal::NegateSignal;
use dsp::add_signals::AddSignals;
use dsp::evaluatable::Evaluatable;

// Constants:
const NUM_CHANNELS: i32 = 2;
const SAMPLE_RATE: f64 = 44100.0;
const FRAMES_PER_BUFFER: u32 = 1024;
const BUFFER_SECONDS: f64 = 0.100;  // Buffer samples for 100ms -- reduces chances of underrun
const FREQUENCY: f64 = 440.0;

fn main() {
    // Collect all our threads:
    let mut children = vec![];

    // Signal flow:
    // (signal generators + filters + combinations) --> (portaudio) --> (grapher - first x samples)

    // Create a channel for (signals) --> (portaudio)
    let (send_audio, recv_audio) = chan::sync(SAMPLE_RATE as usize);

    // Create a channel for (portaudio) --> (grapher)
    let (send_points, recv_points) = chan::sync(SAMPLE_RATE as usize);

    // TODO: maybe split this out into different functions / modules

    // signal generators:
    children.push(thread::spawn(move || {
        // A-Minor chord
        let sine_generator1 = generators::Sine::new(SAMPLE_RATE, FREQUENCY * 1.0, 0.1);
        let sine_generator2 = generators::Sine::new(SAMPLE_RATE, FREQUENCY * 1.2 * 0.5, 0.1);
        let sine_generator3 = generators::Sine::new(SAMPLE_RATE, FREQUENCY * 1.5 * 0.5, 0.1);
        let add_vec: Vec<Box<Evaluatable>> = vec![Box::new(sine_generator1), Box::new(sine_generator2), Box::new(sine_generator3)];

        let mut add_signals = AddSignals::new(add_vec);
        loop {
            send_audio.send(add_signals.evaluate());
        }
    }));

    // PortAudio:
    children.push(thread::spawn(move || {
        thread::sleep(time::Duration::new(0, 1_000_000));
        run(recv_audio, send_points).unwrap()
    }));

    // Grapher:
    children.push(thread::spawn(move || {
        let mut x = vec![];
        let mut y = vec![];

        let period = SAMPLE_RATE / FREQUENCY;

        for i in 0..((period*50.0) as usize) {
            let sample = recv_points.recv().unwrap();
            x.push(i as f32);
            y.push(sample.0);
        }

        // TODO: all we need is a plotting library to plot here...

        // TODO: Just close the channel instead of constantly recv-ing here
        loop {
            recv_points.recv();
        }
    }));

    // Wait for all the child threads to finish:
    for child in children {
        let _ = child.join();
    }
}

fn run(recv_audio: chan::Receiver<(f32, f32)>, send_points: chan::Sender<(f32, f32)>) -> Result<(), pa::Error> {
    // Fire up ye olde PortAudio:
    println!("=============");
    let pa = try!(pa::PortAudio::new());
    println!("=============");

    // Set up our settings - set a buffer amount to try to reduce underruns:
    let mut settings = try!(pa.default_output_stream_settings(NUM_CHANNELS, SAMPLE_RATE, FRAMES_PER_BUFFER));
    settings.params.suggested_latency = BUFFER_SECONDS;

    // This callback function will be called by PortAudio when it needs more audio samples.
    // It may be called at interrupt level on some machines, so don't do anything that could mess
    // up the system like dynamic resource allocation or I/O. (although doing so seems to be fine on
    // my machine...?)
    //
    // The job of this callback is to fill up the buffer that PortAudio tells us to fill up.
    // Each "frame" represents one sample for each channel that we have, so we need to put a total
    // of (NUM_CHANNELS * frames) samples into the buffer.
    // The samples are "interleaved" by default, so the structure of buffer looks like:
    // [ch0_sample0, ch1_sample0, ch0_sample1, ch1_sample1, ch0_sample2, ch1_sample2, ...]
    let callback = move |pa::OutputStreamCallbackArgs { buffer, frames, .. }| {
        let mut i = 0;
        for _ in 0..frames {
            let samples = recv_audio.recv().unwrap();
            send_points.send(samples.clone());
            buffer[i]   = samples.0;
            buffer[i+1] = samples.1;
            i += 2;
        }
        pa::Continue
    };

    // Now that we have the settings and the callback function set up, we can finally open the
    // stream, through which we will actually play audio:
    let mut stream = try!(pa.open_non_blocking_stream(settings, callback));

    // And now that we have the stream, we can start playing sounds!
    try!(stream.start());

    // We're using PortAudio in non-blocking mode, so execution will fall through immedately.
    // Loop indefinitely to let audio keep playing.
    loop {
        thread::sleep(time::Duration::new(1, 0));
    }

    // We're done playing, gracefully shut down the stream:
    try!(stream.stop());
    try!(stream.close());

    Ok(())
}<|endoftext|>


<commit_before>extern crate iron;
#[macro_use]
extern crate router;
extern crate staticfile;
extern crate mount;
extern crate urlencoded;
extern crate rustc_serialize;
extern crate html5ever;
extern crate regex;
extern crate uuid;
extern crate chrono;

use std::net::SocketAddrV4;
use std::net::Ipv4Addr;
use std::path::Path;
use iron::prelude::*;
use iron::status;
use iron::mime::Mime;
use staticfile::Static;
use mount::Mount;
use router::{Router};
use urlencoded::UrlEncodedQuery;
use urlencoded::UrlEncodedBody;
use std::str::FromStr;
use uuid::Uuid;

#[macro_use]
extern crate string_cache;
extern crate pink_spider;

use pink_spider::error::Error;
use pink_spider::scraper::extract;
use pink_spider::model::{Track, Playlist, Entry, Provider, PaginatedCollection};
use rustc_serialize::json::{ToJson, Json};
use pink_spider::youtube;
use pink_spider::soundcloud;

pub fn index_entries(req: &mut Request) -> IronResult<Response> {
    let (page, per_page) = pagination_params(req);
    let entries          = Entry::find(page, per_page);
    let json_obj: Json   = entries.to_json();
    let json_str: String = json_obj.to_string();
    Ok(Response::with((status::Ok, application_json(), json_str)))
}

pub fn index_tracks(req: &mut Request) -> IronResult<Response> {
    let (page, per_page) = pagination_params(req);
    let tracks  = Track::find(page, per_page);
    let json_obj: Json   = tracks.to_json();
    let json_str: String = json_obj.to_string();
    Ok(Response::with((status::Ok, application_json(), json_str)))
}

pub fn index_tracks_by_entry(req: &mut Request) -> IronResult<Response> {
    let ref entry_id = req.extensions.get::<Router>().unwrap().find("entry_id").unwrap();
    let uuid = try!(Uuid::parse_str(entry_id).map_err(|_| Error::Unprocessable));
    let items = Track::find_by_entry_id(uuid);
    let col = PaginatedCollection {
        page:     0,
        per_page: items.len() as i64,
        total:    items.len() as i64,
        items:    items,
    };
    let json_obj: Json   = col.to_json();
    let json_str: String = json_obj.to_string();
    Ok(Response::with((status::Ok, application_json(), json_str)))
}

pub fn playlistify(req: &mut Request) -> IronResult<Response> {
    pub fn playlistify2(req: &mut Request) -> Result<Response, Error> {
        let ref params  = try!(req.get_ref::<UrlEncodedQuery>());
        let url         = try!(params.get("url").ok_or(Error::BadRequest));
        let defaults    = &vec!("false".to_string());
        let force_param = params.get("force").unwrap_or(defaults);
        let force       = force_param.len() > 0 && &force_param[0] == "true";
        let entry       = try!(find_or_playlistify_entry(&url[0], force));
        let json_obj    = entry.to_json() as Json;
        let json_str    = json_obj.to_string() as String;
        Ok(Response::with((status::Ok, application_json(), json_str)))
    }
    playlistify2(req).map_err(|err| IronError::from(err))
}

pub fn find_or_playlistify_entry(url: &str, force: bool) -> Result<Entry, Error> {
    match Entry::find_by_url(url) {
        Ok(entry) => {
            println!("Get entry from database cache: {}", url);
            if force {
                println!("Update entry: {}", url);
                let entry = try!(playlistify_entry(entry));
                Ok(entry)
            } else {
                Ok(entry)
            }
        },
        Err(_) => {
            let entry = try!(Entry::create_by_url(url.to_string()));
            let entry = try!(playlistify_entry(entry));
            println!("Create new entry to database cache: {}", url);
            Ok(entry)
        },
    }
}

pub fn playlistify_entry(entry: Entry) -> Result<Entry, Error> {
    let mut e = entry.clone();
    let product = try!(extract(&entry.url));
    match product.og_obj {
        Some(og_obj) => {
            e.title       = Some(og_obj.title);
            e.description = og_obj.description;
            e.locale      = og_obj.locale;
            e.visual_url  = og_obj.images.first().map(|i| i.url.clone());
        },
        None => (),
    }
    for t in product.tracks {
        let new_track = try!(Track::find_or_create(t.provider, t.identifier.to_string()));
        let mut track = t.clone();
        track.id      = new_track.id;
        try!(track.fetch_detail().save());
        match entry.tracks.iter().find(|&t| t.id == track.id) {
            Some(_) => (),
            None    => e.add_track(track.clone())
        }
    }
    for p in product.playlists {
        let new_playlist = try!(Playlist::find_or_create(p.provider, p.identifier.to_string()));
        let mut playlist = p.clone();
        playlist.id      = new_playlist.id;
        try!(playlist.save());
        match entry.playlists.iter().find(|&p| p.id == playlist.id) {
            Some(_) => (),
            None    => e.add_playlist(playlist.clone())
        }
    }
    try!(e.save());
    Ok(e)
}

pub fn show_track_by_id(req: &mut Request) -> IronResult<Response> {
    let ref track_id = req.extensions.get::<Router>().unwrap()
                          .find("track_id").unwrap();
    match Track::find_by_id(track_id) {
        Ok(track) => {
            Ok(Response::with((status::Ok,
                               application_json(),
                               track.to_json().to_string())))
        },
        Err(e) => Ok(e.as_response())
    }
}

pub fn show_track(req: &mut Request) -> IronResult<Response> {
    let provider   = req.extensions.get::<Router>().unwrap().find("provider").unwrap();
    let identifier = req.extensions.get::<Router>().unwrap().find("id").unwrap();
    let p = &Provider::new(provider.to_string());
    match Track::find_by(p, identifier) {
        Ok(track) => {
            Ok(Response::with((status::Ok,
                               application_json(),
                               track.to_json().to_string())))
        },
        Err(e) => Ok(e.as_response())
    }
}

pub fn update_track(req: &mut Request) -> IronResult<Response> {
    let mut track = try!(Track::find_by_id(&query_as_string(req, "track_id")));
    match param_as_string(req, "title") {
        Some(title) => track.title = title,
        None        => (),
    }
    match param_as_string(req, "url") {
        Some(url) => track.url = url,
        None      => (),
    }
    let track = match track.provider {
        Provider::YouTube => match youtube::fetch_video(&track.identifier) {
            Ok(video) => track.update_with_yt_video(&video),
            Err(_)    => track.disable(),
        },
        Provider::SoundCloud => match soundcloud::fetch_track(&track.identifier) {
            Ok(sc_track) => track.update_with_sc_track(&sc_track),
            Err(_)       => track.disable(),
        },
        _ => &mut track,
    };
    try!(track.save());
    let res = track.to_json().to_string();
    Ok(Response::with((status::Ok, application_json(), res)))
}

fn param_as_string(req: &mut Request, key: &str) -> Option<String> {
    match req.get_ref::<UrlEncodedBody>() {
        Ok(ref params) => match params.get(key) {
            Some(val) => Some(val[0].clone()),
            None      => None
        },
        Err(_) => None
    }
}

fn query_as_string(req: &mut Request, key: &str) -> String {
    return req.extensions.get::<Router>().unwrap()
        .find(key).unwrap().to_string();
}

fn pagination_params(req: &mut Request) -> (i64, i64) {
    match req.get_ref::<UrlEncodedQuery>() {
        Ok(ref params) =>
            (params.get("page").unwrap()[0].to_string().parse::<i64>().unwrap(),
             params.get("per_page").unwrap()[0].to_string().parse::<i64>().unwrap()),
        Err(_) =>
            (0, 10)
    }
}

fn application_json() -> Mime {
    Mime::from_str("application/json").ok().unwrap()
}

pub fn main() {
    let path = Path::new("public");
    let mut mount = Mount::new();
    mount.mount("/web/", Static::new(Path::new(path)));
    let router = router!(
        playlistify:           get  "/playlistify"              => playlistify,
        show_track_by_id:      get  "/tracks/:track_id"         => show_track_by_id,
        update_track:          post "/tracks/:track_id"         => update_track,
        show_track:            get  "/tracks/:provider/:id"     => show_track,
        index_entries:         get  "/entries"                  => index_entries,
        index_tracks:          get  "/tracks"                   => index_tracks,
        index_tracks_by_entry: get  "/entries/:entry_id/tracks" => index_tracks_by_entry,
        web:                   get  "/*"                        => mount,
    );
    let port_str = match std::env::var("PORT") {
        Ok(n)    => n,
        Err(_) => "8080".to_string()
    };
    let port: u16 = match port_str.trim().parse() {
        Ok(n) => n,
        Err(_) => {
            println!("Faild to parse port");
            return;
        }
    };
    println!("PORT {}", port_str);
    let ip = Ipv4Addr::new(0, 0, 0, 0);
    Iron::new(router).http(SocketAddrV4::new(ip, port)).unwrap();
}
<commit_msg>Implement playlist api<commit_after>extern crate iron;
#[macro_use]
extern crate router;
extern crate staticfile;
extern crate mount;
extern crate urlencoded;
extern crate rustc_serialize;
extern crate html5ever;
extern crate regex;
extern crate uuid;
extern crate chrono;

use std::net::SocketAddrV4;
use std::net::Ipv4Addr;
use std::path::Path;
use iron::prelude::*;
use iron::status;
use iron::mime::Mime;
use staticfile::Static;
use mount::Mount;
use router::{Router};
use urlencoded::UrlEncodedQuery;
use urlencoded::UrlEncodedBody;
use std::str::FromStr;
use uuid::Uuid;

#[macro_use]
extern crate string_cache;
extern crate pink_spider;

use pink_spider::error::Error;
use pink_spider::scraper::extract;
use pink_spider::model::{Track, Playlist, Entry, Provider, PaginatedCollection};
use rustc_serialize::json::{ToJson, Json};
use pink_spider::youtube;
use pink_spider::soundcloud;

pub fn index_entries(req: &mut Request) -> IronResult<Response> {
    let (page, per_page) = pagination_params(req);
    let entries          = Entry::find(page, per_page);
    let json_obj: Json   = entries.to_json();
    let json_str: String = json_obj.to_string();
    Ok(Response::with((status::Ok, application_json(), json_str)))
}

pub fn index_tracks(req: &mut Request) -> IronResult<Response> {
    let (page, per_page) = pagination_params(req);
    let tracks  = Track::find(page, per_page);
    let json_obj: Json   = tracks.to_json();
    let json_str: String = json_obj.to_string();
    Ok(Response::with((status::Ok, application_json(), json_str)))
}

pub fn index_tracks_by_entry(req: &mut Request) -> IronResult<Response> {
    let ref entry_id = req.extensions.get::<Router>().unwrap().find("entry_id").unwrap();
    let uuid = try!(Uuid::parse_str(entry_id).map_err(|_| Error::Unprocessable));
    let items = Track::find_by_entry_id(uuid);
    let col = PaginatedCollection {
        page:     0,
        per_page: items.len() as i64,
        total:    items.len() as i64,
        items:    items,
    };
    let json_obj: Json   = col.to_json();
    let json_str: String = json_obj.to_string();
    Ok(Response::with((status::Ok, application_json(), json_str)))
}

pub fn index_playlists(req: &mut Request) -> IronResult<Response> {
    let (page, per_page) = pagination_params(req);
    let playlists  = Playlist::find(page, per_page);
    let json_obj: Json   = playlists.to_json();
    let json_str: String = json_obj.to_string();
    Ok(Response::with((status::Ok, application_json(), json_str)))
}

pub fn index_playlists_by_entry(req: &mut Request) -> IronResult<Response> {
    let ref entry_id = req.extensions.get::<Router>().unwrap().find("entry_id").unwrap();
    let uuid = try!(Uuid::parse_str(entry_id).map_err(|_| Error::Unprocessable));
    let items = Playlist::find_by_entry_id(uuid);
    let col = PaginatedCollection {
        page:     0,
        per_page: items.len() as i64,
        total:    items.len() as i64,
        items:    items,
    };
    let json_obj: Json   = col.to_json();
    let json_str: String = json_obj.to_string();
    Ok(Response::with((status::Ok, application_json(), json_str)))
}

pub fn playlistify(req: &mut Request) -> IronResult<Response> {
    pub fn playlistify2(req: &mut Request) -> Result<Response, Error> {
        let ref params  = try!(req.get_ref::<UrlEncodedQuery>());
        let url         = try!(params.get("url").ok_or(Error::BadRequest));
        let defaults    = &vec!("false".to_string());
        let force_param = params.get("force").unwrap_or(defaults);
        let force       = force_param.len() > 0 && &force_param[0] == "true";
        let entry       = try!(find_or_playlistify_entry(&url[0], force));
        let json_obj    = entry.to_json() as Json;
        let json_str    = json_obj.to_string() as String;
        Ok(Response::with((status::Ok, application_json(), json_str)))
    }
    playlistify2(req).map_err(|err| IronError::from(err))
}

pub fn find_or_playlistify_entry(url: &str, force: bool) -> Result<Entry, Error> {
    match Entry::find_by_url(url) {
        Ok(entry) => {
            println!("Get entry from database cache: {}", url);
            if force {
                println!("Update entry: {}", url);
                let entry = try!(playlistify_entry(entry));
                Ok(entry)
            } else {
                Ok(entry)
            }
        },
        Err(_) => {
            let entry = try!(Entry::create_by_url(url.to_string()));
            let entry = try!(playlistify_entry(entry));
            println!("Create new entry to database cache: {}", url);
            Ok(entry)
        },
    }
}

pub fn playlistify_entry(entry: Entry) -> Result<Entry, Error> {
    let mut e = entry.clone();
    let product = try!(extract(&entry.url));
    match product.og_obj {
        Some(og_obj) => {
            e.title       = Some(og_obj.title);
            e.description = og_obj.description;
            e.locale      = og_obj.locale;
            e.visual_url  = og_obj.images.first().map(|i| i.url.clone());
        },
        None => (),
    }
    for t in product.tracks {
        let new_track = try!(Track::find_or_create(t.provider, t.identifier.to_string()));
        let mut track = t.clone();
        track.id      = new_track.id;
        try!(track.fetch_detail().save());
        match entry.tracks.iter().find(|&t| t.id == track.id) {
            Some(_) => (),
            None    => e.add_track(track.clone())
        }
    }
    for p in product.playlists {
        let new_playlist = try!(Playlist::find_or_create(p.provider, p.identifier.to_string()));
        let mut playlist = p.clone();
        playlist.id      = new_playlist.id;
        try!(playlist.save());
        match entry.playlists.iter().find(|&p| p.id == playlist.id) {
            Some(_) => (),
            None    => e.add_playlist(playlist.clone())
        }
    }
    try!(e.save());
    Ok(e)
}

pub fn show_track_by_id(req: &mut Request) -> IronResult<Response> {
    let ref track_id = req.extensions.get::<Router>().unwrap()
                          .find("track_id").unwrap();
    match Track::find_by_id(track_id) {
        Ok(track) => {
            Ok(Response::with((status::Ok,
                               application_json(),
                               track.to_json().to_string())))
        },
        Err(e) => Ok(e.as_response())
    }
}

pub fn show_track(req: &mut Request) -> IronResult<Response> {
    let provider   = req.extensions.get::<Router>().unwrap().find("provider").unwrap();
    let identifier = req.extensions.get::<Router>().unwrap().find("id").unwrap();
    let p = &Provider::new(provider.to_string());
    match Track::find_by(p, identifier) {
        Ok(track) => {
            Ok(Response::with((status::Ok,
                               application_json(),
                               track.to_json().to_string())))
        },
        Err(e) => Ok(e.as_response())
    }
}

pub fn update_track(req: &mut Request) -> IronResult<Response> {
    let mut track = try!(Track::find_by_id(&query_as_string(req, "track_id")));
    match param_as_string(req, "title") {
        Some(title) => track.title = title,
        None        => (),
    }
    match param_as_string(req, "url") {
        Some(url) => track.url = url,
        None      => (),
    }
    let track = match track.provider {
        Provider::YouTube => match youtube::fetch_video(&track.identifier) {
            Ok(video) => track.update_with_yt_video(&video),
            Err(_)    => track.disable(),
        },
        Provider::SoundCloud => match soundcloud::fetch_track(&track.identifier) {
            Ok(sc_track) => track.update_with_sc_track(&sc_track),
            Err(_)       => track.disable(),
        },
        _ => &mut track,
    };
    try!(track.save());
    let res = track.to_json().to_string();
    Ok(Response::with((status::Ok, application_json(), res)))
}

pub fn show_playlist_by_id(req: &mut Request) -> IronResult<Response> {
    let ref playlist_id = req.extensions.get::<Router>().unwrap()
                          .find("playlist_id").unwrap();
    match Playlist::find_by_id(playlist_id) {
        Ok(playlist) => {
            Ok(Response::with((status::Ok,
                               application_json(),
                               playlist.to_json().to_string())))
        },
        Err(e) => Ok(e.as_response())
    }
}

pub fn show_playlist(req: &mut Request) -> IronResult<Response> {
    let provider   = req.extensions.get::<Router>().unwrap().find("provider").unwrap();
    let identifier = req.extensions.get::<Router>().unwrap().find("id").unwrap();
    let p = &Provider::new(provider.to_string());
    match Playlist::find_by(p, identifier) {
        Ok(playlist) => {
            Ok(Response::with((status::Ok,
                               application_json(),
                               playlist.to_json().to_string())))
        },
        Err(e) => Ok(e.as_response())
    }
}

fn param_as_string(req: &mut Request, key: &str) -> Option<String> {
    match req.get_ref::<UrlEncodedBody>() {
        Ok(ref params) => match params.get(key) {
            Some(val) => Some(val[0].clone()),
            None      => None
        },
        Err(_) => None
    }
}

fn query_as_string(req: &mut Request, key: &str) -> String {
    return req.extensions.get::<Router>().unwrap()
        .find(key).unwrap().to_string();
}

fn pagination_params(req: &mut Request) -> (i64, i64) {
    match req.get_ref::<UrlEncodedQuery>() {
        Ok(ref params) =>
            (params.get("page").unwrap()[0].to_string().parse::<i64>().unwrap(),
             params.get("per_page").unwrap()[0].to_string().parse::<i64>().unwrap()),
        Err(_) =>
            (0, 10)
    }
}

fn application_json() -> Mime {
    Mime::from_str("application/json").ok().unwrap()
}

pub fn main() {
    let path = Path::new("public");
    let mut mount = Mount::new();
    mount.mount("/web/", Static::new(Path::new(path)));
    let router = router!(
        playlistify:              get  "/playlistify"                 => playlistify,
        show_track_by_id:         get  "/tracks/:track_id"            => show_track_by_id,
        update_track:             post "/tracks/:track_id"            => update_track,
        show_track:               get  "/tracks/:provider/:id"        => show_track,
        show_playlist_by_id:      get  "/playlists/:playlist_id"      => show_playlist_by_id,
//        update_playlist:          post "/playlists/:playlist_id"      => update_playlist,
        show_playlist:            get  "/playlist/:provider/:id"      => show_playlist,
        index_entries:            get  "/entries"                     => index_entries,
        index_tracks:             get  "/tracks"                      => index_tracks,
        index_tracks_by_entry:    get  "/entries/:entry_id/tracks"    => index_tracks_by_entry,
        index_playlists:          get  "/playlists"                   => index_playlists,
        index_playlists_by_entry: get  "/entries/:entry_id/playlists" => index_playlists_by_entry,
        web:                   get  "/*"                        => mount,
    );
    let port_str = match std::env::var("PORT") {
        Ok(n)    => n,
        Err(_) => "8080".to_string()
    };
    let port: u16 = match port_str.trim().parse() {
        Ok(n) => n,
        Err(_) => {
            println!("Faild to parse port");
            return;
        }
    };
    println!("PORT {}", port_str);
    let ip = Ipv4Addr::new(0, 0, 0, 0);
    Iron::new(router).http(SocketAddrV4::new(ip, port)).unwrap();
}
<|endoftext|>


<commit_before>use std::io::prelude::*;
use std::fs::File;
use std::process::{Command, Stdio};

extern crate clap;
use clap::{Arg, ArgGroup, App};

extern crate isbfc;
use isbfc::Token;
use isbfc::Token::*;
use isbfc::parse;
use isbfc::optimize;

fn main() {
    let matches = App::new("isbfc")
        .version("0.0.1")
        .author("Ian D. Scott <ian@iandouglasscott.com>")
        .about("Brainfuck compiler")
        .arg(Arg::with_name("output_asm")
             .short("S")
             .help("Assemble but do not link"))
        .arg(Arg::with_name("dump_ir")
             .long("dumpir")
             .help("Dump intermediate representation; for debugging"))
        .group(ArgGroup::with_name("actions")
             .args(&["output_asm", "dump_ir"]))
        .arg(Arg::with_name("debugging_symbols")
             .short("g")
             .help("Generate debugging information"))
        .arg(Arg::with_name("out_name")
             .short("o")
             .help("Output file name")
             .takes_value(true)
             .empty_values(false)
             .value_name("file"))
        .arg(Arg::with_name("tape_size")
             .long("tape-size")
             .help("Size of tape; defaults to 8192")
             .takes_value(true)
             .empty_values(false)
             .value_name("bytes"))
        .arg(Arg::with_name("FILENAME")
             .help("Source file to compile")
             .required(true)
             .index(1))
        .get_matches();

    let mut tape_size = 8192;
    if let Some(tape_size_str) = matches.value_of("tape_size") {
        tape_size = tape_size_str.parse::<i32>().unwrap();
    }

    let path = matches.value_of("FILENAME").unwrap();
    let name = path.rsplitn(2, '.').last().unwrap();
    let mut file = File::open(&path).unwrap();
    let mut code = String::new();
    file.read_to_string(&mut code).unwrap();

    let tokens = parse(&code);
    let tokens = optimize(tokens);

    if matches.is_present("dump_ir") {
        let output = dump_ir(tokens);
        if let Some(out_name) = matches.value_of("out_name") {
            let mut irfile = File::create(out_name).unwrap();
            irfile.write_all(&output.into_bytes()).unwrap();
        } else {
            print!("{}", output);
        }
    } else if matches.is_present("output_asm") {
        let output = compile(tokens, tape_size);
        let def_name = format!("{}.s", name);
        let out_name = matches.value_of("out_name").unwrap_or(&def_name);
        let mut asmfile = File::create(out_name).unwrap();
        asmfile.write_all(&output.into_bytes()).unwrap();
    } else {
        let output = compile(tokens, tape_size);
        let out_name = matches.value_of("out_name").unwrap_or(name);
        let debug = matches.is_present("debugging_symbols");
        asm_and_link(&output, &name, &out_name, debug);
    }
}

fn compile(tokens: Vec<Token>, tape_size: i32) -> String {
    println!("Compiling...");

    let mut output = String::new();
    let mut loops: Vec<i32> = Vec::new();
    let mut ifs: Vec<i32> = Vec::new();
    let mut loopnum = 0;
    let mut ifnum = 0;
    let mut outbuffpos = 0;
    let mut outbuffsize = 0;
    for token in tokens.iter() {
        match *token {
            Add(offset, value) => {
                let dest = if offset == 0 {
                    "%r12".to_string()
                } else {
                    format!("{}(%rbx)", (offset*8))
                };
                if value == 1 && dest == "%r12" {
                    output.push_str("    inc %r12\n");
                } else if value >= 1 {
                    output.push_str(&format!("    addq ${}, {}\n", value, dest));
                } else if value == -1 && dest == "%r12" {
                    output.push_str("    dec %r12\n");
                } else if value <= -1 {
                    output.push_str(&format!("    subq ${}, {}\n", -value, dest));
                }
            },
            MulCopy(src_idx, dest_idx, mul) => {
                let mut src = if src_idx == 0 {
                    "%r12".to_string()
                } else {
                    format!("{}(%rbx)", (src_idx*8))
                };
                let dest = if dest_idx == 0 {
                    "%r12".to_string()
                } else {
                    format!("{}(%rbx)", (dest_idx*8))
                };

                if mul != -1 && mul != 1 {
                    output.push_str(&format!(concat!(
                                "    movq {}, %rax\n",
                                "    movq ${}, %rdx\n",
                                "    mulq %rdx\n"), src, mul.abs()));
                    src = "%rax".to_string();
                } else if src != "%r12" && dest != "%r12" {
                    // x86 cannot move memory to memory
                    output.push_str(&format!("    movq {}, %rax\n", src));
                    src = "%rax".to_string();
                }

                if mul > 0 {
                    output.push_str(&format!("    addq {}, {}\n", src, dest));
                } else {
                    output.push_str(&format!("    subq {}, {}\n", src, dest));
                }
            },
            Set(offset, value) => {
                if offset == 0 && value == 0 {
                    output.push_str("    xor %r12, %r12\n");
                } else if offset == 0 {
                    output.push_str(&format!("    movq ${}, %r12\n", value));
                } else {
                    output.push_str(&format!("    movq ${}, {}(%rbx)\n", value, offset*8));
                }
            },
            Move(offset) => {
                if offset != 0 {
                    output.push_str("    movq %r12, (%rbx)\n");
                    if offset > 0 {
                        output.push_str(&format!("    addq ${}, %rbx\n", offset*8));
                    } else {
                        output.push_str(&format!("    subq ${}, %rbx\n", -offset*8));
                    }
                    output.push_str("    movq (%rbx), %r12\n");
                }
            },
            Loop => {
                loopnum += 1;
                loops.push(loopnum);
                output.push_str(&format!(concat!(
                        "    jmp endloop{}\n",
                        "    loop{}:\n"),
                        loopnum, loopnum));
            },
            EndLoop => {
                let curloop = loops.pop().unwrap();
                output.push_str(&format!(concat!(
                            "    endloop{}:\n",
                            "    test %r12, %r12\n",
                            "    jnz loop{}\n"),
                            curloop, curloop))
            },
            If(offset) => {
                ifnum += 1;
                ifs.push(ifnum);
                if offset == 0 {
                    output.push_str("    test %r12, %r12\n");
                } else {
                    output.push_str(&format!("    cmpq $0, {}(%rbx)\n", offset*8));
                }
                output.push_str(&format!("    jz endif{}\n", ifnum));
            },
            EndIf =>
                output.push_str(&format!("    endif{}:\n", ifs.pop().unwrap())),
            Scan(offset) => {
                // Slighly more optimal than normal loop and move
                loopnum += 1;
                output.push_str(&format!(concat!(
                            "    movq %r12, (%rbx)\n",
                            "    jmp endloop{}\n",
                            "    loop{}:\n"),
                            loopnum, loopnum));
                if offset > 0 {
                    output.push_str(&format!("    addq ${}, %rbx\n", offset*8));
                } else {
                    output.push_str(&format!("    subq ${}, %rbx\n", -offset*8));
                }
                output.push_str(&format!(concat!(
                            "    endloop{}:\n",
                            "    cmp $0, (%rbx)\n",
                            "    jnz loop{}\n",
                            "    movq (%rbx), %r12\n"),
                            loopnum, loopnum));
            },
            Input =>
                output.push_str(concat!("\n    xor %rax, %rax\n",
                                        "    xor %rdi, %rdi\n",
                                        "    movq %rbx, %rsi\n",
                                        "    movq $1, %rdx\n",
                                        "    syscall\n",
                                        "    movq (%rbx), %r12\n\n")),
            LoadOut(offset, add) => {
                let outaddr = format!("(strbuff+{})", outbuffpos);
                if offset == 0 {
                    output.push_str(&format!("    movq %r12, {}\n", outaddr));
                } else {
                    output.push_str(&format!("    movq {}(%rbx), %rax\n", offset*8));
                    output.push_str(&format!("    movq %rax, {}\n", outaddr));
                }
                if add > 0 {
                    output.push_str(&format!("    addb ${}, {}\n", add, outaddr));
                } else if add < 0 {
                    output.push_str(&format!("    subb ${}, {}\n", -add, outaddr));
                }
                outbuffpos += 1;
            },
            LoadOutSet(value) => {
                let outaddr = format!("(strbuff+{})", outbuffpos);
                output.push_str(&format!("    movq ${}, {}\n", value, outaddr));
                outbuffpos += 1;
            },
            Output => {
                output.push_str(&format!(concat!(
                            "    movq $1, %rax\n",
                            "    movq $1, %rdi\n",
                            "    movq $strbuff, %rsi\n",
                            "    movq ${}, %rdx\n",
                            "    syscall\n\n"),
                            outbuffpos));

                if outbuffsize < outbuffpos + 8 {
                    outbuffsize = outbuffpos + 8;
                }
                outbuffpos = 0;
            }
        }
    }

    // Exit syscall
    output.push_str(concat!("\n    movq $60, %rax\n",
                            "    movq $0, %rdi\n",
                            "    syscall\n"));

    format!(concat!(
            ".section .bss\n",
            "    .lcomm strbuff, {outbuffsize}\n",
            "    .lcomm mem, {}\n",
            "    .set startidx, mem + {}\n",
            ".section .text\n",
            ".global _start\n",
            "_start:\n",
            "    xor %r12, %r12\n",
            "    movq $startidx, %rbx\n\n{}"),
            tape_size, tape_size/2, output, outbuffsize=outbuffsize)
}

fn asm_and_link(code: &str, name: &str, out_name: &str, debug: bool) {
    println!("Assembling...");

    let mut command = Command::new("as");
    if debug {
        command.arg("-g");
    }
    let mut child = command
        .arg("-") // Standard input
        .arg("-o")
        .arg(format!("{}.o", name))
        .stdin(Stdio::piped())
        .spawn().unwrap();

    child.stdin.take().unwrap().write_all(code.as_bytes()).unwrap();

    let status = child.wait().unwrap();
    if status.code() == Some(0) {
        println!("Linking...");
        Command::new("ld")
            .arg(format!("{}.o", name))
            .arg("-o")
            .arg(out_name)
            .spawn().unwrap();
    }
}

fn dump_ir(tokens: Vec<Token>) -> String {
    let mut output = String::new();
    for token in tokens.iter() {
        match *token {
            Output =>
                output.push_str("output\n"),
            Input =>
                output.push_str("input\n"),
            Loop =>
                output.push_str("loop\n"),
            EndLoop =>
                output.push_str("endloop\n"),
            Move(offset) =>
                output.push_str(&format!("move(offset={})\n", offset)),
            Add(offset, value) =>
                output.push_str(&format!("add(offset={}, value={})\n", offset, value)),
            Set(offset, value) =>
                output.push_str(&format!("set(offset={}, value={})\n", offset, value)),
            MulCopy(src, dest, mul) =>
                output.push_str(&format!("mulcopy(src={}, dest={}, mul={})\n", src, dest, mul)),
            Scan(offset) =>
                output.push_str(&format!("scan(offset={})\n", offset)),
            LoadOut(offset, add) =>
                output.push_str(&format!("loadout(offset={}, add={})\n", offset, add)),
            LoadOutSet(value) =>
                output.push_str(&format!("loadoutset(value={})\n", value)),
            If(offset) =>
                output.push_str(&format!("if(offset={})\n", offset)),
            EndIf =>
                output.push_str("endif\n"),
        }
    }
    output
}
<commit_msg>Do not use .iter() where unneeded<commit_after>use std::io::prelude::*;
use std::fs::File;
use std::process::{Command, Stdio};

extern crate clap;
use clap::{Arg, ArgGroup, App};

extern crate isbfc;
use isbfc::Token;
use isbfc::Token::*;
use isbfc::parse;
use isbfc::optimize;

fn main() {
    let matches = App::new("isbfc")
        .version("0.0.1")
        .author("Ian D. Scott <ian@iandouglasscott.com>")
        .about("Brainfuck compiler")
        .arg(Arg::with_name("output_asm")
             .short("S")
             .help("Assemble but do not link"))
        .arg(Arg::with_name("dump_ir")
             .long("dumpir")
             .help("Dump intermediate representation; for debugging"))
        .group(ArgGroup::with_name("actions")
             .args(&["output_asm", "dump_ir"]))
        .arg(Arg::with_name("debugging_symbols")
             .short("g")
             .help("Generate debugging information"))
        .arg(Arg::with_name("out_name")
             .short("o")
             .help("Output file name")
             .takes_value(true)
             .empty_values(false)
             .value_name("file"))
        .arg(Arg::with_name("tape_size")
             .long("tape-size")
             .help("Size of tape; defaults to 8192")
             .takes_value(true)
             .empty_values(false)
             .value_name("bytes"))
        .arg(Arg::with_name("FILENAME")
             .help("Source file to compile")
             .required(true)
             .index(1))
        .get_matches();

    let mut tape_size = 8192;
    if let Some(tape_size_str) = matches.value_of("tape_size") {
        tape_size = tape_size_str.parse::<i32>().unwrap();
    }

    let path = matches.value_of("FILENAME").unwrap();
    let name = path.rsplitn(2, '.').last().unwrap();
    let mut file = File::open(&path).unwrap();
    let mut code = String::new();
    file.read_to_string(&mut code).unwrap();

    let tokens = parse(&code);
    let tokens = optimize(tokens);

    if matches.is_present("dump_ir") {
        let output = dump_ir(tokens);
        if let Some(out_name) = matches.value_of("out_name") {
            let mut irfile = File::create(out_name).unwrap();
            irfile.write_all(&output.into_bytes()).unwrap();
        } else {
            print!("{}", output);
        }
    } else if matches.is_present("output_asm") {
        let output = compile(tokens, tape_size);
        let def_name = format!("{}.s", name);
        let out_name = matches.value_of("out_name").unwrap_or(&def_name);
        let mut asmfile = File::create(out_name).unwrap();
        asmfile.write_all(&output.into_bytes()).unwrap();
    } else {
        let output = compile(tokens, tape_size);
        let out_name = matches.value_of("out_name").unwrap_or(name);
        let debug = matches.is_present("debugging_symbols");
        asm_and_link(&output, &name, &out_name, debug);
    }
}

fn compile(tokens: Vec<Token>, tape_size: i32) -> String {
    println!("Compiling...");

    let mut output = String::new();
    let mut loops: Vec<i32> = Vec::new();
    let mut ifs: Vec<i32> = Vec::new();
    let mut loopnum = 0;
    let mut ifnum = 0;
    let mut outbuffpos = 0;
    let mut outbuffsize = 0;
    for token in tokens {
        match token {
            Add(offset, value) => {
                let dest = if offset == 0 {
                    "%r12".to_string()
                } else {
                    format!("{}(%rbx)", (offset*8))
                };
                if value == 1 && dest == "%r12" {
                    output.push_str("    inc %r12\n");
                } else if value >= 1 {
                    output.push_str(&format!("    addq ${}, {}\n", value, dest));
                } else if value == -1 && dest == "%r12" {
                    output.push_str("    dec %r12\n");
                } else if value <= -1 {
                    output.push_str(&format!("    subq ${}, {}\n", -value, dest));
                }
            },
            MulCopy(src_idx, dest_idx, mul) => {
                let mut src = if src_idx == 0 {
                    "%r12".to_string()
                } else {
                    format!("{}(%rbx)", (src_idx*8))
                };
                let dest = if dest_idx == 0 {
                    "%r12".to_string()
                } else {
                    format!("{}(%rbx)", (dest_idx*8))
                };

                if mul != -1 && mul != 1 {
                    output.push_str(&format!(concat!(
                                "    movq {}, %rax\n",
                                "    movq ${}, %rdx\n",
                                "    mulq %rdx\n"), src, mul.abs()));
                    src = "%rax".to_string();
                } else if src != "%r12" && dest != "%r12" {
                    // x86 cannot move memory to memory
                    output.push_str(&format!("    movq {}, %rax\n", src));
                    src = "%rax".to_string();
                }

                if mul > 0 {
                    output.push_str(&format!("    addq {}, {}\n", src, dest));
                } else {
                    output.push_str(&format!("    subq {}, {}\n", src, dest));
                }
            },
            Set(offset, value) => {
                if offset == 0 && value == 0 {
                    output.push_str("    xor %r12, %r12\n");
                } else if offset == 0 {
                    output.push_str(&format!("    movq ${}, %r12\n", value));
                } else {
                    output.push_str(&format!("    movq ${}, {}(%rbx)\n", value, offset*8));
                }
            },
            Move(offset) => {
                if offset != 0 {
                    output.push_str("    movq %r12, (%rbx)\n");
                    if offset > 0 {
                        output.push_str(&format!("    addq ${}, %rbx\n", offset*8));
                    } else {
                        output.push_str(&format!("    subq ${}, %rbx\n", -offset*8));
                    }
                    output.push_str("    movq (%rbx), %r12\n");
                }
            },
            Loop => {
                loopnum += 1;
                loops.push(loopnum);
                output.push_str(&format!(concat!(
                        "    jmp endloop{}\n",
                        "    loop{}:\n"),
                        loopnum, loopnum));
            },
            EndLoop => {
                let curloop = loops.pop().unwrap();
                output.push_str(&format!(concat!(
                            "    endloop{}:\n",
                            "    test %r12, %r12\n",
                            "    jnz loop{}\n"),
                            curloop, curloop))
            },
            If(offset) => {
                ifnum += 1;
                ifs.push(ifnum);
                if offset == 0 {
                    output.push_str("    test %r12, %r12\n");
                } else {
                    output.push_str(&format!("    cmpq $0, {}(%rbx)\n", offset*8));
                }
                output.push_str(&format!("    jz endif{}\n", ifnum));
            },
            EndIf =>
                output.push_str(&format!("    endif{}:\n", ifs.pop().unwrap())),
            Scan(offset) => {
                // Slighly more optimal than normal loop and move
                loopnum += 1;
                output.push_str(&format!(concat!(
                            "    movq %r12, (%rbx)\n",
                            "    jmp endloop{}\n",
                            "    loop{}:\n"),
                            loopnum, loopnum));
                if offset > 0 {
                    output.push_str(&format!("    addq ${}, %rbx\n", offset*8));
                } else {
                    output.push_str(&format!("    subq ${}, %rbx\n", -offset*8));
                }
                output.push_str(&format!(concat!(
                            "    endloop{}:\n",
                            "    cmp $0, (%rbx)\n",
                            "    jnz loop{}\n",
                            "    movq (%rbx), %r12\n"),
                            loopnum, loopnum));
            },
            Input =>
                output.push_str(concat!("\n    xor %rax, %rax\n",
                                        "    xor %rdi, %rdi\n",
                                        "    movq %rbx, %rsi\n",
                                        "    movq $1, %rdx\n",
                                        "    syscall\n",
                                        "    movq (%rbx), %r12\n\n")),
            LoadOut(offset, add) => {
                let outaddr = format!("(strbuff+{})", outbuffpos);
                if offset == 0 {
                    output.push_str(&format!("    movq %r12, {}\n", outaddr));
                } else {
                    output.push_str(&format!("    movq {}(%rbx), %rax\n", offset*8));
                    output.push_str(&format!("    movq %rax, {}\n", outaddr));
                }
                if add > 0 {
                    output.push_str(&format!("    addb ${}, {}\n", add, outaddr));
                } else if add < 0 {
                    output.push_str(&format!("    subb ${}, {}\n", -add, outaddr));
                }
                outbuffpos += 1;
            },
            LoadOutSet(value) => {
                let outaddr = format!("(strbuff+{})", outbuffpos);
                output.push_str(&format!("    movq ${}, {}\n", value, outaddr));
                outbuffpos += 1;
            },
            Output => {
                output.push_str(&format!(concat!(
                            "    movq $1, %rax\n",
                            "    movq $1, %rdi\n",
                            "    movq $strbuff, %rsi\n",
                            "    movq ${}, %rdx\n",
                            "    syscall\n\n"),
                            outbuffpos));

                if outbuffsize < outbuffpos + 8 {
                    outbuffsize = outbuffpos + 8;
                }
                outbuffpos = 0;
            }
        }
    }

    // Exit syscall
    output.push_str(concat!("\n    movq $60, %rax\n",
                            "    movq $0, %rdi\n",
                            "    syscall\n"));

    format!(concat!(
            ".section .bss\n",
            "    .lcomm strbuff, {outbuffsize}\n",
            "    .lcomm mem, {}\n",
            "    .set startidx, mem + {}\n",
            ".section .text\n",
            ".global _start\n",
            "_start:\n",
            "    xor %r12, %r12\n",
            "    movq $startidx, %rbx\n\n{}"),
            tape_size, tape_size/2, output, outbuffsize=outbuffsize)
}

fn asm_and_link(code: &str, name: &str, out_name: &str, debug: bool) {
    println!("Assembling...");

    let mut command = Command::new("as");
    if debug {
        command.arg("-g");
    }
    let mut child = command
        .arg("-") // Standard input
        .arg("-o")
        .arg(format!("{}.o", name))
        .stdin(Stdio::piped())
        .spawn().unwrap();

    child.stdin.take().unwrap().write_all(code.as_bytes()).unwrap();

    let status = child.wait().unwrap();
    if status.code() == Some(0) {
        println!("Linking...");
        Command::new("ld")
            .arg(format!("{}.o", name))
            .arg("-o")
            .arg(out_name)
            .spawn().unwrap();
    }
}

fn dump_ir(tokens: Vec<Token>) -> String {
    let mut output = String::new();
    for token in tokens.iter() {
        match *token {
            Output =>
                output.push_str("output\n"),
            Input =>
                output.push_str("input\n"),
            Loop =>
                output.push_str("loop\n"),
            EndLoop =>
                output.push_str("endloop\n"),
            Move(offset) =>
                output.push_str(&format!("move(offset={})\n", offset)),
            Add(offset, value) =>
                output.push_str(&format!("add(offset={}, value={})\n", offset, value)),
            Set(offset, value) =>
                output.push_str(&format!("set(offset={}, value={})\n", offset, value)),
            MulCopy(src, dest, mul) =>
                output.push_str(&format!("mulcopy(src={}, dest={}, mul={})\n", src, dest, mul)),
            Scan(offset) =>
                output.push_str(&format!("scan(offset={})\n", offset)),
            LoadOut(offset, add) =>
                output.push_str(&format!("loadout(offset={}, add={})\n", offset, add)),
            LoadOutSet(value) =>
                output.push_str(&format!("loadoutset(value={})\n", value)),
            If(offset) =>
                output.push_str(&format!("if(offset={})\n", offset)),
            EndIf =>
                output.push_str("endif\n"),
        }
    }
    output
}
<|endoftext|>