<commit_before>use std::cell::RefCell;
use std::str::FromStr;
use std::rc::Rc;

use iup;
use iup::prelude::*;
use iup::control::{Frame, List, Text};
use iup::led;

use prop::{Property, PropertyMap, PropertyMapRef};

// LED dialog specification.
static DIALOG: &'static str = r#"
    text_emeralds = text[SPIN=YES, SPINMAX=99999, MASKINT=0:99999, ALIGNMENT=ARIGHT](_)
    text_int = text[SPIN=YES, SPINMIN=-9, SPINMAX=99, MASKINT=-9:99, ALIGNMENT=ARIGHT](_)
    text_dex = text[SPIN=YES, SPINMIN=-9, SPINMAX=99, MASKINT=-9:99, ALIGNMENT=ARIGHT](_)
    text_str = text[SPIN=YES, SPINMIN=-9, SPINMAX=99, MASKINT=-9:99, ALIGNMENT=ARIGHT](_)
    text_occ = text[SPIN=YES, SPINMIN=-9, SPINMAX=99, MASKINT=-9:99, ALIGNMENT=ARIGHT](_)
    text_per = text[SPIN=YES, SPINMIN=-9, SPINMAX=99, MASKINT=-9:99, ALIGNMENT=ARIGHT](_)

    list_party = list[DROPDOWN=YES, VALUE=1](_)

    dlg = dialog(
        vbox[CGAP=2, CMARGIN=4x2, ALIGNMENT=ARIGHT](
            gridbox[NUMDIV=2, SIZECOL=1, CGAPCOL=4, CGAPLIN=2, NORMALIZESIZE=HORIZONTAL](
                label("Emeralds"), text_emeralds,
                label("Party"), list_party
            ),
            member = frame[TITLE=""](
                gridbox[NUMDIV=2, SIZECOL=1, CGAPCOL=4, CGAPLIN=2, NORMALIZESIZE=HORIZONTAL](
                    label("Intelligence (+10)"), text_int,
                    label("Dexterity (+10)"),    text_dex,
                    label("Strength (+10)"),     text_str,
                    label("Occult (+10)"),       text_occ,
                    label("Perception (+10)"),   text_per
                )
            )
        )
    )
"#;

// Get an element from named handle (defined in the LED input)
fn from_name<E>(name: &str) -> E where E: Element {
    E::from_handle(E::from_name(name).unwrap()).unwrap()
}

// Data-bind an element to a numeric property value.
//
// Value of the element is set to the current value of the property, and
// changes to element value are written back to the property map.
//
// @param props {PropertyMapRef} a cloned refcounted property map.
//
fn bind<T, E>(elem: &mut E, props: PropertyMapRef, key: &'static str)
    where Property: From<T>,
          T: FromStr + Default,
          E: Element + ValueChangedCb {

    // Remove previous bindings, if any.
    elem.remove_valuechanged_cb();

    if let Some(ref prop) = props.borrow().get(key) {
        elem.set_attrib("VALUE", prop.to_string());
    }
    elem.set_valuechanged_cb(move |(elem,): (E,)| {
        if let Some(ref value) = elem.attrib("VALUE") {
            if let Ok(v) = T::from_str(value) {
                props.borrow_mut().insert(key.to_string(), Property::from(v));
            } else {
                props.borrow_mut().insert(key.to_string(), Property::from(T::default()));
            }
            println!("{:?}", props.borrow().get(key));
        }
    });
}

// Data-bind all elements relevant to a party member.
//
// @param props {PropertyMapRef} a cloned refcounted property map.
//
fn bind_member(props: PropertyMapRef) {
    if let Some(&Property::String(ref v)) = props.borrow().get("Name") {
        from_name::<Frame>("member").set_attrib("TITLE", v.to_string());
    }
    let mut text_int = from_name::<Text>("text_int");
    bind::<f32,_>(&mut text_int, props.clone(), "Int");

    let mut text_dex = from_name::<Text>("text_dex");
    bind::<f32,_>(&mut text_dex, props.clone(), "Dex");

    let mut text_str = from_name::<Text>("text_str");
    bind::<f32,_>(&mut text_str, props.clone(), "Str");

    let mut text_occ = from_name::<Text>("text_occ");
    bind::<f32,_>(&mut text_occ, props.clone(), "Occ");

    let mut text_per = from_name::<Text>("text_per");
    bind::<f32,_>(&mut text_per, props.clone(), "Per");
}

pub fn run_ui_loop(game: PropertyMapRef, party: Rc<RefCell<Vec<PropertyMapRef>>>) {
    iup::with_iup(|| {
        // See also led::load(path) to load from a file
        led::load_buffer(DIALOG).unwrap();

        let mut text_emeralds = from_name::<Text>("text_emeralds");
        bind::<u32,_>(&mut text_emeralds, game, "Emeralds");

        let mut list_party = from_name::<List>("list_party");
        let mut list_party_items: Vec<String> = Vec::new();
        for member in party.borrow().iter().skip(1) {
            if let Some(&Property::String(ref v)) = member.borrow().get("Name") {
                list_party_items.push(v.to_string());
            }
        }
        let party_clone = party.clone();
        list_party.set_items(list_party_items);
        list_party.set_action(move |(_, _, i, _)| {
            let member = party_clone.borrow()[i as usize].clone();
            bind_member(member);
        });
        bind_member(party.borrow()[1].clone());

        let mut dlg = from_name::<Dialog>("dlg");
        dlg.show()
    }).unwrap();
}
<commit_msg>Tweak ui: party list without scrollbar, include level<commit_after>use std::cell::RefCell;
use std::str::FromStr;
use std::rc::Rc;

use iup;
use iup::prelude::*;
use iup::control::{Frame, List, Text};
use iup::led;

use prop::{Property, PropertyMap, PropertyMapRef};

// LED dialog specification.
static DIALOG: &'static str = r#"
    text_emeralds = text[SPIN=YES, SPINMAX=99999, MASKINT=0:99999, ALIGNMENT=ARIGHT](_)
    text_int = text[SPIN=YES, SPINMIN=-9, SPINMAX=99, MASKINT=-9:99, ALIGNMENT=ARIGHT](_)
    text_dex = text[SPIN=YES, SPINMIN=-9, SPINMAX=99, MASKINT=-9:99, ALIGNMENT=ARIGHT](_)
    text_str = text[SPIN=YES, SPINMIN=-9, SPINMAX=99, MASKINT=-9:99, ALIGNMENT=ARIGHT](_)
    text_occ = text[SPIN=YES, SPINMIN=-9, SPINMAX=99, MASKINT=-9:99, ALIGNMENT=ARIGHT](_)
    text_per = text[SPIN=YES, SPINMIN=-9, SPINMAX=99, MASKINT=-9:99, ALIGNMENT=ARIGHT](_)

    list_party = list[DROPDOWN=YES, VALUE=1, VISIBLE_ITEMS=6](_)

    dlg = dialog(
        vbox[CGAP=2, CMARGIN=4x2, ALIGNMENT=ARIGHT](
            gridbox[NUMDIV=2, SIZECOL=1, CGAPCOL=4, CGAPLIN=2, NORMALIZESIZE=HORIZONTAL](
                label("Emeralds"), text_emeralds,
                label("Party"), list_party
            ),
            member = frame[TITLE=""](
                gridbox[NUMDIV=2, SIZECOL=1, CGAPCOL=4, CGAPLIN=2, NORMALIZESIZE=HORIZONTAL](
                    label("Intelligence (+10)"), text_int,
                    label("Dexterity (+10)"),    text_dex,
                    label("Strength (+10)"),     text_str,
                    label("Occult (+10)"),       text_occ,
                    label("Perception (+10)"),   text_per
                )
            )
        )
    )
"#;

// Get an element from named handle (defined in the LED input)
fn from_name<E>(name: &str) -> E where E: Element {
    E::from_handle(E::from_name(name).unwrap()).unwrap()
}

// Data-bind an element to a numeric property value.
//
// Value of the element is set to the current value of the property, and
// changes to element value are written back to the property map.
//
// @param props {PropertyMapRef} a cloned refcounted property map.
//
fn bind<T, E>(elem: &mut E, props: PropertyMapRef, key: &'static str)
    where Property: From<T>,
          T: FromStr + Default,
          E: Element + ValueChangedCb {

    // Remove previous bindings, if any.
    elem.remove_valuechanged_cb();

    if let Some(ref prop) = props.borrow().get(key) {
        elem.set_attrib("VALUE", prop.to_string());
    }
    elem.set_valuechanged_cb(move |(elem,): (E,)| {
        if let Some(ref value) = elem.attrib("VALUE") {
            if let Ok(v) = T::from_str(value) {
                props.borrow_mut().insert(key.to_string(), Property::from(v));
            } else {
                props.borrow_mut().insert(key.to_string(), Property::from(T::default()));
            }
        }
    });
}

// Data-bind all elements relevant to a party member.
//
// @param props {PropertyMapRef} a cloned refcounted property map.
//
fn bind_member(props: PropertyMapRef) {
    if let Some(&Property::String(ref name)) = props.borrow().get("Name") {
        if let Some(&Property::Float(level)) = props.borrow().get("Level") {
            let title = format!("{} (Level {})", name, level);
            from_name::<Frame>("member").set_attrib("TITLE", title);
        }
    }
    let mut text_int = from_name::<Text>("text_int");
    bind::<f32,_>(&mut text_int, props.clone(), "Int");

    let mut text_dex = from_name::<Text>("text_dex");
    bind::<f32,_>(&mut text_dex, props.clone(), "Dex");

    let mut text_str = from_name::<Text>("text_str");
    bind::<f32,_>(&mut text_str, props.clone(), "Str");

    let mut text_occ = from_name::<Text>("text_occ");
    bind::<f32,_>(&mut text_occ, props.clone(), "Occ");

    let mut text_per = from_name::<Text>("text_per");
    bind::<f32,_>(&mut text_per, props.clone(), "Per");
}

pub fn run_ui_loop(game: PropertyMapRef, party: Rc<RefCell<Vec<PropertyMapRef>>>) {
    iup::with_iup(|| {
        // See also led::load(path) to load from a file
        led::load_buffer(DIALOG).unwrap();

        let mut text_emeralds = from_name::<Text>("text_emeralds");
        bind::<u32,_>(&mut text_emeralds, game, "Emeralds");

        let mut list_party = from_name::<List>("list_party");
        let mut list_party_items: Vec<String> = Vec::new();
        for member in party.borrow().iter().skip(1) {
            if let Some(&Property::String(ref v)) = member.borrow().get("Name") {
                list_party_items.push(v.to_string());
            }
        }
        let party_clone = party.clone();
        list_party.set_items(list_party_items);
        list_party.set_action(move |(_, _, i, _)| {
            let member = party_clone.borrow()[i as usize].clone();
            bind_member(member);
        });
        bind_member(party.borrow()[1].clone());

        let mut dlg = from_name::<Dialog>("dlg");
        dlg.show()
    }).unwrap();
}
<|endoftext|>


<commit_before>//! Volume Groups

use std::io::Result;
use std::io::Error;
use std::io::ErrorKind::Other;
use std::collections::btree_map::BTreeMap;

use uuid::Uuid;
use time::now;
use nix;

use lv::{LV, Segment};
use pv::PV;
use parser::{LvmTextMap, Entry};
use pvlabel::MDA;
use lvmetad::vg_update_lvmetad;
use dm::DM;

/// A Volume Group.
#[derive(Debug, PartialEq, Clone)]
pub struct VG {
    /// Name.
    pub name: String,
    /// Uuid.
    pub id: String,
    /// The generation of metadata this VG represents.
    pub seqno: u64,
    /// Always "LVM2".
    pub format: String,
    /// Status.
    pub status: Vec<String>,
    /// Flags.
    pub flags: Vec<String>,
    /// Size of each extent, in 512-byte sectors.
    pub extent_size: u64,
    /// Maximum number of LVs, 0 means no limit.
    pub max_lv: u64,
    /// Maximum number of PVs, 0 means no limit.
    pub max_pv: u64,
    /// How many metadata copies (?)
    pub metadata_copies: u64,
    /// Physical Volumes within this volume group.
    pub pvs: BTreeMap<String, PV>,
    /// Logical Volumes within this volume group.
    pub lvs: BTreeMap<String, LV>,
}

impl VG {
    /// The total number of extents in use in the volume group.
    pub fn extents_in_use(&self) -> u64 {
        self.lvs
            .values()
            .map(|x| x.used_extents())
            .sum()
    }

    /// The total number of free extents in the volume group.
    pub fn extents_free(&self) -> u64 {
        self.extents() - self.extents_in_use()
    }

    /// The total number of extents in the volume group.
    pub fn extents(&self) -> u64 {
        self.pvs
            .values()
            .map(|x| x.pe_count)
            .sum()
    }

    /// Create a new linear logival volume in the volume group.
    pub fn new_linear_lv(&mut self, name: &str, extent_size: u64) -> Result<()> {
        if self.lvs.contains_key(name) {
            return Err(Error::new(Other, "LV already exists"));
        }

        let mut contig_area = None;
        for (pvname, areas) in self.free_areas() {
            for (start, len) in areas {
                if len >= extent_size {
                    contig_area = Some((pvname, start));
                    break;
                }
            }
        }

        // we don't support multiple segments yet
        let (pv_with_area, area_start) = match contig_area {
            None => return Err(Error::new(Other, "no contiguous area for new LV")),
            Some(x) => x,
        };

        let segment = Segment {
            name: "segment1".to_string(),
            start_extent: 0,
            extent_count: extent_size,
            ty: "striped".to_string(),
            stripes: vec![(pv_with_area, area_start)],
        };

        let mut lv = LV {
            name: name.to_string(),
            id: Uuid::new_v4().to_hyphenated_string(),
            status: vec!["READ".to_string(),
                         "WRITE".to_string(),
                         "VISIBLE".to_string()],
            flags: Vec::new(),
            creation_host: nix::sys::utsname::uname().nodename().to_string(),
            creation_time: now().to_timespec().sec,
            segments: vec![segment],
            device: None,
        };

        // poke dm and tell it about a new device
        {
            let dm = try!(DM::new(self));
            try!(dm.activate_device(&mut lv));
        }

        self.lvs.insert(name.to_string(), lv);

        let map = self.clone().into();

        // TODO: atomicity of updating pvs, metad, dm
        for pv in self.pvs.values() {
            if let Some(path) = pv.device.path() {
                let mut mda = MDA::new(&path).expect("could not open MDA");

                try!(mda.write_metadata(&map));
            }
        }

        // tell lvmetad
        try!(vg_update_lvmetad(&map));

        // open champagne ?

        Ok(())
    }

    fn used_areas(&self) -> BTreeMap<String, BTreeMap<u64, u64>> {
        let mut used_map = BTreeMap::new();

        // pretty sure this is only correct for my system...
        for lv in self.lvs.values() {
            for seg in &lv.segments {
                for &(ref pvname, start) in &seg.stripes {
                    used_map.entry(pvname.to_string())
                        .or_insert(BTreeMap::new())
                        .insert(start as u64, seg.extent_count);
                }
            }
        }

        used_map
    }

    fn free_areas(&self) -> BTreeMap<String, BTreeMap<u64, u64>> {
        let mut free_map = BTreeMap::new();

        for (pvname, area_map) in &mut self.used_areas() {

            // Insert an entry to mark the end of the PV so the fold works
            // correctly
            let pv = self.pvs.get(pvname)
                .expect("area map name refers to nonexistent PV");
            area_map.insert(pv.pe_count, 0);

            area_map.iter()
                .fold(0, |prev_end, (start, len)| {
                    if prev_end < *start {
                        free_map.entry(pvname.clone())
                            .or_insert(BTreeMap::new())
                            .insert(prev_end, start-prev_end);
                    }
                    start + len
                });
        }

        for (pvname, pv) in &self.pvs {
            if !free_map.contains_key(pvname) {
                let mut map = BTreeMap::new();
                map.insert(0, pv.pe_count);
                free_map.insert(pvname.clone(), map);
            }
        }

        free_map
    }
}

impl From<VG> for LvmTextMap {
    fn from(vg: VG) -> Self {
        let mut map = LvmTextMap::new();

        map.insert("id".to_string(), Entry::String(vg.id));
        map.insert("seqno".to_string(),
                   Entry::Number(vg.seqno as i64 + 1));
        map.insert("format".to_string(), Entry::String(vg.format));

        map.insert("max_pv".to_string(), Entry::Number(0));
        map.insert("max_lv".to_string(), Entry::Number(0));

        map.insert("status".to_string(),
                   Entry::List(
                       Box::new(
                           vg.status
                               .into_iter()
                               .map(|x| Entry::String(x))
                               .collect())));

        map.insert("flags".to_string(),
                   Entry::List(
                       Box::new(
                           vg.flags
                               .into_iter()
                               .map(|x| Entry::String(x))
                               .collect())));

        map.insert("extent_size".to_string(),
                   Entry::Number(vg.extent_size as i64));
        map.insert("metadata_copies".to_string(),
                   Entry::Number(vg.metadata_copies as i64));
        map.insert("physical_volumes".to_string(),
                   Entry::TextMap(
                       Box::new(
                           vg.pvs
                               .into_iter()
                               .map(|(k, v)|
                                    (k, Entry::TextMap(Box::new(v.into()))))
                               .collect())));

        map.insert("logical_volumes".to_string(),
                   Entry::TextMap(
                       Box::new(
                           vg.lvs
                               .into_iter()
                               .map(|(k, v)|
                                    (k, Entry::TextMap(Box::new(v.into()))))
                               .collect())));

        let mut outer_map = LvmTextMap::new();

        outer_map.insert(vg.name, Entry::TextMap(Box::new(map)));

        outer_map
    }
}
<commit_msg>add comment in vg.free_areas<commit_after>//! Volume Groups

use std::io::Result;
use std::io::Error;
use std::io::ErrorKind::Other;
use std::collections::btree_map::BTreeMap;

use uuid::Uuid;
use time::now;
use nix;

use lv::{LV, Segment};
use pv::PV;
use parser::{LvmTextMap, Entry};
use pvlabel::MDA;
use lvmetad::vg_update_lvmetad;
use dm::DM;

/// A Volume Group.
#[derive(Debug, PartialEq, Clone)]
pub struct VG {
    /// Name.
    pub name: String,
    /// Uuid.
    pub id: String,
    /// The generation of metadata this VG represents.
    pub seqno: u64,
    /// Always "LVM2".
    pub format: String,
    /// Status.
    pub status: Vec<String>,
    /// Flags.
    pub flags: Vec<String>,
    /// Size of each extent, in 512-byte sectors.
    pub extent_size: u64,
    /// Maximum number of LVs, 0 means no limit.
    pub max_lv: u64,
    /// Maximum number of PVs, 0 means no limit.
    pub max_pv: u64,
    /// How many metadata copies (?)
    pub metadata_copies: u64,
    /// Physical Volumes within this volume group.
    pub pvs: BTreeMap<String, PV>,
    /// Logical Volumes within this volume group.
    pub lvs: BTreeMap<String, LV>,
}

impl VG {
    /// The total number of extents in use in the volume group.
    pub fn extents_in_use(&self) -> u64 {
        self.lvs
            .values()
            .map(|x| x.used_extents())
            .sum()
    }

    /// The total number of free extents in the volume group.
    pub fn extents_free(&self) -> u64 {
        self.extents() - self.extents_in_use()
    }

    /// The total number of extents in the volume group.
    pub fn extents(&self) -> u64 {
        self.pvs
            .values()
            .map(|x| x.pe_count)
            .sum()
    }

    /// Create a new linear logival volume in the volume group.
    pub fn new_linear_lv(&mut self, name: &str, extent_size: u64) -> Result<()> {
        if self.lvs.contains_key(name) {
            return Err(Error::new(Other, "LV already exists"));
        }

        let mut contig_area = None;
        for (pvname, areas) in self.free_areas() {
            for (start, len) in areas {
                if len >= extent_size {
                    contig_area = Some((pvname, start));
                    break;
                }
            }
        }

        // we don't support multiple segments yet
        let (pv_with_area, area_start) = match contig_area {
            None => return Err(Error::new(Other, "no contiguous area for new LV")),
            Some(x) => x,
        };

        let segment = Segment {
            name: "segment1".to_string(),
            start_extent: 0,
            extent_count: extent_size,
            ty: "striped".to_string(),
            stripes: vec![(pv_with_area, area_start)],
        };

        let mut lv = LV {
            name: name.to_string(),
            id: Uuid::new_v4().to_hyphenated_string(),
            status: vec!["READ".to_string(),
                         "WRITE".to_string(),
                         "VISIBLE".to_string()],
            flags: Vec::new(),
            creation_host: nix::sys::utsname::uname().nodename().to_string(),
            creation_time: now().to_timespec().sec,
            segments: vec![segment],
            device: None,
        };

        // poke dm and tell it about a new device
        {
            let dm = try!(DM::new(self));
            try!(dm.activate_device(&mut lv));
        }

        self.lvs.insert(name.to_string(), lv);

        let map = self.clone().into();

        // TODO: atomicity of updating pvs, metad, dm
        for pv in self.pvs.values() {
            if let Some(path) = pv.device.path() {
                let mut mda = MDA::new(&path).expect("could not open MDA");

                try!(mda.write_metadata(&map));
            }
        }

        // tell lvmetad
        try!(vg_update_lvmetad(&map));

        // open champagne ?

        Ok(())
    }

    fn used_areas(&self) -> BTreeMap<String, BTreeMap<u64, u64>> {
        let mut used_map = BTreeMap::new();

        // pretty sure this is only correct for my system...
        for lv in self.lvs.values() {
            for seg in &lv.segments {
                for &(ref pvname, start) in &seg.stripes {
                    used_map.entry(pvname.to_string())
                        .or_insert(BTreeMap::new())
                        .insert(start as u64, seg.extent_count);
                }
            }
        }

        used_map
    }

    fn free_areas(&self) -> BTreeMap<String, BTreeMap<u64, u64>> {
        let mut free_map = BTreeMap::new();

        for (pvname, area_map) in &mut self.used_areas() {

            // Insert an entry to mark the end of the PV so the fold works
            // correctly
            let pv = self.pvs.get(pvname)
                .expect("area map name refers to nonexistent PV");
            area_map.insert(pv.pe_count, 0);

            area_map.iter()
                .fold(0, |prev_end, (start, len)| {
                    if prev_end < *start {
                        free_map.entry(pvname.clone())
                            .or_insert(BTreeMap::new())
                            .insert(prev_end, start-prev_end);
                    }
                    start + len
                });
        }

        // Also return completely-unused PVs
        for (pvname, pv) in &self.pvs {
            if !free_map.contains_key(pvname) {
                let mut map = BTreeMap::new();
                map.insert(0, pv.pe_count);
                free_map.insert(pvname.clone(), map);
            }
        }

        free_map
    }
}

impl From<VG> for LvmTextMap {
    fn from(vg: VG) -> Self {
        let mut map = LvmTextMap::new();

        map.insert("id".to_string(), Entry::String(vg.id));
        map.insert("seqno".to_string(),
                   Entry::Number(vg.seqno as i64 + 1));
        map.insert("format".to_string(), Entry::String(vg.format));

        map.insert("max_pv".to_string(), Entry::Number(0));
        map.insert("max_lv".to_string(), Entry::Number(0));

        map.insert("status".to_string(),
                   Entry::List(
                       Box::new(
                           vg.status
                               .into_iter()
                               .map(|x| Entry::String(x))
                               .collect())));

        map.insert("flags".to_string(),
                   Entry::List(
                       Box::new(
                           vg.flags
                               .into_iter()
                               .map(|x| Entry::String(x))
                               .collect())));

        map.insert("extent_size".to_string(),
                   Entry::Number(vg.extent_size as i64));
        map.insert("metadata_copies".to_string(),
                   Entry::Number(vg.metadata_copies as i64));
        map.insert("physical_volumes".to_string(),
                   Entry::TextMap(
                       Box::new(
                           vg.pvs
                               .into_iter()
                               .map(|(k, v)|
                                    (k, Entry::TextMap(Box::new(v.into()))))
                               .collect())));

        map.insert("logical_volumes".to_string(),
                   Entry::TextMap(
                       Box::new(
                           vg.lvs
                               .into_iter()
                               .map(|(k, v)|
                                    (k, Entry::TextMap(Box::new(v.into()))))
                               .collect())));

        let mut outer_map = LvmTextMap::new();

        outer_map.insert(vg.name, Entry::TextMap(Box::new(map)));

        outer_map
    }
}
<|endoftext|>


<commit_before>use std::error::Error as StdError;
use std::result::Result as StdResult;

type Result<T> = StdResult<T, Box<StdError>>;

use chainerror::*;
use std::io::BufRead;
use std::{thread, time};
use varlink::Connection;

fn run_self_test(address: String, multiplex: bool) -> Result<()> {
    let server_address = address.clone();

    let child = thread::spawn(move || {
        if let Err(e) = crate::run_server(&server_address, 4, multiplex) {
            match e.kind() {
                ::varlink::ErrorKind::Timeout => {}
                _ => panic!("error: {}", e),
            }
        }
    });

    // give server time to start
    thread::sleep(time::Duration::from_secs(1));

    {
        let con = Connection::with_address(&address)
            .map_err(mstrerr!("Could not connect to {}", address))?;

        let mut conn = con.write().unwrap();
        let mut writer = conn.writer.take().unwrap();
        let mut reader = conn.reader.take().unwrap();

        {
            let br = concat!(
                r#"{"method" : "org.example.ping.Ping", "parameters": { "ping": "ping" }}"#,
                "\0"
            )
            .as_bytes();

            let a = &br[0..10];
            let b = &br[10..20];
            let c = &br[20..];

            for i in vec![a, b, c] {
                assert!(writer.write_all(i).is_ok());
                assert!(writer.flush().is_ok());
                thread::sleep(time::Duration::from_millis(500));
            }

            let mut inbuf = Vec::new();
            let reply = concat!(r#"{"parameters":{"pong":"ping"}}"#, "\0").as_bytes();

            assert!(reader.read_until(0, &mut inbuf).is_ok());
            eprintln!("Got reply: {}", String::from_utf8_lossy(&inbuf));
            assert_eq!(inbuf, reply);
        }

        {
            let mut inbuf = Vec::new();
            let reply = "{}\0".as_bytes();

            assert!(writer
                .write_all(
                    concat!(
                        r#"{"method" : "org.example.ping.Upgrade", "upgrade" : true}"#,
                        "\0"
                    )
                    .as_bytes(),
                )
                .is_ok());
            assert!(writer.flush().is_ok());

            assert!(reader.read_until(0, &mut inbuf).is_ok());
            eprintln!("Got reply: {}", String::from_utf8_lossy(&inbuf));
            assert_eq!(inbuf, reply);
        }
        {
            let br = concat!(
                r#"{"method" : "org.example.ping.Ping", "parameters": { "ping": "ping" }}"#,
                "\n"
            )
            .as_bytes();

            let a = &br[0..10];
            let b = &br[10..20];
            let c = &br[20..];

            for i in vec![a, b, c] {
                assert!(writer.write_all(i).is_ok());
                assert!(writer.flush().is_ok());
                thread::sleep(time::Duration::from_millis(500));
            }

            let mut inbuf = Vec::new();
            let reply = concat!(
                r#"server reply: {"method" : "org.example.ping.Ping", "#,
                r#""parameters": { "ping": "ping" }}"#,
                "\n"
            )
            .as_bytes();

            assert!(reader.read_until(0x0a, &mut inbuf).is_ok());
            eprintln!("Got reply: {}", String::from_utf8_lossy(&inbuf));
            assert_eq!(inbuf, reply);
        }
        {
            let br = concat!(
                r#"{"method" : "org.example.ping.Ping", "parameters": { "ping": "ping" }}"#,
                "\nEnd\n"
            )
            .as_bytes();

            let a = &br[0..10];
            let b = &br[10..20];
            let c = &br[20..];

            for i in vec![a, b, c] {
                assert!(writer.write_all(i).is_ok());
                assert!(writer.flush().is_ok());
                thread::sleep(time::Duration::from_millis(500));
            }

            let mut inbuf = Vec::new();
            let reply = concat!(
                r#"server reply: {"method" : "org.example.ping.Ping", "#,
                r#""parameters": { "ping": "ping" }}"#,
                "\n"
            )
            .as_bytes();

            assert!(reader.read_until(0x0a, &mut inbuf).is_ok());
            eprintln!("Got reply: {}", String::from_utf8_lossy(&inbuf));
            assert_eq!(inbuf, reply);
        }
    }

    {
        let con = Connection::with_address(&address)
            .map_err(mstrerr!("Could not connect to {}", address))?;

        let ret = crate::run_client(&con);
        if let Err(e) = ret {
            panic!("error: {:#?}", e);
        }
    }
    eprintln!("run_client finished");

    if let Err(_) = child.join() {
        Err(strerr!("Error joining thread").into())
    } else {
        Ok(())
    }
}

#[cfg(unix)]
#[test]
fn test_unix_multiplex() -> Result<()> {
    run_self_test("unix:org.example.ping_multiplex".into(), true)
}

#[test]
fn test_unix() -> Result<()> {
    run_self_test("unix:org.example.ping".into(), false)
}

#[test]
fn test_tcp() -> Result<()> {
    run_self_test("tcp:127.0.0.1:12345".into(), false)
}
<commit_msg>examples/ping: run multiplex test only on linux<commit_after>use std::error::Error as StdError;
use std::result::Result as StdResult;

type Result<T> = StdResult<T, Box<StdError>>;

use chainerror::*;
use std::io::BufRead;
use std::{thread, time};
use varlink::Connection;

fn run_self_test(address: String, multiplex: bool) -> Result<()> {
    let server_address = address.clone();

    let child = thread::spawn(move || {
        if let Err(e) = crate::run_server(&server_address, 4, multiplex) {
            match e.kind() {
                ::varlink::ErrorKind::Timeout => {}
                _ => panic!("error: {}", e),
            }
        }
    });

    // give server time to start
    thread::sleep(time::Duration::from_secs(1));

    {
        let con = Connection::with_address(&address)
            .map_err(mstrerr!("Could not connect to {}", address))?;

        let mut conn = con.write().unwrap();
        let mut writer = conn.writer.take().unwrap();
        let mut reader = conn.reader.take().unwrap();

        {
            let br = concat!(
                r#"{"method" : "org.example.ping.Ping", "parameters": { "ping": "ping" }}"#,
                "\0"
            )
            .as_bytes();

            let a = &br[0..10];
            let b = &br[10..20];
            let c = &br[20..];

            for i in vec![a, b, c] {
                assert!(writer.write_all(i).is_ok());
                assert!(writer.flush().is_ok());
                thread::sleep(time::Duration::from_millis(500));
            }

            let mut inbuf = Vec::new();
            let reply = concat!(r#"{"parameters":{"pong":"ping"}}"#, "\0").as_bytes();

            assert!(reader.read_until(0, &mut inbuf).is_ok());
            eprintln!("Got reply: {}", String::from_utf8_lossy(&inbuf));
            assert_eq!(inbuf, reply);
        }

        {
            let mut inbuf = Vec::new();
            let reply = "{}\0".as_bytes();

            assert!(writer
                .write_all(
                    concat!(
                        r#"{"method" : "org.example.ping.Upgrade", "upgrade" : true}"#,
                        "\0"
                    )
                    .as_bytes(),
                )
                .is_ok());
            assert!(writer.flush().is_ok());

            assert!(reader.read_until(0, &mut inbuf).is_ok());
            eprintln!("Got reply: {}", String::from_utf8_lossy(&inbuf));
            assert_eq!(inbuf, reply);
        }
        {
            let br = concat!(
                r#"{"method" : "org.example.ping.Ping", "parameters": { "ping": "ping" }}"#,
                "\n"
            )
            .as_bytes();

            let a = &br[0..10];
            let b = &br[10..20];
            let c = &br[20..];

            for i in vec![a, b, c] {
                assert!(writer.write_all(i).is_ok());
                assert!(writer.flush().is_ok());
                thread::sleep(time::Duration::from_millis(500));
            }

            let mut inbuf = Vec::new();
            let reply = concat!(
                r#"server reply: {"method" : "org.example.ping.Ping", "#,
                r#""parameters": { "ping": "ping" }}"#,
                "\n"
            )
            .as_bytes();

            assert!(reader.read_until(0x0a, &mut inbuf).is_ok());
            eprintln!("Got reply: {}", String::from_utf8_lossy(&inbuf));
            assert_eq!(inbuf, reply);
        }
        {
            let br = concat!(
                r#"{"method" : "org.example.ping.Ping", "parameters": { "ping": "ping" }}"#,
                "\nEnd\n"
            )
            .as_bytes();

            let a = &br[0..10];
            let b = &br[10..20];
            let c = &br[20..];

            for i in vec![a, b, c] {
                assert!(writer.write_all(i).is_ok());
                assert!(writer.flush().is_ok());
                thread::sleep(time::Duration::from_millis(500));
            }

            let mut inbuf = Vec::new();
            let reply = concat!(
                r#"server reply: {"method" : "org.example.ping.Ping", "#,
                r#""parameters": { "ping": "ping" }}"#,
                "\n"
            )
            .as_bytes();

            assert!(reader.read_until(0x0a, &mut inbuf).is_ok());
            eprintln!("Got reply: {}", String::from_utf8_lossy(&inbuf));
            assert_eq!(inbuf, reply);
        }
    }

    {
        let con = Connection::with_address(&address)
            .map_err(mstrerr!("Could not connect to {}", address))?;

        let ret = crate::run_client(&con);
        if let Err(e) = ret {
            panic!("error: {:#?}", e);
        }
    }
    eprintln!("run_client finished");

    if let Err(_) = child.join() {
        Err(strerr!("Error joining thread").into())
    } else {
        Ok(())
    }
}

#[cfg(target_os = "linux")]
#[test]
fn test_unix_multiplex() -> Result<()> {
    run_self_test("unix:org.example.ping_multiplex".into(), true)
}

#[test]
fn test_unix() -> Result<()> {
    run_self_test("unix:org.example.ping".into(), false)
}

#[test]
fn test_tcp() -> Result<()> {
    run_self_test("tcp:127.0.0.1:12345".into(), false)
}
<|endoftext|>


<commit_before>extern crate rand;
extern crate sync;

#[cfg(test)]
extern crate test;

use std::{cmp, str, fmt, rt, option, io};
use rand::{task_rng, Rng};
use sync::{Arc, Future};

#[cfg(test)]
use test::Bencher;

static LIVE_CELL: char = '@';
static DEAD_CELL: char = '.';

fn main() {
  let mut brd = Board::new(65, 248).random();
  let mut timer = io::Timer::new().unwrap();

  let periodic = timer.periodic(64);
  loop {
    println!("\x1b[H\x1b[2J{}", brd);
    periodic.recv();
    brd = brd.parallel_next_generation();
  }
}

#[deriving(Eq, Clone)]
struct Board {
  board: Vec<bool>,
  survive: Vec<uint>,
  born: Vec<uint>,
  rows: uint,
  cols: uint
}

impl Board {
  fn new(rows: uint, cols: uint) -> Board {
    let born = vec![3];
    let survive = vec![2, 3];

    Board::new_with_custom_rules(rows, cols, born, survive)
  }

  fn new_with_custom_rules(rows: uint, cols: uint, born: Vec<uint>, survive: Vec<uint>) -> Board {
    let new_board = Vec::from_elem(rows * cols, false);

    Board { board  : new_board,
            born   : born,
            survive: survive,
            rows   : rows,
            cols   : cols }
  }

  fn len(&self) -> uint {
    self.rows * self.cols
  }

  fn next_board(&self, new_board: Vec<bool>) -> Board {
    assert!(new_board.len() == self.len());

    Board { board  : new_board,
            born   : self.born.clone(),
            survive: self.survive.clone(),
            rows   : self.rows,
            cols   : self.cols }
  }

  fn random(&self) -> Board {
    let board = task_rng().gen_vec(self.len());

    self.next_board(board)
  }

  fn next_generation(&self) -> Board {
    let new_brd = Vec::from_fn(self.len(), |cell| self.successor_cell(cell));

    self.next_board(new_brd)
  }

  fn parallel_next_generation(&self) -> Board {
    let length = self.len();
    let num_tasks = cmp::min(rt::default_sched_threads(), length);
    let shared_brd = Arc::new(self.clone());
    let all_tasks: Vec<uint> = range(0, length).collect();
    let tasks: Vec<&[uint]> = all_tasks.as_slice().chunks(length / num_tasks).collect();

    fn future_batch(task_brd: Arc<Board>, task: ~[uint]) -> Future<Vec<bool>> {
      Future::spawn(proc()
        task.iter().map(|&idx| task_brd.successor_cell(idx)).collect()
      )
    }

    let future_batches: Vec<Future<Vec<bool>>> = tasks.move_iter().map(|task| {
      future_batch(shared_brd.clone(), task.to_owned())
    }).collect();

    let mut new_brd: Vec<bool> = Vec::with_capacity(length);

    for b in future_batches.move_iter() {
      new_brd.push_all_move(b.unwrap());
    }

    self.next_board(new_brd)
  }

  fn cell_live(&self, x: uint, y: uint) -> bool {
    !(x >= self.cols || y >= self.rows) && *self.board.get(y * self.cols + x)
  }

  fn living_neighbors(&self, x: uint, y: uint) -> uint {
    let neighbors = [
      self.cell_live(x-1, y-1), self.cell_live(x, y-1), self.cell_live(x+1, y-1),
      self.cell_live(x-1, y+0),                         self.cell_live(x+1, y+0),
      self.cell_live(x-1, y+1), self.cell_live(x, y+1), self.cell_live(x+1, y+1),
    ];
    neighbors.iter().count(|&x| x)
  }

  fn successor_cell(&self, cell:uint) -> bool {
    self.successor(cell % self.cols, cell / self.cols)
  }

  fn successor(&self, x:uint, y:uint) -> bool {
    let neighbors = self.living_neighbors(x, y);
    if self.cell_live(x, y) {
      self.survive.contains(&neighbors)
    } else {
      self.born.contains(&neighbors)
    }
  }

  fn from_str(string: &str) -> Option<Board> {
    let rows: Vec<&str> = string.split_terminator('\n').collect();
    let (row_cnt, col_cnt) = (rows.get(0).len(), rows.len());

    if rows.iter().any(|x| x.len() != row_cnt) { return None; };

    let brd = option::collect(
      rows.concat().chars().map(|c| match c {
        LIVE_CELL => Some(true),
        DEAD_CELL => Some(false),
        _         => None
      })
    );

    match brd {
      Some(board) => Some(Board::new(row_cnt, col_cnt).next_board(board)),
      None        => None
    }
  }
}

impl fmt::Show for Board {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {

    fn row_to_str(row: &[bool]) -> ~str {
      let chars: Vec<char> = row.iter().map(|&cell|
        if cell {LIVE_CELL} else {DEAD_CELL}
      ).collect();
      str::from_chars(chars.as_slice())
    }

    let rows: Vec<~str> = self.board.as_slice().chunks(self.cols).map(|row|
      row_to_str(row)
    ).collect();

    write!(f, "{}", rows.connect("\n"))
  }
}

#[cfg(test)]
static test_boards: [&'static str, ..3] = [
  ".@.\n.@@\n.@@",
  "...\n@@@\n...",
  ".@.\n.@.\n.@."
];

#[cfg(test)]
fn testing_board(n: uint) -> Board {
  Board::from_str(test_boards[n]).unwrap()
}

#[test]
fn test_board_str_conversion() {
  assert_eq!(testing_board(0).to_str(), test_boards[0].to_owned());
}

#[test]
fn test_cell_live() {
  let brd = testing_board(0);
  assert!(!brd.cell_live(0, 0));
  assert!(brd.cell_live(2, 2));
}

#[test]
fn test_live_count() {
  let brd = testing_board(0);
  assert_eq!(brd.living_neighbors(0, 0), 2);
  assert_eq!(brd.living_neighbors(2, 2), 3);
}

#[test]
fn test_next_generation() {
  assert_eq!(testing_board(1).next_generation(), testing_board(2));
}

#[test]
fn test_parallel_next_generation() {
  assert_eq!(testing_board(1).parallel_next_generation(), testing_board(2));
}

#[bench]
fn bench_random(b: &mut Bencher) {
  let brd = Board::new(200,200);
  b.iter(|| brd.random());
}

#[bench]
fn bench_ten_generations(b: &mut Bencher) {
  let mut brd = Board::new(200,200).random();
  b.iter(||
    for _ in range(0,10) { brd = brd.next_generation() }
  );
}

#[bench]
fn bench_ten_parallel_generations(b: &mut Bencher) {
  let mut brd = Board::new(200,200).random();
  b.iter(||
    for _ in range(0,10) { brd = brd.parallel_next_generation() }
  );
}
<commit_msg>works w/ lastest nightly<commit_after>extern crate rand;
extern crate sync;

#[cfg(test)]
extern crate test;

use std::{cmp, str, fmt, rt, option, io};
use rand::{task_rng, Rng};
use sync::{Arc, Future};

#[cfg(test)]
use test::Bencher;

static LIVE_CELL: char = '@';
static DEAD_CELL: char = '.';

fn main() {
  let mut brd = Board::new(65, 248).random();
  let mut timer = io::Timer::new().unwrap();

  let periodic = timer.periodic(64);
  loop {
    println!("\x1b[H\x1b[2J{}", brd);
    periodic.recv();
    brd = brd.parallel_next_generation();
  }
}

#[deriving(Eq, Clone)]
struct Board {
  board: Vec<bool>,
  survive: Vec<uint>,
  born: Vec<uint>,
  rows: uint,
  cols: uint
}

impl Board {
  fn new(rows: uint, cols: uint) -> Board {
    let born = vec![3];
    let survive = vec![2, 3];

    Board::new_with_custom_rules(rows, cols, born, survive)
  }

  fn new_with_custom_rules(rows: uint, cols: uint, born: Vec<uint>, survive: Vec<uint>) -> Board {
    let new_board = Vec::from_elem(rows * cols, false);

    Board { board  : new_board,
            born   : born,
            survive: survive,
            rows   : rows,
            cols   : cols }
  }

  fn len(&self) -> uint {
    self.rows * self.cols
  }

  fn next_board(&self, new_board: Vec<bool>) -> Board {
    assert!(new_board.len() == self.len());

    Board { board  : new_board,
            born   : self.born.clone(),
            survive: self.survive.clone(),
            rows   : self.rows,
            cols   : self.cols }
  }

  fn random(&self) -> Board {
    let board = task_rng().gen_vec(self.len());

    self.next_board(board)
  }

  fn next_generation(&self) -> Board {
    let new_brd = Vec::from_fn(self.len(), |cell| self.successor_cell(cell));

    self.next_board(new_brd)
  }

  fn parallel_next_generation(&self) -> Board {
    let length = self.len();
    let num_tasks = cmp::min(rt::default_sched_threads(), length);
    let shared_brd = Arc::new(self.clone());
    let all_tasks: Vec<uint> = range(0, length).collect();
    let tasks: Vec<&[uint]> = all_tasks.as_slice().chunks(length / num_tasks).collect();

    fn future_batch(task_brd: Arc<Board>, task: ~[uint]) -> Future<Vec<bool>> {
      Future::spawn(proc()
        task.iter().map(|&idx| task_brd.successor_cell(idx)).collect()
      )
    }

    let future_batches: Vec<Future<Vec<bool>>> = tasks.move_iter().map(|task| {
      future_batch(shared_brd.clone(), task.to_owned())
    }).collect();

    let mut new_brd: Vec<bool> = Vec::with_capacity(length);

    for b in future_batches.move_iter() {
      new_brd.push_all_move(b.unwrap());
    }

    self.next_board(new_brd)
  }

  fn cell_live(&self, x: uint, y: uint) -> bool {
    !(x >= self.cols || y >= self.rows) && *self.board.get(y * self.cols + x)
  }

  fn living_neighbors(&self, x: uint, y: uint) -> uint {
    let neighbors = [
      self.cell_live(x-1, y-1), self.cell_live(x, y-1), self.cell_live(x+1, y-1),
      self.cell_live(x-1, y+0),                         self.cell_live(x+1, y+0),
      self.cell_live(x-1, y+1), self.cell_live(x, y+1), self.cell_live(x+1, y+1),
    ];
    neighbors.iter().count(|&x| x)
  }

  fn successor_cell(&self, cell:uint) -> bool {
    self.successor(cell % self.cols, cell / self.cols)
  }

  fn successor(&self, x:uint, y:uint) -> bool {
    let neighbors = self.living_neighbors(x, y);
    if self.cell_live(x, y) {
      self.survive.contains(&neighbors)
    } else {
      self.born.contains(&neighbors)
    }
  }

  fn from_str(string: &str) -> Option<Board> {
    let rows: Vec<&str> = string.split_terminator('\n').collect();
    let (row_cnt, col_cnt) = (rows.get(0).len(), rows.len());

    if rows.iter().any(|x| x.len() != row_cnt) { return None; };

    let brd = option::collect(
      rows.concat().into_bytes().move_iter().map(|c| match c as char {
        LIVE_CELL => Some(true),
        DEAD_CELL => Some(false),
        _         => None
      })
    );

    match brd {
      Some(board) => Some(Board::new(row_cnt, col_cnt).next_board(board)),
      None        => None
    }
  }
}

impl fmt::Show for Board {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {

    fn row_to_str(row: &[bool]) -> String {
      let chars: Vec<char> = row.iter().map(|&cell|
        if cell {LIVE_CELL} else {DEAD_CELL}
      ).collect();
      str::from_chars(chars.as_slice())
    }

    let rows: Vec<String> = self.board.as_slice().chunks(self.cols).map(|row|
      row_to_str(row)
    ).collect();

    write!(f, "{}", rows.connect("\n"))
  }
}

#[cfg(test)]
static test_boards: [&'static str, ..3] = [
  ".@.\n.@@\n.@@",
  "...\n@@@\n...",
  ".@.\n.@.\n.@."
];

#[cfg(test)]
fn testing_board(n: uint) -> Board {
  Board::from_str(test_boards[n]).unwrap()
}

#[test]
fn test_board_str_conversion() {
  assert_eq!(testing_board(0).to_str(), test_boards[0].to_owned());
}

#[test]
fn test_cell_live() {
  let brd = testing_board(0);
  assert!(!brd.cell_live(0, 0));
  assert!(brd.cell_live(2, 2));
}

#[test]
fn test_live_count() {
  let brd = testing_board(0);
  assert_eq!(brd.living_neighbors(0, 0), 2);
  assert_eq!(brd.living_neighbors(2, 2), 3);
}

#[test]
fn test_next_generation() {
  assert_eq!(testing_board(1).next_generation(), testing_board(2));
}

#[test]
fn test_parallel_next_generation() {
  assert_eq!(testing_board(1).parallel_next_generation(), testing_board(2));
}

#[bench]
fn bench_random(b: &mut Bencher) {
  let brd = Board::new(200,200);
  b.iter(|| brd.random());
}

#[bench]
fn bench_ten_generations(b: &mut Bencher) {
  let mut brd = Board::new(200,200).random();
  b.iter(||
    for _ in range(0,10) { brd = brd.next_generation() }
  );
}

#[bench]
fn bench_ten_parallel_generations(b: &mut Bencher) {
  let mut brd = Board::new(200,200).random();
  b.iter(||
    for _ in range(0,10) { brd = brd.parallel_next_generation() }
  );
}
<|endoftext|>


<commit_before>///Lazy git tool
use std::env::args as cmd_args;
use std::process::Command;

///Executes git command with arguments
///Building sequence of arguments like .arg(@arg)
macro_rules! exec_git_cmd {
    ($($arg:expr),*) => { Command::new("git")$(.arg($arg))*.status().unwrap(); }
}

#[inline(always)]
fn usage() {
    println!("lgit [option]\n");
    println!("options:");
    println!("  amend [edit] - amend all changes into last commit");
    println!("  add - add all changes");
    println!("  clean - undo all changes");
    println!("  push [force] - push current branch");
    println!("  fetch - get updates from upstream\n");
}

///Amend changes
fn git_amend(args: &[String]) {
    git_add();
    if args.len() != 0 && args[0] == "edit" { exec_git_cmd!("commit", "--amend"); }
    else { exec_git_cmd!("commit", "--amend", "--no-edit"); }
}

///Fetch changes from upstream
fn git_fetch() {
    exec_git_cmd!("checkout", "master");
    exec_git_cmd!("fetch", "upstream");
    exec_git_cmd!("merge", "upstream/master");
}

///Push the current branch
fn git_push(args: &[String]) {
    if args.len() != 0 && args[0] =="force" { exec_git_cmd!("push", "--force", "origin", "HEAD"); }
    else { exec_git_cmd!("push", "origin", "HEAD"); }
}

///Add all changes
fn git_add() {
    exec_git_cmd!("add", "--all");
}

///Undo all changes
fn git_clean() {
    exec_git_cmd!("checkout", "-f");
    exec_git_cmd!("clean", "-fdq");
}

///Unexpected argument handler
fn unexpected(arg: &str) {
    println!("Unexpected argument: {}", arg);
    usage();
}

fn main() {
    let args: Vec<String> = cmd_args().collect();
    //exclude file name
    let is_repo = Command::new("git").arg("rev-parse").arg("-q").output().unwrap();

    if is_repo.status.code().unwrap() != 0 {
        println!("Not a git repository");
        return;
    }
    else if args.len() == 1 {
        usage();
        return;
    }

    match args[1].as_ref() {
        "amend" => git_amend(&args[2..]),
        "fetch" => git_fetch(),
        "push"  => git_push(&args[2..]),
        "add"   => git_add(),
        "clean" => git_clean(),
        _       => unexpected(&args[1]),
    }
}
<commit_msg>lgit: add commit option<commit_after>///Lazy git tool
use std::env::args as cmd_args;
use std::process::Command;

///Executes git command with arguments
///Building sequence of arguments like .arg(@arg)
macro_rules! exec_git_cmd {
    ($($arg:expr),*) => { Command::new("git")$(.arg($arg))*.status().unwrap(); }
}

///Print with prefix >>>
macro_rules! trace {
    ($($arg:expr),*) => { println!(">>>{}", [$(format!("{}", $arg),)*].connect(" ")); }
}

#[inline(always)]
fn usage() {
    println!("lgit [option]\n");
    println!("options:");
    println!("  amend [edit] - amend all changes into last commit");
    println!("  add - add all changes");
    println!("  clean - undo all changes");
    println!("  push [force] - push current branch");
    println!("  commit [message] - commit with message");
    println!("  fetch - get updates from upstream\n");
}

///Amend changes
fn git_amend(args: &[String]) {
    git_add();
    if args.len() != 0 && args[0] == "edit" { exec_git_cmd!("commit", "--amend"); }
    else { exec_git_cmd!("commit", "--amend", "--no-edit"); }
}

///Fetch changes from upstream
fn git_fetch() {
    exec_git_cmd!("checkout", "master");
    exec_git_cmd!("fetch", "upstream");
    exec_git_cmd!("merge", "upstream/master");
}

///Push the current branch
fn git_push(args: &[String]) {
    if args.len() != 0 && args[0] =="force" { exec_git_cmd!("push", "--force", "origin", "HEAD"); }
    else { exec_git_cmd!("push", "origin", "HEAD"); }
}

///Add all changes
fn git_add() {
    exec_git_cmd!("add", "--all");
}

///Undo all changes
fn git_clean() {
    exec_git_cmd!("checkout", "-f");
    exec_git_cmd!("clean", "-fdq");
}

///Commit with message
fn git_commit(args: &[String]) {
    if args.len() == 0 {
        trace!("Empty commit message");
    }
    else {
        exec_git_cmd!("commit", "-m", args.connect(" "));
    }
}

///Unexpected argument handler
fn unexpected(arg: &str) {
    trace!("Unexpected argument:", arg);
    usage();
}

fn main() {
    let args: Vec<String> = cmd_args().collect();
    //To check if git repo present
    let is_repo = exec_git_cmd!("rev-parse", "-q");

    if !is_repo.success() {
        trace!("Not a git repository");
        return;
    }
    else if args.len() == 1 {
        usage();
        return;
    }

    match args[1].as_ref() {
        "amend"  => git_amend(&args[2..]),
        "fetch"  => git_fetch(),
        "push"   => git_push(&args[2..]),
        "add"    => git_add(),
        "clean"  => git_clean(),
        "commit" => git_commit(&args[2..]),
        _        => unexpected(&args[1]),
    }
}
<|endoftext|>


<commit_before>use std::io::{stdin, File};
use std::io::buffered::BufferedReader;
use std::str::{from_utf8};
use std::os;

fn print_usage () {
	println("A tool to keep your reading skills bright and shiny.");
	println("Usage: rustoleum [arguments] path/to/input/file");
	println("-i (I)nteractive mode");
	println("-wpm N Display N words per (m)inute (default: 300)");
	println("-wpl N Display N words per (l)ine (default: 1)");
	println("-h Display this (h)elp");
}

fn get_args_interactive () -> (uint, uint, Path) {
	let mut reader = BufferedReader::new(stdin());

	println("Input path to file to read: ");
	let input = reader.read_line().unwrap_or(~"data/example.txt");
	let p = Path::new(input.trim());

	println("Input WPM: ");
	let input = reader.read_line().unwrap();
	let WPM = from_str::<uint>(input.trim()).unwrap_or(300);

	println("Input the number of words to view at a time: ");
	let input = reader.read_line().unwrap();
	let WPL = from_str::<uint>(input.trim()).unwrap_or(1);

	(WPM, WPL, p)
}

fn get_vals (args: ~[~str]) -> (uint, uint, Path){
	if args.contains(&~"-i"){
		get_args_interactive()
	}  else {
		let fallbackfile = &~"data/example.txt";
		let path = args.tail().last_opt().unwrap_or(fallbackfile);
		let p = Path::new(path.as_slice());
		//let WPM = from_str::<uint>(args[1 + args.position_elem(&~"-wpm").unwrap_or(-1)]).unwrap_or(300);
		//let WPL = from_str::<uint>(args[1 + args.position_elem(&~"-wpl").unwrap_or(-1)]).unwrap_or(1);
		let WPM = get_arg(&args, &~"-wpm", 300);
		let WPL = get_arg(&args, &~"-wpl", 300);
		(WPM,WPL,p)
	}
}

fn get_arg (args: &~[~str], flag: &~str, default: uint) -> uint {
	from_str::<uint>(args[1 + args.position_elem(flag).unwrap_or(-1)]).unwrap_or(default)
}

fn print_file(WPM: uint, WPL: uint, p: Path){
	if p.exists(){
		let contents = File::open(&p).read_to_end();
		let mut string_iter = from_utf8(contents).words();
		let string_vec = string_iter.to_owned_vec();
		let sleep_time = (60000 / WPM * WPL) as u64;
		for words in string_vec.chunks(WPL) {
			println("\x1bc".into_owned().append(words.connect(" ")));
			std::io::timer::sleep(sleep_time);
		}
	} else {
		println("The path specified does not exist. Please specify a valid path, or run rustoleum -h for help.");
		println("Calling rustoleum with no arguments will display data/example.txt at 300 WPM, with 1 word per line.");
	}
}

fn main () {
	if os::args().contains(&~"-h"){
		print_usage();
	} else {
		let (WPM, WPL, p) = get_vals(os::args());
		print_file(WPM, WPL, p);
	}

}
<commit_msg>Removed old comment lines<commit_after>use std::io::{stdin, File};
use std::io::buffered::BufferedReader;
use std::str::{from_utf8};
use std::os;

fn print_usage () {
	println("A tool to keep your reading skills bright and shiny.");
	println("Usage: rustoleum [arguments] path/to/input/file");
	println("-i (I)nteractive mode");
	println("-wpm N Display N words per (m)inute (default: 300)");
	println("-wpl N Display N words per (l)ine (default: 1)");
	println("-h Display this (h)elp");
}

fn get_args_interactive () -> (uint, uint, Path) {
	let mut reader = BufferedReader::new(stdin());

	println("Input path to file to read: ");
	let input = reader.read_line().unwrap_or(~"data/example.txt");
	let p = Path::new(input.trim());

	println("Input WPM: ");
	let input = reader.read_line().unwrap();
	let WPM = from_str::<uint>(input.trim()).unwrap_or(300);

	println("Input the number of words to view at a time: ");
	let input = reader.read_line().unwrap();
	let WPL = from_str::<uint>(input.trim()).unwrap_or(1);

	(WPM, WPL, p)
}

fn get_vals (args: ~[~str]) -> (uint, uint, Path){
	if args.contains(&~"-i"){
		get_args_interactive()
	}  else {
		let fallbackfile = &~"data/example.txt";
		let path = args.tail().last_opt().unwrap_or(fallbackfile);
		let p = Path::new(path.as_slice());
		let WPM = get_arg(&args, &~"-wpm", 300);
		let WPL = get_arg(&args, &~"-wpl", 300);
		(WPM,WPL,p)
	}
}

fn get_arg (args: &~[~str], flag: &~str, default: uint) -> uint {
	from_str::<uint>(args[1 + args.position_elem(flag).unwrap_or(-1)]).unwrap_or(default)
}

fn print_file(WPM: uint, WPL: uint, p: Path){
	if p.exists(){
		let contents = File::open(&p).read_to_end();
		let mut string_iter = from_utf8(contents).words();
		let string_vec = string_iter.to_owned_vec();
		let sleep_time = (60000 / WPM * WPL) as u64;
		for words in string_vec.chunks(WPL) {
			println("\x1bc".into_owned().append(words.connect(" ")));
			std::io::timer::sleep(sleep_time);
		}
	} else {
		println("The path specified does not exist. Please specify a valid path, or run rustoleum -h for help.");
		println("Calling rustoleum with no arguments will display data/example.txt at 300 WPM, with 1 word per line.");
	}
}

fn main () {
	if os::args().contains(&~"-h"){
		print_usage();
	} else {
		let (WPM, WPL, p) = get_vals(os::args());
		print_file(WPM, WPL, p);
	}

}
<|endoftext|>


<commit_before>#![feature(test)]
extern crate parser_combinators as pc;
extern crate test;

use std::collections::HashMap;
use std::io::Read;
use std::fs::File;
use std::path::Path;

use pc::primitives::{Parser, State, ParseResult};
use pc::combinator::{between, many, many1, optional, parser, sep_by, With, ParserExt};
use pc::char::{digit, satisfy, spaces, Spaces, string};

#[derive(PartialEq, Debug)]
enum Value {
    Number(f64),
    String(String),
    Bool(bool),
    Null,
    Object(HashMap<String, Value>),
    Array(Vec<Value>)
}

fn lex<'a, P>(p: P) -> With<Spaces<&'a str>, P>
    where P: Parser<Input=&'a str> {
    spaces().with(p)
}

fn integer(input: State<&str>) -> ParseResult<i64, &str> {
    let (s, input) = try!(many1::<String, _>(digit())
        .expected("integer")
        .parse_state(input));
    let mut n = 0;
    for c in s.chars() {
        n = n * 10 + (c as i64 - '0' as i64);
    }
    Ok((n, input))
}

fn number(input: State<&str>) -> ParseResult<f64, &str> {
    let i = satisfy(|c| c == '0').map(|_| 0.0)
             .or(parser(integer).map(|x| x as f64));
    let fractional = many(digit())
        .map(|digits: String| {
            let mut magnitude = 1.0;
            digits.chars().fold(0.0, |acc, d| {
                magnitude /= 10.0;
                match d.to_digit(10) {
                    Some(d) => acc + (d as f64) * magnitude,
                    None => panic!("Not a digit")
                }
            })
        });

    let exp = satisfy(|c| c == 'e' || c == 'E')
        .with(optional(string("-")).and(parser(integer)));
    optional(string("-"))
        .and(i)
        .map(|(sign, n)| if sign.is_some() { -n } else { n })
        .and(optional(string(".")).with(fractional))
        .map(|(x, y)| if x > 0.0 { x + y } else { x - y })
        .and(optional(exp))
        .map(|(n, exp_option)| {
            match exp_option {
                Some((sign, e)) => {
                    let e = if sign.is_some() { -e } else { e };
                    n * 10.0f64.powi(e as i32)
                }
                None => n
            }
        })
    .parse_state(input)
}

fn json_char(input: State<&str>) -> ParseResult<char, &str> {
    let back_slash_char = string("\\")
        .with(satisfy(|c| "\"\\/bfnrt".chars().find(|x| *x == c).is_some()).map(|c| {
            match c {
                '"' => '"',
                '\\' => '\\',
                '/' => '/',
                'b' => '\u{0008}',
                'f' => '\u{000c}',
                'n' => '\n',
                'r' => '\r',
                't' => '\t',
                c => c//Should never happen
            }
        }));
    satisfy(|c| "\"\\".chars().find(|x| *x == c).is_none())
        .or(back_slash_char)
        .parse_state(input)
}
fn json_string(input: State<&str>) -> ParseResult<String, &str> {
    between(string("\""), string("\""), many(parser(json_char)))
        .parse_state(input)
}
fn object(input: State<&str>) -> ParseResult<Value, &str> {
    let field = lex(parser(json_string))
        .skip(lex(string(":")))
        .and(lex(parser(json_value)));
    let fields = sep_by(field, string(","));
    between(string("{"), lex(string("}")), fields)
        .map(Value::Object)
        .parse_state(input)
}

fn json_value(input: State<&str>) -> ParseResult<Value, &str> {
    let array = between(string("["), lex(string("]")), sep_by(parser(json_value), string(",")))
        .map(Value::Array);

    //Wrap a few of the value parsers to workaround the slow compiletimes
    fn rest(input: State<&str>) -> ParseResult<Value, &str> {
        string("false").map(|_| Value::Bool(false))
            .or(string("true").map(|_| Value::Bool(true)))
            .or(string("null").map(|_| Value::Null))
            .parse_state(input)
    }
    lex(array
        .or(parser(object))
        .or(parser(number).map(Value::Number))
        .or(parser(json_string).map(Value::String))
        .or(parser(rest))
    )
        .parse_state(input)
}

#[test]
fn json_test() {
    use self::Value::*;
    let input =
r#"
{
    "array": [1, ""],
    "object": {},
    "number": 3.14,
    "int": -100,
    "exp": -1e2,
    "exp_neg": 23e-2,
    "true": true,
    "false"  : false,
    "null" : null
}"#;
    let result = parser(json_value)
        .parse(input);
    let expected = Object(vec![
        ("array", Array(vec![Number(1.0), String("".to_string())])),
        ("object", Object(HashMap::new())),
        ("number", Number(3.14)),
        ("int", Number(-100.)),
        ("exp", Number(-1e2)),
        ("exp_neg", Number(23E-2)),
        ("true", Bool(true)),
        ("false", Bool(false)),
        ("null", Null)
    ].into_iter().map(|(k, v)| (k.to_string(), v)).collect());
    match result {
        Ok(result) => assert_eq!(result, (expected, "")),
        Err(e) => {
            println!("{}", e);
            assert!(false);
        }
    }
}

#[bench]
fn bench_json(bencher: &mut ::test::Bencher) {
    let mut data = String::new();
    File::open(&Path::new(&"benches/data.json"))
        .and_then(|mut file| file.read_to_string(&mut data))
        .unwrap();
    let mut parser = parser(json_value);
    match parser.parse(&data[..]) {
        Ok((Value::Array(_), "\r\n")) => (),
        Ok(x) => { println!("{:?}", x); assert!(false); }
        Err(err) => { println!("{}", err); assert!(false); }
    }
    bencher.iter(|| {
        let result = parser.parse(&data);
        ::test::black_box(result)
    });
}
<commit_msg>Optimized the character parser in the json benchmark<commit_after>#![feature(test)]
extern crate parser_combinators as pc;
extern crate test;

use std::collections::HashMap;
use std::io::Read;
use std::fs::File;
use std::path::Path;

use pc::primitives::{Parser, State, ParseResult};
use pc::combinator::{between, many, many1, optional, parser, sep_by, unexpected, value, With, ParserExt};
use pc::char::{any_char, digit, satisfy, spaces, Spaces, string};

#[derive(PartialEq, Debug)]
enum Value {
    Number(f64),
    String(String),
    Bool(bool),
    Null,
    Object(HashMap<String, Value>),
    Array(Vec<Value>)
}

fn lex<'a, P>(p: P) -> With<Spaces<&'a str>, P>
    where P: Parser<Input=&'a str> {
    spaces().with(p)
}

fn integer(input: State<&str>) -> ParseResult<i64, &str> {
    let (s, input) = try!(many1::<String, _>(digit())
        .expected("integer")
        .parse_state(input));
    let mut n = 0;
    for c in s.chars() {
        n = n * 10 + (c as i64 - '0' as i64);
    }
    Ok((n, input))
}

fn number(input: State<&str>) -> ParseResult<f64, &str> {
    let i = satisfy(|c| c == '0').map(|_| 0.0)
             .or(parser(integer).map(|x| x as f64));
    let fractional = many(digit())
        .map(|digits: String| {
            let mut magnitude = 1.0;
            digits.chars().fold(0.0, |acc, d| {
                magnitude /= 10.0;
                match d.to_digit(10) {
                    Some(d) => acc + (d as f64) * magnitude,
                    None => panic!("Not a digit")
                }
            })
        });

    let exp = satisfy(|c| c == 'e' || c == 'E')
        .with(optional(string("-")).and(parser(integer)));
    optional(string("-"))
        .and(i)
        .map(|(sign, n)| if sign.is_some() { -n } else { n })
        .and(optional(string(".")).with(fractional))
        .map(|(x, y)| if x > 0.0 { x + y } else { x - y })
        .and(optional(exp))
        .map(|(n, exp_option)| {
            match exp_option {
                Some((sign, e)) => {
                    let e = if sign.is_some() { -e } else { e };
                    n * 10.0f64.powi(e as i32)
                }
                None => n
            }
        })
    .parse_state(input)
}

fn json_char(input: State<&str>) -> ParseResult<char, &str> {
    let (c, input) = try!(parser(any_char).parse_state(input));
    let mut back_slash_char = satisfy(|c| "\"\\/bfnrt".chars().find(|x| *x == c).is_some()).map(|c| {
        match c {
            '"' => '"',
            '\\' => '\\',
            '/' => '/',
            'b' => '\u{0008}',
            'f' => '\u{000c}',
            'n' => '\n',
            'r' => '\r',
            't' => '\t',
            c => c//Should never happen
        }
    });
    match c {
        '\\' => input.combine(|input| back_slash_char.parse_state(input)),
        '"'  => unexpected("\"").parse_state(input.into_inner()).map(|_| unreachable!()),
        _    => Ok((c, input))
    }
}
fn json_string(input: State<&str>) -> ParseResult<String, &str> {
    between(string("\""), string("\""), many(parser(json_char)))
        .parse_state(input)
}
fn object(input: State<&str>) -> ParseResult<Value, &str> {
    let field = lex(parser(json_string))
        .skip(lex(string(":")))
        .and(lex(parser(json_value)));
    let fields = sep_by(field, string(","));
    between(string("{"), lex(string("}")), fields)
        .map(Value::Object)
        .parse_state(input)
}

fn json_value(input: State<&str>) -> ParseResult<Value, &str> {
    let array = between(string("["), lex(string("]")), sep_by(parser(json_value), string(",")))
        .map(Value::Array);

    //Wrap a few of the value parsers to workaround the slow compiletimes
    fn rest(input: State<&str>) -> ParseResult<Value, &str> {
        string("false").map(|_| Value::Bool(false))
            .or(string("true").map(|_| Value::Bool(true)))
            .or(string("null").map(|_| Value::Null))
            .parse_state(input)
    }
    lex(parser(json_string).map(Value::String)
        .or(parser(object))
        .or(array)
        .or(parser(number).map(Value::Number))
        .or(parser(rest))
    )
        .parse_state(input)
}

#[test]
fn json_test() {
    use self::Value::*;
    let input =
r#"
{
    "array": [1, ""],
    "object": {},
    "number": 3.14,
    "int": -100,
    "exp": -1e2,
    "exp_neg": 23e-2,
    "true": true,
    "false"  : false,
    "null" : null
}"#;
    let result = parser(json_value)
        .parse(input);
    let expected = Object(vec![
        ("array", Array(vec![Number(1.0), String("".to_string())])),
        ("object", Object(HashMap::new())),
        ("number", Number(3.14)),
        ("int", Number(-100.)),
        ("exp", Number(-1e2)),
        ("exp_neg", Number(23E-2)),
        ("true", Bool(true)),
        ("false", Bool(false)),
        ("null", Null)
    ].into_iter().map(|(k, v)| (k.to_string(), v)).collect());
    match result {
        Ok(result) => assert_eq!(result, (expected, "")),
        Err(e) => {
            println!("{}", e);
            assert!(false);
        }
    }
}

#[bench]
fn bench_json(bencher: &mut ::test::Bencher) {
    let mut data = String::new();
    File::open(&Path::new(&"benches/data.json"))
        .and_then(|mut file| file.read_to_string(&mut data))
        .unwrap();
    let mut parser = parser(json_value);
    match parser.parse(&data[..]) {
        Ok((Value::Array(_), "\r\n")) => (),
        Ok(x) => { println!("{:?}", x); assert!(false); }
        Err(err) => { println!("{}", err); assert!(false); }
    }
    bencher.iter(|| {
        let result = parser.parse(&data);
        ::test::black_box(result)
    });
}
<|endoftext|>


<commit_before>use criterion::{criterion_group, criterion_main, Criterion};

use rsvg_internals::rect::IRect;
use rsvg_internals::surface_utils::shared_surface::{
    AlphaOnly, Horizontal, NotAlphaOnly, SharedImageSurface, SurfaceType, Vertical,
};

const SURFACE_SIDE: i32 = 512;
const BOUNDS: IRect = IRect {
    x0: 64,
    y0: 64,
    x1: 64 + 64,
    y1: 64 + 64,
};

fn bench_box_blur(c: &mut Criterion) {
    c.bench_function_over_inputs(
        "box_blur 9",
        |b, &(vertical, alpha_only)| {
            let surface_type = if alpha_only {
                SurfaceType::AlphaOnly
            } else {
                SurfaceType::SRgb
            };
            let input_surface =
                SharedImageSurface::empty(SURFACE_SIDE, SURFACE_SIDE, surface_type).unwrap();

            let mut output_surface =
                cairo::ImageSurface::create(cairo::Format::ARgb32, SURFACE_SIDE, SURFACE_SIDE)
                    .unwrap();
            const KERNEL_SIZE: usize = 9;

            let f = match (vertical, alpha_only) {
                (true, true) => SharedImageSurface::box_blur_loop::<Vertical, AlphaOnly>,
                (true, false) => SharedImageSurface::box_blur_loop::<Vertical, NotAlphaOnly>,
                (false, true) => SharedImageSurface::box_blur_loop::<Horizontal, AlphaOnly>,
                (false, false) => SharedImageSurface::box_blur_loop::<Horizontal, NotAlphaOnly>,
            };

            b.iter(|| {
                f(
                    &input_surface,
                    &mut output_surface,
                    BOUNDS,
                    KERNEL_SIZE,
                    KERNEL_SIZE / 2,
                )
            })
        },
        vec![(false, false), (false, true), (true, false), (true, true)],
    );
}

criterion_group!(benches, bench_box_blur);
criterion_main!(benches);
<commit_msg>benches: box_blur: Replace deprecated function<commit_after>use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion};

use rsvg_internals::rect::IRect;
use rsvg_internals::surface_utils::shared_surface::{
    AlphaOnly, Horizontal, NotAlphaOnly, SharedImageSurface, SurfaceType, Vertical,
};

const SURFACE_SIDE: i32 = 512;
const BOUNDS: IRect = IRect {
    x0: 64,
    y0: 64,
    x1: 64 + 64,
    y1: 64 + 64,
};

fn bench_box_blur(c: &mut Criterion) {
    let mut group = c.benchmark_group("box_blur 9");

    for input in [(false, false), (false, true), (true, false), (true, true)].iter() {
        group.bench_with_input(
            BenchmarkId::from_parameter(format!("{:?}", input)),
            &input,
            |b, &(vertical, alpha_only)| {
                let surface_type = if *alpha_only {
                    SurfaceType::AlphaOnly
                } else {
                    SurfaceType::SRgb
                };
                let input_surface =
                    SharedImageSurface::empty(SURFACE_SIDE, SURFACE_SIDE, surface_type).unwrap();

                let mut output_surface =
                    cairo::ImageSurface::create(cairo::Format::ARgb32, SURFACE_SIDE, SURFACE_SIDE)
                        .unwrap();
                const KERNEL_SIZE: usize = 9;

                let f = match (vertical, alpha_only) {
                    (true, true) => SharedImageSurface::box_blur_loop::<Vertical, AlphaOnly>,
                    (true, false) => SharedImageSurface::box_blur_loop::<Vertical, NotAlphaOnly>,
                    (false, true) => SharedImageSurface::box_blur_loop::<Horizontal, AlphaOnly>,
                    (false, false) => SharedImageSurface::box_blur_loop::<Horizontal, NotAlphaOnly>,
                };

                b.iter(|| {
                    f(
                        &input_surface,
                        &mut output_surface,
                        BOUNDS,
                        KERNEL_SIZE,
                        KERNEL_SIZE / 2,
                    )
                })
            },
        );
    }
}

criterion_group!(benches, bench_box_blur);
criterion_main!(benches);
<|endoftext|>


<commit_before>//! Handling of `preserveAspectRatio` values
//!
//! This module handles `preserveAspectRatio` values [per the SVG specification][spec].
//! We have an [`AspectRatio`] struct which encapsulates such a value.
//!
//! ```ignore
//! assert_eq!(
//!     AspectRatio::parse("xMidYMid", ()),
//!     Ok(AspectRatio {
//!         defer: false,
//!         align: Some(Align {
//!             x: Align1D::Mid,
//!             y: Align1D::Mid,
//!             fit: FitMode::Meet,
//!         }),
//!     })
//! );
//! ```
//!
//! [`AspectRatio`]: struct.AspectRatio.html
//! [spec]: https://www.w3.org/TR/SVG/coords.html#PreserveAspectRatioAttribute

use cssparser::{Parser, ParserInput};
use error::*;
use parsers::Parse;
use parsers::ParseError;

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct AspectRatio {
    defer: bool,
    align: Option<Align>,
}

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
enum FitMode {
    Meet,
    Slice,
}

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
struct Align {
    x: Align1D,
    y: Align1D,
    fit: FitMode,
}

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
struct AlignXY {
    x: Align1D,
    y: Align1D,
}

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
enum Align1D {
    Min,
    Mid,
    Max,
}

impl Align1D {
    fn compute(self, dest_pos: f64, dest_size: f64, obj_size: f64) -> f64 {
        match self {
            Align1D::Min => dest_pos,
            Align1D::Mid => dest_pos + (dest_size - obj_size) / 2.0,
            Align1D::Max => dest_pos + dest_size - obj_size,
        }
    }
}

impl AspectRatio {
    pub fn is_slice(&self) -> bool {
        match self.align {
            Some(Align {
                fit: FitMode::Slice,
                ..
            }) => true,

            _ => false,
        }
    }

    pub fn compute(
        &self,
        object_width: f64,
        object_height: f64,
        dest_x: f64,
        dest_y: f64,
        dest_width: f64,
        dest_height: f64,
    ) -> (f64, f64, f64, f64) {
        match self.align {
            None => (dest_x, dest_y, dest_width, dest_height),

            Some(Align { x, y, fit }) => {
                let w_factor = dest_width / object_width;
                let h_factor = dest_height / object_height;
                let factor = match fit {
                    FitMode::Meet => w_factor.min(h_factor),
                    FitMode::Slice => w_factor.max(h_factor),
                };

                let w = object_width * factor;
                let h = object_height * factor;

                let xpos = x.compute(dest_x, dest_width, w);
                let ypos = y.compute(dest_y, dest_height, h);

                (xpos, ypos, w, h)
            }
        }
    }

    fn parse_input<'i, 't>(p: &mut Parser<'i, 't>) -> Result<AspectRatio, ()> {
        let defer = p.try(|p| p.expect_ident_matching("defer")).is_ok();

        let align_xy = p.try(|p| {
            p.expect_ident()
                .map_err(|_| ())
                .and_then(|ident| AlignXY::parse(ident))
        })?;

        let fit = p.try(|p| {
            p.expect_ident()
                .map_err(|_| ())
                .and_then(|ident| FitMode::parse(ident))
        }).unwrap_or(FitMode::default());

        p.expect_exhausted().map_err(|_| ())?;

        let align = align_xy.map(|AlignXY { x: x, y: y }| Align { x, y, fit });

        Ok(AspectRatio { defer, align })
    }
}

impl Default for AspectRatio {
    fn default() -> AspectRatio {
        AspectRatio {
            defer: false,
            align: Some(Align::default()),
        }
    }
}

impl Default for FitMode {
    fn default() -> FitMode {
        FitMode::Meet
    }
}

impl Default for Align {
    fn default() -> Align {
        Align {
            x: Align1D::Mid,
            y: Align1D::Mid,
            fit: FitMode::default(),
        }
    }
}

impl AlignXY {
    fn parse(s: &str) -> Result<Option<AlignXY>, ()> {
        use self::Align1D::*;

        match s {
            "none" => Ok(None),

            "xMinYMin" => Ok(Some(AlignXY { x: Min, y: Min })),
            "xMidYMin" => Ok(Some(AlignXY { x: Mid, y: Min })),
            "xMaxYMin" => Ok(Some(AlignXY { x: Max, y: Min })),

            "xMinYMid" => Ok(Some(AlignXY { x: Min, y: Mid })),
            "xMidYMid" => Ok(Some(AlignXY { x: Mid, y: Mid })),
            "xMaxYMid" => Ok(Some(AlignXY { x: Max, y: Mid })),

            "xMinYMax" => Ok(Some(AlignXY { x: Min, y: Max })),
            "xMidYMax" => Ok(Some(AlignXY { x: Mid, y: Max })),
            "xMaxYMax" => Ok(Some(AlignXY { x: Max, y: Max })),

            _ => Err(()),
        }
    }
}

impl FitMode {
    fn parse(s: &str) -> Result<FitMode, ()> {
        match s {
            "meet" => Ok(FitMode::Meet),
            "slice" => Ok(FitMode::Slice),
            _ => Err(()),
        }
    }
}

impl Parse for AspectRatio {
    type Data = ();
    type Err = AttributeError;

    fn parse(s: &str, _: ()) -> Result<AspectRatio, AttributeError> {
        let mut input = ParserInput::new(s);
        AspectRatio::parse_input(&mut Parser::new(&mut input)).map_err(|_| {
            AttributeError::Parse(ParseError::new(
                "expected \"[defer] <align> [meet | slice]\"",
            ))
        })
    }
}

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

    #[test]
    fn parsing_invalid_strings_yields_error() {
        assert!(AspectRatio::parse("", ()).is_err());

        assert!(AspectRatio::parse("defer", ()).is_err());

        assert!(AspectRatio::parse("defer foo", ()).is_err());

        assert!(AspectRatio::parse("defer xmidymid", ()).is_err());

        assert!(AspectRatio::parse("defer xMidYMid foo", ()).is_err());

        assert!(AspectRatio::parse("xmidymid", ()).is_err());

        assert!(AspectRatio::parse("xMidYMid foo", ()).is_err());

        assert!(AspectRatio::parse("defer xMidYMid meet foo", ()).is_err());
    }

    #[test]
    fn parses_valid_strings() {
        assert_eq!(
            AspectRatio::parse("defer none", ()),
            Ok(AspectRatio {
                defer: true,
                align: None,
            },)
        );

        assert_eq!(
            AspectRatio::parse("xMidYMid", ()),
            Ok(AspectRatio {
                defer: false,
                align: Some(Align {
                    x: Align1D::Mid,
                    y: Align1D::Mid,
                    fit: FitMode::Meet,
                },),
            },)
        );

        assert_eq!(
            AspectRatio::parse("defer xMidYMid", ()),
            Ok(AspectRatio {
                defer: true,
                align: Some(Align {
                    x: Align1D::Mid,
                    y: Align1D::Mid,
                    fit: FitMode::Meet,
                },),
            },)
        );

        assert_eq!(
            AspectRatio::parse("defer xMinYMax", ()),
            Ok(AspectRatio {
                defer: true,
                align: Some(Align {
                    x: Align1D::Min,
                    y: Align1D::Max,
                    fit: FitMode::Meet,
                },),
            },)
        );

        assert_eq!(
            AspectRatio::parse("defer xMaxYMid meet", ()),
            Ok(AspectRatio {
                defer: true,
                align: Some(Align {
                    x: Align1D::Max,
                    y: Align1D::Mid,
                    fit: FitMode::Meet,
                },),
            },)
        );

        assert_eq!(
            AspectRatio::parse("defer xMinYMax slice", ()),
            Ok(AspectRatio {
                defer: true,
                align: Some(Align {
                    x: Align1D::Min,
                    y: Align1D::Max,
                    fit: FitMode::Slice,
                },),
            },)
        );
    }

    #[test]
    fn aligns() {
        assert_eq!(
            AspectRatio::parse("xMinYMin meet", ()).unwrap().compute(
                1.0,
                10.0,
                0.0,
                0.0,
                10.0,
                1.0,
            ),
            (0.0, 0.0, 0.1, 1.0)
        );
        assert_eq!(
            AspectRatio::parse("xMinYMin slice", ()).unwrap().compute(
                1.0,
                10.0,
                0.0,
                0.0,
                10.0,
                1.0,
            ),
            (0.0, 0.0, 10.0, 100.0)
        );

        assert_eq!(
            AspectRatio::parse("xMinYMid meet", ()).unwrap().compute(
                1.0,
                10.0,
                0.0,
                0.0,
                10.0,
                1.0,
            ),
            (0.0, 0.0, 0.1, 1.0)
        );
        assert_eq!(
            AspectRatio::parse("xMinYMid slice", ()).unwrap().compute(
                1.0,
                10.0,
                0.0,
                0.0,
                10.0,
                1.0,
            ),
            (0.0, -49.5, 10.0, 100.0)
        );

        assert_eq!(
            AspectRatio::parse("xMinYMax meet", ()).unwrap().compute(
                1.0,
                10.0,
                0.0,
                0.0,
                10.0,
                1.0,
            ),
            (0.0, 0.0, 0.1, 1.0)
        );
        assert_eq!(
            AspectRatio::parse("xMinYMax slice", ()).unwrap().compute(
                1.0,
                10.0,
                0.0,
                0.0,
                10.0,
                1.0,
            ),
            (0.0, -99.0, 10.0, 100.0)
        );

        assert_eq!(
            AspectRatio::parse("xMidYMin meet", ()).unwrap().compute(
                1.0,
                10.0,
                0.0,
                0.0,
                10.0,
                1.0,
            ),
            (4.95, 0.0, 0.1, 1.0)
        );
        assert_eq!(
            AspectRatio::parse("xMidYMin slice", ()).unwrap().compute(
                1.0,
                10.0,
                0.0,
                0.0,
                10.0,
                1.0,
            ),
            (0.0, 0.0, 10.0, 100.0)
        );

        assert_eq!(
            AspectRatio::parse("xMidYMid meet", ()).unwrap().compute(
                1.0,
                10.0,
                0.0,
                0.0,
                10.0,
                1.0,
            ),
            (4.95, 0.0, 0.1, 1.0)
        );
        assert_eq!(
            AspectRatio::parse("xMidYMid slice", ()).unwrap().compute(
                1.0,
                10.0,
                0.0,
                0.0,
                10.0,
                1.0,
            ),
            (0.0, -49.5, 10.0, 100.0)
        );

        assert_eq!(
            AspectRatio::parse("xMidYMax meet", ()).unwrap().compute(
                1.0,
                10.0,
                0.0,
                0.0,
                10.0,
                1.0,
            ),
            (4.95, 0.0, 0.1, 1.0)
        );
        assert_eq!(
            AspectRatio::parse("xMidYMax slice", ()).unwrap().compute(
                1.0,
                10.0,
                0.0,
                0.0,
                10.0,
                1.0,
            ),
            (0.0, -99.0, 10.0, 100.0)
        );

        assert_eq!(
            AspectRatio::parse("xMaxYMin meet", ()).unwrap().compute(
                1.0,
                10.0,
                0.0,
                0.0,
                10.0,
                1.0,
            ),
            (9.9, 0.0, 0.1, 1.0)
        );
        assert_eq!(
            AspectRatio::parse("xMaxYMin slice", ()).unwrap().compute(
                1.0,
                10.0,
                0.0,
                0.0,
                10.0,
                1.0,
            ),
            (0.0, 0.0, 10.0, 100.0)
        );

        assert_eq!(
            AspectRatio::parse("xMaxYMid meet", ()).unwrap().compute(
                1.0,
                10.0,
                0.0,
                0.0,
                10.0,
                1.0,
            ),
            (9.9, 0.0, 0.1, 1.0)
        );
        assert_eq!(
            AspectRatio::parse("xMaxYMid slice", ()).unwrap().compute(
                1.0,
                10.0,
                0.0,
                0.0,
                10.0,
                1.0,
            ),
            (0.0, -49.5, 10.0, 100.0)
        );

        assert_eq!(
            AspectRatio::parse("xMaxYMax meet", ()).unwrap().compute(
                1.0,
                10.0,
                0.0,
                0.0,
                10.0,
                1.0,
            ),
            (9.9, 0.0, 0.1, 1.0)
        );
        assert_eq!(
            AspectRatio::parse("xMaxYMax slice", ()).unwrap().compute(
                1.0,
                10.0,
                0.0,
                0.0,
                10.0,
                1.0,
            ),
            (0.0, -99.0, 10.0, 100.0)
        );
    }
}
<commit_msg>aspect_ratio: Remove AlignXY struct.<commit_after>//! Handling of `preserveAspectRatio` values
//!
//! This module handles `preserveAspectRatio` values [per the SVG specification][spec].
//! We have an [`AspectRatio`] struct which encapsulates such a value.
//!
//! ```ignore
//! assert_eq!(
//!     AspectRatio::parse("xMidYMid", ()),
//!     Ok(AspectRatio {
//!         defer: false,
//!         align: Some(Align {
//!             x: Align1D::Mid,
//!             y: Align1D::Mid,
//!             fit: FitMode::Meet,
//!         }),
//!     })
//! );
//! ```
//!
//! [`AspectRatio`]: struct.AspectRatio.html
//! [spec]: https://www.w3.org/TR/SVG/coords.html#PreserveAspectRatioAttribute

use cssparser::{Parser, ParserInput};
use error::*;
use parsers::Parse;
use parsers::ParseError;

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct AspectRatio {
    defer: bool,
    align: Option<Align>,
}

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
enum FitMode {
    Meet,
    Slice,
}

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
struct Align {
    x: Align1D,
    y: Align1D,
    fit: FitMode,
}

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
enum Align1D {
    Min,
    Mid,
    Max,
}

impl Align1D {
    fn compute(self, dest_pos: f64, dest_size: f64, obj_size: f64) -> f64 {
        match self {
            Align1D::Min => dest_pos,
            Align1D::Mid => dest_pos + (dest_size - obj_size) / 2.0,
            Align1D::Max => dest_pos + dest_size - obj_size,
        }
    }
}

impl AspectRatio {
    pub fn is_slice(&self) -> bool {
        match self.align {
            Some(Align {
                fit: FitMode::Slice,
                ..
            }) => true,

            _ => false,
        }
    }

    pub fn compute(
        &self,
        object_width: f64,
        object_height: f64,
        dest_x: f64,
        dest_y: f64,
        dest_width: f64,
        dest_height: f64,
    ) -> (f64, f64, f64, f64) {
        match self.align {
            None => (dest_x, dest_y, dest_width, dest_height),

            Some(Align { x, y, fit }) => {
                let w_factor = dest_width / object_width;
                let h_factor = dest_height / object_height;
                let factor = match fit {
                    FitMode::Meet => w_factor.min(h_factor),
                    FitMode::Slice => w_factor.max(h_factor),
                };

                let w = object_width * factor;
                let h = object_height * factor;

                let xpos = x.compute(dest_x, dest_width, w);
                let ypos = y.compute(dest_y, dest_height, h);

                (xpos, ypos, w, h)
            }
        }
    }

    fn parse_input<'i, 't>(p: &mut Parser<'i, 't>) -> Result<AspectRatio, ()> {
        let defer = p.try(|p| p.expect_ident_matching("defer")).is_ok();

        let align_xy = p.try(|p| {
            p.expect_ident()
                .map_err(|_| ())
                .and_then(|ident| Align::parse_xy(ident))
        })?;

        let fit = p.try(|p| {
            p.expect_ident()
                .map_err(|_| ())
                .and_then(|ident| FitMode::parse(ident))
        }).unwrap_or(FitMode::default());

        p.expect_exhausted().map_err(|_| ())?;

        let align = align_xy.map(|(x, y)| Align { x, y, fit });

        Ok(AspectRatio { defer, align })
    }
}

impl Default for AspectRatio {
    fn default() -> AspectRatio {
        AspectRatio {
            defer: false,
            align: Some(Align::default()),
        }
    }
}

impl Default for FitMode {
    fn default() -> FitMode {
        FitMode::Meet
    }
}

impl Default for Align {
    fn default() -> Align {
        Align {
            x: Align1D::Mid,
            y: Align1D::Mid,
            fit: FitMode::default(),
        }
    }
}

impl Align {
    fn parse_xy(s: &str) -> Result<Option<(Align1D, Align1D)>, ()> {
        use self::Align1D::*;

        match s {
            "none" => Ok(None),

            "xMinYMin" => Ok(Some((Min, Min))),
            "xMidYMin" => Ok(Some((Mid, Min))),
            "xMaxYMin" => Ok(Some((Max, Min))),

            "xMinYMid" => Ok(Some((Min, Mid))),
            "xMidYMid" => Ok(Some((Mid, Mid))),
            "xMaxYMid" => Ok(Some((Max, Mid))),

            "xMinYMax" => Ok(Some((Min, Max))),
            "xMidYMax" => Ok(Some((Mid, Max))),
            "xMaxYMax" => Ok(Some((Max, Max))),

            _ => Err(()),
        }
    }
}

impl FitMode {
    fn parse(s: &str) -> Result<FitMode, ()> {
        match s {
            "meet" => Ok(FitMode::Meet),
            "slice" => Ok(FitMode::Slice),
            _ => Err(()),
        }
    }
}

impl Parse for AspectRatio {
    type Data = ();
    type Err = AttributeError;

    fn parse(s: &str, _: ()) -> Result<AspectRatio, AttributeError> {
        let mut input = ParserInput::new(s);
        AspectRatio::parse_input(&mut Parser::new(&mut input)).map_err(|_| {
            AttributeError::Parse(ParseError::new(
                "expected \"[defer] <align> [meet | slice]\"",
            ))
        })
    }
}

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

    #[test]
    fn parsing_invalid_strings_yields_error() {
        assert!(AspectRatio::parse("", ()).is_err());

        assert!(AspectRatio::parse("defer", ()).is_err());

        assert!(AspectRatio::parse("defer foo", ()).is_err());

        assert!(AspectRatio::parse("defer xmidymid", ()).is_err());

        assert!(AspectRatio::parse("defer xMidYMid foo", ()).is_err());

        assert!(AspectRatio::parse("xmidymid", ()).is_err());

        assert!(AspectRatio::parse("xMidYMid foo", ()).is_err());

        assert!(AspectRatio::parse("defer xMidYMid meet foo", ()).is_err());
    }

    #[test]
    fn parses_valid_strings() {
        assert_eq!(
            AspectRatio::parse("defer none", ()),
            Ok(AspectRatio {
                defer: true,
                align: None,
            },)
        );

        assert_eq!(
            AspectRatio::parse("xMidYMid", ()),
            Ok(AspectRatio {
                defer: false,
                align: Some(Align {
                    x: Align1D::Mid,
                    y: Align1D::Mid,
                    fit: FitMode::Meet,
                },),
            },)
        );

        assert_eq!(
            AspectRatio::parse("defer xMidYMid", ()),
            Ok(AspectRatio {
                defer: true,
                align: Some(Align {
                    x: Align1D::Mid,
                    y: Align1D::Mid,
                    fit: FitMode::Meet,
                },),
            },)
        );

        assert_eq!(
            AspectRatio::parse("defer xMinYMax", ()),
            Ok(AspectRatio {
                defer: true,
                align: Some(Align {
                    x: Align1D::Min,
                    y: Align1D::Max,
                    fit: FitMode::Meet,
                },),
            },)
        );

        assert_eq!(
            AspectRatio::parse("defer xMaxYMid meet", ()),
            Ok(AspectRatio {
                defer: true,
                align: Some(Align {
                    x: Align1D::Max,
                    y: Align1D::Mid,
                    fit: FitMode::Meet,
                },),
            },)
        );

        assert_eq!(
            AspectRatio::parse("defer xMinYMax slice", ()),
            Ok(AspectRatio {
                defer: true,
                align: Some(Align {
                    x: Align1D::Min,
                    y: Align1D::Max,
                    fit: FitMode::Slice,
                },),
            },)
        );
    }

    #[test]
    fn aligns() {
        assert_eq!(
            AspectRatio::parse("xMinYMin meet", ()).unwrap().compute(
                1.0,
                10.0,
                0.0,
                0.0,
                10.0,
                1.0,
            ),
            (0.0, 0.0, 0.1, 1.0)
        );
        assert_eq!(
            AspectRatio::parse("xMinYMin slice", ()).unwrap().compute(
                1.0,
                10.0,
                0.0,
                0.0,
                10.0,
                1.0,
            ),
            (0.0, 0.0, 10.0, 100.0)
        );

        assert_eq!(
            AspectRatio::parse("xMinYMid meet", ()).unwrap().compute(
                1.0,
                10.0,
                0.0,
                0.0,
                10.0,
                1.0,
            ),
            (0.0, 0.0, 0.1, 1.0)
        );
        assert_eq!(
            AspectRatio::parse("xMinYMid slice", ()).unwrap().compute(
                1.0,
                10.0,
                0.0,
                0.0,
                10.0,
                1.0,
            ),
            (0.0, -49.5, 10.0, 100.0)
        );

        assert_eq!(
            AspectRatio::parse("xMinYMax meet", ()).unwrap().compute(
                1.0,
                10.0,
                0.0,
                0.0,
                10.0,
                1.0,
            ),
            (0.0, 0.0, 0.1, 1.0)
        );
        assert_eq!(
            AspectRatio::parse("xMinYMax slice", ()).unwrap().compute(
                1.0,
                10.0,
                0.0,
                0.0,
                10.0,
                1.0,
            ),
            (0.0, -99.0, 10.0, 100.0)
        );

        assert_eq!(
            AspectRatio::parse("xMidYMin meet", ()).unwrap().compute(
                1.0,
                10.0,
                0.0,
                0.0,
                10.0,
                1.0,
            ),
            (4.95, 0.0, 0.1, 1.0)
        );
        assert_eq!(
            AspectRatio::parse("xMidYMin slice", ()).unwrap().compute(
                1.0,
                10.0,
                0.0,
                0.0,
                10.0,
                1.0,
            ),
            (0.0, 0.0, 10.0, 100.0)
        );

        assert_eq!(
            AspectRatio::parse("xMidYMid meet", ()).unwrap().compute(
                1.0,
                10.0,
                0.0,
                0.0,
                10.0,
                1.0,
            ),
            (4.95, 0.0, 0.1, 1.0)
        );
        assert_eq!(
            AspectRatio::parse("xMidYMid slice", ()).unwrap().compute(
                1.0,
                10.0,
                0.0,
                0.0,
                10.0,
                1.0,
            ),
            (0.0, -49.5, 10.0, 100.0)
        );

        assert_eq!(
            AspectRatio::parse("xMidYMax meet", ()).unwrap().compute(
                1.0,
                10.0,
                0.0,
                0.0,
                10.0,
                1.0,
            ),
            (4.95, 0.0, 0.1, 1.0)
        );
        assert_eq!(
            AspectRatio::parse("xMidYMax slice", ()).unwrap().compute(
                1.0,
                10.0,
                0.0,
                0.0,
                10.0,
                1.0,
            ),
            (0.0, -99.0, 10.0, 100.0)
        );

        assert_eq!(
            AspectRatio::parse("xMaxYMin meet", ()).unwrap().compute(
                1.0,
                10.0,
                0.0,
                0.0,
                10.0,
                1.0,
            ),
            (9.9, 0.0, 0.1, 1.0)
        );
        assert_eq!(
            AspectRatio::parse("xMaxYMin slice", ()).unwrap().compute(
                1.0,
                10.0,
                0.0,
                0.0,
                10.0,
                1.0,
            ),
            (0.0, 0.0, 10.0, 100.0)
        );

        assert_eq!(
            AspectRatio::parse("xMaxYMid meet", ()).unwrap().compute(
                1.0,
                10.0,
                0.0,
                0.0,
                10.0,
                1.0,
            ),
            (9.9, 0.0, 0.1, 1.0)
        );
        assert_eq!(
            AspectRatio::parse("xMaxYMid slice", ()).unwrap().compute(
                1.0,
                10.0,
                0.0,
                0.0,
                10.0,
                1.0,
            ),
            (0.0, -49.5, 10.0, 100.0)
        );

        assert_eq!(
            AspectRatio::parse("xMaxYMax meet", ()).unwrap().compute(
                1.0,
                10.0,
                0.0,
                0.0,
                10.0,
                1.0,
            ),
            (9.9, 0.0, 0.1, 1.0)
        );
        assert_eq!(
            AspectRatio::parse("xMaxYMax slice", ()).unwrap().compute(
                1.0,
                10.0,
                0.0,
                0.0,
                10.0,
                1.0,
            ),
            (0.0, -99.0, 10.0, 100.0)
        );
    }
}
<|endoftext|>