<commit_before>use std::io::Cursor;
use std::thread;
use std::time::{Duration, Instant};

use glium::backend::glutin_backend::GlutinFacade;
use glium::draw_parameters::DrawParameters;
use glium::glutin::{ElementState, Event, VirtualKeyCode as KeyCode};
use glium::{self, texture, Surface};

use graphics_gl::Vert2;
use input::Input;
use units::V2;

static BASIC_VRT: &'static str = include_str!("shaders/basic.v.glsl");
static BASIC_FRG: &'static str = include_str!("shaders/basic.f.glsl");

static TEXT_VRT: &'static str = include_str!("shaders/text.v.glsl");
static TEXT_FRG: &'static str = include_str!("shaders/text.f.glsl");

/// On GPU Text Blitting program
pub struct TextBlitter {
    atlas:   glium::Texture2d,
    vbuf:    glium::VertexBuffer<Vert2>,
    program: glium::Program,
    indices: glium::index::NoIndices,
}

impl TextBlitter {
    pub fn new(context: &mut GlutinFacade) -> Self {
        // simple square
        let indices = glium::index::NoIndices(glium::index::PrimitiveType::TrianglesList);
        let shape = [
            Vert2 { pos: [-  1.0,   1.0,  0.0], color: [1.0, 1.0, 1.0] },
            Vert2 { pos: [-0.875,   1.0,  0.0], color: [1.0, 1.0, 1.0] },
            Vert2 { pos: [-  1.0,  0.75,  0.0], color: [1.0, 1.0, 1.0] },

            Vert2 { pos: [-0.875,  0.75,  0.0], color: [1.0, 1.0, 1.0] },
            Vert2 { pos: [-0.875,   1.0,  0.0], color: [1.0, 1.0, 1.0] },
            Vert2 { pos: [-  1.0,  0.75,  0.0], color: [1.0, 1.0, 1.0] },
        ];

        let vbuf = glium::VertexBuffer::dynamic(context, &shape)
            .ok().expect("could not alloc vbuf");

        let program = match glium::Program::from_source(context, TEXT_VRT, TEXT_FRG, None) {
            Ok(program) => program,
            Err(msg) => panic!("could not load shader: {}", msg),
        };

        let (image, dim) = load_image_tga("./simple-font.tga");
        let image = texture::RawImage2d::from_raw_rgba(image, (dim.0 as u32, dim.1 as u32));
        let texture = texture::Texture2d::new(context, image)
            .ok().expect("could not upload texture");

        TextBlitter {
            atlas:   texture,
            vbuf:    vbuf,
            program: program,
            indices: indices,
        }
    }

    pub fn draw(&self, target: &mut glium::Frame) {
        let text = "hello world";

        let mapping: Vec<(f32,f32)> = text.chars()
            .map(|cp| TextBlitter::ascii_to_ofs(cp))
            .collect();

        let mut ofs_x = 0.0;
        for &(char_x, char_y) in mapping.iter() {
            let char_uni = uniform! {
                atlas: &self.atlas,
                w_ofs: [ofs_x, 0.0, 0.0f32],
                c_ofs: [char_x, -char_y],
                scale: 0.25f32,
            };

            ofs_x += 0.03125;

            target.draw(&self.vbuf, &self.indices, &self.program, &char_uni, &DrawParameters {
                .. Default::default()
            }).ok().expect("could not blit char example");
        }
    }

    fn ascii_to_ofs(cp: char) -> (f32, f32) {
        use std::ascii::AsciiExt;

        assert!(cp.is_ascii());
        let sprite_ofs = match cp {
            'A'...'P' => (cp as u8 - 'A' as u8,      0),
            'Q'...'Z' => (cp as u8 - 'Q' as u8,      1),
            'a'...'f' => (cp as u8 - 'a' as u8 + 10, 1),
            'g'...'v' => (cp as u8 - 'g' as u8,      2),
            'w'...'z' => (cp as u8 - 'w' as u8,      3),

            ' ' => (0, 7),

            _ => panic!("unhandled character in spritemap"),
        };

        let char_x: f32 =  sprite_ofs.0 as f32 * (0.125 / 2.0);
        let char_y: f32 =  sprite_ofs.1 as f32 * (0.125 / 1.0);

        (char_x, char_y)
    }
}

pub struct Engine {
    is_running: bool,

    context:    GlutinFacade,
    controller: Input,
}

impl Engine {
    pub fn new(gl_ctx: GlutinFacade) -> Engine {
        Engine {
            is_running: true,

            context:    gl_ctx,
            controller: Input::new(),
        }
    }

    pub fn run(&mut self) {
        let target_fps_ms = Duration::from_millis(1000 / 120); // TODO: const fn?
        let game_start_at = Instant::now();

        let mut frame_start_at;
        let mut elapsed_time;

        // draw a basic shape using standard shader
        let shape = [
            // face 1
            Vert2 { pos: [ 1.0,  0.0, 0.0], color: [1.0, 0.0, 0.0] },
            Vert2 { pos: [ 0.0,  0.0, 0.0], color: [1.0, 0.0, 0.0] },
            Vert2 { pos: [ 0.0, -1.0, 0.0], color: [1.0, 0.0, 0.0] },
        ];

        let indices = glium::index::NoIndices(glium::index::PrimitiveType::TrianglesList);
        let vbuf = glium::VertexBuffer::dynamic(&self.context, &shape)
            .ok().expect("could not alloc vbuf");

        let program = match glium::Program::from_source(&self.context, BASIC_VRT, BASIC_FRG, None) {
            Ok(program) => program,
            Err(msg) => panic!("could not load shader: {}", msg),
        };

        let mut cursor_down  = false;
        let mut cursor_pts   = vec![];

        let mut cursor_x = 0;
        let mut cursor_y = 0;

        let text_blitter = TextBlitter::new(&mut self.context);

        while self.is_running {
            // cut new frame
            frame_start_at = Instant::now();
            self.controller.begin_new_frame();

            // process platform events
            for evt in self.context.poll_events() {
                match evt {
                    Event::Closed => self.is_running = false,
                    Event::KeyboardInput(ElementState::Pressed, _, Some(key)) => {
                        self.controller.key_down_event(key);
                    },

                    Event::KeyboardInput(ElementState::Released, _, Some(key)) => {
                        self.controller.key_up_event(key);
                    },

                    Event::MouseInput(ElementState::Pressed,  _)  => cursor_down = true,
                    Event::MouseInput(ElementState::Released, _)  => cursor_down = false,

                    Event::MouseMoved(x,y) => { cursor_x = x; cursor_y = y },

                    _ => (),
                }
            }

            let (wx, wy) = Engine::world_to_unit(cursor_x as f64, cursor_y as f64);
            if cursor_down {
                cursor_pts.push(V2(cursor_x as i64, cursor_y as i64));
            }

            if self.controller.was_key_pressed(KeyCode::Escape) {
                self.is_running = false;
            }

            // composite frame
            let mut target = self.context.draw();
            target.clear_color(0.05, 0.05, 0.05, 1.0);

            let tri_params = glium::DrawParameters {
                .. Default::default()
            };

            let mut time_ms = 0.0;
            let time = Instant::now().duration_since(game_start_at);
            time_ms += time.as_secs() as f64 * 1000.0;
            time_ms += time.subsec_nanos() as f64 * 0.001 * 0.001;

            // cursor
            let cursor_uni = uniform! {
                ofs:   [wx as f32, wy as f32, 0.0f32],
                scale: 0.15f32,
                timer: time_ms as f32 * 0.001,
            };

            text_blitter.draw(&mut target);

            target.draw(&vbuf, &indices, &program, &cursor_uni, &tri_params)
                .ok().expect("could not blit cursor example");

            target.finish()
                .ok().expect("could not render frame");

            // sleep for a bit if we made our deadline
            elapsed_time = frame_start_at.elapsed();
            let sleep_time = if elapsed_time > target_fps_ms {
                Duration::from_millis(0)
            } else { target_fps_ms - elapsed_time };

            thread::sleep(sleep_time);
        }
    }

    fn world_to_unit(x: f64, y: f64) -> (f64, f64) {
        let adj_x = x / 640.0;
        let adj_y = y / 360.0;
        ( (adj_x - 1.0), -(adj_y - 1.0) )
    }
}

// TODO: move to util module
// TODO: -> Result<>
//
fn load_image_tga(path_text: &str) -> (Vec<u8>, (usize,usize)) {
    use std::fs::File;
    use std::io::Read;
    use std::path::Path;

    let path = Path::new(path_text);
    let mut file = File::open(path)
        .ok().expect(&format!("could not find image file @ {:?}", path)[..]);

    // read file into byte buffer
    let mut buf = vec![];
    let mut ofs = 0;
    file.read_to_end(&mut buf);

    assert!(buf[0] == 0); // no id field
    assert!(buf[1] == 0); // no color map
    assert!(buf[2] == 2); // uncompressed true color
    ofs += 3; ofs += 5;   // skip header & color map

    let x_origin = (buf[ofs + 0] as u16) << 8 | buf[ofs + 1] as u16; ofs += 2;
    let y_origin = (buf[ofs + 0] as u16) << 8 | buf[ofs + 1] as u16; ofs += 2;

    let width  = (buf[ofs + 1] as u16) << 8 | buf[ofs + 0] as u16; ofs += 2;
    let height = (buf[ofs + 1] as u16) << 8 | buf[ofs + 0] as u16; ofs += 2;
    let depth  = buf[ofs]; ofs += 1;
    let descriptor = buf[ofs]; ofs += 1;

    println!("x origin: {}, y origin: {}", x_origin, y_origin);
    println!("bpp: {}, width: {}, height: {}", depth, width, height);
    println!("descriptor: {:08b}", descriptor);

    println!("reading image data");
    let width  = width as usize;
    let height = height as usize;
    let pitch  = (depth / 8) as usize;
    let size   = width * height * pitch;
    assert!(pitch == 4);

    let mut rgba = Vec::with_capacity(size);
    for row in 0..height {
        for col in 0..width {
            let px_ofs = (row * width * pitch) + (col * pitch);
            if buf[ofs + px_ofs + 3] == 0 {
                rgba.extend_from_slice(&[0x00, 0x00, 0x00, 0xFF]);
            } else {
                rgba.push(buf[ofs + px_ofs + 2]);
                rgba.push(buf[ofs + px_ofs + 1]);
                rgba.push(buf[ofs + px_ofs + 0]);
                rgba.push(buf[ofs + px_ofs + 3]);
            }
        }
    }

    assert!(rgba.len() == width * height * 4);
    return (rgba, (width,height));
}
<commit_msg>dynamic text blitting ex.<commit_after>use std::io::Cursor;
use std::thread;
use std::time::{Duration, Instant};

use glium::backend::glutin_backend::GlutinFacade;
use glium::draw_parameters::DrawParameters;
use glium::glutin::{ElementState, Event, VirtualKeyCode as KeyCode};
use glium::{self, texture, Surface};

use graphics_gl::Vert2;
use input::Input;
use units::V2;

static BASIC_VRT: &'static str = include_str!("shaders/basic.v.glsl");
static BASIC_FRG: &'static str = include_str!("shaders/basic.f.glsl");

static TEXT_VRT: &'static str = include_str!("shaders/text.v.glsl");
static TEXT_FRG: &'static str = include_str!("shaders/text.f.glsl");

/// On GPU Text Blitting program
pub struct TextBlitter {
    atlas:   glium::Texture2d,
    vbuf:    glium::VertexBuffer<Vert2>,
    program: glium::Program,
    indices: glium::index::NoIndices,
}

impl TextBlitter {
    pub fn new(context: &mut GlutinFacade) -> Self {
        // simple square
        let indices = glium::index::NoIndices(glium::index::PrimitiveType::TrianglesList);
        let shape = [
            Vert2 { pos: [-  1.0,   1.0,  0.0], color: [1.0, 1.0, 1.0] },
            Vert2 { pos: [-0.875,   1.0,  0.0], color: [1.0, 1.0, 1.0] },
            Vert2 { pos: [-  1.0,  0.75,  0.0], color: [1.0, 1.0, 1.0] },

            Vert2 { pos: [-0.875,  0.75,  0.0], color: [1.0, 1.0, 1.0] },
            Vert2 { pos: [-0.875,   1.0,  0.0], color: [1.0, 1.0, 1.0] },
            Vert2 { pos: [-  1.0,  0.75,  0.0], color: [1.0, 1.0, 1.0] },
        ];

        let vbuf = glium::VertexBuffer::dynamic(context, &shape)
            .ok().expect("could not alloc vbuf");

        let program = match glium::Program::from_source(context, TEXT_VRT, TEXT_FRG, None) {
            Ok(program) => program,
            Err(msg) => panic!("could not load shader: {}", msg),
        };

        let (image, dim) = load_image_tga("./simple-font.tga");
        let image = texture::RawImage2d::from_raw_rgba(image, (dim.0 as u32, dim.1 as u32));
        let texture = texture::Texture2d::new(context, image)
            .ok().expect("could not upload texture");

        TextBlitter {
            atlas:   texture,
            vbuf:    vbuf,
            program: program,
            indices: indices,
        }
    }

    pub fn draw(&self, text: &str, target: &mut glium::Frame) {
        let mapping: Vec<(f32,f32)> = text.chars()
            .map(|cp| TextBlitter::ascii_to_ofs(cp))
            .collect();

        let mut ofs_x = 0.0;
        for &(char_x, char_y) in mapping.iter() {
            let char_uni = uniform! {
                atlas: &self.atlas,
                w_ofs: [ofs_x, 0.0, 0.0f32],
                c_ofs: [char_x, -char_y],
                scale: 0.25f32,
            };

            // (one character in texture space) * (screen space)
            ofs_x += (16.0 / 256.0) * 0.5;

            target.draw(&self.vbuf, &self.indices, &self.program, &char_uni, &DrawParameters {
                .. Default::default()
            }).ok().expect("could not blit char example");
        }
    }

    fn ascii_to_ofs(cp: char) -> (f32, f32) {
        use std::ascii::AsciiExt;

        assert!(cp.is_ascii());
        let sprite_ofs = match cp {
            'A'...'P' => (cp as u8 - 'A' as u8,      0),
            'Q'...'Z' => (cp as u8 - 'Q' as u8,      1),
            'a'...'f' => (cp as u8 - 'a' as u8 + 10, 1),
            'g'...'v' => (cp as u8 - 'g' as u8,      2),
            'w'...'z' => (cp as u8 - 'w' as u8,      3),

            '1'...'9'  => ((cp as u8 - '1' as u8)  + 4, 3),
            '0'        => (('9' as u8 - '0' as u8) + 4, 3),

            ' ' => (0, 7),

            _ => panic!("unhandled character in spritemap"),
        };

        let char_x: f32 =  sprite_ofs.0 as f32 * (0.125 / 2.0);
        let char_y: f32 =  sprite_ofs.1 as f32 * (0.125 / 1.0);

        (char_x, char_y)
    }
}

pub struct Engine {
    is_running: bool,

    context:    GlutinFacade,
    controller: Input,
}

impl Engine {
    pub fn new(gl_ctx: GlutinFacade) -> Engine {
        Engine {
            is_running: true,

            context:    gl_ctx,
            controller: Input::new(),
        }
    }

    pub fn run(&mut self) {
        let target_fps_ms = Duration::from_millis(1000 / 120); // TODO: const fn?
        let game_start_at = Instant::now();

        let mut frame_start_at;
        let mut elapsed_time;

        // draw a basic shape using standard shader
        let shape = [
            // face 1
            Vert2 { pos: [ 1.0,  0.0, 0.0], color: [1.0, 0.0, 0.0] },
            Vert2 { pos: [ 0.0,  0.0, 0.0], color: [1.0, 0.0, 0.0] },
            Vert2 { pos: [ 0.0, -1.0, 0.0], color: [1.0, 0.0, 0.0] },
        ];

        let indices = glium::index::NoIndices(glium::index::PrimitiveType::TrianglesList);
        let vbuf = glium::VertexBuffer::dynamic(&self.context, &shape)
            .ok().expect("could not alloc vbuf");

        let program = match glium::Program::from_source(&self.context, BASIC_VRT, BASIC_FRG, None) {
            Ok(program) => program,
            Err(msg) => panic!("could not load shader: {}", msg),
        };

        let mut cursor_down  = false;
        let mut cursor_pts   = vec![];

        let mut cursor_x = 0;
        let mut cursor_y = 0;

        let text_blitter = TextBlitter::new(&mut self.context);
        let mut text_count = 0;
        let mut frame_count = 0;

        while self.is_running {
            // cut new frame
            frame_start_at = Instant::now();
            self.controller.begin_new_frame();

            // process platform events
            for evt in self.context.poll_events() {
                match evt {
                    Event::Closed => self.is_running = false,
                    Event::KeyboardInput(ElementState::Pressed, _, Some(key)) => {
                        self.controller.key_down_event(key);
                    },

                    Event::KeyboardInput(ElementState::Released, _, Some(key)) => {
                        self.controller.key_up_event(key);
                    },

                    Event::MouseInput(ElementState::Pressed,  _)  => cursor_down = true,
                    Event::MouseInput(ElementState::Released, _)  => cursor_down = false,

                    Event::MouseMoved(x,y) => { cursor_x = x; cursor_y = y },

                    _ => (),
                }
            }

            let (wx, wy) = Engine::world_to_unit(cursor_x as f64, cursor_y as f64);
            if cursor_down {
                cursor_pts.push(V2(cursor_x as i64, cursor_y as i64));
            }

            if self.controller.was_key_pressed(KeyCode::Escape) {
                self.is_running = false;
            }

            // composite frame
            let mut target = self.context.draw();
            target.clear_color(0.05, 0.05, 0.05, 1.0);

            let tri_params = glium::DrawParameters {
                .. Default::default()
            };

            let mut time_ms = 0.0;
            let time = Instant::now().duration_since(game_start_at);
            time_ms += time.as_secs() as f64 * 1000.0;
            time_ms += time.subsec_nanos() as f64 * 0.001 * 0.001;

            // cursor
            let cursor_uni = uniform! {
                ofs:   [wx as f32, wy as f32, 0.0f32],
                scale: 0.15f32,
                timer: time_ms as f32 * 0.001,
            };

            frame_count += 1;
            if frame_count > 10 {
                frame_count = 0;
                text_count = (text_count + 1) % 0xFF;
            }

            let text_out = format!("hi 0x{:02X}", text_count);
            text_blitter.draw(&text_out[..], &mut target);

            target.draw(&vbuf, &indices, &program, &cursor_uni, &tri_params)
                .ok().expect("could not blit cursor example");

            target.finish()
                .ok().expect("could not render frame");

            // sleep for a bit if we made our deadline
            elapsed_time = frame_start_at.elapsed();
            let sleep_time = if elapsed_time > target_fps_ms {
                Duration::from_millis(0)
            } else { target_fps_ms - elapsed_time };

            thread::sleep(sleep_time);
        }
    }

    fn world_to_unit(x: f64, y: f64) -> (f64, f64) {
        let adj_x = x / 640.0;
        let adj_y = y / 360.0;
        ( (adj_x - 1.0), -(adj_y - 1.0) )
    }
}

// TODO: move to util module
// TODO: -> Result<>
//
fn load_image_tga(path_text: &str) -> (Vec<u8>, (usize,usize)) {
    use std::fs::File;
    use std::io::Read;
    use std::path::Path;

    let path = Path::new(path_text);
    let mut file = File::open(path)
        .ok().expect(&format!("could not find image file @ {:?}", path)[..]);

    // read file into byte buffer
    let mut buf = vec![];
    let mut ofs = 0;
    file.read_to_end(&mut buf);

    assert!(buf[0] == 0); // no id field
    assert!(buf[1] == 0); // no color map
    assert!(buf[2] == 2); // uncompressed true color
    ofs += 3; ofs += 5;   // skip header & color map

    let x_origin = (buf[ofs + 0] as u16) << 8 | buf[ofs + 1] as u16; ofs += 2;
    let y_origin = (buf[ofs + 0] as u16) << 8 | buf[ofs + 1] as u16; ofs += 2;

    let width  = (buf[ofs + 1] as u16) << 8 | buf[ofs + 0] as u16; ofs += 2;
    let height = (buf[ofs + 1] as u16) << 8 | buf[ofs + 0] as u16; ofs += 2;
    let depth  = buf[ofs]; ofs += 1;
    let descriptor = buf[ofs]; ofs += 1;

    println!("x origin: {}, y origin: {}", x_origin, y_origin);
    println!("bpp: {}, width: {}, height: {}", depth, width, height);
    println!("descriptor: {:08b}", descriptor);

    println!("reading image data");
    let width  = width as usize;
    let height = height as usize;
    let pitch  = (depth / 8) as usize;
    let size   = width * height * pitch;
    assert!(pitch == 4);

    let mut rgba = Vec::with_capacity(size);
    for row in 0..height {
        for col in 0..width {
            let px_ofs = (row * width * pitch) + (col * pitch);
            if buf[ofs + px_ofs + 3] == 0 {
                rgba.extend_from_slice(&[0x00, 0x00, 0x00, 0xFF]);
            } else {
                rgba.push(buf[ofs + px_ofs + 2]);
                rgba.push(buf[ofs + px_ofs + 1]);
                rgba.push(buf[ofs + px_ofs + 0]);
                rgba.push(buf[ofs + px_ofs + 3]);
            }
        }
    }

    assert!(rgba.len() == width * height * 4);
    return (rgba, (width,height));
}
<|endoftext|>


<commit_before>#[doc ="
Utilities that leverage libuv's `uv_timer_*` API
"];

import uv = uv;
export delayed_send, sleep, recv_timeout;

#[doc = "
Wait for timeout period then send provided value over a channel

This call returns immediately. Useful as the building block for a number
of higher-level timer functions.

Is not guaranteed to wait for exactly the specified time, but will wait
for *at least* that period of time.

# Arguments

* msecs - a timeout period, in milliseconds, to wait
* ch - a channel of type T to send a `val` on
* val - a value of type T to send over the provided `ch`
"]
fn delayed_send<T: send>(msecs: uint, ch: comm::chan<T>, val: T) {
    task::spawn() {||
        unsafe {
            let timer_done_po = comm::port::<()>();
            let timer_done_ch = comm::chan(timer_done_po);
            let timer_done_ch_ptr = ptr::addr_of(timer_done_ch);
            let timer = uv::ll::timer_t();
            let timer_ptr = ptr::addr_of(timer);
            let hl_loop = uv::global_loop::get();
            uv::hl::interact(hl_loop) {|loop_ptr|
                let init_result = uv::ll::timer_init(loop_ptr, timer_ptr);
                if (init_result == 0i32) {
                    let start_result = uv::ll::timer_start(
                        timer_ptr, delayed_send_cb, msecs, 0u);
                    if (start_result == 0i32) {
                        uv::ll::set_data_for_uv_handle(
                            timer_ptr,
                            timer_done_ch_ptr as *libc::c_void);
                    }
                    else {
                        let error_msg = uv::ll::get_last_err_info(loop_ptr);
                        fail "timer::delayed_send() start failed: "+error_msg;
                    }
                }
                else {
                    let error_msg = uv::ll::get_last_err_info(loop_ptr);
                    fail "timer::delayed_send() init failed: "+error_msg;
                }
            };
            // delayed_send_cb has been processed by libuv
            comm::recv(timer_done_po);
            // notify the caller immediately
            comm::send(ch, copy(val));
            // uv_close for this timer has been processed
            comm::recv(timer_done_po);
        }
    };
}

#[doc = "
Blocks the current task for (at least) the specified time period.

Is not guaranteed to sleep for exactly the specified time, but will sleep
for *at least* that period of time.

# Arguments

* msecs - an amount of time, in milliseconds, for the current task to block
"]
fn sleep(msecs: uint) {
    let exit_po = comm::port::<()>();
    let exit_ch = comm::chan(exit_po);
    delayed_send(msecs, exit_ch, ());
    comm::recv(exit_po);
}

#[doc = "
Receive on a port for (up to) a specified time, then return an `option<T>`

This call will block to receive on the provided port for up to the specified
timeout. Depending on whether the provided port receives in that time period,
`recv_timeout` will return an `option<T>` representing the result.

# Arguments

* msecs - an mount of time, in milliseconds, to wait to receive
* wait_port - a `comm::port<T>` to receive on

# Returns

An `option<T>` representing the outcome of the call. If the call `recv`'d on
the provided port in the allotted timeout period, then the result will be a
`some(T)`. If not, then `none` will be returned.
"]
fn recv_timeout<T: send>(msecs: uint, wait_po: comm::port<T>) -> option<T> {
    let timeout_po = comm::port::<()>();
    let timeout_ch = comm::chan(timeout_po);
    delayed_send(msecs, timeout_ch, ());
    either::either(
        {|left_val|
            log(debug, #fmt("recv_time .. left_val %?",
                           left_val));
            none
        }, {|right_val|
            some(right_val)
        }, comm::select2(timeout_po, wait_po)
    )
}

// INTERNAL API
crust fn delayed_send_cb(handle: *uv::ll::uv_timer_t,
                                status: libc::c_int) unsafe {
    log(debug, #fmt("delayed_send_cb handle %? status %?", handle, status));
    let timer_done_ch =
        *(uv::ll::get_data_for_uv_handle(handle) as *comm::chan<()>);
    let stop_result = uv::ll::timer_stop(handle);
    if (stop_result == 0i32) {
        comm::send(timer_done_ch, ());
        uv::ll::close(handle, delayed_send_close_cb);
    }
    else {
        let loop_ptr = uv::ll::get_loop_for_uv_handle(handle);
        let error_msg = uv::ll::get_last_err_info(loop_ptr);
        fail "timer::sleep() init failed: "+error_msg;
    }
}

crust fn delayed_send_close_cb(handle: *uv::ll::uv_timer_t) unsafe {
    log(debug, #fmt("delayed_send_close_cb handle %?", handle));
    let timer_done_ch =
        *(uv::ll::get_data_for_uv_handle(handle) as *comm::chan<()>);
    comm::send(timer_done_ch, ());
}

#[cfg(test)]
mod test {
    #[test]
    fn test_gl_timer_simple_sleep_test() {
        sleep(1u);
    }

    #[test]
    fn test_gl_timer_sleep_stress1() {
        iter::repeat(500u) {||
            sleep(1u);
        }
    }

    #[test]
    fn test_gl_timer_sleep_stress2() {
        let po = comm::port();
        let ch = comm::chan(po);

        let repeat = 100u;
        let spec = {

            [(1u, 100u),
             (10u, 10u),
             (100u, 2u)]

        };

        iter::repeat(repeat) {||

            for spec.each {|spec|
                let (times, maxms) = spec;
                task::spawn {||
                    import rand::*;
                    let rng = rng();
                    iter::repeat(times) {||
                        sleep(rng.next() as uint % maxms);
                    }
                    comm::send(ch, ());
                }
            }
        }

        iter::repeat(repeat * spec.len()) {||
            comm::recv(po)
        }
    }

    #[test]
    fn test_gl_timer_recv_timeout_before_time_passes() {
        let expected = rand::rng().gen_str(16u);
        let test_po = comm::port::<str>();
        let test_ch = comm::chan(test_po);

        task::spawn() {||
            delayed_send(1u, test_ch, expected);
        };

        let actual = alt recv_timeout(1000u, test_po) {
          some(val) { val }
          _ { fail "test_timer_recv_timeout_before_time_passes:"+
                    " didn't receive result before timeout"; }
        };
        assert actual == expected;
    }

    #[test]
    fn test_gl_timer_recv_timeout_after_time_passes() {
        let expected = rand::rng().gen_str(16u);
        let fail_msg = rand::rng().gen_str(16u);
        let test_po = comm::port::<str>();
        let test_ch = comm::chan(test_po);

        task::spawn() {||
            delayed_send(1000u, test_ch, expected);
        };

        let actual = alt recv_timeout(1u, test_po) {
          none { fail_msg }
          _ { fail "test_timer_recv_timeout_before_time_passes:"+
                    " didn't receive result before timeout"; }
        };
        assert actual == fail_msg;
    }
}
<commit_msg>std: Do less work in the timer stress tests<commit_after>#[doc ="
Utilities that leverage libuv's `uv_timer_*` API
"];

import uv = uv;
export delayed_send, sleep, recv_timeout;

#[doc = "
Wait for timeout period then send provided value over a channel

This call returns immediately. Useful as the building block for a number
of higher-level timer functions.

Is not guaranteed to wait for exactly the specified time, but will wait
for *at least* that period of time.

# Arguments

* msecs - a timeout period, in milliseconds, to wait
* ch - a channel of type T to send a `val` on
* val - a value of type T to send over the provided `ch`
"]
fn delayed_send<T: send>(msecs: uint, ch: comm::chan<T>, val: T) {
    task::spawn() {||
        unsafe {
            let timer_done_po = comm::port::<()>();
            let timer_done_ch = comm::chan(timer_done_po);
            let timer_done_ch_ptr = ptr::addr_of(timer_done_ch);
            let timer = uv::ll::timer_t();
            let timer_ptr = ptr::addr_of(timer);
            let hl_loop = uv::global_loop::get();
            uv::hl::interact(hl_loop) {|loop_ptr|
                let init_result = uv::ll::timer_init(loop_ptr, timer_ptr);
                if (init_result == 0i32) {
                    let start_result = uv::ll::timer_start(
                        timer_ptr, delayed_send_cb, msecs, 0u);
                    if (start_result == 0i32) {
                        uv::ll::set_data_for_uv_handle(
                            timer_ptr,
                            timer_done_ch_ptr as *libc::c_void);
                    }
                    else {
                        let error_msg = uv::ll::get_last_err_info(loop_ptr);
                        fail "timer::delayed_send() start failed: "+error_msg;
                    }
                }
                else {
                    let error_msg = uv::ll::get_last_err_info(loop_ptr);
                    fail "timer::delayed_send() init failed: "+error_msg;
                }
            };
            // delayed_send_cb has been processed by libuv
            comm::recv(timer_done_po);
            // notify the caller immediately
            comm::send(ch, copy(val));
            // uv_close for this timer has been processed
            comm::recv(timer_done_po);
        }
    };
}

#[doc = "
Blocks the current task for (at least) the specified time period.

Is not guaranteed to sleep for exactly the specified time, but will sleep
for *at least* that period of time.

# Arguments

* msecs - an amount of time, in milliseconds, for the current task to block
"]
fn sleep(msecs: uint) {
    let exit_po = comm::port::<()>();
    let exit_ch = comm::chan(exit_po);
    delayed_send(msecs, exit_ch, ());
    comm::recv(exit_po);
}

#[doc = "
Receive on a port for (up to) a specified time, then return an `option<T>`

This call will block to receive on the provided port for up to the specified
timeout. Depending on whether the provided port receives in that time period,
`recv_timeout` will return an `option<T>` representing the result.

# Arguments

* msecs - an mount of time, in milliseconds, to wait to receive
* wait_port - a `comm::port<T>` to receive on

# Returns

An `option<T>` representing the outcome of the call. If the call `recv`'d on
the provided port in the allotted timeout period, then the result will be a
`some(T)`. If not, then `none` will be returned.
"]
fn recv_timeout<T: send>(msecs: uint, wait_po: comm::port<T>) -> option<T> {
    let timeout_po = comm::port::<()>();
    let timeout_ch = comm::chan(timeout_po);
    delayed_send(msecs, timeout_ch, ());
    either::either(
        {|left_val|
            log(debug, #fmt("recv_time .. left_val %?",
                           left_val));
            none
        }, {|right_val|
            some(right_val)
        }, comm::select2(timeout_po, wait_po)
    )
}

// INTERNAL API
crust fn delayed_send_cb(handle: *uv::ll::uv_timer_t,
                                status: libc::c_int) unsafe {
    log(debug, #fmt("delayed_send_cb handle %? status %?", handle, status));
    let timer_done_ch =
        *(uv::ll::get_data_for_uv_handle(handle) as *comm::chan<()>);
    let stop_result = uv::ll::timer_stop(handle);
    if (stop_result == 0i32) {
        comm::send(timer_done_ch, ());
        uv::ll::close(handle, delayed_send_close_cb);
    }
    else {
        let loop_ptr = uv::ll::get_loop_for_uv_handle(handle);
        let error_msg = uv::ll::get_last_err_info(loop_ptr);
        fail "timer::sleep() init failed: "+error_msg;
    }
}

crust fn delayed_send_close_cb(handle: *uv::ll::uv_timer_t) unsafe {
    log(debug, #fmt("delayed_send_close_cb handle %?", handle));
    let timer_done_ch =
        *(uv::ll::get_data_for_uv_handle(handle) as *comm::chan<()>);
    comm::send(timer_done_ch, ());
}

#[cfg(test)]
mod test {
    #[test]
    fn test_gl_timer_simple_sleep_test() {
        sleep(1u);
    }

    #[test]
    fn test_gl_timer_sleep_stress1() {
        iter::repeat(200u) {||
            sleep(1u);
        }
    }

    #[test]
    fn test_gl_timer_sleep_stress2() {
        let po = comm::port();
        let ch = comm::chan(po);

        let repeat = 20u;
        let spec = {

            [(1u,  20u),
             (10u, 10u),
             (20u, 2u)]

        };

        iter::repeat(repeat) {||

            for spec.each {|spec|
                let (times, maxms) = spec;
                task::spawn {||
                    import rand::*;
                    let rng = rng();
                    iter::repeat(times) {||
                        sleep(rng.next() as uint % maxms);
                    }
                    comm::send(ch, ());
                }
            }
        }

        iter::repeat(repeat * spec.len()) {||
            comm::recv(po)
        }
    }

    #[test]
    fn test_gl_timer_recv_timeout_before_time_passes() {
        let expected = rand::rng().gen_str(16u);
        let test_po = comm::port::<str>();
        let test_ch = comm::chan(test_po);

        task::spawn() {||
            delayed_send(1u, test_ch, expected);
        };

        let actual = alt recv_timeout(1000u, test_po) {
          some(val) { val }
          _ { fail "test_timer_recv_timeout_before_time_passes:"+
                    " didn't receive result before timeout"; }
        };
        assert actual == expected;
    }

    #[test]
    fn test_gl_timer_recv_timeout_after_time_passes() {
        let expected = rand::rng().gen_str(16u);
        let fail_msg = rand::rng().gen_str(16u);
        let test_po = comm::port::<str>();
        let test_ch = comm::chan(test_po);

        task::spawn() {||
            delayed_send(1000u, test_ch, expected);
        };

        let actual = alt recv_timeout(1u, test_po) {
          none { fail_msg }
          _ { fail "test_timer_recv_timeout_before_time_passes:"+
                    " didn't receive result before timeout"; }
        };
        assert actual == fail_msg;
    }
}
<|endoftext|>


<commit_before>use redox::{Vec, String, ToString};
use redox::collections::{BTreeMap, VecDeque};

use super::dvaddr::DVAddr;
use super::zio;

/// MRU - Most Recently Used cache
struct Mru {
    // TODO: keep track of use counts. So mru_map becomes (use_count: u64, Vec<u8>)
    map: BTreeMap<DVAddr, (u64, Vec<u8>)>,
    queue: VecDeque<DVAddr>, // Oldest DVAddrs are at the end
    size: usize, // Max mru cache size in bytes
    used: usize, // Used bytes in mru cache
}

impl Mru {
    pub fn new() -> Self {
        Mru {
            map: BTreeMap::new(),
            queue: VecDeque::new(),
            size: 10,
            used: 0,
        }
    }
}

/// MFU - Most Frequently Used cache
struct Mfu {
    // TODO: Keep track of use counts. So mfu_map becomes (use_count: u64, Vec<u8>). Reset the use
    // count every once in a while. For instance, every 1000 reads. This will probably end up being
    // a knob for the user.
    // TODO: Keep track of minimum frequency and corresponding DVA
    map: BTreeMap<DVAddr, (u64, Vec<u8>)>,
    size: usize, // Max mfu cache size in bytes
    used: usize, // Used bytes in mfu cache
}

impl Mfu {
    pub fn new() -> Self {
        Mfu {
            map: BTreeMap::new(),
            size: 10,
            used: 0,
        }
    }
}

// Our implementation of the Adaptive Replacement Cache (ARC) is set up to allocate
// its buffer on the heap rather than in a private pool thing. This makes it much
// simpler to implement, but defers the fragmentation problem to the heap allocator.
// We named the type `ArCache` to avoid confusion with Rust's `Arc` reference type.
pub struct ArCache {
    // MRU
    // TODO: keep track of use counts. So mru_map becomes (use_count: u64, Vec<u8>)
    mru_map: BTreeMap<DVAddr, Vec<u8>>, // Most recently used cache
    mru_queue: VecDeque<DVAddr>, // Oldest DVAddrs are at the end
    mru_size: usize, // Max mru cache size in blocks
    mru_used: usize, // Number of used blocks in mru cache

    // MFU
    // TODO: Keep track of use counts. So mfu_map becomes (use_count: u64, Vec<u8>). Reset the use
    // count every once in a while. For instance, every 1000 reads. This will probably end up being
    // a knob for the user.
    // TODO: Keep track of minimum frequency and corresponding DVA
    mfu_map: BTreeMap<DVAddr, Vec<u8>>, // Most frequently used cache
    mfu_size: usize, // Max mfu cache size in blocks
    mfu_used: usize, // Number of used bytes in mfu cache
}

impl ArCache {
    pub fn new() -> Self {
        ArCache {
            mru_map: BTreeMap::new(),
            mru_queue: VecDeque::new(),
            mru_size: 1000,
            mru_used: 0,

            mfu_map: BTreeMap::new(),
            mfu_size: 1000,
            mfu_used: 0,
        }
    }

    pub fn read(&mut self, reader: &mut zio::Reader, dva: &DVAddr) -> Result<Vec<u8>, String> {
        if let Some(block) = self.mru_map.get(dva) {
            // TODO: Keep track of MRU DVA use count. If it gets used a second time, move the block into
            // the MFU cache.

            // Block is cached
            return Ok(block.clone());
        }
        if let Some(block) = self.mfu_map.get(dva) {
            // TODO: keep track of DVA use count
            // Block is cached
            return Ok(block.clone());
        }

        // Block isn't cached, have to read it from disk
        let block = reader.read(dva.sector() as usize, dva.asize() as usize);

        // Blocks start in MRU cache
        self.mru_cache_block(dva, block)
    }

    fn mru_cache_block(&mut self, dva: &DVAddr, block: Vec<u8>) -> Result<Vec<u8>, String>{
        // If necessary, make room for the block in the cache
        while self.mru_used + (dva.asize() as usize) > self.mru_size {
            let last_dva =
                match self.mru_queue.pop_back()
                {
                    Some(dva) => dva,
                    None => return Err("No more ARC MRU items to free".to_string()),
                };
            self.mru_map.remove(&last_dva);
            self.mru_used -= last_dva.asize() as usize;
        }

        // Add the block to the cache
        self.mru_used += dva.asize() as usize;
        self.mru_map.insert(*dva, block);
        self.mru_queue.push_front(*dva);
        Ok(self.mru_map.get(dva).unwrap().clone())
    }

    // TODO: mfu_cache_block. Remove the DVA with the lowest frequency
}
<commit_msg>Reorganized code with the `Mru` and `Mfu` types<commit_after>use redox::{Vec, String, ToString};
use redox::collections::{BTreeMap, VecDeque};

use super::dvaddr::DVAddr;
use super::zio;

/// MRU - Most Recently Used cache
struct Mru {
    // TODO: keep track of use counts. So mru_map becomes (use_count: u64, Vec<u8>)
    map: BTreeMap<DVAddr, (u64, Vec<u8>)>,
    queue: VecDeque<DVAddr>, // Oldest DVAddrs are at the end
    size: usize, // Max mru cache size in bytes
    used: usize, // Used bytes in mru cache
}

impl Mru {
    pub fn new() -> Self {
        Mru {
            map: BTreeMap::new(),
            queue: VecDeque::new(),
            size: 1000,
            used: 0,
        }
    }

    fn cache_block(&mut self, dva: &DVAddr, block: Vec<u8>) -> Result<Vec<u8>, String> {
        // If necessary, make room for the block in the cache
        while self.used + block.len() > self.size {
            let last_dva = match self.queue.pop_back() {
                Some(dva) => dva,
                None => return Err("No more ARC MRU items to free".to_string()),
            };
            self.map.remove(&last_dva);
            self.used -= last_dva.asize() as usize;
        }

        // Add the block to the cache
        self.used += block.len();
        self.map.insert(*dva, (1337, block));
        self.queue.push_front(*dva);
        Ok(self.map.get(dva).unwrap().1.clone())
    }
}

/// MFU - Most Frequently Used cache
struct Mfu {
    // TODO: Keep track of use counts. So mfu_map becomes (use_count: u64, Vec<u8>). Reset the use
    // count every once in a while. For instance, every 1000 reads. This will probably end up being
    // a knob for the user.
    // TODO: Keep track of minimum frequency and corresponding DVA
    map: BTreeMap<DVAddr, (u64, Vec<u8>)>,
    size: usize, // Max mfu cache size in bytes
    used: usize, // Used bytes in mfu cache
}

impl Mfu {
    pub fn new() -> Self {
        Mfu {
            map: BTreeMap::new(),
            size: 1000,
            used: 0,
        }
    }

    // TODO: cache_block. Remove the DVA with the lowest frequency
    fn cache_block(&mut self, dva: &DVAddr, block: Vec<u8>) -> Result<Vec<u8>, String> {
    }
}

// Our implementation of the Adaptive Replacement Cache (ARC) is set up to allocate
// its buffer on the heap rather than in a private pool thing. This makes it much
// simpler to implement, but defers the fragmentation problem to the heap allocator.
// We named the type `ArCache` to avoid confusion with Rust's `Arc` reference type.
pub struct ArCache {
    mru: Mru,
    mfu: Mfu,
}

impl ArCache {
    pub fn new() -> Self {
        ArCache {
            mru: Mru::new(),
            mfu: Mfu::new(),
        }
    }

    pub fn read(&mut self, reader: &mut zio::Reader, dva: &DVAddr) -> Result<Vec<u8>, String> {
        if let Some(block) = self.mru.map.get(dva) {
            // TODO: Keep track of MRU DVA use count. If it gets used a second time, move the block into
            // the MFU cache.

            // Block is cached
            return Ok(block.1.clone());
        }
        if let Some(block) = self.mfu.map.get(dva) {
            // TODO: keep track of DVA use count
            // Block is cached
            return Ok(block.1.clone());
        }

        // Block isn't cached, have to read it from disk
        let block = reader.read(dva.sector() as usize, dva.asize() as usize);

        // Blocks start in MRU cache
        self.mru.cache_block(dva, block)
    }
}
<|endoftext|>


<commit_before>//! Binary lambda calculus execution

use binary_encoding::{from_binary};
use lambda_encoding::{encode, decode};
use lambda_calculus::reduction::beta_full;
use self::Error::*;
use self::Input::*;

/// An error that can occur during BLC execution.
#[derive(Debug, PartialEq)]
pub enum Error {
    InvalidProgram,
    InvalidArgument
}

/// The type of input for BLC execution.
pub enum Input<'a> {
    /// no input parameter
    Nothing,
    /// BLC input
    Binary(&'a [u8]),
    /// unencoded byte input
    Bytes(&'a [u8])
}

/// Executes a binary lambda calculus program, optionally feeding it the given argument.
///
/// # Example
/// ```
/// use blc::execution::run;
///
/// let reverse = b"00 01 01 10 01 00 01 10 10 00 00 00 00 01 01 110\
///                 01 11110 11110 00 01 01 10 11110 110 00 00 10";
///
/// assert_eq!(run(&*reverse, Some(b"herp derp")), Ok("pred preh".into()));
/// ```
pub fn run(blc_program: &[u8], input: Input) -> Result<String, Error> {
    let program = from_binary(blc_program);
    if program.is_err() { return Err(InvalidProgram) }
    let program = program.unwrap(); // safe

    let calculation = match input {
        Nothing     => beta_full(program),
        Bytes(arg)  => beta_full(app!(program, encode(arg))),
        Binary(arg) => {
            let arg = from_binary(arg);
            if arg.is_ok() {
                beta_full(app!(program, arg.unwrap())) // safe
            } else {
                return Err(InvalidArgument)
            }
        }
    };

    Ok(decode(calculation))
}

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

    #[test]
    fn id() {
        let id_compressed = b" ";
        let id_blc        = decompress(&*id_compressed);
        let input         = b"herp derp";

        assert_eq!(run(&*id_blc, Bytes(&*input)).unwrap(), "herp derp".to_owned());
    }

    #[test]
    fn sort() {
        let sort_compressed =
            [0x15, 0x46, 0x84, 0x06, 0x05, 0x46, 0x81, 0x60, 0x15, 0xfb, 0xec, 0x2f, 0x80, 0x01,
             0x5b, 0xf9, 0x7f, 0x0b, 0x7e, 0xf7, 0x2f, 0xec, 0x2d, 0xfb, 0x80, 0x56, 0x05, 0xfd,
             0x85, 0xbb, 0x76, 0x11, 0x5d, 0x50, 0x5c, 0x00, 0xbe, 0x7f, 0xc1, 0x2b, 0xff, 0x0f,
             0xfc, 0x2c, 0x1b, 0x72, 0xbf, 0xf0, 0xff, 0xc2, 0xc1, 0x6d, 0x34, 0x50, 0x40];

        let sort_blc = decompress(&sort_compressed);
        let input  = b"3241";

        assert_eq!(run(&*sort_blc, Bytes(&*input)).unwrap(), "1234".to_owned());
    }

    #[test]
    fn inflating() {
        // program code from http://www.ioccc.org/2012/tromp/inflate.Blc
        let inflate_compressed =
            [0x44, 0x44, 0x68, 0x16, 0x01, 0x79, 0x1a, 0x00, 0x16, 0x7f, 0xfb, 0xcb, 0xcf, 0xdf,
             0x65, 0xfb, 0xed, 0x0f, 0x3c, 0xe7, 0x3c, 0xf3, 0xc2, 0xd8, 0x20, 0x58, 0x2c, 0x0b,
             0x06, 0xc0];

        let inflate_blc = decompress(&inflate_compressed);
        let s_compressed = [0x1, 0x7a, 0x74];

        assert_eq!(run(&*inflate_blc, Bytes(&s_compressed[..])).unwrap(), "000000010111101001110100".to_owned());
    }

    #[test]
    fn deflating() {
        // program code from http://www.ioccc.org/2012/tromp/deflate.Blc
        let deflate_compressed =
            [0x44, 0x68, 0x16, 0x05, 0x7e, 0x01, 0x17, 0x00, 0xbe, 0x55, 0xff, 0xf0, 0x0d, 0xc1,
             0x8b, 0xb2, 0xc1, 0xb0, 0xf8, 0x7c, 0x2d, 0xd8, 0x05, 0x9e, 0x09, 0x7f, 0xbf, 0xb1,
             0x48, 0x39, 0xce, 0x81, 0xce, 0x80];

        let deflate_blc = decompress(&deflate_compressed);
        let s_blc = b"00000001011110100111010";

        assert_eq!(run(&*deflate_blc, Bytes(&s_blc[..])).unwrap().as_bytes(), [0x1, 0x7a, 0x74]);
    }
/*
    #[test] /* WIP; this one parses properly, but doesn't return the expected result */
    fn hilbert() {
        // program code from http://www.ioccc.org/2012/tromp/hilbert.Blc
        let hilbert_compressed =
            [0x18, 0x18, 0x18, 0x18, 0x11, 0x11, 0x54, 0x68, 0x06, 0x04, 0x15, 0x5f, 0xf0, 0x41,
             0x9d, 0xf9, 0xde, 0x16, 0xff, 0xfe, 0x5f, 0x3f, 0xef, 0xf6, 0x15, 0xff, 0x94, 0x68,
             0x40, 0x58, 0x11, 0x7e, 0x05, 0xcb, 0xfe, 0xbc, 0xbf, 0xee, 0x86, 0xcb, 0x94, 0x68,
             0x16, 0x00, 0x5c, 0x0b, 0xfa, 0xcb, 0xfb, 0xf7, 0x1a, 0x85, 0xe0, 0x5c, 0xf4, 0x14,
             0xd5, 0xfe, 0x08, 0x18, 0x0b, 0x04, 0x8d, 0x08, 0x00, 0xe0, 0x78, 0x01, 0x64, 0x45,
             0xff, 0xe5, 0xff, 0x7f, 0xff, 0xfe, 0x5f, 0xff, 0x2f, 0xc0, 0x2f, 0x7a, 0xd9, 0x7f,
             0x5b, 0xff, 0xff, 0xfb, 0xff, 0xfc, 0xaa, 0xff, 0xf7, 0x81, 0x7f, 0xfa, 0xdf, 0x76,
             0x69, 0x54, 0x68, 0x06, 0x01, 0x57, 0xf7, 0xe1, 0x60, 0x5c, 0x13, 0xfe, 0x80, 0xb2,
             0x2c, 0x18, 0x58, 0x1b, 0xfe, 0x5c, 0x10, 0x42, 0xff, 0x80, 0x5d, 0xee, 0xc0, 0x6c,
             0x2c, 0x0c, 0x06, 0x08, 0x19, 0x1a, 0x00, 0x16, 0x7f, 0xbc, 0xbc, 0xfd, 0xf6, 0x5f,
             0x7c, 0x0a, 0x20];

        let hilbert_blc = decompress(&hilbert_compressed);
        let arg = b"1234";

        assert_eq!(run(&hilbert_blc[..], Bytes(&arg[..])), Ok("WIP".into()));
    }
*/
/*
    #[test] /* WIP; this just returns λ1 */
    fn brainfuck() {
        // program code from http://www.ioccc.org/2012/tromp/bf.Blc
        let bf_interpreter_compressed =
            [0x44, 0x51, 0xa1, 0x01, 0x84, 0x55, 0xd5, 0x02, 0xb7, 0x70, 0x30, 0x22, 0xff, 0x32,
             0xf0, 0x00, 0xbf, 0xf9, 0x85, 0x7f, 0x5e, 0xe1, 0x6f, 0x95, 0x7f, 0x7d, 0xee, 0xc0,
             0xe5, 0x54, 0x68, 0x00, 0x58, 0x55, 0xfd, 0xfb, 0xe0, 0x45, 0x57, 0xfd, 0xeb, 0xfb,
             0xf0, 0xb6, 0xf0, 0x2f, 0xd6, 0x07, 0xe1, 0x6f, 0x73, 0xd7, 0xf1, 0x14, 0xbc, 0xc0,
             0x0b, 0xff, 0x2e, 0x1f, 0xa1, 0x6f, 0x66, 0x17, 0xe8, 0x5b, 0xef, 0x2f, 0xcf, 0xff,
             0x13, 0xff, 0xe1, 0xca, 0x34, 0x20, 0x0a, 0xc8, 0xd0, 0x0b, 0x99, 0xee, 0x1f, 0xe5,
             0xff, 0x7f, 0x5a, 0x6a, 0x1f, 0xff, 0x0f, 0xff, 0x87, 0x9d, 0x04, 0xd0, 0xab, 0x00,
             0x05, 0xdb, 0x23, 0x40, 0xb7, 0x3b, 0x28, 0xcc, 0xc0, 0xb0, 0x6c, 0x0e, 0x74, 0x10];

        let bf_interpreter_blc = decompress(&bf_interpreter_compressed);
        let bf_hello =
            b"++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++.";

        assert_eq!(run(&bf_interpreter_blc, Bytes(&bf_hello[..])), Ok("Hello World!".into()));
    }
*/
}
<commit_msg>add a fizzbuzz test<commit_after>//! Binary lambda calculus execution

use binary_encoding::{from_binary};
use lambda_encoding::{encode, decode};
use lambda_calculus::reduction::beta_full;
use self::Error::*;
use self::Input::*;

/// An error that can occur during BLC execution.
#[derive(Debug, PartialEq)]
pub enum Error {
    InvalidProgram,
    InvalidArgument
}

/// The type of input for BLC execution.
pub enum Input<'a> {
    /// no input parameter
    Nothing,
    /// BLC input
    Binary(&'a [u8]),
    /// unencoded byte input
    Bytes(&'a [u8])
}

/// Executes a binary lambda calculus program, optionally feeding it the given argument.
///
/// # Example
/// ```
/// use blc::execution::run;
///
/// let reverse = b"00 01 01 10 01 00 01 10 10 00 00 00 00 01 01 110\
///                 01 11110 11110 00 01 01 10 11110 110 00 00 10";
///
/// assert_eq!(run(&*reverse, Some(b"herp derp")), Ok("pred preh".into()));
/// ```
pub fn run(blc_program: &[u8], input: Input) -> Result<String, Error> {
    let program = from_binary(blc_program);
    if program.is_err() { return Err(InvalidProgram) }
    let program = program.unwrap(); // safe

    let calculation = match input {
        Nothing     => beta_full(program),
        Bytes(arg)  => beta_full(app!(program, encode(arg))),
        Binary(arg) => {
            let arg = from_binary(arg);
            if arg.is_ok() {
                beta_full(app!(program, arg.unwrap())) // safe
            } else {
                return Err(InvalidArgument)
            }
        }
    };

    Ok(decode(calculation))
}

#[cfg(test)]
mod test {
    use super::*;
    use binary_encoding::{decompress, to_binary};
    use lambda_calculus::term::*;
    use lambda_calculus::term::Term::*;
    use lambda_calculus::arithmetic::{is_zero, rem};

    #[test]
    fn id() {
        let id_compressed = b" ";
        let id_blc        = decompress(&*id_compressed);
        let input         = b"herp derp";

        assert_eq!(run(&*id_blc, Bytes(&*input)).unwrap(), "herp derp".to_owned());
    }

    #[test]
    fn sort() {
        let sort_compressed =
            [0x15, 0x46, 0x84, 0x06, 0x05, 0x46, 0x81, 0x60, 0x15, 0xfb, 0xec, 0x2f, 0x80, 0x01,
             0x5b, 0xf9, 0x7f, 0x0b, 0x7e, 0xf7, 0x2f, 0xec, 0x2d, 0xfb, 0x80, 0x56, 0x05, 0xfd,
             0x85, 0xbb, 0x76, 0x11, 0x5d, 0x50, 0x5c, 0x00, 0xbe, 0x7f, 0xc1, 0x2b, 0xff, 0x0f,
             0xfc, 0x2c, 0x1b, 0x72, 0xbf, 0xf0, 0xff, 0xc2, 0xc1, 0x6d, 0x34, 0x50, 0x40];

        let sort_blc = decompress(&sort_compressed);
        let input  = b"3241";

        assert_eq!(run(&*sort_blc, Bytes(&*input)).unwrap(), "1234".to_owned());
    }

    #[test]
    fn inflating() {
        // program code from http://www.ioccc.org/2012/tromp/inflate.Blc
        let inflate_compressed =
            [0x44, 0x44, 0x68, 0x16, 0x01, 0x79, 0x1a, 0x00, 0x16, 0x7f, 0xfb, 0xcb, 0xcf, 0xdf,
             0x65, 0xfb, 0xed, 0x0f, 0x3c, 0xe7, 0x3c, 0xf3, 0xc2, 0xd8, 0x20, 0x58, 0x2c, 0x0b,
             0x06, 0xc0];

        let inflate_blc = decompress(&inflate_compressed);
        let s_compressed = [0x1, 0x7a, 0x74];

        assert_eq!(run(&*inflate_blc, Bytes(&s_compressed[..])).unwrap(), "000000010111101001110100".to_owned());
    }

    #[test]
    fn deflating() {
        // program code from http://www.ioccc.org/2012/tromp/deflate.Blc
        let deflate_compressed =
            [0x44, 0x68, 0x16, 0x05, 0x7e, 0x01, 0x17, 0x00, 0xbe, 0x55, 0xff, 0xf0, 0x0d, 0xc1,
             0x8b, 0xb2, 0xc1, 0xb0, 0xf8, 0x7c, 0x2d, 0xd8, 0x05, 0x9e, 0x09, 0x7f, 0xbf, 0xb1,
             0x48, 0x39, 0xce, 0x81, 0xce, 0x80];

        let deflate_blc = decompress(&deflate_compressed);
        let s_blc = b"00000001011110100111010";

        assert_eq!(run(&*deflate_blc, Bytes(&s_blc[..])).unwrap().as_bytes(), [0x1, 0x7a, 0x74]);
    }

    #[test]
    fn fizz_buzz() {
        let fizzbuzz_single =
            abs(
                app!(
                    is_zero(),
                    app!(rem(), Var(1), 15.into()),
                    encode(&b"FizzBuzz"[..]),
                    app!(
                        is_zero(),
                        app!(rem(), Var(1), 3.into()),
                        encode(&b"Fizz"[..]),
                        app!(
                            is_zero(),
                            app!(rem(), Var(1), 5.into()),
                            encode(&b"Buzz"[..]),
                            Var(1)
                        )
                    )
                )
            );
        let fizzbuzz_blc = to_binary(&fizzbuzz_single);

        assert_eq!(run(&*fizzbuzz_blc, Binary(&to_binary(&1.into()))).unwrap(),  "(λλ21)");
        assert_eq!(run(&*fizzbuzz_blc, Binary(&to_binary(&2.into()))).unwrap(),  "(λλ2(21))");
        assert_eq!(run(&*fizzbuzz_blc, Binary(&to_binary(&3.into()))).unwrap(),  "Fizz");
        assert_eq!(run(&*fizzbuzz_blc, Binary(&to_binary(&4.into()))).unwrap(),  "(λλ2(2(2(21))))");
        assert_eq!(run(&*fizzbuzz_blc, Binary(&to_binary(&5.into()))).unwrap(),  "Buzz");
        assert_eq!(run(&*fizzbuzz_blc, Binary(&to_binary(&15.into()))).unwrap(), "FizzBuzz");
    }

/*
    #[test] /* WIP; this one parses properly, but doesn't return the expected result */
    fn hilbert() {
        // program code from http://www.ioccc.org/2012/tromp/hilbert.Blc
        let hilbert_compressed =
            [0x18, 0x18, 0x18, 0x18, 0x11, 0x11, 0x54, 0x68, 0x06, 0x04, 0x15, 0x5f, 0xf0, 0x41,
             0x9d, 0xf9, 0xde, 0x16, 0xff, 0xfe, 0x5f, 0x3f, 0xef, 0xf6, 0x15, 0xff, 0x94, 0x68,
             0x40, 0x58, 0x11, 0x7e, 0x05, 0xcb, 0xfe, 0xbc, 0xbf, 0xee, 0x86, 0xcb, 0x94, 0x68,
             0x16, 0x00, 0x5c, 0x0b, 0xfa, 0xcb, 0xfb, 0xf7, 0x1a, 0x85, 0xe0, 0x5c, 0xf4, 0x14,
             0xd5, 0xfe, 0x08, 0x18, 0x0b, 0x04, 0x8d, 0x08, 0x00, 0xe0, 0x78, 0x01, 0x64, 0x45,
             0xff, 0xe5, 0xff, 0x7f, 0xff, 0xfe, 0x5f, 0xff, 0x2f, 0xc0, 0x2f, 0x7a, 0xd9, 0x7f,
             0x5b, 0xff, 0xff, 0xfb, 0xff, 0xfc, 0xaa, 0xff, 0xf7, 0x81, 0x7f, 0xfa, 0xdf, 0x76,
             0x69, 0x54, 0x68, 0x06, 0x01, 0x57, 0xf7, 0xe1, 0x60, 0x5c, 0x13, 0xfe, 0x80, 0xb2,
             0x2c, 0x18, 0x58, 0x1b, 0xfe, 0x5c, 0x10, 0x42, 0xff, 0x80, 0x5d, 0xee, 0xc0, 0x6c,
             0x2c, 0x0c, 0x06, 0x08, 0x19, 0x1a, 0x00, 0x16, 0x7f, 0xbc, 0xbc, 0xfd, 0xf6, 0x5f,
             0x7c, 0x0a, 0x20];

        let hilbert_blc = decompress(&hilbert_compressed);
        let arg = b"1234";

        assert_eq!(run(&hilbert_blc[..], Bytes(&arg[..])), Ok("WIP".into()));
    }
*/
/*
    #[test] /* WIP; this just returns λ1 */
    fn brainfuck() {
        // program code from http://www.ioccc.org/2012/tromp/bf.Blc
        let bf_interpreter_compressed =
            [0x44, 0x51, 0xa1, 0x01, 0x84, 0x55, 0xd5, 0x02, 0xb7, 0x70, 0x30, 0x22, 0xff, 0x32,
             0xf0, 0x00, 0xbf, 0xf9, 0x85, 0x7f, 0x5e, 0xe1, 0x6f, 0x95, 0x7f, 0x7d, 0xee, 0xc0,
             0xe5, 0x54, 0x68, 0x00, 0x58, 0x55, 0xfd, 0xfb, 0xe0, 0x45, 0x57, 0xfd, 0xeb, 0xfb,
             0xf0, 0xb6, 0xf0, 0x2f, 0xd6, 0x07, 0xe1, 0x6f, 0x73, 0xd7, 0xf1, 0x14, 0xbc, 0xc0,
             0x0b, 0xff, 0x2e, 0x1f, 0xa1, 0x6f, 0x66, 0x17, 0xe8, 0x5b, 0xef, 0x2f, 0xcf, 0xff,
             0x13, 0xff, 0xe1, 0xca, 0x34, 0x20, 0x0a, 0xc8, 0xd0, 0x0b, 0x99, 0xee, 0x1f, 0xe5,
             0xff, 0x7f, 0x5a, 0x6a, 0x1f, 0xff, 0x0f, 0xff, 0x87, 0x9d, 0x04, 0xd0, 0xab, 0x00,
             0x05, 0xdb, 0x23, 0x40, 0xb7, 0x3b, 0x28, 0xcc, 0xc0, 0xb0, 0x6c, 0x0e, 0x74, 0x10];

        let bf_interpreter_blc = decompress(&bf_interpreter_compressed);
        let bf_hello =
            b"++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++.";

        assert_eq!(run(&bf_interpreter_blc, Bytes(&bf_hello[..])), Ok("Hello World!".into()));
    }
*/
}
<|endoftext|>


<commit_before>//
// Sysinfo
//
// Copyright (c) 2015 Guillaume Gomez
//

use sys::component::{self, Component};
use sys::processor::*;
use sys::process::*;
use sys::Disk;
use sys::disk;
use ::{DiskExt, ProcessExt, SystemExt};
use std::fs::{File, read_link};
use std::io::{self, Read};
use std::str::FromStr;
use std::path::{Path, PathBuf};
use std::collections::HashMap;
use std::fs;
use libc::{pid_t, uid_t, sysconf, _SC_CLK_TCK, _SC_PAGESIZE};
use utils::realpath;

/// Structs containing system's information.
pub struct System {
    process_list: Process,
    mem_total: u64,
    mem_free: u64,
    swap_total: u64,
    swap_free: u64,
    processors: Vec<Processor>,
    page_size_kb: u64,
    temperatures: Vec<Component>,
    disks: Vec<Disk>,
}

impl System {
    fn clear_procs(&mut self) {
        if !self.processors.is_empty() {
            let (new, old) = get_raw_times(&self.processors[0]);
            let total_time = (if old > new { 1 } else { new - old }) as f32;
            let mut to_delete = Vec::new();
            let nb_processors = self.processors.len() as u64 - 1;

            for (pid, proc_) in &mut self.process_list.tasks {
                if !has_been_updated(proc_) {
                    to_delete.push(*pid);
                } else {
                    compute_cpu_usage(proc_, nb_processors, total_time);
                }
            }
            for pid in to_delete {
                self.process_list.tasks.remove(&pid);
            }
        }
    }

    /// **WARNING**: This method is specific to Linux.
    ///
    /// Refresh *only* the process corresponding to `pid`.
    pub fn refresh_process(&mut self, pid: pid_t) -> bool {
        if let Some(proc_) = self.process_list.tasks.get_mut(&pid) {
            _get_process_data(Path::new(&format!("/proc/{}", pid)), proc_, self.page_size_kb, pid);
            true
        } else {
            false
        }
    }
}

impl SystemExt for System {
    fn new() -> System {
        let mut s = System {
            process_list: Process::new(0, None, 0),
            mem_total: 0,
            mem_free: 0,
            swap_total: 0,
            swap_free: 0,
            processors: Vec::new(),
            page_size_kb: unsafe { sysconf(_SC_PAGESIZE) as u64 / 1024 },
            temperatures: component::get_components(),
            disks: get_all_disks(),
        };
        s.refresh_all();
        s
    }

    fn refresh_system(&mut self) {
        let data = get_all_data("/proc/meminfo").unwrap();
        let lines: Vec<&str> = data.split('\n').collect();

        for component in &mut self.temperatures {
            component.update();
        }
        for line in &lines {
            match *line {
                l if l.starts_with("MemTotal:") => {
                    let parts: Vec<&str> = line.split(' ').collect();

                    self.mem_total = u64::from_str(parts[parts.len() - 2]).unwrap();
                },
                l if l.starts_with("MemAvailable:") => {
                    let parts: Vec<&str> = line.split(' ').collect();

                    self.mem_free = u64::from_str(parts[parts.len() - 2]).unwrap();
                },
                l if l.starts_with("SwapTotal:") => {
                    let parts: Vec<&str> = line.split(' ').collect();

                    self.swap_total = u64::from_str(parts[parts.len() - 2]).unwrap();
                },
                l if l.starts_with("SwapFree:") => {
                    let parts: Vec<&str> = line.split(' ').collect();

                    self.swap_free = u64::from_str(parts[parts.len() - 2]).unwrap();
                },
                _ => continue,
            }
        }
        let data = get_all_data("/proc/stat").unwrap();
        let lines: Vec<&str> = data.split('\n').collect();
        let mut i = 0;
        let first = self.processors.is_empty();
        for line in &lines {
            if !line.starts_with("cpu") {
                break;
            }

            let (parts, _): (Vec<&str>, Vec<&str>) = line.split(' ').partition(|s| !s.is_empty());
            if first {
                self.processors.push(new_processor(parts[0], u64::from_str(parts[1]).unwrap(),
                    u64::from_str(parts[2]).unwrap(),
                    u64::from_str(parts[3]).unwrap(),
                    u64::from_str(parts[4]).unwrap(),
                    u64::from_str(parts[5]).unwrap(),
                    u64::from_str(parts[6]).unwrap(),
                    u64::from_str(parts[7]).unwrap(),
                    u64::from_str(parts[8]).unwrap(),
                    u64::from_str(parts[9]).unwrap(),
                    u64::from_str(parts[10]).unwrap()));
            } else {
                set_processor(&mut self.processors[i],
                    u64::from_str(parts[1]).unwrap(),
                    u64::from_str(parts[2]).unwrap(),
                    u64::from_str(parts[3]).unwrap(),
                    u64::from_str(parts[4]).unwrap(),
                    u64::from_str(parts[5]).unwrap(),
                    u64::from_str(parts[6]).unwrap(),
                    u64::from_str(parts[7]).unwrap(),
                    u64::from_str(parts[8]).unwrap(),
                    u64::from_str(parts[9]).unwrap(),
                    u64::from_str(parts[10]).unwrap());
                i += 1;
            }
        }
    }

    fn refresh_processes(&mut self) {
        if refresh_procs(&mut self.process_list, "/proc", self.page_size_kb, 0) {
            self.clear_procs();
        }
    }

    fn refresh_disks(&mut self) {
        for disk in &mut self.disks {
            disk.update();
        }
    }

    fn refresh_disk_list(&mut self) {
        self.disks = get_all_disks();
    }

    // COMMON PART
    //
    // Need to be moved into a "common" file to avoid duplication.

    fn get_process_list(&self) -> &HashMap<pid_t, Process> {
        &self.process_list.tasks
    }

    fn get_process(&self, pid: pid_t) -> Option<&Process> {
        self.process_list.tasks.get(&pid)
    }

    fn get_process_by_name(&self, name: &str) -> Vec<&Process> {
        let mut ret = vec!();
        for val in self.process_list.tasks.values() {
            if val.name.starts_with(name) {
                ret.push(val);
            }
        }
        ret
    }

    fn get_processor_list(&self) -> &[Processor] {
        &self.processors[..]
    }

    fn get_total_memory(&self) -> u64 {
        self.mem_total
    }

    fn get_free_memory(&self) -> u64 {
        self.mem_free
    }

    fn get_used_memory(&self) -> u64 {
        self.mem_total - self.mem_free
    }

    fn get_total_swap(&self) -> u64 {
        self.swap_total
    }

    fn get_free_swap(&self) -> u64 {
        self.swap_free
    }

    // need to be checked
    fn get_used_swap(&self) -> u64 {
        self.swap_total - self.swap_free
    }

    fn get_components_list(&self) -> &[Component] {
        &self.temperatures[..]
    }

    fn get_disks(&self) -> &[Disk] {
        &self.disks[..]
    }
}

impl Default for System {
    fn default() -> System {
        System::new()
    }
}

pub fn get_all_data(file_path: &str) -> io::Result<String> {
    let mut file = File::open(file_path)?;
    let mut data = String::new();

    file.read_to_string(&mut data)?;
    Ok(data)
}

fn refresh_procs<P: AsRef<Path>>(proc_list: &mut Process, path: P, page_size_kb: u64,
                                 pid: pid_t) -> bool {
    if let Ok(d) = fs::read_dir(&Path::new(path.as_ref())) {
        for entry in d {
            if !entry.is_ok() {
                continue;
            }
            let entry = entry.unwrap();
            let entry = entry.path();

            if entry.is_dir() {
                _get_process_data(entry.as_path(), proc_list, page_size_kb, pid);
            }
        }
        true
    } else {
        false
    }
}

fn update_time_and_memory(path: &Path, entry: &mut Process, parts: &[&str], page_size_kb: u64,
                          parent_memory: u64, pid: pid_t) {
    //entry.name = parts[1][1..].to_owned();
    //entry.name.pop();
    // we get the rss
    {
        entry.memory = u64::from_str(parts[23]).unwrap() * page_size_kb;
        if entry.memory >= parent_memory {
            entry.memory -= parent_memory;
        }
        set_time(entry,
                 u64::from_str(parts[13]).unwrap(),
                 u64::from_str(parts[14]).unwrap());
    }
    refresh_procs(entry, path.join(Path::new("task")).as_path(), page_size_kb, pid);
}

fn _get_process_data(path: &Path, proc_list: &mut Process, page_size_kb: u64, pid: pid_t) {
    if !path.exists() || !path.is_dir() {
        return
    }
    let last = path.to_str().unwrap().split('/').last();
    if last.is_none() {
        return
    }
    if let Ok(nb) = pid_t::from_str(last.unwrap()) {
        if nb == pid {
            return
        }
        let mut tmp = PathBuf::from(path);

        tmp.push("stat");
        let data = get_all_data(tmp.to_str().unwrap()).unwrap();

        // The stat file is "interesting" to parse, because spaces cannot
        // be used as delimiters. The second field stores the command name
        // sourrounded by parentheses. Unfortunately, whitespace and
        // parentheses are legal parts of the command, so parsing has to
        // proceed like this: The first field is delimited by the first
        // whitespace, the second field is everything until the last ')'
        // in the entire string. All other fields are delimited by
        // whitespace.

        let mut parts = Vec::new();
        let mut data_it = data.splitn(2, ' ');
        parts.push(data_it.next().unwrap());
        // The following loses the ) from the input, but that's ok because
        // we're not using it anyway.
        let mut data_it = data_it.next().unwrap().rsplitn(2, ')');
        let data = data_it.next().unwrap();
        parts.push(data_it.next().unwrap());
        parts.extend(data.split_whitespace());
        let parent_memory = proc_list.memory;
        if let Some(ref mut entry) = proc_list.tasks.get_mut(&nb) {
            update_time_and_memory(path, entry, &parts, page_size_kb, parent_memory, nb);
            return;
        }

        let parent_pid = if proc_list.pid != 0 {
            Some(proc_list.pid)
        } else {
            match pid_t::from_str(parts[3]).unwrap() {
                0 => None,
                p => Some(p),
            }
        };

        let mut p = Process::new(nb,
                                 parent_pid,
                                 u64::from_str(parts[21]).unwrap() /
                                 unsafe { sysconf(_SC_CLK_TCK) } as u64);

        p.status = parts[2].chars().next().and_then(|c| Some(ProcessStatus::from(c)));

        tmp = PathBuf::from(path);
        tmp.push("status");
        let status_data = get_all_data(tmp.to_str().unwrap()).unwrap();

        // We're only interested in the lines starting with Uid: and Gid:
        // here. From these lines, we're looking at the second entry to get
        // the effective u/gid.

        let f = |h: &str, n: &str| -> Option<uid_t> {
            if h.starts_with(n) {
                h.split_whitespace().nth(2).unwrap().parse().ok()
            } else {
                None
            }
        };
        let mut set_uid = false;
        let mut set_gid = false;
        for line in status_data.lines() {
            if let Some(u) = f(line, "Uid:") {
                assert!(!set_uid);
                set_uid = true;
                p.uid = u;
            }
            if let Some(g) = f(line, "Gid:") {
                assert!(!set_gid);
                set_gid = true;
                p.gid = g;
            }
        }
        assert!(set_uid && set_gid);

        if proc_list.pid != 0 {
            p.cmd = proc_list.cmd.clone();
            p.name = proc_list.name.clone();
            p.environ = proc_list.environ.clone();
            p.exe = proc_list.exe.clone();
            p.cwd = proc_list.cwd.clone();
            p.root = proc_list.root.clone();
        } else {
            tmp = PathBuf::from(path);
            tmp.push("cmdline");
            p.cmd = copy_from_file(&tmp);
            p.name = p.cmd[0].split('/').last().unwrap().to_owned();
            tmp = PathBuf::from(path);
            tmp.push("environ");
            p.environ = copy_from_file(&tmp);
            tmp = PathBuf::from(path);
            tmp.push("exe");

            let s = read_link(tmp.to_str().unwrap());

            if s.is_ok() {
                p.exe = s.unwrap().to_str().unwrap().to_owned();
            }
            tmp = PathBuf::from(path);
            tmp.push("cwd");
            p.cwd = realpath(Path::new(tmp.to_str().unwrap())).to_str().unwrap().to_owned();
            tmp = PathBuf::from(path);
            tmp.push("root");
            p.root = realpath(Path::new(tmp.to_str().unwrap())).to_str().unwrap().to_owned();
        }

        update_time_and_memory(path, &mut p, &parts, page_size_kb, proc_list.memory, nb);
        proc_list.tasks.insert(nb, p);
    }
}

#[allow(unused_must_use)]
fn copy_from_file(entry: &Path) -> Vec<String> {
    match File::open(entry.to_str().unwrap()) {
        Ok(mut f) => {
            let mut d = String::new();

            f.read_to_string(&mut d);
            d.split('\0').map(|x| x.to_owned()).collect()
        },
        Err(_) => Vec::new()
    }
}

fn get_all_disks() -> Vec<Disk> {
    #[allow(or_fun_call)]
    let content = get_all_data("/proc/mounts").unwrap_or(String::new());
    let disks: Vec<_> = content.lines()
                               .filter(|line| line.trim_left()
                                                  .starts_with("/dev/sd"))
                               .collect();
    let mut ret = Vec::with_capacity(disks.len());

    if disks.len() == 1 {
        let info: Vec<_> = disks[0].split(' ').collect();
        if info.len() < 3 {
            return ret;
        }
        ret.push(disk::new_disk("sda", info[1], info[2]));
    } else {
        for line in disks {
            let info: Vec<_> = line.split(' ').collect();
            if info.len() < 3 {
                continue
            }
            ret.push(disk::new_disk(&info[0][5..], info[1], info[2]));
        }
    }
    ret
}
<commit_msg>simplify and speed up some parsing<commit_after>//
// Sysinfo
//
// Copyright (c) 2015 Guillaume Gomez
//

use sys::component::{self, Component};
use sys::processor::*;
use sys::process::*;
use sys::Disk;
use sys::disk;
use ::{DiskExt, ProcessExt, SystemExt};
use std::fs::{File, read_link};
use std::io::{self, Read};
use std::str::FromStr;
use std::path::{Path, PathBuf};
use std::collections::HashMap;
use std::fs;
use libc::{pid_t, uid_t, sysconf, _SC_CLK_TCK, _SC_PAGESIZE};
use utils::realpath;

/// Structs containing system's information.
pub struct System {
    process_list: Process,
    mem_total: u64,
    mem_free: u64,
    swap_total: u64,
    swap_free: u64,
    processors: Vec<Processor>,
    page_size_kb: u64,
    temperatures: Vec<Component>,
    disks: Vec<Disk>,
}

impl System {
    fn clear_procs(&mut self) {
        if !self.processors.is_empty() {
            let (new, old) = get_raw_times(&self.processors[0]);
            let total_time = (if old > new { 1 } else { new - old }) as f32;
            let mut to_delete = Vec::new();
            let nb_processors = self.processors.len() as u64 - 1;

            for (pid, proc_) in &mut self.process_list.tasks {
                if !has_been_updated(proc_) {
                    to_delete.push(*pid);
                } else {
                    compute_cpu_usage(proc_, nb_processors, total_time);
                }
            }
            for pid in to_delete {
                self.process_list.tasks.remove(&pid);
            }
        }
    }

    /// **WARNING**: This method is specific to Linux.
    ///
    /// Refresh *only* the process corresponding to `pid`.
    pub fn refresh_process(&mut self, pid: pid_t) -> bool {
        if let Some(proc_) = self.process_list.tasks.get_mut(&pid) {
            _get_process_data(Path::new(&format!("/proc/{}", pid)), proc_, self.page_size_kb, pid);
            true
        } else {
            false
        }
    }
}

impl SystemExt for System {
    fn new() -> System {
        let mut s = System {
            process_list: Process::new(0, None, 0),
            mem_total: 0,
            mem_free: 0,
            swap_total: 0,
            swap_free: 0,
            processors: Vec::new(),
            page_size_kb: unsafe { sysconf(_SC_PAGESIZE) as u64 / 1024 },
            temperatures: component::get_components(),
            disks: get_all_disks(),
        };
        s.refresh_all();
        s
    }

    fn refresh_system(&mut self) {
        let data = get_all_data("/proc/meminfo").unwrap();

        for component in &mut self.temperatures {
            component.update();
        }
        for line in data.split('\n') {
            let field = match line.split(':').next() {
                Some("MemTotal") => &mut self.mem_total,
                Some("MemAvailable") => &mut self.mem_free,
                Some("SwapTotal") => &mut self.swap_total,
                Some("SwapFree") => &mut self.swap_free,
                _ => continue,
            };
            if let Some(val_str) = line.rsplit(' ').nth(1) {
                *field = u64::from_str(val_str).unwrap();
            }
        }
        let data = get_all_data("/proc/stat").unwrap();
        let mut i = 0;
        let first = self.processors.is_empty();
        for line in data.split('\n') {
            if !line.starts_with("cpu") {
                break;
            }

            let (parts, _): (Vec<&str>, Vec<&str>) = line.split(' ').partition(|s| !s.is_empty());
            if first {
                self.processors.push(new_processor(parts[0], u64::from_str(parts[1]).unwrap(),
                    u64::from_str(parts[2]).unwrap(),
                    u64::from_str(parts[3]).unwrap(),
                    u64::from_str(parts[4]).unwrap(),
                    u64::from_str(parts[5]).unwrap(),
                    u64::from_str(parts[6]).unwrap(),
                    u64::from_str(parts[7]).unwrap(),
                    u64::from_str(parts[8]).unwrap(),
                    u64::from_str(parts[9]).unwrap(),
                    u64::from_str(parts[10]).unwrap()));
            } else {
                set_processor(&mut self.processors[i],
                    u64::from_str(parts[1]).unwrap(),
                    u64::from_str(parts[2]).unwrap(),
                    u64::from_str(parts[3]).unwrap(),
                    u64::from_str(parts[4]).unwrap(),
                    u64::from_str(parts[5]).unwrap(),
                    u64::from_str(parts[6]).unwrap(),
                    u64::from_str(parts[7]).unwrap(),
                    u64::from_str(parts[8]).unwrap(),
                    u64::from_str(parts[9]).unwrap(),
                    u64::from_str(parts[10]).unwrap());
                i += 1;
            }
        }
    }

    fn refresh_processes(&mut self) {
        if refresh_procs(&mut self.process_list, "/proc", self.page_size_kb, 0) {
            self.clear_procs();
        }
    }

    fn refresh_disks(&mut self) {
        for disk in &mut self.disks {
            disk.update();
        }
    }

    fn refresh_disk_list(&mut self) {
        self.disks = get_all_disks();
    }

    // COMMON PART
    //
    // Need to be moved into a "common" file to avoid duplication.

    fn get_process_list(&self) -> &HashMap<pid_t, Process> {
        &self.process_list.tasks
    }

    fn get_process(&self, pid: pid_t) -> Option<&Process> {
        self.process_list.tasks.get(&pid)
    }

    fn get_process_by_name(&self, name: &str) -> Vec<&Process> {
        let mut ret = vec!();
        for val in self.process_list.tasks.values() {
            if val.name.starts_with(name) {
                ret.push(val);
            }
        }
        ret
    }

    fn get_processor_list(&self) -> &[Processor] {
        &self.processors[..]
    }

    fn get_total_memory(&self) -> u64 {
        self.mem_total
    }

    fn get_free_memory(&self) -> u64 {
        self.mem_free
    }

    fn get_used_memory(&self) -> u64 {
        self.mem_total - self.mem_free
    }

    fn get_total_swap(&self) -> u64 {
        self.swap_total
    }

    fn get_free_swap(&self) -> u64 {
        self.swap_free
    }

    // need to be checked
    fn get_used_swap(&self) -> u64 {
        self.swap_total - self.swap_free
    }

    fn get_components_list(&self) -> &[Component] {
        &self.temperatures[..]
    }

    fn get_disks(&self) -> &[Disk] {
        &self.disks[..]
    }
}

impl Default for System {
    fn default() -> System {
        System::new()
    }
}

pub fn get_all_data(file_path: &str) -> io::Result<String> {
    let mut file = File::open(file_path)?;
    let mut data = String::new();

    file.read_to_string(&mut data)?;
    Ok(data)
}

fn refresh_procs<P: AsRef<Path>>(proc_list: &mut Process, path: P, page_size_kb: u64,
                                 pid: pid_t) -> bool {
    if let Ok(d) = fs::read_dir(&Path::new(path.as_ref())) {
        for entry in d {
            if !entry.is_ok() {
                continue;
            }
            let entry = entry.unwrap();
            let entry = entry.path();

            if entry.is_dir() {
                _get_process_data(entry.as_path(), proc_list, page_size_kb, pid);
            }
        }
        true
    } else {
        false
    }
}

fn update_time_and_memory(path: &Path, entry: &mut Process, parts: &[&str], page_size_kb: u64,
                          parent_memory: u64, pid: pid_t) {
    //entry.name = parts[1][1..].to_owned();
    //entry.name.pop();
    // we get the rss
    {
        entry.memory = u64::from_str(parts[23]).unwrap() * page_size_kb;
        if entry.memory >= parent_memory {
            entry.memory -= parent_memory;
        }
        set_time(entry,
                 u64::from_str(parts[13]).unwrap(),
                 u64::from_str(parts[14]).unwrap());
    }
    refresh_procs(entry, path.join(Path::new("task")).as_path(), page_size_kb, pid);
}

fn _get_process_data(path: &Path, proc_list: &mut Process, page_size_kb: u64, pid: pid_t) {
    if !path.exists() || !path.is_dir() {
        return
    }
    let last = path.to_str().unwrap().split('/').last();
    if last.is_none() {
        return
    }
    if let Ok(nb) = pid_t::from_str(last.unwrap()) {
        if nb == pid {
            return
        }
        let mut tmp = PathBuf::from(path);

        tmp.push("stat");
        let data = get_all_data(tmp.to_str().unwrap()).unwrap();

        // The stat file is "interesting" to parse, because spaces cannot
        // be used as delimiters. The second field stores the command name
        // sourrounded by parentheses. Unfortunately, whitespace and
        // parentheses are legal parts of the command, so parsing has to
        // proceed like this: The first field is delimited by the first
        // whitespace, the second field is everything until the last ')'
        // in the entire string. All other fields are delimited by
        // whitespace.

        let mut parts = Vec::new();
        let mut data_it = data.splitn(2, ' ');
        parts.push(data_it.next().unwrap());
        // The following loses the ) from the input, but that's ok because
        // we're not using it anyway.
        let mut data_it = data_it.next().unwrap().rsplitn(2, ')');
        let data = data_it.next().unwrap();
        parts.push(data_it.next().unwrap());
        parts.extend(data.split_whitespace());
        let parent_memory = proc_list.memory;
        if let Some(ref mut entry) = proc_list.tasks.get_mut(&nb) {
            update_time_and_memory(path, entry, &parts, page_size_kb, parent_memory, nb);
            return;
        }

        let parent_pid = if proc_list.pid != 0 {
            Some(proc_list.pid)
        } else {
            match pid_t::from_str(parts[3]).unwrap() {
                0 => None,
                p => Some(p),
            }
        };

        let mut p = Process::new(nb,
                                 parent_pid,
                                 u64::from_str(parts[21]).unwrap() /
                                 unsafe { sysconf(_SC_CLK_TCK) } as u64);

        p.status = parts[2].chars().next().and_then(|c| Some(ProcessStatus::from(c)));

        tmp = PathBuf::from(path);
        tmp.push("status");
        let status_data = get_all_data(tmp.to_str().unwrap()).unwrap();

        // We're only interested in the lines starting with Uid: and Gid:
        // here. From these lines, we're looking at the second entry to get
        // the effective u/gid.

        let f = |h: &str, n: &str| -> Option<uid_t> {
            if h.starts_with(n) {
                h.split_whitespace().nth(2).unwrap().parse().ok()
            } else {
                None
            }
        };
        let mut set_uid = false;
        let mut set_gid = false;
        for line in status_data.lines() {
            if let Some(u) = f(line, "Uid:") {
                assert!(!set_uid);
                set_uid = true;
                p.uid = u;
            }
            if let Some(g) = f(line, "Gid:") {
                assert!(!set_gid);
                set_gid = true;
                p.gid = g;
            }
        }
        assert!(set_uid && set_gid);

        if proc_list.pid != 0 {
            p.cmd = proc_list.cmd.clone();
            p.name = proc_list.name.clone();
            p.environ = proc_list.environ.clone();
            p.exe = proc_list.exe.clone();
            p.cwd = proc_list.cwd.clone();
            p.root = proc_list.root.clone();
        } else {
            tmp = PathBuf::from(path);
            tmp.push("cmdline");
            p.cmd = copy_from_file(&tmp);
            p.name = p.cmd[0].split('/').last().unwrap().to_owned();
            tmp = PathBuf::from(path);
            tmp.push("environ");
            p.environ = copy_from_file(&tmp);
            tmp = PathBuf::from(path);
            tmp.push("exe");

            let s = read_link(tmp.to_str().unwrap());

            if s.is_ok() {
                p.exe = s.unwrap().to_str().unwrap().to_owned();
            }
            tmp = PathBuf::from(path);
            tmp.push("cwd");
            p.cwd = realpath(Path::new(tmp.to_str().unwrap())).to_str().unwrap().to_owned();
            tmp = PathBuf::from(path);
            tmp.push("root");
            p.root = realpath(Path::new(tmp.to_str().unwrap())).to_str().unwrap().to_owned();
        }

        update_time_and_memory(path, &mut p, &parts, page_size_kb, proc_list.memory, nb);
        proc_list.tasks.insert(nb, p);
    }
}

#[allow(unused_must_use)]
fn copy_from_file(entry: &Path) -> Vec<String> {
    match File::open(entry.to_str().unwrap()) {
        Ok(mut f) => {
            let mut d = String::new();

            f.read_to_string(&mut d);
            d.split('\0').map(|x| x.to_owned()).collect()
        },
        Err(_) => Vec::new()
    }
}

fn get_all_disks() -> Vec<Disk> {
    #[allow(or_fun_call)]
    let content = get_all_data("/proc/mounts").unwrap_or(String::new());
    let disks: Vec<_> = content.lines()
                               .filter(|line| line.trim_left()
                                                  .starts_with("/dev/sd"))
                               .collect();
    let mut ret = Vec::with_capacity(disks.len());

    if disks.len() == 1 {
        let info: Vec<_> = disks[0].split(' ').collect();
        if info.len() < 3 {
            return ret;
        }
        ret.push(disk::new_disk("sda", info[1], info[2]));
    } else {
        for line in disks {
            let info: Vec<_> = line.split(' ').collect();
            if info.len() < 3 {
                continue
            }
            ret.push(disk::new_disk(&info[0][5..], info[1], info[2]));
        }
    }
    ret
}
<|endoftext|>


<commit_before>//! The structures used to manage commands sent over to tmux.

use load::tmux;
use std::io;
use std::path::PathBuf;
use std::process::Output;
use std::process;
use std::str;

pub trait Command {
    fn call(&self) -> Result<Output, io::Error>;
    fn command(&self) -> Vec<&str>;
}

/// The Session command is used to fire up a new daemonized session in tmux.
/// `name`: The Name of a named tmux session.
/// `window_name`: The Name of the first window.
/// `root_path`: The root directory for the tmux session.
#[derive(Debug, Clone)]
pub struct Session {
    pub name: String,
    pub window_name: String,
    pub root_path: Option<PathBuf>,
}

// TODO: Real logic exists here. Test it!
impl Command for Session {
    fn command(&self) -> Vec<&str> {
        let command: Vec<&str> = vec!["new", "-d", "-s", &self.name, "-n", &self.window_name];

        match self.root_path.as_ref() {
            Some(path) => [&command[..], &["-c", path.to_str().unwrap()]].concat(),
            None => command,
        }
    }

    fn call(&self) -> Result<Output, io::Error> {
        tmux::call(&self.command())
    }
}

/// The Window command is used to identify every new window opened in the tmux
/// session.
/// `session_name`: The name of the session.
/// `name`: The named window to be opened.
/// `path`: An `Option<PathBuf>` containing a possible root directory passed to the
/// `-c` arguement.
#[derive(Debug, Clone)]
pub struct Window {
    pub session_name: String,
    pub name: String,
    pub path: Option<PathBuf>,
}

impl Command for Window {
    fn command(&self) -> Vec<&str> {
        let command: Vec<&str> = vec!["new-window", "-t", &self.session_name, "-n", &self.name];

        match self.path.as_ref() {
            Some(path) => [&command[..], &["-c", path.to_str().unwrap()]].concat(),
            None => command,
        }
    }

    fn call(&self) -> Result<Output, io::Error> {
        tmux::call(&self.command())
    }
}

/// The Split is used to call split-window on a particular window in the
/// session.
/// `target`: The target window. In the format `{session}:{window}.{paneIndex}`.
#[derive(Debug, Clone)]
pub struct Split {
    pub target: String,
}

impl Command for Split {
    fn command(&self) -> Vec<&str> {
        vec!["split-window", "-t", &self.target]
    }

    fn call(&self) -> Result<Output, io::Error> {
        tmux::call(&self.command())
    }
}

/// The Layout command calls select-layout with a specific pre-defined tmux
/// layout option see `tmux select-layout --help` for more options.
/// `target`: The target window. In the format `{session}:{window}.{paneIndex}`.
/// `layout`: The type of layout. ex `main-horizontal`.
#[derive(Debug, Clone)]
pub struct Layout {
    pub target: String,
    pub layout: String,
}

impl Command for Layout {
    fn command(&self) -> Vec<&str> {
        vec!["select-layout", "-t", &self.target, &self.layout]
    }

    fn call(&self) -> Result<Output, io::Error> {
        tmux::call(&self.command())
    }
}

/// A generic `SendKeys` command used to send "typed" commands to tmux. This is
/// used to initialize processes or tasks in specific window. Such as starting log
/// tails or running servers.
/// target: The target window. In the format `{session}:{window}.{paneIndex}`.
/// exec: The cli command to be run. ex. `tail -f logs/development.log`.
#[derive(Debug, Clone)]
pub struct SendKeys {
    pub target: String,
    pub exec: String,
}

impl Command for SendKeys {
    fn command(&self) -> Vec<&str> {
        vec!["send-keys", "-t", &self.target, &self.exec, "KPEnter"]
    }

    fn call(&self) -> Result<Output, io::Error> {
        tmux::call(&self.command())
    }
}

/// Used to attach to the daemonized session.
/// name: The named session to attach too.
#[derive(Debug, Clone)]
pub struct Attach {
    pub name: String,
}

impl Command for Attach {
    fn command(&self) -> Vec<&str> {
        // No-op!
        vec![]
    }

    fn call(&self) -> Result<Output, io::Error> {
        tmux::attach(&self.name)
    }
}

/// Used to move focus back to the first window.
/// target: The target window. In the format `{session}:{window}`.
#[derive(Debug, Clone)]
pub struct SelectWindow {
    pub target: String,
}

impl Command for SelectWindow {
    fn command(&self) -> Vec<&str> {
        vec!["select-window", "-t", &self.target]
    }

    fn call(&self) -> Result<Output, io::Error> {
        tmux::call(&self.command())
    }
}

/// Used to move focus back to the top pane.
/// target: The target pane. In the format `{session}:{window}.{pane-target}`.
#[derive(Debug, Clone)]
pub struct SelectPane {
    pub target: String,
}

impl Command for SelectPane {
    fn command(&self) -> Vec<&str> {
        vec!["select-pane", "-t", &self.target]
    }

    fn call(&self) -> Result<Output, io::Error> {
        tmux::call(&self.command())
    }
}

/// Used for executing the `pre` option to execute commands before building the
/// tmux session.
/// exec: The command to execute
#[derive(Debug, Clone)]
pub struct Pre {
    pub exec: String,
}

impl Command for Pre {
    fn command(&self) -> Vec<&str> {
        // No-op!
        vec![]
    }

    fn call(&self) -> Result<Output, io::Error> {
        let cmd_array: Vec<&str> = self.exec.split(' ').collect();
        let (program, args) = cmd_array
            .split_first()
            .expect("Couldn't find args for pre option");

        process::Command::new(program)
            .args(args)
            .output()
    }
}

/// The Command enum. Commands represent the series of commands sent to the
/// running tmux process to build a users env. This is an enum to support
/// containing all the commands that require running in a single Vec. This
/// allows a simple process of first in, first out command execution.
#[derive(Debug, Clone)]
pub enum Commands {
    Attach(Attach),
    Layout(Layout),
    Pre(Pre),
    SelectPane(SelectPane),
    SelectWindow(SelectWindow),
    SendKeys(SendKeys),
    Session(Session),
    Split(Split),
    Window(Window),
}

impl Commands {
    pub fn as_trait(&self) -> &Command {
        match *self {
            Commands::Pre(ref c) => c,
            Commands::Attach(ref c) => c,
            Commands::Layout(ref c) => c,
            Commands::SelectPane(ref c) => c,
            Commands::SelectWindow(ref c) => c,
            Commands::SendKeys(ref c) => c,
            Commands::Session(ref c) => c,
            Commands::Split(ref c) => c,
            Commands::Window(ref c) => c,
        }
    }
}
<commit_msg>refactor(Command): Rename arguments to args<commit_after>//! The structures used to manage commands sent over to tmux.

use load::tmux;
use std::io;
use std::path::PathBuf;
use std::process::Output;
use std::process;
use std::str;

pub trait Command {
    fn call(&self) -> Result<Output, io::Error>;
    fn args(&self) -> Vec<&str>;
}

/// The Session command is used to fire up a new daemonized session in tmux.
/// `name`: The Name of a named tmux session.
/// `window_name`: The Name of the first window.
/// `root_path`: The root directory for the tmux session.
#[derive(Debug, Clone)]
pub struct Session {
    pub name: String,
    pub window_name: String,
    pub root_path: Option<PathBuf>,
}

// TODO: Real logic exists here. Test it!
impl Command for Session {
    fn args(&self) -> Vec<&str> {
        let args: Vec<&str> = vec!["new", "-d", "-s", &self.name, "-n", &self.window_name];

        match self.root_path.as_ref() {
            Some(path) => [&args[..], &["-c", path.to_str().unwrap()]].concat(),
            None => args,
        }
    }

    fn call(&self) -> Result<Output, io::Error> {
        tmux::call(&self.args())
    }
}

/// The Window command is used to identify every new window opened in the tmux
/// session.
/// `session_name`: The name of the session.
/// `name`: The named window to be opened.
/// `path`: An `Option<PathBuf>` containing a possible root directory passed to the
/// `-c` arguement.
#[derive(Debug, Clone)]
pub struct Window {
    pub session_name: String,
    pub name: String,
    pub path: Option<PathBuf>,
}

impl Command for Window {
    fn args(&self) -> Vec<&str> {
        let args: Vec<&str> = vec!["new-window", "-t", &self.session_name, "-n", &self.name];

        match self.path.as_ref() {
            Some(path) => [&args[..], &["-c", path.to_str().unwrap()]].concat(),
            None => args,
        }
    }

    fn call(&self) -> Result<Output, io::Error> {
        tmux::call(&self.args())
    }
}

/// The Split is used to call split-window on a particular window in the
/// session.
/// `target`: The target window. In the format `{session}:{window}.{paneIndex}`.
#[derive(Debug, Clone)]
pub struct Split {
    pub target: String,
}

impl Command for Split {
    fn args(&self) -> Vec<&str> {
        vec!["split-window", "-t", &self.target]
    }

    fn call(&self) -> Result<Output, io::Error> {
        tmux::call(&self.args())
    }
}

/// The Layout command calls select-layout with a specific pre-defined tmux
/// layout option see `tmux select-layout --help` for more options.
/// `target`: The target window. In the format `{session}:{window}.{paneIndex}`.
/// `layout`: The type of layout. ex `main-horizontal`.
#[derive(Debug, Clone)]
pub struct Layout {
    pub target: String,
    pub layout: String,
}

impl Command for Layout {
    fn args(&self) -> Vec<&str> {
        vec!["select-layout", "-t", &self.target, &self.layout]
    }

    fn call(&self) -> Result<Output, io::Error> {
        tmux::call(&self.args())
    }
}

/// A generic `SendKeys` command used to send "typed" commands to tmux. This is
/// used to initialize processes or tasks in specific window. Such as starting log
/// tails or running servers.
/// target: The target window. In the format `{session}:{window}.{paneIndex}`.
/// exec: The cli command to be run. ex. `tail -f logs/development.log`.
#[derive(Debug, Clone)]
pub struct SendKeys {
    pub target: String,
    pub exec: String,
}

impl Command for SendKeys {
    fn args(&self) -> Vec<&str> {
        vec!["send-keys", "-t", &self.target, &self.exec, "KPEnter"]
    }

    fn call(&self) -> Result<Output, io::Error> {
        tmux::call(&self.args())
    }
}

/// Used to attach to the daemonized session.
/// name: The named session to attach too.
#[derive(Debug, Clone)]
pub struct Attach {
    pub name: String,
}

impl Command for Attach {
    fn args(&self) -> Vec<&str> {
        // No-op!
        vec![]
    }

    fn call(&self) -> Result<Output, io::Error> {
        tmux::attach(&self.name)
    }
}

/// Used to move focus back to the first window.
/// target: The target window. In the format `{session}:{window}`.
#[derive(Debug, Clone)]
pub struct SelectWindow {
    pub target: String,
}

impl Command for SelectWindow {
    fn args(&self) -> Vec<&str> {
        vec!["select-window", "-t", &self.target]
    }

    fn call(&self) -> Result<Output, io::Error> {
        tmux::call(&self.args())
    }
}

/// Used to move focus back to the top pane.
/// target: The target pane. In the format `{session}:{window}.{pane-target}`.
#[derive(Debug, Clone)]
pub struct SelectPane {
    pub target: String,
}

impl Command for SelectPane {
    fn args(&self) -> Vec<&str> {
        vec!["select-pane", "-t", &self.target]
    }

    fn call(&self) -> Result<Output, io::Error> {
        tmux::call(&self.args())
    }
}

/// Used for executing the `pre` option to execute commands before building the
/// tmux session.
/// exec: The command to execute
#[derive(Debug, Clone)]
pub struct Pre {
    pub exec: String,
}

impl Command for Pre {
    fn args(&self) -> Vec<&str> {
        // No-op!
        vec![]
    }

    fn call(&self) -> Result<Output, io::Error> {
        let cmd_array: Vec<&str> = self.exec.split(' ').collect();
        let (program, args) = cmd_array
            .split_first()
            .expect("Couldn't find args for pre option");

        process::Command::new(program)
            .args(args)
            .output()
    }
}

/// The Command enum. Commands represent the series of commands sent to the
/// running tmux process to build a users env. This is an enum to support
/// containing all the commands that require running in a single Vec. This
/// allows a simple process of first in, first out command execution.
#[derive(Debug, Clone)]
pub enum Commands {
    Attach(Attach),
    Layout(Layout),
    Pre(Pre),
    SelectPane(SelectPane),
    SelectWindow(SelectWindow),
    SendKeys(SendKeys),
    Session(Session),
    Split(Split),
    Window(Window),
}

impl Commands {
    pub fn as_trait(&self) -> &Command {
        match *self {
            Commands::Pre(ref c) => c,
            Commands::Attach(ref c) => c,
            Commands::Layout(ref c) => c,
            Commands::SelectPane(ref c) => c,
            Commands::SelectWindow(ref c) => c,
            Commands::SendKeys(ref c) => c,
            Commands::Session(ref c) => c,
            Commands::Split(ref c) => c,
            Commands::Window(ref c) => c,
        }
    }
}
<|endoftext|>