<commit_before>use std::collections::BTreeMap;
use rustc_serialize::json::{Json, ToJson, Object, Array};
use geojson::{FeatureCollection, Feature};
use {TopoJsonResult, TopoJsonError};

pub struct Topology(BTreeMap<String, FeatureCollection>);
// TODO TopoJSON can have any top-level geometry in an object, it
// doesn't have to be a FeatureCollection (which is spelled
// GeometryCollection in TopoJSON).

fn parse_transform(json: &Json) -> TopoJsonResult<(f64, f64, f64, f64)> {
    let transform = expect_object!(json);
    let translate = expect_array!(expect_property!(transform, "translate", "Missing 'translate' field"));
    let scale = expect_array!(expect_property!(transform, "scale", "Missing 'scale' field"));
    match (translate.len(), scale.len()) {
        (2, 2) => (),
        _ => return Err(TopoJsonError::new("Both 'translate' and 'scale' must be two-element arrays"))
    };
    let t0 = expect_f64!(translate[0]);
    let t1 = expect_f64!(translate[1]);
    let s0 = expect_f64!(scale[0]);
    let s1 = expect_f64!(scale[1]);
    return Ok((t0, t1, s0, s1));
}

struct Space {
    arcs: Vec<Vec<Json>>,
    transform: Option<(f64, f64, f64, f64)>,
}

impl Space {
    fn from_topology(topo: &Object) -> TopoJsonResult<Space> {
        let transform = match topo.get("transform") {
            Some(transform_json) => Some(try!(parse_transform(transform_json))),
            None => None
        };
        let mut arcs = vec![];
        for arc_json in expect_array!(expect_property!(topo, "arcs", "Missing 'arcs' field")).iter() {
            let arc = match transform {
                Some((t0, t1, s0, s1)) => {
                    let mut arc = vec![];
                    let mut x = 0;
                    let mut y = 0;
                    for point_json in expect_array!(arc_json).iter() {
                        let mut p = expect_array!(point_json).clone();
                        if p.len() < 2 {
                            return Err(TopoJsonError::new("Point must have at least two elements"));
                        }
                        x += expect_i64!(p[0]);
                        y += expect_i64!(p[1]);
                        p[0] = Json::F64((x as f64) * s0 + t0);
                        p[1] = Json::F64((y as f64) * s1 + t1);
                        arc.push(Json::Array(p));
                    }
                    arc
                },
                None => expect_array!(arc_json).clone()
            };
            arcs.push(arc);
        }
        return Ok(Space{arcs: arcs, transform: transform});
    }

    pub fn point(&self, p: &Vec<Json>) -> TopoJsonResult<Vec<Json>> {
        if p.len() < 2 {
            return Err(TopoJsonError::new("Point must have at least two elements"));
        }
        let mut rv = p.clone();
        println!("{:?}", self.transform);
        match self.transform {
            Some((t0, t1, s0, s1)) => {
                rv[0] = Json::F64(expect_f64!(rv[0]) * s0 + t0);
                rv[1] = Json::F64(expect_f64!(rv[1]) * s1 + t1);
            },
            None => ()
        };
        return Ok(rv);
    }

    pub fn arc(&self, arc_index: isize) -> TopoJsonResult<Json> {
        let (inverse, offset) = if arc_index > -1 {
            (false, arc_index as usize)
        } else {
            (true, -1 - arc_index as usize)
        };
        if offset >= self.arcs.len() {
            println!("offset = {}", offset);
            return Err(TopoJsonError::new("Arc index too large"));
        }
        let arc = &self.arcs[offset];
        let arcs_json = if inverse {
            arc.iter().rev().map(|p| p.to_json()).collect()
        } else {
            arc.iter().map(|p| p.to_json()).collect()
        };
        return Ok(Json::Array(arcs_json));
    }
}

fn transform_coordinates(topo_coord: &Array, space: &Space) -> TopoJsonResult<Json> {
    return Ok(Json::Array(try!(space.point(topo_coord))));
}

fn transform_arcs(arcs: &Array, space: &Space, depth: usize) -> TopoJsonResult<Json> {
    let mut rv = vec![];
    for arc in arcs.iter() {
        println!("{:?}", arc);
        if depth > 1 {
            rv.push(try!(transform_arcs(expect_array!(arc), space, depth - 1)));
        }
        else {
            rv.push(try!(space.arc(expect_i64!(arc) as isize)));
        }
    }
    if depth == 1 {
        rv = match rv.pop() {
            Some(Json::Array(v)) => v,
            _ => return Err(TopoJsonError::new("Empty arc list"))
        }
    }
    return Ok(Json::Array(rv));
}

fn parse_geometry(object: &Object, space: &Space) -> TopoJsonResult<Feature> {
    let geometry_type = expect_string!(expect_property!(object, "type", "Missing 'type' field"));
    let properties = expect_property!(object, "properties", "Missing 'properties' field");

    let mut geojson_geometry = BTreeMap::new();
    geojson_geometry.insert("type".to_string(), Json::String(geometry_type.to_string()));
    let coordinates = match geometry_type {
        "Point" => {
            let topo_coord = expect_property!(object, "coordinates", "Missing 'coordinates' property");
            try!(transform_coordinates(expect_array!(topo_coord), space))
        },
        "LineString" | "Polygon" => {
            let arcs = expect_property!(object, "arcs", "Missing 'arcs' property");
            println!("arcs! {:?}", arcs);
            let depth = match geometry_type {
                "LineString" => 1,
                "Polygon" => 2,
                _ => unreachable!()
            };
            try!(transform_arcs(expect_array!(arcs), space, depth))
        },
        _ => return Err(TopoJsonError::new("Unknown geometry type"))
    };
    geojson_geometry.insert("coordinates".to_string(), coordinates);

    let mut geojson_feature = BTreeMap::new();
    geojson_feature.insert("type".to_string(), Json::String("Feature".to_string()));
    geojson_feature.insert("geometry".to_string(), Json::Object(geojson_geometry));
    geojson_feature.insert("properties".to_string(), properties.clone());
    println!("feature: {}", Json::Object(geojson_feature.clone()));
    return Ok(Feature::from_json(&geojson_feature));
}

fn parse_geometry_collection(object: &Object, space: &Space) -> TopoJsonResult<FeatureCollection> {
    let geometries = expect_array!(expect_property!(object, "geometries", "Missing 'geometries' field"));
    let mut features = vec![];
    for geometry_json in geometries.iter() {
        features.push(try!(parse_geometry(expect_object!(geometry_json), space)));
    }
    return Ok(FeatureCollection{features: features});
}

impl Topology {
    pub fn from_json(json: &Json) -> TopoJsonResult<Topology> {
        let topo = expect_object!(json);
        match expect_property!(topo, "type", "Missing 'type' field").as_string() {
            Some("Topology") => (),
            _ => return Err(TopoJsonError::new("'type' should be 'Topology'"))
        };
        let space = try!(Space::from_topology(topo));
        let objects = expect_object!(expect_property!(topo, "objects", "Missing 'objects' field"));
        let mut map = BTreeMap::new();
        for (name, value) in objects.iter() {
            let object = expect_object!(value);
            let object_type = expect_string!(expect_property!(object, "type", "Missing 'type' field"));
            let geojson = match object_type {
                "GeometryCollection" => try!(parse_geometry_collection(object, &space)),
                _ => return Err(TopoJsonError::new("'type' should be 'GeometryCollection'"))
            };
            map.insert(name.to_string(), geojson);
        }
        return Ok(Topology(map));
    }

    pub fn object(&self, name: &str) -> Option<&FeatureCollection> {
        let &Topology(ref hash) = self;
        return hash.get(name);
    }
}

#[cfg(test)]
mod tests {
    use rustc_serialize::json::{Json, ToJson};
    use super::Topology;

    #[test]
    fn test_invalid_topo() {
        fn expect_err(json: &str) {
            match Topology::from_json(&Json::from_str(json).unwrap()) {
                Err(_) => (),
                _ => panic!()
            }
        }
        expect_err("{}");
        expect_err(r#"{"type": "foo"}"#);
        expect_err(r#"{"type": "Topology"}"#);
    }

    #[test]
    fn test_parse_simple_topology_from_spec() {
        let topo_str = r#"{"type":"Topology","objects":{"example":{"type":"GeometryCollection","geometries":[{"type":"Point","properties":{"prop0":"value0"},"coordinates":[102,0.5]},{"type":"LineString","properties":{"prop0":"value0","prop1":0},"arcs":[0]},{"type":"Polygon","properties":{"prop0":"value0","prop1":{"this":"that"}},"arcs":[[-2]]}]}},"arcs":[[[102,0],[103,1],[104,0],[105,1]],[[100,0],[101,0],[101,1],[100,1],[100,0]]]}"#;
        let topo_json = Json::from_str(topo_str).unwrap();
        let topology = match Topology::from_json(&topo_json) {
            Ok(t) => t,
            Err(e) => panic!(e.desc)
        };
        let example = topology.object("example").unwrap();
        let geojson = format!("{}", example.to_json());
        assert_eq!(geojson, r#"{"features":[{"geometry":{"coordinates":[102.0,0.5],"type":"Point"},"properties":{"prop0":"value0"},"type":"Feature"},{"geometry":{"coordinates":[[102.0,0.0],[103.0,1.0],[104.0,0.0],[105.0,1.0]],"type":"LineString"},"properties":{"prop0":"value0","prop1":0},"type":"Feature"},{"geometry":{"coordinates":[[[100.0,0.0],[100.0,1.0],[101.0,1.0],[101.0,0.0],[100.0,0.0]]],"type":"Polygon"},"properties":{"prop0":"value0","prop1":{"this":"that"}},"type":"Feature"}],"type":"FeatureCollection"}"#);
    }

    #[test]
    fn test_parse_quantized_topology_from_spec() {
        let topo_str = r#"{"objects":{"example":{"type":"GeometryCollection","geometries":[{"type":"Point","properties":{"prop0":"value0"},"coordinates":[4000,5000]},{"type":"LineString","properties":{"prop0":"value0","prop1":0},"arcs":[0]},{"type":"Polygon","properties":{"prop0":"value0","prop1":{"this":"that"}},"arcs":[[1]]}]}},"type":"Topology","transform":{"translate":[100,0],"scale":[0.0005000500050005,0.00010001000100010001]},"arcs":[[[4000,0],[1999,9999],[2000,-9999],[2000,9999]],[[0,0],[0,9999],[2000,0],[0,-9999],[-2000,0]]]}"#;
        let topo_json = Json::from_str(topo_str).unwrap();
        let topology = match Topology::from_json(&topo_json) {
            Ok(t) => t,
            Err(e) => panic!(e.desc)
        };
        let example = topology.object("example").unwrap();
        let geojson = format!("{}", example.to_json());
        assert_eq!(geojson, r#"{"features":[{"geometry":{"coordinates":[102.0002,0.50005],"type":"Point"},"properties":{"prop0":"value0"},"type":"Feature"},{"geometry":{"coordinates":[[102.0002,0.0],[102.9998,1.0],[103.9999,0.0],[105.0,1.0]],"type":"LineString"},"properties":{"prop0":"value0","prop1":0},"type":"Feature"},{"geometry":{"coordinates":[[[100.0,0.0],[100.0,1.0],[101.0001,1.0],[101.0001,0.0],[100.0,0.0]]],"type":"Polygon"},"properties":{"prop0":"value0","prop1":{"this":"that"}},"type":"Feature"}],"type":"FeatureCollection"}"#);
    }
}
<commit_msg>extract map_arc helper<commit_after>use std::collections::BTreeMap;
use rustc_serialize::json::{Json, ToJson, Object, Array};
use geojson::{FeatureCollection, Feature};
use {TopoJsonResult, TopoJsonError};

pub struct Topology(BTreeMap<String, FeatureCollection>);
// TODO TopoJSON can have any top-level geometry in an object, it
// doesn't have to be a FeatureCollection (which is spelled
// GeometryCollection in TopoJSON).

fn parse_transform(json: &Json) -> TopoJsonResult<(f64, f64, f64, f64)> {
    let transform = expect_object!(json);
    let translate = expect_array!(expect_property!(transform, "translate", "Missing 'translate' field"));
    let scale = expect_array!(expect_property!(transform, "scale", "Missing 'scale' field"));
    match (translate.len(), scale.len()) {
        (2, 2) => (),
        _ => return Err(TopoJsonError::new("Both 'translate' and 'scale' must be two-element arrays"))
    };
    let t0 = expect_f64!(translate[0]);
    let t1 = expect_f64!(translate[1]);
    let s0 = expect_f64!(scale[0]);
    let s1 = expect_f64!(scale[1]);
    return Ok((t0, t1, s0, s1));
}

fn map_arc(arc_json: &Array, tr: (f64, f64, f64, f64)) -> TopoJsonResult<Vec<Json>> {
    let (t0, t1, s0, s1) = tr;
    let mut arc = vec![];
    let mut x = 0;
    let mut y = 0;
    for point_json in arc_json.iter() {
        let mut p = expect_array!(point_json).clone();
        if p.len() < 2 {
            return Err(TopoJsonError::new("Point must have at least two elements"));
        }
        x += expect_i64!(p[0]);
        y += expect_i64!(p[1]);
        p[0] = Json::F64((x as f64) * s0 + t0);
        p[1] = Json::F64((y as f64) * s1 + t1);
        arc.push(Json::Array(p));
    }
    return Ok(arc);
}

struct Space {
    arcs: Vec<Vec<Json>>,
    transform: Option<(f64, f64, f64, f64)>,
}

impl Space {
    fn from_topology(topo: &Object) -> TopoJsonResult<Space> {
        let transform = match topo.get("transform") {
            Some(transform_json) => Some(try!(parse_transform(transform_json))),
            None => None
        };
        let mut arcs = vec![];
        for arc_json in expect_array!(expect_property!(topo, "arcs", "Missing 'arcs' field")).iter() {
            let arc = match transform {
                Some(tr) => try!(map_arc(expect_array!(arc_json), tr)),
                None => expect_array!(arc_json).clone()
            };
            arcs.push(arc);
        }
        return Ok(Space{arcs: arcs, transform: transform});
    }

    pub fn point(&self, p: &Vec<Json>) -> TopoJsonResult<Vec<Json>> {
        if p.len() < 2 {
            return Err(TopoJsonError::new("Point must have at least two elements"));
        }
        let mut rv = p.clone();
        println!("{:?}", self.transform);
        match self.transform {
            Some((t0, t1, s0, s1)) => {
                rv[0] = Json::F64(expect_f64!(rv[0]) * s0 + t0);
                rv[1] = Json::F64(expect_f64!(rv[1]) * s1 + t1);
            },
            None => ()
        };
        return Ok(rv);
    }

    pub fn arc(&self, arc_index: isize) -> TopoJsonResult<Json> {
        let (inverse, offset) = if arc_index > -1 {
            (false, arc_index as usize)
        } else {
            (true, -1 - arc_index as usize)
        };
        if offset >= self.arcs.len() {
            println!("offset = {}", offset);
            return Err(TopoJsonError::new("Arc index too large"));
        }
        let arc = &self.arcs[offset];
        let arcs_json = if inverse {
            arc.iter().rev().map(|p| p.to_json()).collect()
        } else {
            arc.iter().map(|p| p.to_json()).collect()
        };
        return Ok(Json::Array(arcs_json));
    }
}

fn transform_coordinates(topo_coord: &Array, space: &Space) -> TopoJsonResult<Json> {
    return Ok(Json::Array(try!(space.point(topo_coord))));
}

fn transform_arcs(arcs: &Array, space: &Space, depth: usize) -> TopoJsonResult<Json> {
    let mut rv = vec![];
    for arc in arcs.iter() {
        println!("{:?}", arc);
        if depth > 1 {
            rv.push(try!(transform_arcs(expect_array!(arc), space, depth - 1)));
        }
        else {
            rv.push(try!(space.arc(expect_i64!(arc) as isize)));
        }
    }
    if depth == 1 {
        rv = match rv.pop() {
            Some(Json::Array(v)) => v,
            _ => return Err(TopoJsonError::new("Empty arc list"))
        }
    }
    return Ok(Json::Array(rv));
}

fn parse_geometry(object: &Object, space: &Space) -> TopoJsonResult<Feature> {
    let geometry_type = expect_string!(expect_property!(object, "type", "Missing 'type' field"));
    let properties = expect_property!(object, "properties", "Missing 'properties' field");

    let mut geojson_geometry = BTreeMap::new();
    geojson_geometry.insert("type".to_string(), Json::String(geometry_type.to_string()));
    let coordinates = match geometry_type {
        "Point" => {
            let topo_coord = expect_property!(object, "coordinates", "Missing 'coordinates' property");
            try!(transform_coordinates(expect_array!(topo_coord), space))
        },
        "LineString" | "Polygon" => {
            let arcs = expect_property!(object, "arcs", "Missing 'arcs' property");
            println!("arcs! {:?}", arcs);
            let depth = match geometry_type {
                "LineString" => 1,
                "Polygon" => 2,
                _ => unreachable!()
            };
            try!(transform_arcs(expect_array!(arcs), space, depth))
        },
        _ => return Err(TopoJsonError::new("Unknown geometry type"))
    };
    geojson_geometry.insert("coordinates".to_string(), coordinates);

    let mut geojson_feature = BTreeMap::new();
    geojson_feature.insert("type".to_string(), Json::String("Feature".to_string()));
    geojson_feature.insert("geometry".to_string(), Json::Object(geojson_geometry));
    geojson_feature.insert("properties".to_string(), properties.clone());
    println!("feature: {}", Json::Object(geojson_feature.clone()));
    return Ok(Feature::from_json(&geojson_feature));
}

fn parse_geometry_collection(object: &Object, space: &Space) -> TopoJsonResult<FeatureCollection> {
    let geometries = expect_array!(expect_property!(object, "geometries", "Missing 'geometries' field"));
    let mut features = vec![];
    for geometry_json in geometries.iter() {
        features.push(try!(parse_geometry(expect_object!(geometry_json), space)));
    }
    return Ok(FeatureCollection{features: features});
}

impl Topology {
    pub fn from_json(json: &Json) -> TopoJsonResult<Topology> {
        let topo = expect_object!(json);
        match expect_property!(topo, "type", "Missing 'type' field").as_string() {
            Some("Topology") => (),
            _ => return Err(TopoJsonError::new("'type' should be 'Topology'"))
        };
        let space = try!(Space::from_topology(topo));
        let objects = expect_object!(expect_property!(topo, "objects", "Missing 'objects' field"));
        let mut map = BTreeMap::new();
        for (name, value) in objects.iter() {
            let object = expect_object!(value);
            let object_type = expect_string!(expect_property!(object, "type", "Missing 'type' field"));
            let geojson = match object_type {
                "GeometryCollection" => try!(parse_geometry_collection(object, &space)),
                _ => return Err(TopoJsonError::new("'type' should be 'GeometryCollection'"))
            };
            map.insert(name.to_string(), geojson);
        }
        return Ok(Topology(map));
    }

    pub fn object(&self, name: &str) -> Option<&FeatureCollection> {
        let &Topology(ref hash) = self;
        return hash.get(name);
    }
}

#[cfg(test)]
mod tests {
    use rustc_serialize::json::{Json, ToJson};
    use super::Topology;

    #[test]
    fn test_invalid_topo() {
        fn expect_err(json: &str) {
            match Topology::from_json(&Json::from_str(json).unwrap()) {
                Err(_) => (),
                _ => panic!()
            }
        }
        expect_err("{}");
        expect_err(r#"{"type": "foo"}"#);
        expect_err(r#"{"type": "Topology"}"#);
    }

    #[test]
    fn test_parse_simple_topology_from_spec() {
        let topo_str = r#"{"type":"Topology","objects":{"example":{"type":"GeometryCollection","geometries":[{"type":"Point","properties":{"prop0":"value0"},"coordinates":[102,0.5]},{"type":"LineString","properties":{"prop0":"value0","prop1":0},"arcs":[0]},{"type":"Polygon","properties":{"prop0":"value0","prop1":{"this":"that"}},"arcs":[[-2]]}]}},"arcs":[[[102,0],[103,1],[104,0],[105,1]],[[100,0],[101,0],[101,1],[100,1],[100,0]]]}"#;
        let topo_json = Json::from_str(topo_str).unwrap();
        let topology = match Topology::from_json(&topo_json) {
            Ok(t) => t,
            Err(e) => panic!(e.desc)
        };
        let example = topology.object("example").unwrap();
        let geojson = format!("{}", example.to_json());
        assert_eq!(geojson, r#"{"features":[{"geometry":{"coordinates":[102.0,0.5],"type":"Point"},"properties":{"prop0":"value0"},"type":"Feature"},{"geometry":{"coordinates":[[102.0,0.0],[103.0,1.0],[104.0,0.0],[105.0,1.0]],"type":"LineString"},"properties":{"prop0":"value0","prop1":0},"type":"Feature"},{"geometry":{"coordinates":[[[100.0,0.0],[100.0,1.0],[101.0,1.0],[101.0,0.0],[100.0,0.0]]],"type":"Polygon"},"properties":{"prop0":"value0","prop1":{"this":"that"}},"type":"Feature"}],"type":"FeatureCollection"}"#);
    }

    #[test]
    fn test_parse_quantized_topology_from_spec() {
        let topo_str = r#"{"objects":{"example":{"type":"GeometryCollection","geometries":[{"type":"Point","properties":{"prop0":"value0"},"coordinates":[4000,5000]},{"type":"LineString","properties":{"prop0":"value0","prop1":0},"arcs":[0]},{"type":"Polygon","properties":{"prop0":"value0","prop1":{"this":"that"}},"arcs":[[1]]}]}},"type":"Topology","transform":{"translate":[100,0],"scale":[0.0005000500050005,0.00010001000100010001]},"arcs":[[[4000,0],[1999,9999],[2000,-9999],[2000,9999]],[[0,0],[0,9999],[2000,0],[0,-9999],[-2000,0]]]}"#;
        let topo_json = Json::from_str(topo_str).unwrap();
        let topology = match Topology::from_json(&topo_json) {
            Ok(t) => t,
            Err(e) => panic!(e.desc)
        };
        let example = topology.object("example").unwrap();
        let geojson = format!("{}", example.to_json());
        assert_eq!(geojson, r#"{"features":[{"geometry":{"coordinates":[102.0002,0.50005],"type":"Point"},"properties":{"prop0":"value0"},"type":"Feature"},{"geometry":{"coordinates":[[102.0002,0.0],[102.9998,1.0],[103.9999,0.0],[105.0,1.0]],"type":"LineString"},"properties":{"prop0":"value0","prop1":0},"type":"Feature"},{"geometry":{"coordinates":[[[100.0,0.0],[100.0,1.0],[101.0001,1.0],[101.0001,0.0],[100.0,0.0]]],"type":"Polygon"},"properties":{"prop0":"value0","prop1":{"this":"that"}},"type":"Feature"}],"type":"FeatureCollection"}"#);
    }
}
<|endoftext|>


<commit_before>use std::fmt;
use std::fmt::{Show,Formatter};
use std::str;
use std::io::{UserDir,AllPermissions};
use std::io::fs::{mkdir_recursive,rmdir_recursive,chmod};
use serialize::{Encodable,Encoder};

use core::source::{Location, Local, Remote};
use util::{CargoResult, ChainError, ProcessBuilder, process, human};

#[deriving(PartialEq,Clone,Encodable)]
pub enum GitReference {
    Master,
    Other(String)
}

impl GitReference {
    pub fn for_str<S: Str>(string: S) -> GitReference {
        if string.as_slice() == "master" {
            Master
        } else {
            Other(string.as_slice().to_string())
        }
    }
}

impl Str for GitReference {
    fn as_slice<'a>(&'a self) -> &'a str {
        match *self {
            Master => "master",
            Other(ref string) => string.as_slice()
        }
    }
}

impl Show for GitReference {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        self.as_slice().fmt(f)
    }
}

macro_rules! git(
    ($config:expr, $str:expr, $($rest:expr),*) => (
        try!(git_inherit(&$config, format!($str, $($rest),*)))
    );

    ($config:expr, $str:expr) => (
        try!(git_inherit(&$config, format!($str)))
    );
)

macro_rules! git_output(
    ($config:expr, $str:expr, $($rest:expr),*) => ({
        try!(git_output(&$config, format!($str, $($rest),*)))
    });

    ($config:expr, $str:expr) => ({
        try!(git_output(&$config, format!($str)))
    });
)

macro_rules! errln(
    ($($arg:tt)*) => (let _ = writeln!(::std::io::stdio::stderr(), $($arg)*))
)

/// GitRemote represents a remote repository. It gets cloned into a local
/// GitDatabase.
#[deriving(PartialEq,Clone,Show)]
pub struct GitRemote {
    location: Location,
}

#[deriving(PartialEq,Clone,Encodable)]
struct EncodableGitRemote {
    location: String,
}

impl<E, S: Encoder<E>> Encodable<S, E> for GitRemote {
    fn encode(&self, s: &mut S) -> Result<(), E> {
        EncodableGitRemote {
            location: self.location.to_string()
        }.encode(s)
    }
}

/// GitDatabase is a local clone of a remote repository's database. Multiple
/// GitCheckouts can be cloned from this GitDatabase.
#[deriving(PartialEq,Clone)]
pub struct GitDatabase {
    remote: GitRemote,
    path: Path,
}

#[deriving(Encodable)]
pub struct EncodableGitDatabase {
    remote: GitRemote,
    path: String,
}

impl<E, S: Encoder<E>> Encodable<S, E> for GitDatabase {
    fn encode(&self, s: &mut S) -> Result<(), E> {
        EncodableGitDatabase {
            remote: self.remote.clone(),
            path: self.path.display().to_string()
        }.encode(s)
    }
}

/// GitCheckout is a local checkout of a particular revision. Calling
/// `clone_into` with a reference will resolve the reference into a revision,
/// and return a CargoError if no revision for that reference was found.
pub struct GitCheckout {
    database: GitDatabase,
    location: Path,
    reference: GitReference,
    revision: String,
}

#[deriving(Encodable)]
pub struct EncodableGitCheckout {
    database: GitDatabase,
    location: String,
    reference: String,
    revision: String,
}

impl<E, S: Encoder<E>> Encodable<S, E> for GitCheckout {
    fn encode(&self, s: &mut S) -> Result<(), E> {
        EncodableGitCheckout {
            database: self.database.clone(),
            location: self.location.display().to_string(),
            reference: self.reference.to_string(),
            revision: self.revision.to_string()
        }.encode(s)
    }
}

// Implementations

impl GitRemote {
    pub fn new(location: &Location) -> GitRemote {
        GitRemote { location: location.clone() }
    }

    pub fn get_location<'a>(&'a self) -> &'a Location {
        &self.location
    }

    pub fn has_ref<S: Str>(&self, path: &Path, reference: S) -> CargoResult<()> {
        git_output!(*path, "rev-parse {}", reference.as_slice());
        Ok(())
    }

    pub fn checkout(&self, into: &Path) -> CargoResult<GitDatabase> {
        if into.exists() {
            try!(self.fetch_into(into));
        } else {
            try!(self.clone_into(into));
        }

        Ok(GitDatabase { remote: self.clone(), path: into.clone() })
    }

    pub fn db_at(&self, db_path: &Path) -> GitDatabase {
        GitDatabase { remote: self.clone(), path: db_path.clone() }
    }

    fn fetch_into(&self, path: &Path) -> CargoResult<()> {
        Ok(git!(*path, "fetch --force --quiet --tags {} \
                        refs/heads/*:refs/heads/*", self.fetch_location()))
    }

    fn clone_into(&self, path: &Path) -> CargoResult<()> {
        let dirname = Path::new(path.dirname());

        try!(mkdir_recursive(path, UserDir));

        Ok(git!(dirname, "clone {} {} --bare --no-hardlinks --quiet",
                self.fetch_location(), path.display()))
    }

    fn fetch_location(&self) -> String {
        match self.location {
            Local(ref p) => p.display().to_string(),
            Remote(ref u) => u.to_string(),
        }
    }
}

impl GitDatabase {
    fn get_path<'a>(&'a self) -> &'a Path {
        &self.path
    }

    pub fn copy_to<S: Str>(&self, reference: S,
                           dest: &Path) -> CargoResult<GitCheckout> {
        let checkout = try!(GitCheckout::clone_into(dest, self.clone(),
                                  GitReference::for_str(reference.as_slice())));

        try!(checkout.fetch());
        try!(checkout.update_submodules());

        Ok(checkout)
    }

    pub fn rev_for<S: Str>(&self, reference: S) -> CargoResult<String> {
        Ok(git_output!(self.path, "rev-parse {}", reference.as_slice()))
    }

}

impl GitCheckout {
    fn clone_into(into: &Path, database: GitDatabase,
                  reference: GitReference) -> CargoResult<GitCheckout> {
        let revision = try!(database.rev_for(reference.as_slice()));
        let checkout = GitCheckout {
            location: into.clone(),
            database: database,
            reference: reference,
            revision: revision,
        };

        // If the git checkout already exists, we don't need to clone it again
        if !checkout.location.join(".git").exists() {
            try!(checkout.clone_repo());
        }

        Ok(checkout)
    }

    fn get_source<'a>(&'a self) -> &'a Path {
        self.database.get_path()
    }

    fn clone_repo(&self) -> CargoResult<()> {
        let dirname = Path::new(self.location.dirname());

        try!(mkdir_recursive(&dirname, UserDir).chain_error(|| {
            human(format!("Couldn't mkdir {}",
                          Path::new(self.location.dirname()).display()))
        }));

        if self.location.exists() {
            try!(rmdir_recursive(&self.location).chain_error(|| {
                human(format!("Couldn't rmdir {}",
                              Path::new(&self.location).display()))
            }));
        }

        git!(dirname, "clone --no-checkout --quiet {} {}",
             self.get_source().display(), self.location.display());
        try!(chmod(&self.location, AllPermissions));

        Ok(())
    }

    fn fetch(&self) -> CargoResult<()> {
        // In git 1.8, apparently --tags explicitly *only* fetches tags, it does
        // not fetch anything else. In git 1.9, however, git apparently fetches
        // everything when --tags is passed.
        //
        // This means that if we want to fetch everything we need to execute
        // both with and without --tags on 1.8 (apparently), and only with
        // --tags on 1.9. For simplicity, we execute with and without --tags for
        // all gits.
        //
        // FIXME: This is suspicious. I have been informed that, for example,
        //        bundler does not do this, yet bundler appears to work!
        //
        // And to continue the fun, git before 1.7.3 had the fun bug that if a
        // branch was tracking a remote, then `git fetch $url` doesn't work!
        //
        // For details, see
        // https://www.kernel.org/pub/software/scm/git/docs/RelNotes-1.7.3.txt
        //
        // In this case we just use `origin` here instead of the database path.
        git!(self.location, "fetch --force --quiet origin");
        git!(self.location, "fetch --force --quiet --tags origin");
        try!(self.reset(self.revision.as_slice()));
        Ok(())
    }

    fn reset<T: Show>(&self, revision: T) -> CargoResult<()> {
        Ok(git!(self.location, "reset -q --hard {}", revision))
    }

    fn update_submodules(&self) -> CargoResult<()> {
        Ok(git!(self.location, "submodule update --init --recursive --quiet"))
    }
}

fn git(path: &Path, str: &str) -> ProcessBuilder {
    debug!("Executing git {} @ {}", str, path.display());

    process("git").args(str.split(' ').collect::<Vec<&str>>().as_slice())
                  .cwd(path.clone())
}

fn git_inherit(path: &Path, str: String) -> CargoResult<()> {
    git(path, str.as_slice()).exec().chain_error(|| {
        human(format!("Executing `git {}` failed", str))
    })
}

fn git_output(path: &Path, str: String) -> CargoResult<String> {
    let output = try!(git(path, str.as_slice()).exec_with_output()
                                                     .chain_error(||
        human(format!("Executing `git {}` failed", str))));

    Ok(to_str(output.output.as_slice()).as_slice().trim_right().to_string())
}

fn to_str(vec: &[u8]) -> String {
    str::from_utf8_lossy(vec).to_string()
}

<commit_msg>auto merge of #57 : ebfe/cargo/git-chmod, r=alexcrichton<commit_after>use std::fmt;
use std::fmt::{Show,Formatter};
use std::str;
use std::io::{UserDir};
use std::io::fs::{mkdir_recursive,rmdir_recursive};
use serialize::{Encodable,Encoder};

use core::source::{Location, Local, Remote};
use util::{CargoResult, ChainError, ProcessBuilder, process, human};

#[deriving(PartialEq,Clone,Encodable)]
pub enum GitReference {
    Master,
    Other(String)
}

impl GitReference {
    pub fn for_str<S: Str>(string: S) -> GitReference {
        if string.as_slice() == "master" {
            Master
        } else {
            Other(string.as_slice().to_string())
        }
    }
}

impl Str for GitReference {
    fn as_slice<'a>(&'a self) -> &'a str {
        match *self {
            Master => "master",
            Other(ref string) => string.as_slice()
        }
    }
}

impl Show for GitReference {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        self.as_slice().fmt(f)
    }
}

macro_rules! git(
    ($config:expr, $str:expr, $($rest:expr),*) => (
        try!(git_inherit(&$config, format!($str, $($rest),*)))
    );

    ($config:expr, $str:expr) => (
        try!(git_inherit(&$config, format!($str)))
    );
)

macro_rules! git_output(
    ($config:expr, $str:expr, $($rest:expr),*) => ({
        try!(git_output(&$config, format!($str, $($rest),*)))
    });

    ($config:expr, $str:expr) => ({
        try!(git_output(&$config, format!($str)))
    });
)

macro_rules! errln(
    ($($arg:tt)*) => (let _ = writeln!(::std::io::stdio::stderr(), $($arg)*))
)

/// GitRemote represents a remote repository. It gets cloned into a local
/// GitDatabase.
#[deriving(PartialEq,Clone,Show)]
pub struct GitRemote {
    location: Location,
}

#[deriving(PartialEq,Clone,Encodable)]
struct EncodableGitRemote {
    location: String,
}

impl<E, S: Encoder<E>> Encodable<S, E> for GitRemote {
    fn encode(&self, s: &mut S) -> Result<(), E> {
        EncodableGitRemote {
            location: self.location.to_string()
        }.encode(s)
    }
}

/// GitDatabase is a local clone of a remote repository's database. Multiple
/// GitCheckouts can be cloned from this GitDatabase.
#[deriving(PartialEq,Clone)]
pub struct GitDatabase {
    remote: GitRemote,
    path: Path,
}

#[deriving(Encodable)]
pub struct EncodableGitDatabase {
    remote: GitRemote,
    path: String,
}

impl<E, S: Encoder<E>> Encodable<S, E> for GitDatabase {
    fn encode(&self, s: &mut S) -> Result<(), E> {
        EncodableGitDatabase {
            remote: self.remote.clone(),
            path: self.path.display().to_string()
        }.encode(s)
    }
}

/// GitCheckout is a local checkout of a particular revision. Calling
/// `clone_into` with a reference will resolve the reference into a revision,
/// and return a CargoError if no revision for that reference was found.
pub struct GitCheckout {
    database: GitDatabase,
    location: Path,
    reference: GitReference,
    revision: String,
}

#[deriving(Encodable)]
pub struct EncodableGitCheckout {
    database: GitDatabase,
    location: String,
    reference: String,
    revision: String,
}

impl<E, S: Encoder<E>> Encodable<S, E> for GitCheckout {
    fn encode(&self, s: &mut S) -> Result<(), E> {
        EncodableGitCheckout {
            database: self.database.clone(),
            location: self.location.display().to_string(),
            reference: self.reference.to_string(),
            revision: self.revision.to_string()
        }.encode(s)
    }
}

// Implementations

impl GitRemote {
    pub fn new(location: &Location) -> GitRemote {
        GitRemote { location: location.clone() }
    }

    pub fn get_location<'a>(&'a self) -> &'a Location {
        &self.location
    }

    pub fn has_ref<S: Str>(&self, path: &Path, reference: S) -> CargoResult<()> {
        git_output!(*path, "rev-parse {}", reference.as_slice());
        Ok(())
    }

    pub fn checkout(&self, into: &Path) -> CargoResult<GitDatabase> {
        if into.exists() {
            try!(self.fetch_into(into));
        } else {
            try!(self.clone_into(into));
        }

        Ok(GitDatabase { remote: self.clone(), path: into.clone() })
    }

    pub fn db_at(&self, db_path: &Path) -> GitDatabase {
        GitDatabase { remote: self.clone(), path: db_path.clone() }
    }

    fn fetch_into(&self, path: &Path) -> CargoResult<()> {
        Ok(git!(*path, "fetch --force --quiet --tags {} \
                        refs/heads/*:refs/heads/*", self.fetch_location()))
    }

    fn clone_into(&self, path: &Path) -> CargoResult<()> {
        let dirname = Path::new(path.dirname());

        try!(mkdir_recursive(path, UserDir));

        Ok(git!(dirname, "clone {} {} --bare --no-hardlinks --quiet",
                self.fetch_location(), path.display()))
    }

    fn fetch_location(&self) -> String {
        match self.location {
            Local(ref p) => p.display().to_string(),
            Remote(ref u) => u.to_string(),
        }
    }
}

impl GitDatabase {
    fn get_path<'a>(&'a self) -> &'a Path {
        &self.path
    }

    pub fn copy_to<S: Str>(&self, reference: S,
                           dest: &Path) -> CargoResult<GitCheckout> {
        let checkout = try!(GitCheckout::clone_into(dest, self.clone(),
                                  GitReference::for_str(reference.as_slice())));

        try!(checkout.fetch());
        try!(checkout.update_submodules());

        Ok(checkout)
    }

    pub fn rev_for<S: Str>(&self, reference: S) -> CargoResult<String> {
        Ok(git_output!(self.path, "rev-parse {}", reference.as_slice()))
    }

}

impl GitCheckout {
    fn clone_into(into: &Path, database: GitDatabase,
                  reference: GitReference) -> CargoResult<GitCheckout> {
        let revision = try!(database.rev_for(reference.as_slice()));
        let checkout = GitCheckout {
            location: into.clone(),
            database: database,
            reference: reference,
            revision: revision,
        };

        // If the git checkout already exists, we don't need to clone it again
        if !checkout.location.join(".git").exists() {
            try!(checkout.clone_repo());
        }

        Ok(checkout)
    }

    fn get_source<'a>(&'a self) -> &'a Path {
        self.database.get_path()
    }

    fn clone_repo(&self) -> CargoResult<()> {
        let dirname = Path::new(self.location.dirname());

        try!(mkdir_recursive(&dirname, UserDir).chain_error(|| {
            human(format!("Couldn't mkdir {}",
                          Path::new(self.location.dirname()).display()))
        }));

        if self.location.exists() {
            try!(rmdir_recursive(&self.location).chain_error(|| {
                human(format!("Couldn't rmdir {}",
                              Path::new(&self.location).display()))
            }));
        }

        git!(dirname, "clone --no-checkout --quiet {} {}",
             self.get_source().display(), self.location.display());

        Ok(())
    }

    fn fetch(&self) -> CargoResult<()> {
        // In git 1.8, apparently --tags explicitly *only* fetches tags, it does
        // not fetch anything else. In git 1.9, however, git apparently fetches
        // everything when --tags is passed.
        //
        // This means that if we want to fetch everything we need to execute
        // both with and without --tags on 1.8 (apparently), and only with
        // --tags on 1.9. For simplicity, we execute with and without --tags for
        // all gits.
        //
        // FIXME: This is suspicious. I have been informed that, for example,
        //        bundler does not do this, yet bundler appears to work!
        //
        // And to continue the fun, git before 1.7.3 had the fun bug that if a
        // branch was tracking a remote, then `git fetch $url` doesn't work!
        //
        // For details, see
        // https://www.kernel.org/pub/software/scm/git/docs/RelNotes-1.7.3.txt
        //
        // In this case we just use `origin` here instead of the database path.
        git!(self.location, "fetch --force --quiet origin");
        git!(self.location, "fetch --force --quiet --tags origin");
        try!(self.reset(self.revision.as_slice()));
        Ok(())
    }

    fn reset<T: Show>(&self, revision: T) -> CargoResult<()> {
        Ok(git!(self.location, "reset -q --hard {}", revision))
    }

    fn update_submodules(&self) -> CargoResult<()> {
        Ok(git!(self.location, "submodule update --init --recursive --quiet"))
    }
}

fn git(path: &Path, str: &str) -> ProcessBuilder {
    debug!("Executing git {} @ {}", str, path.display());

    process("git").args(str.split(' ').collect::<Vec<&str>>().as_slice())
                  .cwd(path.clone())
}

fn git_inherit(path: &Path, str: String) -> CargoResult<()> {
    git(path, str.as_slice()).exec().chain_error(|| {
        human(format!("Executing `git {}` failed", str))
    })
}

fn git_output(path: &Path, str: String) -> CargoResult<String> {
    let output = try!(git(path, str.as_slice()).exec_with_output()
                                                     .chain_error(||
        human(format!("Executing `git {}` failed", str))));

    Ok(to_str(output.output.as_slice()).as_slice().trim_right().to_string())
}

fn to_str(vec: &[u8]) -> String {
    str::from_utf8_lossy(vec).to_string()
}

<|endoftext|>


<commit_before>use std::sync::Arc;

use eew::EEW;
use condition::Condition;

pub struct DisjunctiveCondition<C> {
	clauses: Vec<C>,
}

impl<C> DisjunctiveCondition<C> {

	fn new(clauses: Vec<C>) -> DisjunctiveCondition<C>
	{
		DisjunctiveCondition { clauses: clauses }
	}
}

impl<C> Condition for DisjunctiveCondition<C> where C: Condition {

	fn is_satisfied(&self, latest: &Arc<EEW>, eews: &[Arc<EEW>]) -> bool
	{
		self.clauses.iter().any(|c| c.is_satisfied(latest, eews))
	}
}
<commit_msg>DisjunctiveCondition: Implement From<Vec<C>><commit_after>use std::sync::Arc;

use eew::EEW;
use condition::Condition;

pub struct DisjunctiveCondition<C> {
	clauses: Vec<C>,
}

impl<C> DisjunctiveCondition<C> {

	fn new(clauses: Vec<C>) -> DisjunctiveCondition<C>
	{
		DisjunctiveCondition { clauses: clauses }
	}
}

impl<C> Condition for DisjunctiveCondition<C> where C: Condition {

	fn is_satisfied(&self, latest: &Arc<EEW>, eews: &[Arc<EEW>]) -> bool
	{
		self.clauses.iter().any(|c| c.is_satisfied(latest, eews))
	}
}

impl<C> From<Vec<C>> for DisjunctiveCondition<C> {

	fn from(clauses: Vec<C>) -> DisjunctiveCondition<C>
	{
		DisjunctiveCondition::new(clauses)
	}
}
<|endoftext|>


<commit_before>use rustc_serialize;
use std::mem;

use super::{Decoder, DecodeError};
use super::DecodeErrorKind::*;
use Value;

impl rustc_serialize::Decoder for Decoder {
    type Error = DecodeError;
    fn read_nil(&mut self) -> Result<(), DecodeError> {
        match self.toml {
            Some(Value::String(ref s)) if s.len() == 0 => {}
            Some(Value::String(..)) => return Err(self.err(NilTooLong)),
            ref found => return Err(self.mismatch("string", found)),
        }
        self.toml.take();
        Ok(())
    }
    fn read_usize(&mut self) -> Result<usize, DecodeError> {
        self.read_i64().map(|i| i as usize)
    }
    fn read_u64(&mut self) -> Result<u64, DecodeError> {
        self.read_i64().map(|i| i as u64)
    }
    fn read_u32(&mut self) -> Result<u32, DecodeError> {
        self.read_i64().map(|i| i as u32)
    }
    fn read_u16(&mut self) -> Result<u16, DecodeError> {
        self.read_i64().map(|i| i as u16)
    }
    fn read_u8(&mut self) -> Result<u8, DecodeError> {
        self.read_i64().map(|i| i as u8)
    }
    fn read_isize(&mut self) -> Result<isize, DecodeError> {
        self.read_i64().map(|i| i as isize)
    }
    fn read_i64(&mut self) -> Result<i64, DecodeError> {
        match self.toml {
            Some(Value::Integer(i)) => { self.toml.take(); Ok(i) }
            ref found => Err(self.mismatch("integer", found)),
        }
    }
    fn read_i32(&mut self) -> Result<i32, DecodeError> {
        self.read_i64().map(|i| i as i32)
    }
    fn read_i16(&mut self) -> Result<i16, DecodeError> {
        self.read_i64().map(|i| i as i16)
    }
    fn read_i8(&mut self) -> Result<i8, DecodeError> {
        self.read_i64().map(|i| i as i8)
    }
    fn read_bool(&mut self) -> Result<bool, DecodeError> {
        match self.toml {
            Some(Value::Boolean(b)) => { self.toml.take(); Ok(b) }
            ref found => Err(self.mismatch("bool", found)),
        }
    }
    fn read_f64(&mut self) -> Result<f64, DecodeError> {
        match self.toml {
            Some(Value::Float(f)) => Ok(f),
            ref found => Err(self.mismatch("float", found)),
        }
    }
    fn read_f32(&mut self) -> Result<f32, DecodeError> {
        self.read_f64().map(|f| f as f32)
    }
    fn read_char(&mut self) -> Result<char, DecodeError> {
        let ch = match self.toml {
            Some(Value::String(ref s)) if s.chars().count() == 1 =>
                s.chars().next().unwrap(),
            ref found => return Err(self.mismatch("string", found)),
        };
        self.toml.take();
        Ok(ch)
    }
    fn read_str(&mut self) -> Result<String, DecodeError> {
        match self.toml.take() {
            Some(Value::String(s)) => Ok(s),
            found => {
                let err = Err(self.mismatch("string", &found));
                self.toml = found;
                err
            }
        }
    }

    // Compound types:
    fn read_enum<T, F>(&mut self, _name: &str, f: F)
        -> Result<T, DecodeError>
        where F: FnOnce(&mut Decoder) -> Result<T, DecodeError>
    {
        f(self)
    }

    fn read_enum_variant<T, F>(&mut self, names: &[&str], mut f: F)
        -> Result<T, DecodeError>
        where F: FnMut(&mut Decoder, usize) -> Result<T, DecodeError>
    {
        let mut first_error = None;
        for i in 0..names.len() {
            let mut d = self.sub_decoder(self.toml.clone(), "");
            match f(&mut d, i) {
                Ok(t) => { self.toml = d.toml; return Ok(t) }
                Err(e) => {
                    if first_error.is_none() {
                        first_error = Some(e);
                    }
                }
            }
        }
        Err(first_error.unwrap_or_else(|| self.err(NoEnumVariants)))
    }
    fn read_enum_variant_arg<T, F>(&mut self, _a_idx: usize, f: F)
        -> Result<T, DecodeError>
        where F: FnOnce(&mut Decoder) -> Result<T, DecodeError>
    {
        f(self)
    }

    fn read_enum_struct_variant<T, F>(&mut self, _names: &[&str], _f: F)
        -> Result<T, DecodeError>
        where F: FnMut(&mut Decoder, usize) -> Result<T, DecodeError>
    {
        panic!()
    }
    fn read_enum_struct_variant_field<T, F>(&mut self,
                                            _f_name: &str,
                                            _f_idx: usize,
                                            _f: F)
        -> Result<T, DecodeError>
        where F: FnOnce(&mut Decoder) -> Result<T, DecodeError>
    {
        panic!()
    }

    fn read_struct<T, F>(&mut self, _s_name: &str, _len: usize, f: F)
        -> Result<T, DecodeError>
        where F: FnOnce(&mut Decoder) -> Result<T, DecodeError>
    {
        match self.toml {
            Some(Value::Table(..)) => {
                let ret = try!(f(self));
                match self.toml {
                    Some(Value::Table(ref t)) if t.len() == 0 => {}
                    _ => return Ok(ret)
                }
                self.toml.take();
                Ok(ret)
            }
            ref found => Err(self.mismatch("table", found)),
        }
    }
    fn read_struct_field<T, F>(&mut self, f_name: &str, _f_idx: usize, f: F)
        -> Result<T, DecodeError>
        where F: FnOnce(&mut Decoder) -> Result<T, DecodeError>
    {
        let field = format!("{}", f_name);
        let toml = match self.toml {
            Some(Value::Table(ref mut table)) => {
                table.remove(&field)
                    .or_else(|| table.remove(&f_name.replace("_", "-")))
            },
            ref found => return Err(self.mismatch("table", found)),
        };
        let mut d = self.sub_decoder(toml, f_name);
        let ret = try!(f(&mut d));
        if let Some(value) = d.toml {
            if let Some(Value::Table(ref mut table)) = self.toml {
                table.insert(field, value);
            }
        }
        Ok(ret)
    }

    fn read_tuple<T, F>(&mut self, tuple_len: usize, f: F)
        -> Result<T, DecodeError>
        where F: FnOnce(&mut Decoder) -> Result<T, DecodeError>
    {
        self.read_seq(move |d, len| {
            assert!(len == tuple_len,
                    "expected tuple of length `{}`, found tuple \
                         of length `{}`", tuple_len, len);
            f(d)
        })
    }
    fn read_tuple_arg<T, F>(&mut self, a_idx: usize, f: F)
        -> Result<T, DecodeError>
        where F: FnOnce(&mut Decoder) -> Result<T, DecodeError>
    {
        self.read_seq_elt(a_idx, f)
    }

    fn read_tuple_struct<T, F>(&mut self, _s_name: &str, _len: usize, _f: F)
        -> Result<T, DecodeError>
        where F: FnOnce(&mut Decoder) -> Result<T, DecodeError>
    {
        panic!()
    }
    fn read_tuple_struct_arg<T, F>(&mut self, _a_idx: usize, _f: F)
        -> Result<T, DecodeError>
        where F: FnOnce(&mut Decoder) -> Result<T, DecodeError>
    {
        panic!()
    }

    // Specialized types:
    fn read_option<T, F>(&mut self, mut f: F)
        -> Result<T, DecodeError>
        where F: FnMut(&mut Decoder, bool) -> Result<T, DecodeError>
    {
        match self.toml {
            Some(..) => f(self, true),
            None => f(self, false),
        }
    }

    fn read_seq<T, F>(&mut self, f: F) -> Result<T, DecodeError>
        where F: FnOnce(&mut Decoder, usize) -> Result<T, DecodeError>
    {
        let len = match self.toml {
            Some(Value::Array(ref arr)) => arr.len(),
            None => 0,
            ref found => return Err(self.mismatch("array", found)),
        };
        let ret = try!(f(self, len));
        match self.toml {
            Some(Value::Array(ref mut arr)) => {
                arr.retain(|slot| slot.as_integer() != Some(0));
                if arr.len() != 0 { return Ok(ret) }
            }
            _ => return Ok(ret)
        }
        self.toml.take();
        Ok(ret)
    }
    fn read_seq_elt<T, F>(&mut self, idx: usize, f: F)
        -> Result<T, DecodeError>
        where F: FnOnce(&mut Decoder) -> Result<T, DecodeError>
    {
        let toml = match self.toml {
            Some(Value::Array(ref mut arr)) => {
                mem::replace(&mut arr[idx], Value::Integer(0))
            }
            ref found => return Err(self.mismatch("array", found)),
        };
        let mut d = self.sub_decoder(Some(toml), "");
        let ret = try!(f(&mut d));
        match d.toml {
            Some(toml) => match self.toml {
                Some(Value::Array(ref mut arr)) => arr[idx] = toml,
                _ => {}
            },
            _ => {}
        }
        Ok(ret)
    }

    fn read_map<T, F>(&mut self, f: F)
        -> Result<T, DecodeError>
        where F: FnOnce(&mut Decoder, usize) -> Result<T, DecodeError>
    {
        let len = match self.toml {
            Some(Value::Table(ref table)) => table.len(),
            ref found => return Err(self.mismatch("table", found)),
        };
        let ret = try!(f(self, len));
        self.toml.take();
        Ok(ret)
    }
    fn read_map_elt_key<T, F>(&mut self, idx: usize, f: F)
        -> Result<T, DecodeError>
        where F: FnOnce(&mut Decoder) -> Result<T, DecodeError>
    {
        match self.toml {
            Some(Value::Table(ref table)) => {
                match table.iter().skip(idx).next() {
                    Some((key, _)) => {
                        let val = Value::String(format!("{}", key));
                        f(&mut self.sub_decoder(Some(val), &**key))
                    }
                    None => Err(self.err(ExpectedMapKey(idx))),
                }
            }
            ref found => Err(self.mismatch("table", found)),
        }
    }
    fn read_map_elt_val<T, F>(&mut self, idx: usize, f: F)
        -> Result<T, DecodeError>
        where F: FnOnce(&mut Decoder) -> Result<T, DecodeError>
    {
        match self.toml {
            Some(Value::Table(ref table)) => {
                match table.iter().skip(idx).next() {
                    Some((_, value)) => {
                        // XXX: this shouldn't clone
                        f(&mut self.sub_decoder(Some(value.clone()), ""))
                    }
                    None => Err(self.err(ExpectedMapElement(idx))),
                }
            }
            ref found => Err(self.mismatch("table", found)),
        }
    }

    fn error(&mut self, err: &str) -> DecodeError {
        DecodeError {
            field: self.cur_field.clone(),
            kind: ApplicationError(format!("{}", err))
        }
    }
}
<commit_msg>Improve the error message in failing enums<commit_after>use rustc_serialize;
use std::mem;

use super::{Decoder, DecodeError};
use super::DecodeErrorKind::*;
use Value;

impl rustc_serialize::Decoder for Decoder {
    type Error = DecodeError;
    fn read_nil(&mut self) -> Result<(), DecodeError> {
        match self.toml {
            Some(Value::String(ref s)) if s.len() == 0 => {}
            Some(Value::String(..)) => return Err(self.err(NilTooLong)),
            ref found => return Err(self.mismatch("string", found)),
        }
        self.toml.take();
        Ok(())
    }
    fn read_usize(&mut self) -> Result<usize, DecodeError> {
        self.read_i64().map(|i| i as usize)
    }
    fn read_u64(&mut self) -> Result<u64, DecodeError> {
        self.read_i64().map(|i| i as u64)
    }
    fn read_u32(&mut self) -> Result<u32, DecodeError> {
        self.read_i64().map(|i| i as u32)
    }
    fn read_u16(&mut self) -> Result<u16, DecodeError> {
        self.read_i64().map(|i| i as u16)
    }
    fn read_u8(&mut self) -> Result<u8, DecodeError> {
        self.read_i64().map(|i| i as u8)
    }
    fn read_isize(&mut self) -> Result<isize, DecodeError> {
        self.read_i64().map(|i| i as isize)
    }
    fn read_i64(&mut self) -> Result<i64, DecodeError> {
        match self.toml {
            Some(Value::Integer(i)) => { self.toml.take(); Ok(i) }
            ref found => Err(self.mismatch("integer", found)),
        }
    }
    fn read_i32(&mut self) -> Result<i32, DecodeError> {
        self.read_i64().map(|i| i as i32)
    }
    fn read_i16(&mut self) -> Result<i16, DecodeError> {
        self.read_i64().map(|i| i as i16)
    }
    fn read_i8(&mut self) -> Result<i8, DecodeError> {
        self.read_i64().map(|i| i as i8)
    }
    fn read_bool(&mut self) -> Result<bool, DecodeError> {
        match self.toml {
            Some(Value::Boolean(b)) => { self.toml.take(); Ok(b) }
            ref found => Err(self.mismatch("bool", found)),
        }
    }
    fn read_f64(&mut self) -> Result<f64, DecodeError> {
        match self.toml {
            Some(Value::Float(f)) => Ok(f),
            ref found => Err(self.mismatch("float", found)),
        }
    }
    fn read_f32(&mut self) -> Result<f32, DecodeError> {
        self.read_f64().map(|f| f as f32)
    }
    fn read_char(&mut self) -> Result<char, DecodeError> {
        let ch = match self.toml {
            Some(Value::String(ref s)) if s.chars().count() == 1 =>
                s.chars().next().unwrap(),
            ref found => return Err(self.mismatch("string", found)),
        };
        self.toml.take();
        Ok(ch)
    }
    fn read_str(&mut self) -> Result<String, DecodeError> {
        match self.toml.take() {
            Some(Value::String(s)) => Ok(s),
            found => {
                let err = Err(self.mismatch("string", &found));
                self.toml = found;
                err
            }
        }
    }

    // Compound types:
    fn read_enum<T, F>(&mut self, _name: &str, f: F)
        -> Result<T, DecodeError>
        where F: FnOnce(&mut Decoder) -> Result<T, DecodeError>
    {
        f(self)
    }

    fn read_enum_variant<T, F>(&mut self, names: &[&str], mut f: F)
        -> Result<T, DecodeError>
        where F: FnMut(&mut Decoder, usize) -> Result<T, DecodeError>
    {
        // When decoding enums, this crate takes the strategy of trying to
        // decode the current TOML as all of the possible variants, returning
        // success on the first one that succeeds.
        //
        // Note that fidelity of the errors returned here is a little nebulous,
        // but we try to return the error that had the relevant field as the
        // longest field. This way we hopefully match an error against what was
        // most likely being written down without losing too much info.
        let mut first_error = None::<DecodeError>;
        for i in 0..names.len() {
            let mut d = self.sub_decoder(self.toml.clone(), "");
            match f(&mut d, i) {
                Ok(t) => {
                    self.toml = d.toml;
                    return Ok(t)
                }
                Err(e) => {
                    if let Some(ref first) = first_error {
                        let my_len = e.field.as_ref().map(|s| s.len());
                        let first_len = first.field.as_ref().map(|s| s.len());
                        if my_len <= first_len {
                            continue
                        }
                    }
                    first_error = Some(e);
                }
            }
        }
        Err(first_error.unwrap_or_else(|| self.err(NoEnumVariants)))
    }
    fn read_enum_variant_arg<T, F>(&mut self, _a_idx: usize, f: F)
        -> Result<T, DecodeError>
        where F: FnOnce(&mut Decoder) -> Result<T, DecodeError>
    {
        f(self)
    }

    fn read_enum_struct_variant<T, F>(&mut self, _names: &[&str], _f: F)
        -> Result<T, DecodeError>
        where F: FnMut(&mut Decoder, usize) -> Result<T, DecodeError>
    {
        panic!()
    }
    fn read_enum_struct_variant_field<T, F>(&mut self,
                                            _f_name: &str,
                                            _f_idx: usize,
                                            _f: F)
        -> Result<T, DecodeError>
        where F: FnOnce(&mut Decoder) -> Result<T, DecodeError>
    {
        panic!()
    }

    fn read_struct<T, F>(&mut self, _s_name: &str, _len: usize, f: F)
        -> Result<T, DecodeError>
        where F: FnOnce(&mut Decoder) -> Result<T, DecodeError>
    {
        match self.toml {
            Some(Value::Table(..)) => {
                let ret = try!(f(self));
                match self.toml {
                    Some(Value::Table(ref t)) if t.len() == 0 => {}
                    _ => return Ok(ret)
                }
                self.toml.take();
                Ok(ret)
            }
            ref found => Err(self.mismatch("table", found)),
        }
    }
    fn read_struct_field<T, F>(&mut self, f_name: &str, _f_idx: usize, f: F)
        -> Result<T, DecodeError>
        where F: FnOnce(&mut Decoder) -> Result<T, DecodeError>
    {
        let field = format!("{}", f_name);
        let toml = match self.toml {
            Some(Value::Table(ref mut table)) => {
                table.remove(&field)
                     .or_else(|| table.remove(&f_name.replace("_", "-")))
            },
            ref found => return Err(self.mismatch("table", found)),
        };
        let mut d = self.sub_decoder(toml, f_name);
        let ret = try!(f(&mut d));
        if let Some(value) = d.toml {
            if let Some(Value::Table(ref mut table)) = self.toml {
                table.insert(field, value);
            }
        }
        Ok(ret)
    }

    fn read_tuple<T, F>(&mut self, tuple_len: usize, f: F)
        -> Result<T, DecodeError>
        where F: FnOnce(&mut Decoder) -> Result<T, DecodeError>
    {
        self.read_seq(move |d, len| {
            assert!(len == tuple_len,
                    "expected tuple of length `{}`, found tuple \
                         of length `{}`", tuple_len, len);
            f(d)
        })
    }
    fn read_tuple_arg<T, F>(&mut self, a_idx: usize, f: F)
        -> Result<T, DecodeError>
        where F: FnOnce(&mut Decoder) -> Result<T, DecodeError>
    {
        self.read_seq_elt(a_idx, f)
    }

    fn read_tuple_struct<T, F>(&mut self, _s_name: &str, _len: usize, _f: F)
        -> Result<T, DecodeError>
        where F: FnOnce(&mut Decoder) -> Result<T, DecodeError>
    {
        panic!()
    }
    fn read_tuple_struct_arg<T, F>(&mut self, _a_idx: usize, _f: F)
        -> Result<T, DecodeError>
        where F: FnOnce(&mut Decoder) -> Result<T, DecodeError>
    {
        panic!()
    }

    // Specialized types:
    fn read_option<T, F>(&mut self, mut f: F)
        -> Result<T, DecodeError>
        where F: FnMut(&mut Decoder, bool) -> Result<T, DecodeError>
    {
        match self.toml {
            Some(..) => f(self, true),
            None => f(self, false),
        }
    }

    fn read_seq<T, F>(&mut self, f: F) -> Result<T, DecodeError>
        where F: FnOnce(&mut Decoder, usize) -> Result<T, DecodeError>
    {
        let len = match self.toml {
            Some(Value::Array(ref arr)) => arr.len(),
            None => 0,
            ref found => return Err(self.mismatch("array", found)),
        };
        let ret = try!(f(self, len));
        match self.toml {
            Some(Value::Array(ref mut arr)) => {
                arr.retain(|slot| slot.as_integer() != Some(0));
                if arr.len() != 0 { return Ok(ret) }
            }
            _ => return Ok(ret)
        }
        self.toml.take();
        Ok(ret)
    }
    fn read_seq_elt<T, F>(&mut self, idx: usize, f: F)
        -> Result<T, DecodeError>
        where F: FnOnce(&mut Decoder) -> Result<T, DecodeError>
    {
        let toml = match self.toml {
            Some(Value::Array(ref mut arr)) => {
                mem::replace(&mut arr[idx], Value::Integer(0))
            }
            ref found => return Err(self.mismatch("array", found)),
        };
        let mut d = self.sub_decoder(Some(toml), "");
        let ret = try!(f(&mut d));
        match d.toml {
            Some(toml) => match self.toml {
                Some(Value::Array(ref mut arr)) => arr[idx] = toml,
                _ => {}
            },
            _ => {}
        }
        Ok(ret)
    }

    fn read_map<T, F>(&mut self, f: F)
        -> Result<T, DecodeError>
        where F: FnOnce(&mut Decoder, usize) -> Result<T, DecodeError>
    {
        let len = match self.toml {
            Some(Value::Table(ref table)) => table.len(),
            ref found => return Err(self.mismatch("table", found)),
        };
        let ret = try!(f(self, len));
        self.toml.take();
        Ok(ret)
    }
    fn read_map_elt_key<T, F>(&mut self, idx: usize, f: F)
        -> Result<T, DecodeError>
        where F: FnOnce(&mut Decoder) -> Result<T, DecodeError>
    {
        match self.toml {
            Some(Value::Table(ref table)) => {
                match table.iter().skip(idx).next() {
                    Some((key, _)) => {
                        let val = Value::String(key.to_string());
                        f(&mut self.sub_decoder(Some(val), key))
                    }
                    None => Err(self.err(ExpectedMapKey(idx))),
                }
            }
            ref found => Err(self.mismatch("table", found)),
        }
    }
    fn read_map_elt_val<T, F>(&mut self, idx: usize, f: F)
        -> Result<T, DecodeError>
        where F: FnOnce(&mut Decoder) -> Result<T, DecodeError>
    {
        match self.toml {
            Some(Value::Table(ref table)) => {
                match table.iter().skip(idx).next() {
                    Some((key, value)) => {
                        // XXX: this shouldn't clone
                        f(&mut self.sub_decoder(Some(value.clone()), key))
                    }
                    None => Err(self.err(ExpectedMapElement(idx))),
                }
            }
            ref found => Err(self.mismatch("table", found)),
        }
    }

    fn error(&mut self, err: &str) -> DecodeError {
        DecodeError {
            field: self.cur_field.clone(),
            kind: ApplicationError(format!("{}", err))
        }
    }
}

#[cfg(test)]
mod tests {
    use rustc_serialize::Decodable;
    use std::collections::HashMap;

    use {Parser, Decoder, Value};

    #[test]
    fn bad_enum_chooses_longest_error() {
        #[derive(RustcDecodable)]
        #[allow(dead_code)]
        struct Foo {
            wut: HashMap<String, Bar>,
        }

        #[derive(RustcDecodable)]
        enum Bar {
            Simple(String),
            Detailed(Baz),
        }

        #[derive(RustcDecodable, Debug)]
        struct Baz {
            features: Vec<String>,
        }

        let s = r#"
            [wut]
            a = { features = "" }
        "#;
        let v = Parser::new(s).parse().unwrap();
        let mut d = Decoder::new(Value::Table(v));
        let err = match Foo::decode(&mut d) {
            Ok(_) => panic!("expected error"),
            Err(e) => e,
        };
        assert_eq!(err.field.as_ref().unwrap(), "wut.a.features");

    }
}
<|endoftext|>


<commit_before>// Copyright 2014-2018 Optimal Computing (NZ) Ltd.
// Licensed under the MIT license.  See LICENSE for details.

use std::cmp::Ordering;
use std::num::{FpCategory};
use super::{Ulps, ApproxEqUlps};

/// ApproxOrdUlps is for sorting floating point values where approximate equality
/// is considered equal.
pub trait ApproxOrdUlps: ApproxEqUlps {
    /// This method returns an ordering between `self` and `other` values
    /// if one exists, where Equal is returned if they are approximately
    /// equal within `ulps` floating point representations.  See module
    /// documentation for an understanding of `ulps`
    fn approx_cmp(&self, other: &Self, ulps: <<Self as ApproxEqUlps>::Flt as Ulps>::U)
                  -> Ordering;

    /// This method tests less than (for `self` < `other`), where values
    /// within `ulps` of each other are not less than.  See module
    /// documentation for an understanding of `ulps`.
    #[inline]
    fn approx_lt(&self, other: &Self, ulps: <<Self as ApproxEqUlps>::Flt as Ulps>::U)
                 -> bool
    {
        match self.approx_cmp(other, ulps) {
            Ordering::Less => true,
            _ => false,
        }
    }

    /// This method tests less than or equal to (for `self` <= `other`)
    /// where values within `ulps` are equal.  See module documentation
    /// for an understanding of `ulps`.
    #[inline]
    fn approx_le(&self, other: &Self, ulps: <<Self as ApproxEqUlps>::Flt as Ulps>::U)
                 -> bool
    {
        match self.approx_cmp(other, ulps) {
            Ordering::Less | Ordering::Equal => true,
            _ => false,
        }
    }

    /// This method tests greater than (for `self` > `other`)
    /// where values within `ulps` are not greater than.  See module
    /// documentation for an understanding of `ulps`
    #[inline]
    fn approx_gt(&self, other: &Self, ulps: <<Self as ApproxEqUlps>::Flt as Ulps>::U)
                 -> bool
    {
        match self.approx_cmp(other, ulps) {
            Ordering::Greater => true,
            _ => false,
        }
    }

    /// This method tests greater than or equal to (for `self` > `other`)
    /// where values within `ulps` are equal.  See module documentation
    /// for an understanding of `ulps`.
    #[inline]
    fn approx_ge(&self, other: &Self, ulps: <<Self as ApproxEqUlps>::Flt as Ulps>::U)
                 -> bool
    {
        match self.approx_cmp(other, ulps) {
            Ordering::Greater | Ordering::Equal => true,
            _ => false,
        }
    }
}

impl ApproxOrdUlps for f32 {
    fn approx_cmp(&self, other: &f32, ulps: <<Self as ApproxEqUlps>::Flt as Ulps>::U)
                  -> Ordering
    {
        let selfclass = self.classify();
        let otherclass = other.classify();

        // -0 and +0 are drastically far in ulps terms, so
        // we need a special case for that.
        if selfclass==FpCategory::Zero && otherclass==FpCategory::Zero {
            return Ordering::Equal;
        }

        // Handle differing signs as a special case, even if they are very
        // close, most people consider them unequal.
        let self_pos = self.is_sign_positive();
        let other_pos = other.is_sign_positive();

        let udiff: i32 = match (self_pos, other_pos) {
            (true, false) => return Ordering::Greater,
            (false, true) => return Ordering::Less,
            (true, true) => self.ulps(other),
            (false, false) => other.ulps(self), // invert ulps for negatives
        };

        match udiff {
            x if x < -ulps => Ordering::Less,
            x if x >= -ulps && x <= ulps => Ordering::Equal,
            x if x > ulps => Ordering::Greater,
            _ => unreachable!()
        }
    }
}

#[test]
fn f32_approx_cmp_test1() {
    let f: f32 = 0.1_f32;
    let mut sum: f32 = 0.0_f32;
    for _ in 0_isize..10_isize { sum += f; }
    let product: f32 = f * 10.0_f32;
    assert!(sum != product); // Should not be directly equal:
    println!("Ulps Difference: {}",sum.ulps(&product));
    assert!(sum.approx_cmp(&product,1) == Ordering::Equal); // But should be close
    assert!(sum.approx_cmp(&product,0) != Ordering::Equal);
    assert!(product.approx_cmp(&sum,0) != Ordering::Equal);
}
#[test]
fn f32_approx_cmp_test2() {
    let x: f32 = 1000000_f32;
    let y: f32 = 1000000.1_f32;
    assert!(x != y); // Should not be directly equal
    println!("Ulps Difference: {}",x.ulps(&y));
    assert!(x.approx_cmp(&y,2) == Ordering::Equal);
    assert!(x.approx_cmp(&y,1) == Ordering::Less);
    assert!(y.approx_cmp(&x,1) == Ordering::Greater);
}
#[test]
fn f32_approx_cmp_negatives() {
    let x: f32 = -1.0;
    let y: f32 = -2.0;
    assert!(x.approx_cmp(&y, 2) == Ordering::Greater);
}

// In all cases, approx_cmp() should be the same as cmp() if ulps=0
#[test]
fn f32_approx_cmp_vs_partial_cmp() {
    use std::mem;

    let testcases: [u32; 20] = [
        0,          // +0
        0x80000000, // -0
        0x00000101, // + denormal
        0x80000101, // - denormal
        0x00001101, // + denormal
        0x80001101, // - denormal
        0x01000101, // + normal
        0x81000101, // - normal
        0x01001101, // + normal
        0x81001101, // - normal
        0x7F800000, // +infinity
        0xFF800000, // -infinity
        0x7F801101, // SNaN
        0xFF801101, // SNaN
        0x7FC01101, // QNaN
        0xFFC01101, // QNaN
        0x7F801110, // SNaN
        0xFF801110, // SNaN
        0x7FC01110, // QNaN
        0xFFC01110, // QNaN

    ];

    let mut xf: f32;
    let mut yf: f32;
    for xbits in testcases.iter() {
        for ybits in testcases.iter() {
            xf = unsafe { mem::transmute::<u32,f32>(*xbits) };
            yf = unsafe { mem::transmute::<u32,f32>(*ybits) };
            if let Some(ordering) = xf.partial_cmp(&yf) {
                if ordering != xf.approx_cmp(&yf, 0) {
                    panic!("{} ({:x}) vs {} ({:x}): partial_cmp gives {:?} approx_cmp gives {:?}",
                           xf, xbits, yf, ybits, ordering , xf.approx_cmp(&yf, 0));
                }
            }
        }
        print!(".");
    }
}

impl ApproxOrdUlps for f64 {
    fn approx_cmp(&self, other: &f64, ulps: <<Self as ApproxEqUlps>::Flt as Ulps>::U)
                  -> Ordering
    {
        let selfclass = self.classify();
        let otherclass = other.classify();

        // -0 and +0 are drastically far in ulps terms, so
        // we need a special case for that.
        if selfclass==FpCategory::Zero && otherclass==FpCategory::Zero {
            return Ordering::Equal;
        }

        // Handle differing signs as a special case, even if they are very
        // close, most people consider them unequal.
        let self_pos = self.is_sign_positive();
        let other_pos = other.is_sign_positive();

        let udiff: i64 = match (self_pos, other_pos) {
            (true, false) => return Ordering::Greater,
            (false, true) => return Ordering::Less,
            (true, true) => self.ulps(other),
            (false, false) => other.ulps(self), // invert ulps for negatives
        };

        match udiff {
            x if x < -ulps => Ordering::Less,
            x if x >= -ulps && x <= ulps => Ordering::Equal,
            x if x > ulps => Ordering::Greater,
            _ => unreachable!()
        }
    }
}

#[test]
fn f64_approx_cmp_test1() {
    let f: f64 = 0.000000001_f64;
    let mut sum: f64 = 0.0_f64;
    for _ in 0_isize..10_isize { sum += f; }
    let product: f64 = f * 10.0_f64;
    assert!(sum != product); // Should not be directly equal:
    println!("Ulps Difference: {}",sum.ulps(&product));
    assert!(sum.approx_cmp(&product,1) == Ordering::Equal); // But should be close
    assert!(sum.approx_cmp(&product,0) != Ordering::Equal);
    assert!(product.approx_cmp(&sum,0) != Ordering::Equal);
}
#[test]
fn f64_approx_cmp_test2() {
    let x: f64 = 1000000_f64;
    let y: f64 = 1000000.0000000003_f64;
    assert!(x != y); // Should not be directly equal
    println!("Ulps Difference: {}",x.ulps(&y));
    assert!(x.approx_cmp(&y,3) == Ordering::Equal);
    assert!(x.approx_cmp(&y,2) == Ordering::Less);
    assert!(y.approx_cmp(&x,2) == Ordering::Greater);
}
#[test]
fn f64_approx_cmp_negatives() {
    let x: f64 = -1.0;
    let y: f64 = -2.0;
    assert!(x.approx_cmp(&y, 2) == Ordering::Greater);
}

// In all cases, approx_cmp() should be the same as cmp() if ulps=0
#[test]
fn f64_approx_cmp_vs_partial_cmp() {
    use std::mem;

    let testcases: [u64; 20] = [
        0,                   // +0
        0x80000000_00000000, // -0
        0x00000010_10000000, // + denormal
        0x80000010_10000000, // - denormal
        0x00000110_10000000, // + denormal
        0x80000110_10000000, // - denormal
        0x01000101_00100100, // + normal
        0x81000101_00100100, // - normal
        0x01001101_00100100, // + normal
        0x81001101_00100100, // - normal
        0x7FF00000_00000000, // +infinity
        0xFFF00000_00000000, // -infinity
        0x7FF01101_00100100, // SNaN
        0xFFF01101_00100100, // SNaN
        0x7FF81101_00100100, // QNaN
        0xFFF81101_00100100, // QNaN
        0x7FF01110_00100100, // SNaN
        0xFFF01110_00100100, // SNaN
        0x7FF81110_00100100, // QNaN
        0xFFF81110_00100100, // QNaN
    ];

    let mut xf: f64;
    let mut yf: f64;
    for xbits in testcases.iter() {
        for ybits in testcases.iter() {
            xf = unsafe { mem::transmute::<u64,f64>(*xbits) };
            yf = unsafe { mem::transmute::<u64,f64>(*ybits) };
            if let Some(ordering) = xf.partial_cmp(&yf) {
                if ordering != xf.approx_cmp(&yf, 0) {
                    panic!("{} ({:x}) vs {} ({:x}): partial_cmp gives {:?} approx_cmp gives {:?}",
                           xf, xbits, yf, ybits, ordering , xf.approx_cmp(&yf, 0));
                }
            }
        }
        print!(".");
    }
}
<commit_msg>BREAKING CHANGE - ApproxCmpUlps functions all renamed with "_ulps" in the name<commit_after>// Copyright 2014-2018 Optimal Computing (NZ) Ltd.
// Licensed under the MIT license.  See LICENSE for details.

use std::cmp::Ordering;
use std::num::{FpCategory};
use super::{Ulps, ApproxEqUlps};

/// ApproxOrdUlps is for sorting floating point values where approximate equality
/// is considered equal.
pub trait ApproxOrdUlps: ApproxEqUlps {
    /// This method returns an ordering between `self` and `other` values
    /// if one exists, where Equal is returned if they are approximately
    /// equal within `ulps` floating point representations.  See module
    /// documentation for an understanding of `ulps`
    fn approx_cmp_ulps(&self, other: &Self, ulps: <<Self as ApproxEqUlps>::Flt as Ulps>::U)
                       -> Ordering;

    /// This method tests less than (for `self` < `other`), where values
    /// within `ulps` of each other are not less than.  See module
    /// documentation for an understanding of `ulps`.
    #[inline]
    fn approx_lt_ulps(&self, other: &Self, ulps: <<Self as ApproxEqUlps>::Flt as Ulps>::U)
                      -> bool
    {
        match self.approx_cmp_ulps(other, ulps) {
            Ordering::Less => true,
            _ => false,
        }
    }

    /// This method tests less than or equal to (for `self` <= `other`)
    /// where values within `ulps` are equal.  See module documentation
    /// for an understanding of `ulps`.
    #[inline]
    fn approx_le_ulps(&self, other: &Self, ulps: <<Self as ApproxEqUlps>::Flt as Ulps>::U)
                      -> bool
    {
        match self.approx_cmp_ulps(other, ulps) {
            Ordering::Less | Ordering::Equal => true,
            _ => false,
        }
    }

    /// This method tests greater than (for `self` > `other`)
    /// where values within `ulps` are not greater than.  See module
    /// documentation for an understanding of `ulps`
    #[inline]
    fn approx_gt_ulps(&self, other: &Self, ulps: <<Self as ApproxEqUlps>::Flt as Ulps>::U)
                      -> bool
    {
        match self.approx_cmp_ulps(other, ulps) {
            Ordering::Greater => true,
            _ => false,
        }
    }

    /// This method tests greater than or equal to (for `self` > `other`)
    /// where values within `ulps` are equal.  See module documentation
    /// for an understanding of `ulps`.
    #[inline]
    fn approx_ge_ulps(&self, other: &Self, ulps: <<Self as ApproxEqUlps>::Flt as Ulps>::U)
                      -> bool
    {
        match self.approx_cmp_ulps(other, ulps) {
            Ordering::Greater | Ordering::Equal => true,
            _ => false,
        }
    }
}

impl ApproxOrdUlps for f32 {
    fn approx_cmp_ulps(&self, other: &f32, ulps: <<Self as ApproxEqUlps>::Flt as Ulps>::U)
                       -> Ordering
    {
        let selfclass = self.classify();
        let otherclass = other.classify();

        // -0 and +0 are drastically far in ulps terms, so
        // we need a special case for that.
        if selfclass==FpCategory::Zero && otherclass==FpCategory::Zero {
            return Ordering::Equal;
        }

        // Handle differing signs as a special case, even if they are very
        // close, most people consider them unequal.
        let self_pos = self.is_sign_positive();
        let other_pos = other.is_sign_positive();

        let udiff: i32 = match (self_pos, other_pos) {
            (true, false) => return Ordering::Greater,
            (false, true) => return Ordering::Less,
            (true, true) => self.ulps(other),
            (false, false) => other.ulps(self), // invert ulps for negatives
        };

        match udiff {
            x if x < -ulps => Ordering::Less,
            x if x >= -ulps && x <= ulps => Ordering::Equal,
            x if x > ulps => Ordering::Greater,
            _ => unreachable!()
        }
    }
}

#[test]
fn f32_approx_cmp_test1() {
    let f: f32 = 0.1_f32;
    let mut sum: f32 = 0.0_f32;
    for _ in 0_isize..10_isize { sum += f; }
    let product: f32 = f * 10.0_f32;
    assert!(sum != product); // Should not be directly equal:
    println!("Ulps Difference: {}",sum.ulps(&product));
    assert!(sum.approx_cmp_ulps(&product,1) == Ordering::Equal); // But should be close
    assert!(sum.approx_cmp_ulps(&product,0) != Ordering::Equal);
    assert!(product.approx_cmp_ulps(&sum,0) != Ordering::Equal);
}
#[test]
fn f32_approx_cmp_test2() {
    let x: f32 = 1000000_f32;
    let y: f32 = 1000000.1_f32;
    assert!(x != y); // Should not be directly equal
    println!("Ulps Difference: {}",x.ulps(&y));
    assert!(x.approx_cmp_ulps(&y,2) == Ordering::Equal);
    assert!(x.approx_cmp_ulps(&y,1) == Ordering::Less);
    assert!(y.approx_cmp_ulps(&x,1) == Ordering::Greater);
}
#[test]
fn f32_approx_cmp_negatives() {
    let x: f32 = -1.0;
    let y: f32 = -2.0;
    assert!(x.approx_cmp_ulps(&y, 2) == Ordering::Greater);
}

// In all cases, approx_cmp() should be the same as cmp() if ulps=0
#[test]
fn f32_approx_cmp_vs_partial_cmp() {
    use std::mem;

    let testcases: [u32; 20] = [
        0,          // +0
        0x80000000, // -0
        0x00000101, // + denormal
        0x80000101, // - denormal
        0x00001101, // + denormal
        0x80001101, // - denormal
        0x01000101, // + normal
        0x81000101, // - normal
        0x01001101, // + normal
        0x81001101, // - normal
        0x7F800000, // +infinity
        0xFF800000, // -infinity
        0x7F801101, // SNaN
        0xFF801101, // SNaN
        0x7FC01101, // QNaN
        0xFFC01101, // QNaN
        0x7F801110, // SNaN
        0xFF801110, // SNaN
        0x7FC01110, // QNaN
        0xFFC01110, // QNaN

    ];

    let mut xf: f32;
    let mut yf: f32;
    for xbits in testcases.iter() {
        for ybits in testcases.iter() {
            xf = unsafe { mem::transmute::<u32,f32>(*xbits) };
            yf = unsafe { mem::transmute::<u32,f32>(*ybits) };
            if let Some(ordering) = xf.partial_cmp(&yf) {
                if ordering != xf.approx_cmp_ulps(&yf, 0) {
                    panic!("{} ({:x}) vs {} ({:x}): partial_cmp gives {:?} \
                            approx_cmp_ulps gives {:?}",
                           xf, xbits, yf, ybits, ordering , xf.approx_cmp_ulps(&yf, 0));
                }
            }
        }
        print!(".");
    }
}

impl ApproxOrdUlps for f64 {
    fn approx_cmp_ulps(&self, other: &f64, ulps: <<Self as ApproxEqUlps>::Flt as Ulps>::U)
                       -> Ordering
    {
        let selfclass = self.classify();
        let otherclass = other.classify();

        // -0 and +0 are drastically far in ulps terms, so
        // we need a special case for that.
        if selfclass==FpCategory::Zero && otherclass==FpCategory::Zero {
            return Ordering::Equal;
        }

        // Handle differing signs as a special case, even if they are very
        // close, most people consider them unequal.
        let self_pos = self.is_sign_positive();
        let other_pos = other.is_sign_positive();

        let udiff: i64 = match (self_pos, other_pos) {
            (true, false) => return Ordering::Greater,
            (false, true) => return Ordering::Less,
            (true, true) => self.ulps(other),
            (false, false) => other.ulps(self), // invert ulps for negatives
        };

        match udiff {
            x if x < -ulps => Ordering::Less,
            x if x >= -ulps && x <= ulps => Ordering::Equal,
            x if x > ulps => Ordering::Greater,
            _ => unreachable!()
        }
    }
}

#[test]
fn f64_approx_cmp_ulps_test1() {
    let f: f64 = 0.000000001_f64;
    let mut sum: f64 = 0.0_f64;
    for _ in 0_isize..10_isize { sum += f; }
    let product: f64 = f * 10.0_f64;
    assert!(sum != product); // Should not be directly equal:
    println!("Ulps Difference: {}",sum.ulps(&product));
    assert!(sum.approx_cmp_ulps(&product,1) == Ordering::Equal); // But should be close
    assert!(sum.approx_cmp_ulps(&product,0) != Ordering::Equal);
    assert!(product.approx_cmp_ulps(&sum,0) != Ordering::Equal);
}
#[test]
fn f64_approx_cmp_ulps_test2() {
    let x: f64 = 1000000_f64;
    let y: f64 = 1000000.0000000003_f64;
    assert!(x != y); // Should not be directly equal
    println!("Ulps Difference: {}",x.ulps(&y));
    assert!(x.approx_cmp_ulps(&y,3) == Ordering::Equal);
    assert!(x.approx_cmp_ulps(&y,2) == Ordering::Less);
    assert!(y.approx_cmp_ulps(&x,2) == Ordering::Greater);
}
#[test]
fn f64_approx_cmp_ulps_negatives() {
    let x: f64 = -1.0;
    let y: f64 = -2.0;
    assert!(x.approx_cmp_ulps(&y, 2) == Ordering::Greater);
}

// In all cases, approx_cmp_ulps() should be the same as cmp() if ulps=0
#[test]
fn f64_approx_cmp_ulps_vs_partial_cmp() {
    use std::mem;

    let testcases: [u64; 20] = [
        0,                   // +0
        0x80000000_00000000, // -0
        0x00000010_10000000, // + denormal
        0x80000010_10000000, // - denormal
        0x00000110_10000000, // + denormal
        0x80000110_10000000, // - denormal
        0x01000101_00100100, // + normal
        0x81000101_00100100, // - normal
        0x01001101_00100100, // + normal
        0x81001101_00100100, // - normal
        0x7FF00000_00000000, // +infinity
        0xFFF00000_00000000, // -infinity
        0x7FF01101_00100100, // SNaN
        0xFFF01101_00100100, // SNaN
        0x7FF81101_00100100, // QNaN
        0xFFF81101_00100100, // QNaN
        0x7FF01110_00100100, // SNaN
        0xFFF01110_00100100, // SNaN
        0x7FF81110_00100100, // QNaN
        0xFFF81110_00100100, // QNaN
    ];

    let mut xf: f64;
    let mut yf: f64;
    for xbits in testcases.iter() {
        for ybits in testcases.iter() {
            xf = unsafe { mem::transmute::<u64,f64>(*xbits) };
            yf = unsafe { mem::transmute::<u64,f64>(*ybits) };
            if let Some(ordering) = xf.partial_cmp(&yf) {
                if ordering != xf.approx_cmp_ulps(&yf, 0) {
                    panic!("{} ({:x}) vs {} ({:x}): partial_cmp gives {:?} \
                            approx_cmp_ulps gives {:?}",
                           xf, xbits, yf, ybits, ordering , xf.approx_cmp_ulps(&yf, 0));
                }
            }
        }
        print!(".");
    }
}
<|endoftext|>


<commit_before>use std::f32::consts::PI;

use collect_slice::CollectSlice;
use num::complex::Complex32;
use num::traits::Zero;
use quad_osc::QuadOsc;

use consts::SAMPLES;
use descramble::VoiceDecisions;
use enhance::EnhancedSpectrals;
use noise::Noise;
use params::BaseParams;
use window;

// Result of equation 121
const SCALING_COEF: f32 = 146.6432708443356;

const DFT_SIZE: usize = 256;
const IDFT_SIZE: usize = 256;

const DFT_HALF: usize = DFT_SIZE / 2;
const IDFT_HALF: usize = IDFT_SIZE / 2;

const SIG_START: isize = -104;
const SIG_STOP: isize = 105;
const SIG_SIZE: usize = (SIG_STOP - SIG_START) as usize;

fn edges(l: usize, params: &BaseParams) -> (usize, usize) {
    let common = DFT_SIZE as f32 / (2.0 * PI) * params.fundamental;

    (
        (common * (l as f32 - 0.5)).ceil() as usize,
        (common * (l as f32 + 0.5)).ceil() as usize,
    )
}

pub struct UnvoicedDFT([Complex32; DFT_HALF]);

impl UnvoicedDFT {
    pub fn new(params: &BaseParams, voice: &VoiceDecisions, enhanced: &EnhancedSpectrals)
        -> UnvoicedDFT
    {
        let sig = {
            let mut sig = [0.0; SIG_SIZE];

            let mut noise = Noise::new();
            let window = window::synthesis_trunc();

            (SIG_START..SIG_STOP)
                .map(|n| noise.next() * window.get(n))
                .collect_slice_checked(&mut sig[..]);

            sig
        };

        let mut dft = [Complex32::zero(); DFT_HALF];

        for (l, &spectral) in enhanced.iter().enumerate() {
            let l = l + 1;

            if voice.is_voiced(l) {
                continue;
            }

            let (lower, upper) = edges(l, params);

            for m in lower..upper {
                let common = 2.0 / DFT_SIZE as f32 * PI * m as f32;
                let mut osc = QuadOsc::new(common * SIG_START as f32, common);

                dft[m] = sig.iter().map(|&x| {
                    let (sin, cos) = osc.next();
                    x * Complex32::new(cos, -sin)
                }).fold(Complex32::zero(), |s, x| s + x)
            }

            let energy = (lower..upper)
                .map(|m| dft[m].norm_sqr())
                .fold(0.0, |s, x| s + x);
            let power = energy / (upper - lower) as f32;

            let scale = SCALING_COEF * spectral / power.sqrt();

            for m in lower..upper {
                dft[m] = scale * dft[m];
            }
        }

        UnvoicedDFT(dft)
    }

    pub fn idft(&self, n: isize) -> f32 {
        if n < -(IDFT_HALF as isize) || n >= IDFT_HALF as isize {
            return 0.0;
        }

        2.0 / (DFT_SIZE as f32 * IDFT_SIZE as f32).sqrt() * (0..DFT_HALF).map(|m| {
            let (sin, cos) = (2.0 / IDFT_SIZE as f32 * PI * m as f32 * n as f32).sin_cos();
            self.0[m].re * cos - self.0[m].im * sin
        }).fold(0.0, |s, x| s + x)
    }
}

impl Default for UnvoicedDFT {
    fn default() -> UnvoicedDFT {
        UnvoicedDFT([Complex32::zero(); DFT_HALF])
    }
}

pub struct Unvoiced<'a, 'b> {
    cur: &'a UnvoicedDFT,
    prev: &'b UnvoicedDFT,
    window: window::Window,
}

impl<'a, 'b> Unvoiced<'a, 'b> {
    pub fn new(cur: &'a UnvoicedDFT, prev: &'b UnvoicedDFT) -> Unvoiced<'a, 'b> {
        Unvoiced {
            cur: cur,
            prev: prev,
            window: window::synthesis_full(),
        }
    }

    pub fn get(&self, n: usize) -> f32 {
        let n = n as isize;

        let numer = self.window.get(n) * self.prev.idft(n) +
            self.window.get(n - SAMPLES as isize) * self.cur.idft(n - SAMPLES as isize);
        let denom = self.window.get(n).powi(2) +
            self.window.get(n - SAMPLES as isize).powi(2);

        numer / denom
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use super::edges;
    use descramble::Bootstrap;
    use params::BaseParams;

    #[test]
    fn test_edges() {
        let chunks = [
            0b001000010010,
            0b110011001100,
            0b111000111000,
            0b111111111111,
            0b10100110101,
            0b00101111010,
            0b01110111011,
            0b00001000,
        ];

        let b = Bootstrap::new(&chunks);
        let p = BaseParams::new(b.unwrap_period());

        assert!((p.fundamental - 0.17575344).abs() < 0.000001);

        let (lower, upper) = edges(1, &p);
        assert_eq!(lower, 4);
        assert_eq!(upper, 11);

        let (lower, upper) = edges(2, &p);
        assert_eq!(lower, 11);
        assert_eq!(upper, 18);

        let (lower, upper) = edges(5, &p);
        assert_eq!(lower, 33);
        assert_eq!(upper, 40);

        let (lower, upper) = edges(8, &p);
        assert_eq!(lower, 54);
        assert_eq!(upper, 61);

        let (lower, upper) = edges(16, &p);
        assert_eq!(lower, 111);
        assert_eq!(upper, 119);
    }
}
<commit_msg>Use QuadOsc in unvoiced idft computation<commit_after>use std::f32::consts::PI;

use collect_slice::CollectSlice;
use num::complex::Complex32;
use num::traits::Zero;
use quad_osc::QuadOsc;

use consts::SAMPLES;
use descramble::VoiceDecisions;
use enhance::EnhancedSpectrals;
use noise::Noise;
use params::BaseParams;
use window;

// Result of equation 121
const SCALING_COEF: f32 = 146.6432708443356;

const DFT_SIZE: usize = 256;
const IDFT_SIZE: usize = 256;

const DFT_HALF: usize = DFT_SIZE / 2;
const IDFT_HALF: usize = IDFT_SIZE / 2;

const SIG_START: isize = -104;
const SIG_STOP: isize = 105;
const SIG_SIZE: usize = (SIG_STOP - SIG_START) as usize;

fn edges(l: usize, params: &BaseParams) -> (usize, usize) {
    let common = DFT_SIZE as f32 / (2.0 * PI) * params.fundamental;

    (
        (common * (l as f32 - 0.5)).ceil() as usize,
        (common * (l as f32 + 0.5)).ceil() as usize,
    )
}

pub struct UnvoicedDFT([Complex32; DFT_HALF]);

impl UnvoicedDFT {
    pub fn new(params: &BaseParams, voice: &VoiceDecisions, enhanced: &EnhancedSpectrals)
        -> UnvoicedDFT
    {
        let sig = {
            let mut sig = [0.0; SIG_SIZE];

            let mut noise = Noise::new();
            let window = window::synthesis_trunc();

            (SIG_START..SIG_STOP)
                .map(|n| noise.next() * window.get(n))
                .collect_slice_checked(&mut sig[..]);

            sig
        };

        let mut dft = [Complex32::zero(); DFT_HALF];

        for (l, &spectral) in enhanced.iter().enumerate() {
            let l = l + 1;

            if voice.is_voiced(l) {
                continue;
            }

            let (lower, upper) = edges(l, params);

            for m in lower..upper {
                let common = 2.0 / DFT_SIZE as f32 * PI * m as f32;
                let mut osc = QuadOsc::new(common * SIG_START as f32, common);

                dft[m] = sig.iter().map(|&x| {
                    let (sin, cos) = osc.next();
                    x * Complex32::new(cos, -sin)
                }).fold(Complex32::zero(), |s, x| s + x)
            }

            let energy = (lower..upper)
                .map(|m| dft[m].norm_sqr())
                .fold(0.0, |s, x| s + x);
            let power = energy / (upper - lower) as f32;

            let scale = SCALING_COEF * spectral / power.sqrt();

            for m in lower..upper {
                dft[m] = scale * dft[m];
            }
        }

        UnvoicedDFT(dft)
    }

    pub fn idft(&self, n: isize) -> f32 {
        if n < -(IDFT_HALF as isize) || n >= IDFT_HALF as isize {
            return 0.0;
        }

        let common = 2.0 / IDFT_SIZE as f32 * PI * n as f32;
        let mut osc = QuadOsc::new(0.0, common);

        2.0 / (DFT_SIZE as f32 * IDFT_SIZE as f32).sqrt() * (0..DFT_HALF).map(|m| {
            let (sin, cos) = osc.next();
            self.0[m].re * cos - self.0[m].im * sin
        }).fold(0.0, |s, x| s + x)
    }
}

impl Default for UnvoicedDFT {
    fn default() -> UnvoicedDFT {
        UnvoicedDFT([Complex32::zero(); DFT_HALF])
    }
}

pub struct Unvoiced<'a, 'b> {
    cur: &'a UnvoicedDFT,
    prev: &'b UnvoicedDFT,
    window: window::Window,
}

impl<'a, 'b> Unvoiced<'a, 'b> {
    pub fn new(cur: &'a UnvoicedDFT, prev: &'b UnvoicedDFT) -> Unvoiced<'a, 'b> {
        Unvoiced {
            cur: cur,
            prev: prev,
            window: window::synthesis_full(),
        }
    }

    pub fn get(&self, n: usize) -> f32 {
        let n = n as isize;

        let numer = self.window.get(n) * self.prev.idft(n) +
            self.window.get(n - SAMPLES as isize) * self.cur.idft(n - SAMPLES as isize);
        let denom = self.window.get(n).powi(2) +
            self.window.get(n - SAMPLES as isize).powi(2);

        numer / denom
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use super::edges;
    use descramble::Bootstrap;
    use params::BaseParams;

    #[test]
    fn test_edges() {
        let chunks = [
            0b001000010010,
            0b110011001100,
            0b111000111000,
            0b111111111111,
            0b10100110101,
            0b00101111010,
            0b01110111011,
            0b00001000,
        ];

        let b = Bootstrap::new(&chunks);
        let p = BaseParams::new(b.unwrap_period());

        assert!((p.fundamental - 0.17575344).abs() < 0.000001);

        let (lower, upper) = edges(1, &p);
        assert_eq!(lower, 4);
        assert_eq!(upper, 11);

        let (lower, upper) = edges(2, &p);
        assert_eq!(lower, 11);
        assert_eq!(upper, 18);

        let (lower, upper) = edges(5, &p);
        assert_eq!(lower, 33);
        assert_eq!(upper, 40);

        let (lower, upper) = edges(8, &p);
        assert_eq!(lower, 54);
        assert_eq!(upper, 61);

        let (lower, upper) = edges(16, &p);
        assert_eq!(lower, 111);
        assert_eq!(upper, 119);
    }
}
<|endoftext|>