<commit_before>use crate::hb;
use std;
use std::os::raw::c_void;
use std::ptr::NonNull;

use std::marker::PhantomData;
use std::path::Path;

use crate::blob::Blob;
use crate::common::{HarfbuzzObject, Owned, Shared, Tag};

/// A wrapper around `hb_face_t`.
#[derive(Debug)]
pub struct Face<'a> {
    raw: NonNull<hb::hb_face_t>,
    marker: PhantomData<&'a [u8]>,
}

impl<'a> Face<'a> {
    /// Create a new `Face` from the data.
    ///
    /// If `data` is not a valid font then this function returns an empty proxy
    /// value.
    pub fn new<'b, T: Into<Shared<Blob<'b>>>>(data: T, index: u32) -> Owned<Face<'b>> {
        let blob = data.into();
        let hb_face = unsafe { hb::hb_face_create(Shared::into_raw(blob), index) };
        unsafe { Owned::from_raw(hb_face) }
    }

    /// Create a new face from the contents of the file at `path`.
    ///
    /// This function reads the contents of the file at `path` into memory,
    /// creates a `Blob` and then calls `Face::new`.
    ///
    /// See also the discussion in `Blob::from_file`.
    pub fn from_file<P: AsRef<Path>>(path: P, index: u32) -> std::io::Result<Owned<Face<'static>>> {
        let blob = Blob::from_file(path)?;
        Ok(Face::new(blob, index))
    }

    /// Create a face from the bytes of a given slice and an index specifying
    /// which font to read from an OpenType font collection.
    pub fn from_bytes<'b>(bytes: &'b [u8], index: u32) -> Owned<Face<'b>> {
        let blob = Blob::with_bytes(bytes);
        Face::new(blob, index)
    }

    /// Create a new face from a closure that returns a raw
    /// [`Blob`](struct.Blob.html) of table data.
    pub fn from_table_func<'b, F>(func: F) -> Owned<Face<'b>>
    where
        F: 'b + Send + Sync + FnMut(Tag) -> Option<Shared<Blob<'b>>>,
    {
        extern "C" fn destroy_box<U>(ptr: *mut c_void) {
            unsafe { Box::from_raw(ptr as *mut U) };
        }
        extern "C" fn table_func<'b, F>(
            _: *mut hb::hb_face_t,
            tag: hb::hb_tag_t,
            user_data: *mut c_void,
        ) -> *mut hb::hb_blob_t
        where
            F: FnMut(Tag) -> Option<Shared<Blob<'b>>>,
        {
            let tag = Tag(tag);
            let closure = unsafe { &mut *(user_data as *mut F) };
            let blob = closure(tag);
            match blob {
                Some(blob) => Shared::into_raw(blob),
                None => std::ptr::null_mut(),
            }
        }
        let boxed_closure = Box::new(func);
        unsafe {
            let face = hb::hb_face_create_for_tables(
                Some(table_func::<'b, F>),
                Box::into_raw(boxed_closure) as *mut _,
                Some(destroy_box::<F>),
            );
            Owned::from_raw(face)
        }
    }

    pub fn face_data(&self) -> Shared<Blob<'a>> {
        unsafe {
            let raw_blob = hb::hb_face_reference_blob(self.as_raw());
            Shared::from_raw_owned(raw_blob)
        }
    }

    /// Returns the slice of bytes for the table named `tag` or None if there is
    /// no table with `tag`.
    pub fn table_with_tag(&self, tag: Tag) -> Option<Shared<Blob<'a>>> {
        unsafe {
            let raw_blob = hb::hb_face_reference_table(self.as_raw(), tag.0);
            if raw_blob.is_null() {
                None
            } else {
                let blob: Shared<Blob<'_>> = Shared::from_raw_owned(raw_blob);
                if blob.is_empty() {
                    None
                } else {
                    Some(blob)
                }
            }
        }
    }

    pub fn index(&self) -> u32 {
        unsafe { hb::hb_face_get_index(self.as_raw()) }
    }

    pub fn set_upem(&mut self, upem: u32) {
        unsafe { hb::hb_face_set_upem(self.as_raw(), upem) };
    }

    pub fn upem(&self) -> u32 {
        unsafe { hb::hb_face_get_upem(self.as_raw()) }
    }

    pub fn set_glyph_count(&mut self, count: u32) {
        unsafe { hb::hb_face_set_glyph_count(self.as_raw(), count) };
    }

    /// Returns the number of glyphs contained in the face.
    pub fn glyph_count(&self) -> u32 {
        unsafe { hb::hb_face_get_glyph_count(self.as_raw()) }
    }
}

unsafe impl<'a> HarfbuzzObject for Face<'a> {
    type Raw = hb::hb_face_t;

    unsafe fn from_raw(raw: *const hb::hb_face_t) -> Self {
        Face {
            raw: NonNull::new_unchecked(raw as *mut _),
            marker: PhantomData,
        }
    }

    fn as_raw(&self) -> *mut Self::Raw {
        self.raw.as_ptr()
    }

    unsafe fn reference(&self) {
        hb::hb_face_reference(self.as_raw());
    }

    unsafe fn dereference(&self) {
        hb::hb_face_destroy(self.as_raw());
    }
}

unsafe impl<'a> Send for Face<'a> {}
unsafe impl<'a> Sync for Face<'a> {}

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

    #[test]
    fn test_face_from_table_func() {
        let face = Face::from_table_func(|table_tag| {
            let content = format!("{}-table", table_tag);
            Some(content.into_bytes().into())
        });

        let maxp_table = face.table_with_tag(Tag::new('m', 'a', 'x', 'p')).unwrap();
        assert_eq!(maxp_table.as_ref(), b"maxp-table");

        let maxp_table = face.table_with_tag(Tag::new('h', 'h', 'e', 'a')).unwrap();
        assert_eq!(&maxp_table.as_ref(), b"hhea-table");
    }
}
<commit_msg>Add a Face::empty constructor<commit_after>use crate::hb;
use std;
use std::os::raw::c_void;
use std::ptr::NonNull;

use std::marker::PhantomData;
use std::path::Path;

use crate::blob::Blob;
use crate::common::{HarfbuzzObject, Owned, Shared, Tag};

/// A wrapper around `hb_face_t`.
#[derive(Debug)]
pub struct Face<'a> {
    raw: NonNull<hb::hb_face_t>,
    marker: PhantomData<&'a [u8]>,
}

impl<'a> Face<'a> {
    /// Create a new `Face` from the data.
    ///
    /// If `data` is not a valid font then this function returns an empty proxy
    /// value.
    pub fn new<'b, T: Into<Shared<Blob<'b>>>>(data: T, index: u32) -> Owned<Face<'b>> {
        let blob = data.into();
        let hb_face = unsafe { hb::hb_face_create(Shared::into_raw(blob), index) };
        unsafe { Owned::from_raw(hb_face) }
    }

    /// Returns a "null" face.
    pub fn empty() -> Owned<Face<'static>> {
        let hb_face = unsafe { hb::hb_face_get_empty() };
        unsafe { Owned::from_raw(hb_face) }
    }

    /// Create a new face from the contents of the file at `path`.
    ///
    /// This function reads the contents of the file at `path` into memory,
    /// creates a `Blob` and then calls `Face::new`.
    ///
    /// See also the discussion in `Blob::from_file`.
    pub fn from_file<P: AsRef<Path>>(path: P, index: u32) -> std::io::Result<Owned<Face<'static>>> {
        let blob = Blob::from_file(path)?;
        Ok(Face::new(blob, index))
    }

    /// Create a face from the bytes of a given slice and an index specifying
    /// which font to read from an OpenType font collection.
    pub fn from_bytes<'b>(bytes: &'b [u8], index: u32) -> Owned<Face<'b>> {
        let blob = Blob::with_bytes(bytes);
        Face::new(blob, index)
    }

    /// Create a new face from a closure that returns a raw
    /// [`Blob`](struct.Blob.html) of table data.
    pub fn from_table_func<'b, F>(func: F) -> Owned<Face<'b>>
    where
        F: 'b + Send + Sync + FnMut(Tag) -> Option<Shared<Blob<'b>>>,
    {
        extern "C" fn destroy_box<U>(ptr: *mut c_void) {
            unsafe { Box::from_raw(ptr as *mut U) };
        }
        extern "C" fn table_func<'b, F>(
            _: *mut hb::hb_face_t,
            tag: hb::hb_tag_t,
            user_data: *mut c_void,
        ) -> *mut hb::hb_blob_t
        where
            F: FnMut(Tag) -> Option<Shared<Blob<'b>>>,
        {
            let tag = Tag(tag);
            let closure = unsafe { &mut *(user_data as *mut F) };
            let blob = closure(tag);
            match blob {
                Some(blob) => Shared::into_raw(blob),
                None => std::ptr::null_mut(),
            }
        }
        let boxed_closure = Box::new(func);
        unsafe {
            let face = hb::hb_face_create_for_tables(
                Some(table_func::<'b, F>),
                Box::into_raw(boxed_closure) as *mut _,
                Some(destroy_box::<F>),
            );
            Owned::from_raw(face)
        }
    }

    pub fn face_data(&self) -> Shared<Blob<'a>> {
        unsafe {
            let raw_blob = hb::hb_face_reference_blob(self.as_raw());
            Shared::from_raw_owned(raw_blob)
        }
    }

    /// Returns the slice of bytes for the table named `tag` or None if there is
    /// no table with `tag`.
    pub fn table_with_tag(&self, tag: Tag) -> Option<Shared<Blob<'a>>> {
        unsafe {
            let raw_blob = hb::hb_face_reference_table(self.as_raw(), tag.0);
            if raw_blob.is_null() {
                None
            } else {
                let blob: Shared<Blob<'_>> = Shared::from_raw_owned(raw_blob);
                if blob.is_empty() {
                    None
                } else {
                    Some(blob)
                }
            }
        }
    }

    pub fn index(&self) -> u32 {
        unsafe { hb::hb_face_get_index(self.as_raw()) }
    }

    pub fn set_upem(&mut self, upem: u32) {
        unsafe { hb::hb_face_set_upem(self.as_raw(), upem) };
    }

    pub fn upem(&self) -> u32 {
        unsafe { hb::hb_face_get_upem(self.as_raw()) }
    }

    pub fn set_glyph_count(&mut self, count: u32) {
        unsafe { hb::hb_face_set_glyph_count(self.as_raw(), count) };
    }

    /// Returns the number of glyphs contained in the face.
    pub fn glyph_count(&self) -> u32 {
        unsafe { hb::hb_face_get_glyph_count(self.as_raw()) }
    }
}

unsafe impl<'a> HarfbuzzObject for Face<'a> {
    type Raw = hb::hb_face_t;

    unsafe fn from_raw(raw: *const hb::hb_face_t) -> Self {
        Face {
            raw: NonNull::new_unchecked(raw as *mut _),
            marker: PhantomData,
        }
    }

    fn as_raw(&self) -> *mut Self::Raw {
        self.raw.as_ptr()
    }

    unsafe fn reference(&self) {
        hb::hb_face_reference(self.as_raw());
    }

    unsafe fn dereference(&self) {
        hb::hb_face_destroy(self.as_raw());
    }
}

unsafe impl<'a> Send for Face<'a> {}
unsafe impl<'a> Sync for Face<'a> {}

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

    #[test]
    fn test_face_from_table_func() {
        let face = Face::from_table_func(|table_tag| {
            let content = format!("{}-table", table_tag);
            Some(content.into_bytes().into())
        });

        let maxp_table = face.table_with_tag(Tag::new('m', 'a', 'x', 'p')).unwrap();
        assert_eq!(maxp_table.as_ref(), b"maxp-table");

        let maxp_table = face.table_with_tag(Tag::new('h', 'h', 'e', 'a')).unwrap();
        assert_eq!(&maxp_table.as_ref(), b"hhea-table");
    }
}
<|endoftext|>


<commit_before>use imgui::{Ui,ImStr,ImString};

use std::fs;
use std::path::Path;

use app::AppState;

pub struct FileState {
    /// MAX_PATH set to 260 chars
    cd: ImString,
    idx: i32,
    files: Vec<String>,
    selected: Option<String>,
}

impl Default for FileState {
    fn default() -> Self {
        let mut cd = ImString::from(im_str!("./"));
        cd.reserve(261); //arbitrary max path size

        FileState {
            cd: cd,
            idx: 1,
            files: vec![],
            selected: None,
        }
    }
}

impl FileState {
    fn update(&mut self) {
        if self.idx > 1 { // chose a file?
            if let Some(file) = self.files.get(self.idx as usize) {
                self.cd.clear();
                self.cd.push_str(file);
                self.idx = 1;
            }
        }
        else if self.idx < 1 { // up a directory?
            self.to_parent();
        }

        let cd = self.cd.to_string();
        if Path::new(&cd).is_dir() {
            if let Ok(paths) = fs::read_dir(&cd) {
                self.files = paths
                    .filter(|p| p.is_ok())
                    .map(|p| {
                        p.unwrap().path().as_path().to_str().unwrap().to_owned()
                    })
                    .collect();

                self.files.insert(0,"./".to_owned());
                self.files.insert(0,"..".to_owned());
            }
            // NOTE: we should throw a warning here if directory cannot be traversed
        }
        else {
            if !cd.is_empty() {
                self.selected = Some(cd);
            }
        }
    }

    fn to_parent(&mut self) {
        self.selected = None;
        if let Some(path) = Path::new(&self.cd.to_string()).parent() {
            self.cd.clear();
            self.cd.push_str(path.to_str().expect("ERROR: Cannot parse path into string"));
            self.idx = 1;
        }
        // NOTE: this fails if we're at the top of a relative directory listing,
        // or the root of the drive/share
    }

    pub fn render (&mut self, ui: &Ui, state: &mut AppState) {
        if !state.open_file { return }

        ui.window(im_str!("Select source"))
            .always_auto_resize(true)
            .movable(true)
            .show_borders(true)
            .opened(&mut state.open_file)
            .build(||{
                ui.text(im_str!("Select a source file to be parsed and evaluated.\nSource files must be parsable through the lichen crate."));
                ui.separator();
                ui.input_text(im_str!("Current Directory"), &mut self.cd)
                    .enter_returns_true(true)
                    .build();
                ui.separator();

                self.update();

                // NOTE: to appease the borrow checker and have the proper argument for list_box
                // we must build the list in two steps
                let paths: Vec<ImString> = self.files.iter().map(|p| {
                    im_str!("{:}", p).to_owned()
                })
                    .collect();

                let paths: Vec<&ImStr> = paths.iter().map(|p|p.as_ref()).collect();

                ui.list_box(im_str!("Files"),
                            &mut self.idx,
                            &paths[..],
                            paths.len() as i32);

                if let Some(ref file) = self.selected {
                    ui.text(im_str!("Select File {:}?",file));

                    if ui.small_button(im_str!("open")) {
                    }
                }
            })
    }

}

<commit_msg>small change<commit_after>use imgui::{Ui,ImStr,ImString};

use std::fs;
use std::path::Path;

use app::AppState;

pub struct FileState {
    /// MAX_PATH set to 260 chars
    cd: ImString,
    idx: i32,
    files: Vec<String>,
    selected: Option<String>,
}

impl Default for FileState {
    fn default() -> Self {
        let mut cd = ImString::from(im_str!("./"));
        cd.reserve(261); //arbitrary max path size

        FileState {
            cd: cd,
            idx: 1,
            files: vec![],
            selected: None,
        }
    }
}

impl FileState {
    fn update(&mut self) {
        if self.idx > 1 { // chose a file?
            if let Some(file) = self.files.get(self.idx as usize) {
                self.cd.clear();
                self.cd.push_str(file);
                self.idx = 1;
            }
        }
        else if self.idx < 1 { // up a directory?
            self.to_parent();
        }

        let cd = self.cd.to_string();
        if Path::new(&cd).is_dir() {
            if let Ok(paths) = fs::read_dir(&cd) {
                self.files = paths
                    .filter(|p| p.is_ok())
                    .map(|p| {
                        p.unwrap().path().as_path().to_str().unwrap().to_owned()
                    })
                    .collect();

                self.files.insert(0,"./".to_owned());
                self.files.insert(0,"..".to_owned());
            }
            // NOTE: we should throw a warning here if directory cannot be traversed
        }
        else {
            if !cd.is_empty() {
                self.selected = Some(cd);
            }
        }
    }

    fn to_parent(&mut self) {
        self.selected = None;
        if let Some(path) = Path::new(&self.cd.to_string()).parent() {
            self.cd.clear();
            self.cd.push_str(path.to_str().expect("ERROR: Cannot parse path into string"));
            self.idx = 1;
        }
        // NOTE: this fails if we're at the top of a relative directory listing,
        // or the root of the drive/share
    }

    pub fn render (&mut self, ui: &Ui, state: &mut AppState) {
        if !state.open_file { return }

        ui.window(im_str!("Select source"))
            .always_auto_resize(true)
            .movable(true)
            .show_borders(true)
            .opened(&mut state.open_file)
            .build(||{
                ui.text(im_str!("Select a source file to be parsed and evaluated.\nSource files must be parsable through the lichen crate."));
                ui.separator();
                ui.input_text(im_str!("Current Directory"), &mut self.cd)
                    .enter_returns_true(true)
                    .build();
                ui.separator();

                self.update();

                // NOTE: to appease the borrow checker and have the proper argument for list_box
                // we must build the list in two steps
                let paths: Vec<ImString> = self.files.iter().map(|p| {
                    im_str!("{:}", p).to_owned()
                }).collect();

                let paths: Vec<&ImStr> = paths.iter().map(|p|p.as_ref()).collect();

                ui.list_box(im_str!("Files"),
                            &mut self.idx,
                            &paths[..],
                            paths.len() as i32);

                if let Some(ref file) = self.selected {
                    ui.text(im_str!("Select File {:}?",file));

                    if ui.small_button(im_str!("open")) {
                    }
                }
            })
    }

}

<|endoftext|>


<commit_before>
use std::num::Float;
use sdl::video::{Surface};
use sdl::event::{Key};

use world::{World};
use math::{LineSeg, Vec2, Mat3};
use input::{InputState};

pub struct Game<'a> {
    pub pos: Vec2,
    pub face_angle: f32,
    pub world: &'a World,
    pub show_map: bool,
}

const SPEED: f32 = 0.4;
const TURN: f32 = 0.03;
const FOV_DIV: f32 = 500.0;
const WALL: f32 = 3000.0;

impl<'a> Game<'a> {

    pub fn step(&mut self, input: &InputState) {
        if input.has_key(Key::Left) {
            self.face_angle -= TURN;
        }
        if input.has_key(Key::Right) {
            self.face_angle += TURN;
        }
        if input.has_key(Key::W) {
            self.pos.x += SPEED*Float::sin(self.face_angle);
            self.pos.y -= SPEED*Float::cos(self.face_angle);
        }
        if input.has_key(Key::S) {
            self.pos.x -= SPEED*Float::sin(self.face_angle);
            self.pos.y += SPEED*Float::cos(self.face_angle);
        }
        if input.has_key(Key::A) {
            self.pos.x -= SPEED*Float::cos(self.face_angle);
            self.pos.y -= SPEED*Float::sin(self.face_angle);
        }
        if input.has_key(Key::D) {
            self.pos.x += SPEED*Float::cos(self.face_angle);
            self.pos.y += SPEED*Float::sin(self.face_angle);
        }
        self.show_map = input.has_key(Key::Tab);
    }

    pub fn render(&self, surf: &Surface) {
        if self.show_map {
            self.render_lines(&surf);
        } else {
            self.render_cast(&surf);
        }
    }

    fn render_lines(&self, surf: &Surface) {
        let trans = Mat3::rotation(-self.face_angle)
                  * Mat3::translation(-self.pos);

        surf.clear();
        for wall in self.world.walls.iter() {
            draw_seg(&surf, wall.transform(trans), 0xFF, 0x00, 0x00);
        }

        let w = surf.get_width() as usize;
        for x in 0..w {
            let offset = ((x as f32) - (w as f32) / 2.0) / FOV_DIV;

            self.world.cast_ray(self.pos, self.face_angle + offset).map(|(_, pos)| {
                let draw_sg = LineSeg::new(self.pos.x, self.pos.y, pos.x, pos.y);
                draw_seg(&surf, draw_sg.transform(trans), 0x00, 0xFF, 0x00);
            });
        }
    }

    fn render_cast(&self, surf: &Surface) {
        let w = surf.get_width() as usize;
        let h = surf.get_height() as usize;

        surf.clear();
        surf.with_lock(|pixels| {
            for x in 0..w {
                let offset = ((x as f32) - (w as f32) / 2.0) / FOV_DIV;

                let (top, bottom) = match self.world.cast_ray(self.pos, self.face_angle + offset) {
                    Some((dist, _)) => {
                        let px_height = (WALL / (dist * Float::cos(offset))) as usize;
                        let top = if h > px_height { (h - px_height) / 2 } else { 0 };
                        (top, h-top)
                    }
                    None => { (h/2, h/2) }
                };

                let color = if top == 0 || top == h/2 { 0xFF } else { (0xFF-top) as u8 };

                for y in 0..top      { put_px(pixels, w, x, y,  0x00, 0x66, 0xFF); }
                for y in top..bottom { put_px(pixels, w, x, y, color, 0x00, 0x00); }
                for y in bottom..h   { put_px(pixels, w, x, y,  0x00, 0xBB, 0x00); }
            }
            true
        });
    }

}

fn put_px(pixels: &mut [u8], w: usize, x: usize, y: usize, r: u8, g: u8, b: u8) {
    pixels[3*(w*y+x) + 0] = b;
    pixels[3*(w*y+x) + 1] = g;
    pixels[3*(w*y+x) + 2] = r;
}

fn draw_seg(surf: &Surface, seg: LineSeg, r: u8, g: u8, b: u8) {
    let w = surf.get_width() as usize;
    let h = surf.get_height() as usize;
    let len = seg.get_length();

    surf.with_lock(|pixels| {
        for t in 0..(len as usize) {
            let pt = seg.at(t as f32 / len);
            let ux = (pt.x + (w/2) as f32) as usize;
            let uy = (pt.y + (h/2) as f32) as usize;

            if ux < w && uy < h {
                put_px(pixels, w, ux, uy, r, g, b);
            }
        };
        true
    });
}
<commit_msg>Simplified movement<commit_after>
use std::num::Float;
use sdl::video::{Surface};
use sdl::event::{Key};

use world::{World};
use math::{LineSeg, Vec2, Mat3};
use input::{InputState};

pub struct Game<'a> {
    pub pos: Vec2,
    pub face_angle: f32,
    pub world: &'a World,
    pub show_map: bool,
}

const SPEED: f32 = 0.4;
const TURN: f32 = 0.03;
const FOV_DIV: f32 = 500.0;
const WALL: f32 = 3000.0;

impl<'a> Game<'a> {

    pub fn step(&mut self, input: &InputState) {
        if input.has_key(Key::Left)  { self.face_angle -= TURN; }
        if input.has_key(Key::Right) { self.face_angle += TURN; }

        if input.has_key(Key::W) { self.do_move( 1.0,  0.0); }
        if input.has_key(Key::S) { self.do_move(-1.0,  0.0); }
        if input.has_key(Key::A) { self.do_move( 0.0, -1.0); }
        if input.has_key(Key::D) { self.do_move( 0.0,  1.0); }

        self.show_map = input.has_key(Key::Tab);
    }

    fn do_move(&mut self, para: f32, perp: f32) {
        let sin = SPEED*Float::sin(self.face_angle);
        let cos = SPEED*Float::cos(self.face_angle);
        self.pos.x +=  sin*para + cos*perp;
        self.pos.y += -cos*para + sin*perp;
    }

    pub fn render(&self, surf: &Surface) {
        if self.show_map {
            self.render_map(&surf);
        } else {
            self.render_game(&surf);
        }
    }

    fn render_map(&self, surf: &Surface) {
        let trans = Mat3::rotation(-self.face_angle)
                  * Mat3::translation(-self.pos);

        surf.clear();
        for wall in self.world.walls.iter() {
            draw_seg(&surf, wall.transform(trans), 0xFF, 0x00, 0x00);
        }

        let w = surf.get_width() as usize;
        for x in 0..w {
            let offset = ((x as f32) - (w as f32) / 2.0) / FOV_DIV;

            self.world.cast_ray(self.pos, self.face_angle + offset).map(|(_, pos)| {
                let draw_sg = LineSeg::new(self.pos.x, self.pos.y, pos.x, pos.y);
                draw_seg(&surf, draw_sg.transform(trans), 0x00, 0xFF, 0x00);
            });
        }
    }

    fn render_game(&self, surf: &Surface) {
        let w = surf.get_width() as usize;
        let h = surf.get_height() as usize;

        surf.clear();
        surf.with_lock(|pixels| {
            for x in 0..w {
                let offset = ((x as f32) - (w as f32) / 2.0) / FOV_DIV;

                let (top, bottom) = match self.world.cast_ray(self.pos, self.face_angle + offset) {
                    Some((dist, _)) => {
                        let px_height = (WALL / (dist * Float::cos(offset))) as usize;
                        let top = if h > px_height { (h - px_height) / 2 } else { 0 };
                        (top, h-top)
                    }
                    None => { (h/2, h/2) }
                };

                let color = if top == 0 || top == h/2 { 0xFF } else { (0xFF-top) as u8 };

                for y in 0..top      { put_px(pixels, w, x, y,  0x00, 0x66, 0xFF); }
                for y in top..bottom { put_px(pixels, w, x, y, color, 0x00, 0x00); }
                for y in bottom..h   { put_px(pixels, w, x, y,  0x00, 0xBB, 0x00); }
            }
            true
        });
    }
}

fn put_px(pixels: &mut [u8], w: usize, x: usize, y: usize, r: u8, g: u8, b: u8) {
    pixels[3*(w*y+x) + 0] = b;
    pixels[3*(w*y+x) + 1] = g;
    pixels[3*(w*y+x) + 2] = r;
}

fn draw_seg(surf: &Surface, seg: LineSeg, r: u8, g: u8, b: u8) {
    let w = surf.get_width() as usize;
    let h = surf.get_height() as usize;
    let len = seg.get_length();

    surf.with_lock(|pixels| {
        for t in 0..(len as usize) {
            let pt = seg.at(t as f32 / len);
            let ux = (pt.x + (w/2) as f32) as usize;
            let uy = (pt.y + (h/2) as f32) as usize;

            if ux < w && uy < h {
                put_px(pixels, w, ux, uy, r, g, b);
            }
        };
        true
    });
}
<|endoftext|>


<commit_before>
use std::cmp;

use colored::*;
use card::*;

pub struct Game{
    deck:           Deck,
    tableau:        Vec<Stack>,
    target:         Vec<Stack>,
    open_tableau:   Vec<Stack>,
    stack:          Stack,
    round:          u16
}

impl Game{

    pub fn new() -> Game{
        let mut deck = Deck::new();
        deck.shuffle();

        let tableau = (0..7).map(|_| Stack::new()).collect();
        let target = (0..4).map(|_| Stack::new()).collect();
        let open_tableau = (0..7).map(|_| Stack::new()).collect();

        Game{
            deck:           deck,
            tableau:        tableau,
            open_tableau:   open_tableau,
            target:         target,
            stack:          Stack::new(),
            round:          0
        }
    }

    pub fn start(&mut self){
        println!("\n\t\t--------------------");
        println!("{}", "\t\tWelcome to Solitaire!".bold());
        println!("\t\t--------------------\n");

        // deal cards
        let tableau_len = self.tableau.len();
        let mut stacks_to_deal = tableau_len;

        while stacks_to_deal > 0 {
            for n in 1..(stacks_to_deal + 1){
                let card = self.deck.take(1);
                self.tableau[tableau_len - n].add_to_top(card);
            }
            stacks_to_deal = stacks_to_deal - 1;
        }

        for n in 0..(tableau_len){
            let card = self.tableau[n].take(1);
            self.open_tableau[n].add_to_top(card);
        }

        self.display_board();
    }

    pub fn display_board(&self){
        // deck
        let deck = "?".blue();

        // stack
        let stack = if self.stack.count() > 0 {
            if let Some(card) = self.stack.show(self.stack.count() - 1){
                card.to_string()
            }else{
                "  ".to_string()
            }
        } else {
            "".to_string()
        };

        // targets
        let target = &self.target.iter().map(|stack|
            if stack.count() > 0 {
                if let Some(card) = stack.show(stack.count() - 1){
                    card.to_string()
                }else{
                    " ".to_string()
                }
            } else {
                " ".to_string()
            }
        ).collect::<Vec<String>>();

        println!("\n\n{}: {}", "Moves".bold(), self.round);

        println!("\n\n<h>\t<s>\t\t\t<a>\t<b>\t<c>\t<d>");

        println!("[{}]\t{}\t\t\t[{}]\t[{}]\t[{}]\t[{}]", deck, stack, target[0], target[1], target[2], target[3]);

        let stacks = &self.tableau.iter().map(|stack|
            stack.count().to_string().blue()
        ).collect::<Vec<ColoredString>>();

        // stacks
      println!("\n\t<1>\t<2>\t<3>\t<4>\t<5>\t<6>\t<7>");
        println!("\t[{}]\t[{}]\t[{}]\t[{}]\t[{}]\t[{}]\t[{}]", stacks[0], stacks[1], stacks[2], stacks[3],
            stacks[4], stacks[5], stacks[6]);

        let mut longest_stack = 0;
        let mut stack_iter = 0;

        for stack in &self.open_tableau{
            longest_stack = cmp::max(stack.count(), longest_stack);
        }

        while stack_iter < longest_stack {
            let stacks = &self.open_tableau.iter().map(|stack|

                if let Some(card) = stack.show(stack_iter){
                    card.to_string()
                }else{
                    "  ".to_string()
                }

            ).collect::<Vec<String>>();
            // cards
            println!("\t{}\t{}\t{}\t{}\t{}\t{}\t{}\t\t:{}", stacks[0], stacks[1], stacks[2],
                stacks[3], stacks[4], stacks[5], stacks[6], stack_iter);

            stack_iter = stack_iter + 1;
        }

        println!("\n");
    }

    pub fn deal(&mut self){
        self.round = self.round + 1;
        let take = cmp::min(3, self.deck.count());

        if take > 0 {
            let card = self.deck.take(take);
            self.stack.add_to_top(card);
        } else {
            let count = self.stack.count();
            let cards = self.stack.take(count);
            self.deck.add_to_top(cards);
        }
    }

    pub fn move_card(&mut self, source:&str, target:&str){

        // todo - move king to empty space
        // todo - move stacks of cards
        // todo - move suits up to targets

        println!("Checking move {} to {}", source, target);

        let empty_target_stack = Stack::new();
        let empty_source_stack = Stack::new();

        let mut target_stack: Stack = match self.get_target_stack(target){
            Some(target_stack)  => target_stack,
            None                => empty_target_stack
        };

        let mut source_stack: Stack = match self.get_source_stack(source){
            Some(source_stack)  => source_stack,
            None                => empty_source_stack
        };

        // 1 - target is open_tableau then needs to be alternate colour and smaller card
        // OR - a King

        // 2 - target is a target stack so card needs to be same suit and higher card
        // OR an ace

        // index of target stack card
        let n = if target_stack.count() > 1 {
            target_stack.count() - 1
        } else {
            0
        };

        let do_move = match target.chars().next().unwrap() {
            'a' ... 'd'  => {
                match target_stack.show(n) {

                    Some(target_card) => {
                        let next_rank = target_card.next_rank().unwrap_or(Rank::King);

                        if let Some(source_card) = source_stack.show(source_stack.count() - 1){
                            if source_card.suit == target_card.suit && source_card.rank == next_rank {
                                let card = source_stack.take(1);
                                target_stack.add_to_top(card);
                                true
                            }else {
                                false
                            }
                        }else{
                            false
                        }
                    },

                    None => {
                        let source_card = source_stack.show(source_stack.count() - 1).unwrap_or(Card::new(Rank::King, Suit::Spades));
                        if source_card.rank == Rank::Ace {
                            let card = source_stack.take(1);
                            target_stack.add_to_top(card);
                            true
                        }else {
                            false
                        }
                    }
                }
            },
            '1' ... '7' => {

                match target_stack.show(n) {

                    Some(target_card) => {

                        let previous_rank = target_card.previous_rank().unwrap_or(Rank::Ace);

                        if let Some(source_card) = source_stack.show(source_stack.count() - 1){
                            if source_card.colour == target_card.alternate_colour() && source_card.rank == previous_rank {
                                let card = source_stack.take(1);
                                target_stack.add_to_top(card);
                                true
                            }else {
                                false
                            }
                        }else{
                            false
                        }
                    },
                    None => {
                        let source_card = source_stack.show(source_stack.count() - 1).unwrap_or(Card::new(Rank::Ace, Suit::Spades));
                        if source_card.rank == Rank::King {
                            let card = source_stack.take(1);
                            target_stack.add_to_top(card);
                            true
                        }else {
                            false
                        }
                    }
                }
            },
            _           => {false}
        };

        // replace new source and target with modified stacks
        if do_move {

            match &*target {
                "a" => {self.target[0] = target_stack;},
                "b" => {self.target[1] = target_stack;},
                "c" => {self.target[2] = target_stack;},
                "d" => {self.target[3] = target_stack;},
                "1" => {self.open_tableau[0] = target_stack;},
                "2" => {self.open_tableau[1] = target_stack;},
                "3" => {self.open_tableau[2] = target_stack;},
                "4" => {self.open_tableau[3] = target_stack;},
                "5" => {self.open_tableau[4] = target_stack;},
                "6" => {self.open_tableau[5] = target_stack;},
                "7" => {self.open_tableau[6] = target_stack;},
                _   => {}
            }

            match &*source {
                "s" => {self.stack = source_stack;},
                "1" => {self.open_tableau[0] = source_stack;},
                "2" => {self.open_tableau[1] = source_stack;},
                "3" => {self.open_tableau[2] = source_stack;},
                "4" => {self.open_tableau[3] = source_stack;},
                "5" => {self.open_tableau[4] = source_stack;},
                "6" => {self.open_tableau[5] = source_stack;},
                "7" => {self.open_tableau[6] = source_stack;},
                _   => {}
            }
        }

    }

    pub fn deal_stack(&mut self, source:&str){
        println!("Checking stack {}", source);
    }

    fn get_target_stack(&self, target:&str)->Option<Stack>{
        let open_tableau = &self.open_tableau;
        let target_ref = &self.target;
        match &*target {
            "a" => Some(target_ref[0].clone()),
            "b" => Some(target_ref[1].clone()),
            "c" => Some(target_ref[2].clone()),
            "d" => Some(target_ref[3].clone()),
            "1" => Some(open_tableau[0].clone()),
            "2" => Some(open_tableau[1].clone()),
            "3" => Some(open_tableau[2].clone()),
            "4" => Some(open_tableau[3].clone()),
            "5" => Some(open_tableau[4].clone()),
            "6" => Some(open_tableau[5].clone()),
            "7" => Some(open_tableau[6].clone()),
            _   => None
        }

    }

    fn get_source_stack(&self, source:&str)->Option<Stack>{

        let open_tableau = &self.open_tableau;

        match &*source {
            "s" => Some(self.stack.clone()),
            "1" => Some(open_tableau[0].clone()),
            "2" => Some(open_tableau[1].clone()),
            "3" => Some(open_tableau[2].clone()),
            "4" => Some(open_tableau[3].clone()),
            "5" => Some(open_tableau[4].clone()),
            "6" => Some(open_tableau[5].clone()),
            "7" => Some(open_tableau[6].clone()),
            _   => None
        }
    }
}<commit_msg>ability to move stacks of cards<commit_after>
use std::cmp;

use colored::*;
use card::*;

pub struct Game{
    deck:           Deck,
    tableau:        Vec<Stack>,
    target:         Vec<Stack>,
    open_tableau:   Vec<Stack>,
    stack:          Stack,
    round:          u16
}

impl Game{

    pub fn new() -> Game{
        let mut deck = Deck::new();
        deck.shuffle();

        let tableau = (0..7).map(|_| Stack::new()).collect();
        let target = (0..4).map(|_| Stack::new()).collect();
        let open_tableau = (0..7).map(|_| Stack::new()).collect();

        Game{
            deck:           deck,
            tableau:        tableau,
            open_tableau:   open_tableau,
            target:         target,
            stack:          Stack::new(),
            round:          0
        }
    }

    pub fn start(&mut self){
        println!("\n\t\t--------------------");
        println!("{}", "\t\tWelcome to Solitaire!".bold());
        println!("\t\t--------------------\n");

        // deal cards
        let tableau_len = self.tableau.len();
        let mut stacks_to_deal = tableau_len;

        while stacks_to_deal > 0 {
            for n in 1..(stacks_to_deal + 1){
                let card = self.deck.take(1);
                self.tableau[tableau_len - n].add_to_top(card);
            }
            stacks_to_deal = stacks_to_deal - 1;
        }

        for n in 0..(tableau_len){
            let card = self.tableau[n].take(1);
            self.open_tableau[n].add_to_top(card);
        }

        self.display_board();
    }

    pub fn display_board(&self){
        // deck
        let deck = "?".blue();

        // stack
        let stack = if self.stack.count() > 0 {
            if let Some(card) = self.stack.show(self.stack.count() - 1){
                card.to_string()
            }else{
                "  ".to_string()
            }
        } else {
            "".to_string()
        };

        // targets
        let target = &self.target.iter().map(|stack|
            if stack.count() > 0 {
                if let Some(card) = stack.show(stack.count() - 1){
                    card.to_string()
                }else{
                    " ".to_string()
                }
            } else {
                " ".to_string()
            }
        ).collect::<Vec<String>>();

        println!("\n\n{}: {}", "Moves".bold(), self.round);

        println!("\n\n<h>\t<s>\t\t\t<a>\t<b>\t<c>\t<d>");

        println!("[{}]\t{}\t\t\t[{}]\t[{}]\t[{}]\t[{}]", deck, stack, target[0], target[1], target[2], target[3]);

        let stacks = &self.tableau.iter().map(|stack|
            stack.count().to_string().blue()
        ).collect::<Vec<ColoredString>>();

        // stacks
      println!("\n\t<1>\t<2>\t<3>\t<4>\t<5>\t<6>\t<7>");
        println!("\t[{}]\t[{}]\t[{}]\t[{}]\t[{}]\t[{}]\t[{}]", stacks[0], stacks[1], stacks[2], stacks[3],
            stacks[4], stacks[5], stacks[6]);

        let mut longest_stack = 0;
        let mut stack_iter = 0;

        for stack in &self.open_tableau{
            longest_stack = cmp::max(stack.count(), longest_stack);
        }

        while stack_iter < longest_stack {
            let stacks = &self.open_tableau.iter().map(|stack|

                if let Some(card) = stack.show(stack_iter){
                    card.to_string()
                }else{
                    "  ".to_string()
                }

            ).collect::<Vec<String>>();
            // cards
            println!("\t{}\t{}\t{}\t{}\t{}\t{}\t{}\t\t:{}", stacks[0], stacks[1], stacks[2],
                stacks[3], stacks[4], stacks[5], stacks[6], stack_iter);

            stack_iter = stack_iter + 1;
        }

        println!("\n");
    }

    pub fn deal(&mut self){
        self.round = self.round + 1;
        let take = cmp::min(3, self.deck.count());

        if take > 0 {
            let card = self.deck.take(take);
            self.stack.add_to_top(card);
        } else {
            let count = self.stack.count();
            let cards = self.stack.take(count);
            self.deck.add_to_top(cards);
        }
    }

    pub fn move_card(&mut self, source:&str, target:&str){

        // todo - move king to empty space
        // todo - move stacks of cards
        // todo - move suits up to targets

        println!("Checking move {} to {}", source, target);

        let empty_target_stack = Stack::new();
        let empty_source_stack = Stack::new();

        let mut target_stack: Stack = match self.get_target_stack(target){
            Some(target_stack)  => target_stack,
            None                => empty_target_stack
        };

        let mut source_stack: Stack = match self.get_source_stack(source){
            Some(source_stack)  => source_stack,
            None                => empty_source_stack
        };

        // -- returning here if moving from empty stack
        if source_stack.count() < 1 { return; }

        // 1 - target is open_tableau then needs to be alternate colour and smaller card
        // OR - a King

        // 2 - target is a target stack so card needs to be same suit and higher card
        // OR an ace

        // index of target stack card
        let n = if target_stack.count() > 1 {
            target_stack.count() - 1
        } else {
            0
        };

        let do_move = match target.chars().next().unwrap() {
            'a' ... 'd'  => {
                match target_stack.show(n) {

                    Some(target_card) => {
                        let next_rank = target_card.next_rank().unwrap_or(Rank::King);

                        if let Some(source_card) = source_stack.show(source_stack.count() - 1){
                            if source_card.suit == target_card.suit && source_card.rank == next_rank {
                                let card = source_stack.take(1);
                                target_stack.add_to_top(card);
                                true
                            }else {
                                false
                            }
                        }else{
                            false
                        }
                    },

                    None => {
                        let source_card = source_stack.show(source_stack.count() - 1).unwrap_or(Card::new(Rank::King, Suit::Spades));
                        if source_card.rank == Rank::Ace {
                            let card = source_stack.take(1);
                            target_stack.add_to_top(card);
                            true
                        }else {
                            false
                        }
                    }
                }
            },
            '1' ... '7' => {

                match target_stack.show(n) {

                    Some(target_card) => {

                        let previous_rank = target_card.previous_rank().unwrap_or(Rank::Ace);

                        //
                        // todo - this is where we need to check for moving stacks of cards
                        //

                        let mut n = 1;
                        let mut moved = false;

                        while n <= source_stack.count() {
                            let source_card = source_stack.show(source_stack.count() - n).unwrap();
                            if source_card.colour == target_card.alternate_colour() && source_card.rank == previous_rank {
                                let mut card = source_stack.take(n);
                                card.reverse();
                                target_stack.add_to_top(card);
                                moved = true;
                                break;
                            }
                            n = n + 1;
                        }

                        moved

                        // if let Some(source_card) = source_stack.show(source_stack.count() - 1){

                        //     if source_card.colour == target_card.alternate_colour() && source_card.rank == previous_rank {
                        //         let card = source_stack.take(1);
                        //         target_stack.add_to_top(card);
                        //         true
                        //     }else {
                        //         false
                        //     }

                        // }else{
                        //     false
                        // }

                        //
                        // end todo
                        //

                    },
                    None => {
                        let source_card = source_stack.show(source_stack.count() - 1).unwrap_or(Card::new(Rank::Ace, Suit::Spades));
                        if source_card.rank == Rank::King {
                            let card = source_stack.take(1);
                            target_stack.add_to_top(card);
                            true
                        }else {
                            false
                        }
                    }
                }
            },
            _           => {false}
        };

        // replace new source and target with modified stacks
        if do_move {

            match &*target {
                "a" => {self.target[0] = target_stack;},
                "b" => {self.target[1] = target_stack;},
                "c" => {self.target[2] = target_stack;},
                "d" => {self.target[3] = target_stack;},
                "1" => {self.open_tableau[0] = target_stack;},
                "2" => {self.open_tableau[1] = target_stack;},
                "3" => {self.open_tableau[2] = target_stack;},
                "4" => {self.open_tableau[3] = target_stack;},
                "5" => {self.open_tableau[4] = target_stack;},
                "6" => {self.open_tableau[5] = target_stack;},
                "7" => {self.open_tableau[6] = target_stack;},
                _   => {}
            }

            match &*source {
                "s" => {self.stack = source_stack;},
                "1" => {self.open_tableau[0] = source_stack;},
                "2" => {self.open_tableau[1] = source_stack;},
                "3" => {self.open_tableau[2] = source_stack;},
                "4" => {self.open_tableau[3] = source_stack;},
                "5" => {self.open_tableau[4] = source_stack;},
                "6" => {self.open_tableau[5] = source_stack;},
                "7" => {self.open_tableau[6] = source_stack;},
                _   => {}
            }

            self.round = self.round + 1;
        }

    }

    pub fn deal_stack(&mut self, source:&str){
        println!("Checking stack {}", source);
        match self.get_source_stack(source) {
            Some(source_stack) => {
                let mut stack = source_stack.clone();
                if stack.count() == 0 {
                    let mut deck = self.get_source_deck(source).unwrap();
                    if deck.count() > 0 {
                        let card = deck.take(1);
                        stack.add_to_top(card);

                        match &*source {
                            "1" => {self.open_tableau[0] = stack; self.tableau[0] = deck;},
                            "2" => {self.open_tableau[1] = stack; self.tableau[1] = deck;},
                            "3" => {self.open_tableau[2] = stack; self.tableau[2] = deck;},
                            "4" => {self.open_tableau[3] = stack; self.tableau[3] = deck;},
                            "5" => {self.open_tableau[4] = stack; self.tableau[4] = deck;},
                            "6" => {self.open_tableau[5] = stack; self.tableau[5] = deck;},
                            "7" => {self.open_tableau[6] = stack; self.tableau[6] = deck;},
                            _   => ()
                        }
                    }
                }
            },
            None => ()
        }
    }

    fn get_target_stack(&self, target:&str)->Option<Stack>{
        let open_tableau = &self.open_tableau;
        let target_ref = &self.target;
        match &*target {
            "a" => Some(target_ref[0].clone()),
            "b" => Some(target_ref[1].clone()),
            "c" => Some(target_ref[2].clone()),
            "d" => Some(target_ref[3].clone()),
            "1" => Some(open_tableau[0].clone()),
            "2" => Some(open_tableau[1].clone()),
            "3" => Some(open_tableau[2].clone()),
            "4" => Some(open_tableau[3].clone()),
            "5" => Some(open_tableau[4].clone()),
            "6" => Some(open_tableau[5].clone()),
            "7" => Some(open_tableau[6].clone()),
            _   => None
        }

    }

    fn get_source_stack(&self, source:&str)->Option<Stack>{

        let open_tableau = &self.open_tableau;

        match &*source {
            "s" => Some(self.stack.clone()),
            "1" => Some(open_tableau[0].clone()),
            "2" => Some(open_tableau[1].clone()),
            "3" => Some(open_tableau[2].clone()),
            "4" => Some(open_tableau[3].clone()),
            "5" => Some(open_tableau[4].clone()),
            "6" => Some(open_tableau[5].clone()),
            "7" => Some(open_tableau[6].clone()),
            _   => None
        }
    }

    // need better naming but this is the pile of hidden cards that we can
    // pick from
    fn get_source_deck(&self, source:&str)->Option<Stack>{
        let tableau = &self.tableau;

        match &*source {
            "1" => Some(tableau[0].clone()),
            "2" => Some(tableau[1].clone()),
            "3" => Some(tableau[2].clone()),
            "4" => Some(tableau[3].clone()),
            "5" => Some(tableau[4].clone()),
            "6" => Some(tableau[5].clone()),
            "7" => Some(tableau[6].clone()),
            _   => None
        }
    }
}<|endoftext|>


<commit_before>//! Helpers for writing Alfred script filter JSON output (Alfred 3)
//!
//! # Examples
//!
//! ### Writing items
//!
//! ```
//! # extern crate alfred;
//! # use std::io::{self, Write};
//! #
//! # fn write_items() -> io::Result<()> {
//! alfred::json::write_items(io::stdout(), &[
//!     alfred::Item::new("Item 1"),
//!     alfred::ItemBuilder::new("Item 2")
//!                         .subtitle("Subtitle")
//!                         .into_item(),
//!     alfred::ItemBuilder::new("Item 3")
//!                         .arg("Argument")
//!                         .subtitle("Subtitle")
//!                         .icon_filetype("public.folder")
//!                         .into_item()
//! ])
//! # }
//! #
//! # fn main() {
//! #     match write_items() {
//! #         Ok(()) => {},
//! #         Err(err) => {
//! #             let _ = writeln!(&mut io::stderr(), "Error writing items: {}", err);
//! #         }
//! #     }
//! # }
//! ```
//!
//! ### Writing items with variables
//!
//! ```
//! # extern crate alfred;
//! # use alfred::Modifier;
//! # use std::io::{self, Write};
//! #
//! # fn write_items() -> io::Result<()> {
//! alfred::json::Builder::with_items(&[
//!     alfred::Item::new("Item 1"),
//!     alfred::ItemBuilder::new("Item 2")
//!                         .subtitle("Subtitle")
//!                         .variable("fruit", "banana")
//!                         .into_item(),
//!     alfred::ItemBuilder::new("Item 3")
//!                         .arg("Argument")
//!                         .subtitle("Subtitle")
//!                         .icon_filetype("public.folder")
//!                         .arg_mod(Modifier::Option, "Alt Argument")
//!                         .variable_mod(Modifier::Option, "vegetable", "carrot")
//!                         .into_item()
//! ]).variable("fruit", "banana")
//!   .variable("vegetable", "carrot")
//!   .write(io::stdout())
//! # }
//! #
//! # fn main() {
//! #     match write_items() {
//! #         Ok(()) => {},
//! #         Err(err) => {
//! #             let _ = writeln!(&mut io::stderr(), "Error writing items: {}", err);
//! #         }
//! #     }
//! # }
//! ```

use ::{Item, ItemType, Modifier, Icon, ModifierData};
use serde_json as json;
use serde_json::value::Value;
use std::collections::HashMap;
use std::io;
use std::io::prelude::*;

/// Writes a complete JSON document representing the `Item`s to the `Write`.
///
/// The `Write` is flushed after the JSON document is written.
pub fn write_items<W: Write>(w: W, items: &[Item]) -> io::Result<()> {
    Builder::with_items(items).write(w)
}

/// A helper type for writing out items with top-level variables.
///
/// Note: If you don't need top-level variables the `write_items()` function is easier to use.
#[derive(Clone,Debug,Default)]
pub struct Builder<'a> {
    /// The items that will be written out.
    pub items: &'a [Item<'a>],
    /// The variables that will be written out.
    pub variables: HashMap<&'a str, &'a str>
}

impl<'a> Builder<'a> {
    /// Returns a new `Builder` with no items.
    pub fn new() -> Builder<'a> {
        Builder { items: &[], variables: HashMap::new() }
    }

    /// Returns a new `Builder` with the given items.
    pub fn with_items(items: &'a [Item]) -> Builder<'a> {
        Builder { items, variables: HashMap::new() }
    }

    /// Writes a complete JSON document representing the items and variables to the `Write`.
    ///
    /// The `Write` is flushed after the JSON document is written.
    pub fn write<W: Write>(self, mut w: W) -> io::Result<()> {
        write!(&mut w, "{}", self.into_json())?;
        w.flush()
    }

    /// Serializes items into their JSON representation.
    pub fn into_json(self) -> Value {
        let mut root = json::Map::new();
        // We know for a fact that our implementation of ToJson cannot return an error.
        root.insert("items".to_string(), Value::Array(self.items.into_iter()
                                                                .map(|x| x.to_json())
                                                                .collect()));
        let mut iter = self.variables.into_iter();
        if let Some(first) = iter.next() {
            let mut vars = json::Map::new();
            vars.insert(first.0.into(), Value::String(first.1.into()));
            for elt in iter {
                vars.insert(elt.0.into(), Value::String(elt.1.into()));
            }
            root.insert("variables".to_owned(), Value::Object(vars));
        }
        Value::Object(root)
    }

    /// Replaces the builder's items with `items`.
    pub fn items(mut self, items: &'a [Item]) -> Builder<'a> {
        self.set_items(items);
        self
    }

    /// Replaces the builder's variables with `variables`.
    pub fn variables(mut self, variables: HashMap<&'a str, &'a str>) -> Builder<'a> {
        self.set_variables(variables);
        self
    }

    /// Inserts a new variable into the builder's variables.
    pub fn variable(mut self, key: &'a str, value: &'a str) -> Builder<'a> {
        self.set_variable(key, value);
        self
    }

    /// Replaces the builder's items with `items`.
    pub fn set_items(&mut self, items: &'a [Item]) {
        self.items = items
    }

    /// Replaces the builder's variables with `variables`.
    pub fn set_variables(&mut self, variables: HashMap<&'a str, &'a str>) {
        self.variables = variables
    }

    /// Inserts a new variable into the builder's variables.
    pub fn set_variable(&mut self, key: &'a str, value: &'a str) {
        self.variables.insert(key, value);
    }
}

impl<'a> Item<'a> {
    fn to_json(&self) -> Value {
        let mut d = json::Map::new();
        d.insert("title".to_string(), json!(self.title));
        if let Some(ref subtitle) = self.subtitle {
            d.insert("subtitle".to_string(), json!(subtitle));
        }
        if let Some(ref icon) = self.icon {
            d.insert("icon".to_string(), icon.to_json());
        }
        if let Some(ref uid) = self.uid {
            d.insert("uid".to_string(), json!(uid));
        }
        if let Some(ref arg) = self.arg {
            d.insert("arg".to_string(), json!(arg));
        }
        match self.type_ {
            ItemType::Default => {}
            ItemType::File => {
                d.insert("type".to_string(), json!("file"));
            }
            ItemType::FileSkipCheck => {
                d.insert("type".to_string(), json!("file:skipcheck"));
            }
        }
        if !self.valid {
            d.insert("valid".to_string(), Value::Bool(false));
        }
        if let Some(ref autocomplete) = self.autocomplete {
            d.insert("autocomplete".to_string(), json!(autocomplete));
        }
        if self.text_copy.is_some() || self.text_large_type.is_some() {
            let mut text = json::Map::new();
            if let Some(ref text_copy) = self.text_copy {
                text.insert("copy".to_string(), json!(text_copy));
            }
            if let Some(ref text_large_type) = self.text_large_type {
                text.insert("largetype".to_string(), json!(text_large_type));
            }
            d.insert("text".to_string(), Value::Object(text));
        }
        if let Some(ref url) = self.quicklook_url {
            d.insert("quicklookurl".to_string(), json!(url));
        }
        if !self.modifiers.is_empty() {
            let mut mods = json::Map::with_capacity(self.modifiers.len());
            for (modifier, data) in &self.modifiers {
                let key = match *modifier {
                    Modifier::Command => "cmd",
                    Modifier::Option => "alt",
                    Modifier::Control => "ctrl",
                    Modifier::Shift => "shift",
                    Modifier::Fn => "fn"
                }.to_string();
                mods.insert(key, data.to_json());
            }
            d.insert("mods".to_string(), Value::Object(mods));
        }
        if !self.variables.is_empty() {
            let mut vars = json::Map::with_capacity(self.variables.len());
            for (key, value) in &self.variables {
                vars.insert(key.clone().into_owned(), json!(value.clone().into_owned()));
            }
            d.insert("variables".to_string(), Value::Object(vars));
        }
        Value::Object(d)
    }
}

impl<'a> Icon<'a> {
    fn to_json(&self) -> Value {
        match *self {
            Icon::Path(ref s) => json!({"path": s}),
            Icon::File(ref s) => json!({"type": "fileicon", "path": s}),
            Icon::FileType(ref s) => json!({"type": "filetype", "path": s})
        }
    }
}

impl<'a> ModifierData<'a> {
    fn to_json(&self) -> Value {
        let mut mod_ = json::Map::new();
        if let Some(ref subtitle) = self.subtitle {
            mod_.insert("subtitle".to_string(), json!(subtitle));
        }
        if let Some(ref arg) = self.arg {
            mod_.insert("arg".to_string(), json!(arg));
        }
        if let Some(valid) = self.valid {
            mod_.insert("valid".to_string(), json!(valid));
        }
        if let Some(ref icon) = self.icon {
            mod_.insert("icon".to_string(), icon.to_json());
        }
        if !self.variables.is_empty() {
            let mut vars = json::Map::with_capacity(self.variables.len());
            for (key, value) in &self.variables {
                vars.insert(key.clone().into_owned(), json!(value.clone().into_owned()));
            }
            mod_.insert("variables".to_string(), Value::Object(vars));
        }
        Value::Object(mod_)
    }
}

#[test]
fn test_to_json() {
    let item = Item::new("Item 1");
    assert_eq!(item.to_json(), json!({"title": "Item 1"}));
    let item = ::ItemBuilder::new("Item 2")
                              .subtitle("Subtitle")
                              .into_item();
    assert_eq!(item.to_json(),
              json!({
                  "title": "Item 2",
                  "subtitle": "Subtitle"
              }));
    let item = ::ItemBuilder::new("Item 3")
                              .arg("Argument")
                              .subtitle("Subtitle")
                              .icon_filetype("public.folder")
                              .into_item();
    assert_eq!(item.to_json(),
               json!({
                   "title": "Item 3",
                   "subtitle": "Subtitle",
                   "arg": "Argument",
                   "icon": { "type": "filetype", "path": "public.folder" }
               }));
    let item = ::ItemBuilder::new("Item 4")
                              .arg("Argument")
                              .subtitle("Subtitle")
                              .arg_mod(Modifier::Option, "Alt Argument")
                              .valid_mod(Modifier::Option, false)
                              .icon_file_mod(Modifier::Option, "opt.png")
                              .arg_mod(Modifier::Control, "Ctrl Argument")
                              .subtitle_mod(Modifier::Control, "Ctrl Subtitle")
                              .icon_path_mod(Modifier::Control, "ctrl.png")
                              .arg_mod(Modifier::Shift, "Shift Argument")
                              .into_item();
    assert_eq!(item.to_json(),
               json!({
                   "title": "Item 4",
                   "subtitle": "Subtitle",
                   "arg": "Argument",
                   "mods": {
                       "alt": {
                            "arg": "Alt Argument",
                            "valid": false,
                            "icon": { "type": "fileicon", "path": "opt.png" }
                       },
                       "ctrl": {
                           "arg": "Ctrl Argument",
                           "subtitle": "Ctrl Subtitle",
                           "icon": { "path": "ctrl.png" }
                       },
                       "shift": {
                           "arg": "Shift Argument"
                       }
                   }
               }));
    let item = ::ItemBuilder::new("Item 5")
                             .arg("Argument")
                             .variable("fruit", "banana")
                             .variable("vegetable", "carrot")
                             .into_item();
    assert_eq!(item.to_json(),
               json!({
                   "title": "Item 5",
                   "arg": "Argument",
                   "variables": {
                       "fruit": "banana",
                       "vegetable": "carrot"
                   }
               }));
    let item = ::ItemBuilder::new("Item 6")
                             .subtitle("Subtitle")
                             .variable("fruit", "banana")
                             .variable_mod(Modifier::Option, "vegetable", "carrot")
                             .into_item();
    assert_eq!(item.to_json(),
               json!({
                   "title": "Item 6",
                   "subtitle": "Subtitle",
                   "mods": {
                       "alt": {
                           "variables": {
                               "vegetable": "carrot"
                           }
                       }
                   },
                   "variables": {
                       "fruit": "banana"
                   }
               }));
}

#[test]
fn test_builder() {
    let json = Builder::with_items(&[
        Item::new("Item 1"),
        ::ItemBuilder::new("Item 2")
                      .subtitle("Subtitle")
                      .into_item(),
        ::ItemBuilder::new("Item 3")
                      .arg("Argument")
                      .subtitle("Subtitle")
                      .icon_filetype("public.folder")
                      .into_item()
    ]).variable("fruit", "banana")
      .variable("vegetable", "carrot")
      .into_json();
    assert_eq!(json,
               json!({
                   "items": [
                       {
                           "title": "Item 1"
                       },
                       {
                           "title": "Item 2",
                           "subtitle": "Subtitle"
                       },
                       {
                           "title": "Item 3",
                           "arg": "Argument",
                           "subtitle": "Subtitle",
                           "icon": { "type": "filetype", "path": "public.folder" }
                       }
                   ],
                   "variables": {
                       "fruit": "banana",
                       "vegetable": "carrot"
                   }
               }));
}
<commit_msg>Make to_json on Item and its helper types public<commit_after>//! Helpers for writing Alfred script filter JSON output (Alfred 3)
//!
//! # Examples
//!
//! ### Writing items
//!
//! ```
//! # extern crate alfred;
//! # use std::io::{self, Write};
//! #
//! # fn write_items() -> io::Result<()> {
//! alfred::json::write_items(io::stdout(), &[
//!     alfred::Item::new("Item 1"),
//!     alfred::ItemBuilder::new("Item 2")
//!                         .subtitle("Subtitle")
//!                         .into_item(),
//!     alfred::ItemBuilder::new("Item 3")
//!                         .arg("Argument")
//!                         .subtitle("Subtitle")
//!                         .icon_filetype("public.folder")
//!                         .into_item()
//! ])
//! # }
//! #
//! # fn main() {
//! #     match write_items() {
//! #         Ok(()) => {},
//! #         Err(err) => {
//! #             let _ = writeln!(&mut io::stderr(), "Error writing items: {}", err);
//! #         }
//! #     }
//! # }
//! ```
//!
//! ### Writing items with variables
//!
//! ```
//! # extern crate alfred;
//! # use alfred::Modifier;
//! # use std::io::{self, Write};
//! #
//! # fn write_items() -> io::Result<()> {
//! alfred::json::Builder::with_items(&[
//!     alfred::Item::new("Item 1"),
//!     alfred::ItemBuilder::new("Item 2")
//!                         .subtitle("Subtitle")
//!                         .variable("fruit", "banana")
//!                         .into_item(),
//!     alfred::ItemBuilder::new("Item 3")
//!                         .arg("Argument")
//!                         .subtitle("Subtitle")
//!                         .icon_filetype("public.folder")
//!                         .arg_mod(Modifier::Option, "Alt Argument")
//!                         .variable_mod(Modifier::Option, "vegetable", "carrot")
//!                         .into_item()
//! ]).variable("fruit", "banana")
//!   .variable("vegetable", "carrot")
//!   .write(io::stdout())
//! # }
//! #
//! # fn main() {
//! #     match write_items() {
//! #         Ok(()) => {},
//! #         Err(err) => {
//! #             let _ = writeln!(&mut io::stderr(), "Error writing items: {}", err);
//! #         }
//! #     }
//! # }
//! ```

use ::{Item, ItemType, Modifier, Icon, ModifierData};
use serde_json as json;
use serde_json::value::Value;
use std::collections::HashMap;
use std::io;
use std::io::prelude::*;

/// Writes a complete JSON document representing the `Item`s to the `Write`.
///
/// The `Write` is flushed after the JSON document is written.
pub fn write_items<W: Write>(w: W, items: &[Item]) -> io::Result<()> {
    Builder::with_items(items).write(w)
}

/// A helper type for writing out items with top-level variables.
///
/// Note: If you don't need top-level variables the `write_items()` function is easier to use.
#[derive(Clone,Debug,Default)]
pub struct Builder<'a> {
    /// The items that will be written out.
    pub items: &'a [Item<'a>],
    /// The variables that will be written out.
    pub variables: HashMap<&'a str, &'a str>
}

impl<'a> Builder<'a> {
    /// Returns a new `Builder` with no items.
    pub fn new() -> Builder<'a> {
        Builder { items: &[], variables: HashMap::new() }
    }

    /// Returns a new `Builder` with the given items.
    pub fn with_items(items: &'a [Item]) -> Builder<'a> {
        Builder { items, variables: HashMap::new() }
    }

    /// Writes a complete JSON document representing the items and variables to the `Write`.
    ///
    /// The `Write` is flushed after the JSON document is written.
    pub fn write<W: Write>(self, mut w: W) -> io::Result<()> {
        write!(&mut w, "{}", self.into_json())?;
        w.flush()
    }

    /// Serializes items into their JSON representation.
    pub fn into_json(self) -> Value {
        let mut root = json::Map::new();
        root.insert("items".to_string(), Value::Array(self.items.into_iter()
                                                                .map(|x| x.to_json())
                                                                .collect()));
        let mut iter = self.variables.into_iter();
        if let Some(first) = iter.next() {
            let mut vars = json::Map::new();
            vars.insert(first.0.into(), Value::String(first.1.into()));
            for elt in iter {
                vars.insert(elt.0.into(), Value::String(elt.1.into()));
            }
            root.insert("variables".to_owned(), Value::Object(vars));
        }
        Value::Object(root)
    }

    /// Replaces the builder's items with `items`.
    pub fn items(mut self, items: &'a [Item]) -> Builder<'a> {
        self.set_items(items);
        self
    }

    /// Replaces the builder's variables with `variables`.
    pub fn variables(mut self, variables: HashMap<&'a str, &'a str>) -> Builder<'a> {
        self.set_variables(variables);
        self
    }

    /// Inserts a new variable into the builder's variables.
    pub fn variable(mut self, key: &'a str, value: &'a str) -> Builder<'a> {
        self.set_variable(key, value);
        self
    }

    /// Replaces the builder's items with `items`.
    pub fn set_items(&mut self, items: &'a [Item]) {
        self.items = items
    }

    /// Replaces the builder's variables with `variables`.
    pub fn set_variables(&mut self, variables: HashMap<&'a str, &'a str>) {
        self.variables = variables
    }

    /// Inserts a new variable into the builder's variables.
    pub fn set_variable(&mut self, key: &'a str, value: &'a str) {
        self.variables.insert(key, value);
    }
}

impl<'a> Item<'a> {
    /// Serializes the `Item` into its JSON representation.
    pub fn to_json(&self) -> Value {
        let mut d = json::Map::new();
        d.insert("title".to_string(), json!(self.title));
        if let Some(ref subtitle) = self.subtitle {
            d.insert("subtitle".to_string(), json!(subtitle));
        }
        if let Some(ref icon) = self.icon {
            d.insert("icon".to_string(), icon.to_json());
        }
        if let Some(ref uid) = self.uid {
            d.insert("uid".to_string(), json!(uid));
        }
        if let Some(ref arg) = self.arg {
            d.insert("arg".to_string(), json!(arg));
        }
        match self.type_ {
            ItemType::Default => {}
            ItemType::File => {
                d.insert("type".to_string(), json!("file"));
            }
            ItemType::FileSkipCheck => {
                d.insert("type".to_string(), json!("file:skipcheck"));
            }
        }
        if !self.valid {
            d.insert("valid".to_string(), Value::Bool(false));
        }
        if let Some(ref autocomplete) = self.autocomplete {
            d.insert("autocomplete".to_string(), json!(autocomplete));
        }
        if self.text_copy.is_some() || self.text_large_type.is_some() {
            let mut text = json::Map::new();
            if let Some(ref text_copy) = self.text_copy {
                text.insert("copy".to_string(), json!(text_copy));
            }
            if let Some(ref text_large_type) = self.text_large_type {
                text.insert("largetype".to_string(), json!(text_large_type));
            }
            d.insert("text".to_string(), Value::Object(text));
        }
        if let Some(ref url) = self.quicklook_url {
            d.insert("quicklookurl".to_string(), json!(url));
        }
        if !self.modifiers.is_empty() {
            let mut mods = json::Map::with_capacity(self.modifiers.len());
            for (modifier, data) in &self.modifiers {
                let key = match *modifier {
                    Modifier::Command => "cmd",
                    Modifier::Option => "alt",
                    Modifier::Control => "ctrl",
                    Modifier::Shift => "shift",
                    Modifier::Fn => "fn"
                }.to_string();
                mods.insert(key, data.to_json());
            }
            d.insert("mods".to_string(), Value::Object(mods));
        }
        if !self.variables.is_empty() {
            let mut vars = json::Map::with_capacity(self.variables.len());
            for (key, value) in &self.variables {
                vars.insert(key.clone().into_owned(), json!(value.clone().into_owned()));
            }
            d.insert("variables".to_string(), Value::Object(vars));
        }
        Value::Object(d)
    }
}

impl<'a> Icon<'a> {
    /// Serializes the `Icon` into its JSON representation.
    pub fn to_json(&self) -> Value {
        match *self {
            Icon::Path(ref s) => json!({"path": s}),
            Icon::File(ref s) => json!({"type": "fileicon", "path": s}),
            Icon::FileType(ref s) => json!({"type": "filetype", "path": s})
        }
    }
}

impl<'a> ModifierData<'a> {
    /// Serializes the `ModifierData` into its JSON representation.
    pub fn to_json(&self) -> Value {
        let mut mod_ = json::Map::new();
        if let Some(ref subtitle) = self.subtitle {
            mod_.insert("subtitle".to_string(), json!(subtitle));
        }
        if let Some(ref arg) = self.arg {
            mod_.insert("arg".to_string(), json!(arg));
        }
        if let Some(valid) = self.valid {
            mod_.insert("valid".to_string(), json!(valid));
        }
        if let Some(ref icon) = self.icon {
            mod_.insert("icon".to_string(), icon.to_json());
        }
        if !self.variables.is_empty() {
            let mut vars = json::Map::with_capacity(self.variables.len());
            for (key, value) in &self.variables {
                vars.insert(key.clone().into_owned(), json!(value.clone().into_owned()));
            }
            mod_.insert("variables".to_string(), Value::Object(vars));
        }
        Value::Object(mod_)
    }
}

#[test]
fn test_to_json() {
    let item = Item::new("Item 1");
    assert_eq!(item.to_json(), json!({"title": "Item 1"}));
    let item = ::ItemBuilder::new("Item 2")
                              .subtitle("Subtitle")
                              .into_item();
    assert_eq!(item.to_json(),
              json!({
                  "title": "Item 2",
                  "subtitle": "Subtitle"
              }));
    let item = ::ItemBuilder::new("Item 3")
                              .arg("Argument")
                              .subtitle("Subtitle")
                              .icon_filetype("public.folder")
                              .into_item();
    assert_eq!(item.to_json(),
               json!({
                   "title": "Item 3",
                   "subtitle": "Subtitle",
                   "arg": "Argument",
                   "icon": { "type": "filetype", "path": "public.folder" }
               }));
    let item = ::ItemBuilder::new("Item 4")
                              .arg("Argument")
                              .subtitle("Subtitle")
                              .arg_mod(Modifier::Option, "Alt Argument")
                              .valid_mod(Modifier::Option, false)
                              .icon_file_mod(Modifier::Option, "opt.png")
                              .arg_mod(Modifier::Control, "Ctrl Argument")
                              .subtitle_mod(Modifier::Control, "Ctrl Subtitle")
                              .icon_path_mod(Modifier::Control, "ctrl.png")
                              .arg_mod(Modifier::Shift, "Shift Argument")
                              .into_item();
    assert_eq!(item.to_json(),
               json!({
                   "title": "Item 4",
                   "subtitle": "Subtitle",
                   "arg": "Argument",
                   "mods": {
                       "alt": {
                            "arg": "Alt Argument",
                            "valid": false,
                            "icon": { "type": "fileicon", "path": "opt.png" }
                       },
                       "ctrl": {
                           "arg": "Ctrl Argument",
                           "subtitle": "Ctrl Subtitle",
                           "icon": { "path": "ctrl.png" }
                       },
                       "shift": {
                           "arg": "Shift Argument"
                       }
                   }
               }));
    let item = ::ItemBuilder::new("Item 5")
                             .arg("Argument")
                             .variable("fruit", "banana")
                             .variable("vegetable", "carrot")
                             .into_item();
    assert_eq!(item.to_json(),
               json!({
                   "title": "Item 5",
                   "arg": "Argument",
                   "variables": {
                       "fruit": "banana",
                       "vegetable": "carrot"
                   }
               }));
    let item = ::ItemBuilder::new("Item 6")
                             .subtitle("Subtitle")
                             .variable("fruit", "banana")
                             .variable_mod(Modifier::Option, "vegetable", "carrot")
                             .into_item();
    assert_eq!(item.to_json(),
               json!({
                   "title": "Item 6",
                   "subtitle": "Subtitle",
                   "mods": {
                       "alt": {
                           "variables": {
                               "vegetable": "carrot"
                           }
                       }
                   },
                   "variables": {
                       "fruit": "banana"
                   }
               }));
}

#[test]
fn test_builder() {
    let json = Builder::with_items(&[
        Item::new("Item 1"),
        ::ItemBuilder::new("Item 2")
                      .subtitle("Subtitle")
                      .into_item(),
        ::ItemBuilder::new("Item 3")
                      .arg("Argument")
                      .subtitle("Subtitle")
                      .icon_filetype("public.folder")
                      .into_item()
    ]).variable("fruit", "banana")
      .variable("vegetable", "carrot")
      .into_json();
    assert_eq!(json,
               json!({
                   "items": [
                       {
                           "title": "Item 1"
                       },
                       {
                           "title": "Item 2",
                           "subtitle": "Subtitle"
                       },
                       {
                           "title": "Item 3",
                           "arg": "Argument",
                           "subtitle": "Subtitle",
                           "icon": { "type": "filetype", "path": "public.folder" }
                       }
                   ],
                   "variables": {
                       "fruit": "banana",
                       "vegetable": "carrot"
                   }
               }));
}
<|endoftext|>


<commit_before>//
// main.rs
// Copyright (C) 2017 Szymon Urbaś <szymon.urbas@aol.com>
// Distributed under terms of the BSD (2-clause) license.
//
// Created on: 13 Mar 2017 21:03:41 +0100 (CET)
//

extern crate rand;
extern crate cursive;

use std::thread;
use std::time;

mod process;
mod scheduler;
mod fcfs;
mod tui;

use scheduler::*;
use tui::*;

/// Length of a single system clock tick (frequency of actual, real time)
const CLOCK_HZ: u64 = 50;
/// Switch context every <value> clock ticks
const SYSTEM_HZ: usize = 8;

fn main() {
  let mut tui = tui::new();

  run_simulation(&mut tui, fcfs::new(), vec![8, 8, 8, 8, 8, 8, 8, 64]);
  run_simulation(&mut tui, fcfs::new(), vec![64, 8, 8, 8, 8, 8, 8, 8]);
  run_simulation(&mut tui, fcfs::new(), vec![8, 8, 8, 32, 32, 6, 8, 8]);

  loop {}
}

fn run_simulation<S>(mut tui: &mut Tui, mut scheduler: S, mut process_list: Vec<usize>)
where S: Scheduler {
  let mut clock_tick = 0;
  let mut process_spawner = process::new_spawner();

  tui.set_header(format!("Using algorithm: {}", scheduler.name()));
  tui.debug(format!("{:05}: Starting simulation using {}", clock_tick, scheduler.name()));

  // vectors only support the pop (from the back) operation so
  // reverse it (so we pop from the front)
  process_list.reverse();

  // Add the equivalent of the 'init' process
  scheduler.add_process(process_spawner.spawn(process_list.pop().unwrap()));

  loop {
    // update all the views
    tui.update();

    if !scheduler.has_processes() {
      // if there's no more processes - end the simulation
      // if a process can only be created by another process - there's
      // nothing else to do
      break;
    }

    // simulate executing the current process
    scheduler.current_proc_mut().unwrap().record_execution();

    if scheduler.current_proc().unwrap().done_executing() {
      tui.debug(format!("{:05}: Killing process {}", clock_tick, scheduler.current_proc().unwrap().name));
      scheduler.kill_current_proc();
    }

    if clock_tick % SYSTEM_HZ == SYSTEM_HZ - 1 {
      tui.debug(format!("{:05}: Triggering a scheduling round", clock_tick));
      scheduler.schedule();
    }

    // increase the waiting time for every process
    scheduler.increase_waiting_times();

    // Simulate other processes creating new processes
    // Assume that you can't create new processes, if there's none already
    if clock_tick % SYSTEM_HZ == 0 {
      if let Some(burst_time) = process_list.pop() {
        let new_proc = process_spawner.spawn(burst_time);
        tui.debug(format!("{:05}: Spawning {}", clock_tick, new_proc.name));
        scheduler.add_process(new_proc);
      }
    }

    // update the process view
    scheduler.list_processes(&mut tui);

    thread::sleep(time::Duration::from_millis(1000 / CLOCK_HZ));

    clock_tick += 1;
  }
}

/*
 * vi: ts=2 sw=2 expandtab
 */

<commit_msg>Reorder the execute/kill/schedule cycle<commit_after>//
// main.rs
// Copyright (C) 2017 Szymon Urbaś <szymon.urbas@aol.com>
// Distributed under terms of the BSD (2-clause) license.
//
// Created on: 13 Mar 2017 21:03:41 +0100 (CET)
//

extern crate rand;
extern crate cursive;

use std::thread;
use std::time;

mod process;
mod scheduler;
mod fcfs;
mod tui;

use scheduler::*;
use tui::*;

/// Length of a single system clock tick (frequency of actual, real time)
const CLOCK_HZ: u64 = 50;
/// Switch context every <value> clock ticks
const SYSTEM_HZ: usize = 8;

fn main() {
  let mut tui = tui::new();

  run_simulation(&mut tui, fcfs::new(), vec![8, 8, 8, 8, 8, 8, 8, 64]);
  run_simulation(&mut tui, fcfs::new(), vec![64, 8, 8, 8, 8, 8, 8, 8]);
  run_simulation(&mut tui, fcfs::new(), vec![8, 8, 8, 32, 32, 6, 8, 8]);

  loop {}
}

fn run_simulation<S>(mut tui: &mut Tui, mut scheduler: S, mut process_list: Vec<usize>)
where S: Scheduler {
  let mut clock_tick = 0;
  let mut process_spawner = process::new_spawner();

  tui.set_header(format!("Using algorithm: {}", scheduler.name()));
  tui.debug(format!("{:05}: Starting simulation using {}", clock_tick, scheduler.name()));

  // vectors only support the pop (from the back) operation so
  // reverse it (so we pop from the front)
  process_list.reverse();

  // Add the equivalent of the 'init' process
  scheduler.add_process(process_spawner.spawn(process_list.pop().unwrap()));

  loop {

    // update the process view
    scheduler.list_processes(&mut tui);
    // update all the views
    tui.update();

    if !scheduler.has_processes() {
      // if there's no more processes - end the simulation
      // if a process can only be created by another process - there's
      // nothing else to do
      break;
    }

    // simulate executing the current process
    scheduler.current_proc_mut().unwrap().record_execution();

    // Simulate other processes creating new processes
    // Assume that you can't create new processes, if there's none already
    if clock_tick % SYSTEM_HZ == 0 {
      if let Some(burst_time) = process_list.pop() {
        let new_proc = process_spawner.spawn(burst_time);
        tui.debug(format!("{:05}: Spawning {}", clock_tick, new_proc.name));
        scheduler.add_process(new_proc);
      }
    }

    if scheduler.current_proc().unwrap().done_executing() {
      tui.debug(format!("{:05}: Killing process {}", clock_tick, scheduler.current_proc().unwrap().name));
      scheduler.kill_current_proc();
    }

    if scheduler.has_processes() {
      if clock_tick % SYSTEM_HZ == SYSTEM_HZ - 1 {
        tui.debug(format!("{:05}: Triggering a scheduling round", clock_tick));
        scheduler.schedule();
        tui.debug(format!("{:05}: {} was waiting {} clock ticks", clock_tick, scheduler.current_proc().unwrap().name, scheduler.current_proc().unwrap().waiting_time));
      }

      // increase the waiting time for every process
      scheduler.increase_waiting_times();
    }

    thread::sleep(time::Duration::from_millis(1000 / CLOCK_HZ));

    clock_tick += 1;
  }
}

/*
 * vi: ts=2 sw=2 expandtab
 */

<|endoftext|>