<commit_before>//! Utility methods and constructors for Lua classes

use std::convert::From;
use std::default::Default;
use std::rc::Rc;
use rlua::{self, Lua, ToLua, Table, Value, UserData, AnyUserData, Function,
           UserDataMethods, MetaMethod};
use super::object::{self, Object};
use super::property::Property;

pub type Checker = Rc<Fn(Object) -> bool>;

#[derive(Clone, Debug)]
pub struct Class<'lua> {
    class: AnyUserData<'lua>
}

impl <'lua> From<AnyUserData<'lua>> for Class<'lua> {
    fn from(class: AnyUserData<'lua>) -> Self {
        Class { class }
    }
}

#[derive(Clone)]
pub struct ClassState {
    // NOTE That this is missing fields from the C version.
    // They stored in the meta table instead, to not have unsafety.
    // They are fetchable using getters.
    name: String,
    checker: Option<Checker>,
    instances: u32
}

unsafe impl Send for ClassState {}

pub struct ClassBuilder<'lua>{
    lua: &'lua Lua,
    class: Class<'lua>
}

impl <'lua> ClassBuilder<'lua> {
    pub fn method(self, name: String, meth: rlua::Function)
                  -> rlua::Result<Self> {
        let table = self.class.class.get_user_value::<Table>()?;
        let meta = table.get_metatable()
            .expect("Class had no meta table!");
        meta.set(name, meth)?;
        Ok(self)
    }

    pub fn property(self, prop: Property<'lua>) -> rlua::Result<Self> {
        let table = self.class.class.get_user_value::<Table>()?;
        let properties = table.get::<_, Table>("properties")?;
        let length = properties.len().unwrap_or(0) + 1;
        properties.set(length, prop)?;
        Ok(self)
    }

    // TODO remove, do right
    pub fn dummy_property(self, key: String, val: rlua::Value<'lua>) -> rlua::Result<Self> {
        let table = self.class.class.get_user_value::<Table>()?;
        let meta = table.get_metatable()
            .expect("Class had no meta table!");
        meta.set(key, val)?;
        Ok(self)
    }

    pub fn save_class(self, name: &str)
                      -> rlua::Result<Self> {
        self.lua.globals().set(name, self.class.class.clone())?;
        Ok(self)
    }

    pub fn build(self) -> rlua::Result<Class<'lua>> {
        Ok(self.class)
    }
}

impl <'lua> ToLua<'lua> for Class<'lua> {
    fn to_lua(self, lua: &'lua Lua) -> rlua::Result<Value<'lua>> {
        self.class.to_lua(lua)
    }
}

impl Default for ClassState {
    fn default() -> Self {
        ClassState {
            name: String::default(),
            checker: Option::default(),
            instances: 0
        }
    }
}

impl UserData for ClassState {
    fn add_methods(methods: &mut UserDataMethods<Self>) {
        methods.add_meta_function(MetaMethod::Index, class_index);
        // TODO Class new index?
        methods.add_meta_function(MetaMethod::NewIndex, object::default_newindex);
        fn call<'lua>(lua: &'lua Lua,
                      (class, args): (AnyUserData<'lua>, rlua::MultiValue<'lua>))
                      -> rlua::Result<Value<'lua>> {
            match class_index(lua, (class, "__call".to_lua(lua)?))? {
                Value::Function(function) => function.call(args),
                v => Ok(v)
            }
        }
        methods.add_meta_function(MetaMethod::Call, call);
        methods.add_meta_function(MetaMethod::ToString, |_, class: AnyUserData| {
            let table = class.get_user_value::<Table>()?;
            table.get::<_, String>("name")
        });
    }
}

impl <'lua> Class<'lua> {
    pub fn builder(lua: &'lua Lua,
                   name: &str,
                   checker: Option<Checker>)
                   -> rlua::Result<ClassBuilder<'lua>> {
        let mut class = ClassState::default();
        class.checker = checker;
        let user_data = lua.create_userdata(class)?;
        let table = lua.create_table()?;
        // Store in not meta table so we can't index it
        table.set("name", name)?;
        table.set("properties", Vec::<Property>::new().to_lua(lua)?)?;
        let meta = lua.create_table()?;
        meta.set("signals", lua.create_table()?)?;
        meta.set("set_index_miss_handler",
                 lua.create_function(set_index_miss_handler)?.bind(user_data.clone())?)?;
        meta.set("set_newindex_miss_handler",
                 lua.create_function(set_newindex_miss_handler)?.bind(user_data.clone())?)?;
        meta.set("__index", meta.clone())?;
        table.set_metatable(Some(meta.clone()));
        user_data.set_user_value(table)?;
        Ok(ClassBuilder{
            lua: lua,
            class: Class { class: user_data }
        })
    }

    pub fn checker(&self) -> rlua::Result<Option<Checker>> {
        self.class.borrow::<ClassState>().map(|class| class.checker.clone())
    }
}

fn set_index_miss_handler<'lua>(_: &'lua Lua, (class, func): (AnyUserData, Function))
                                -> rlua::Result<()> {
    let table = class.get_user_value::<Table>()?;
    let meta = table.get_metatable()
        .expect("Object had no metatable");
    meta.set("__index_miss_handler", func)?;
    Ok(())
}
fn set_newindex_miss_handler<'lua>(_: &'lua Lua, (class, func): (AnyUserData, Function))
                                   -> rlua::Result<()> {
    let table = class.get_user_value::<Table>()?;
    let meta = table.get_metatable()
        .expect("Object had no metatable");
    meta.set("__newindex_miss_handler", func)?;
    Ok(())
}

pub fn class_setup<'lua>(lua: &'lua Lua, name: &str) -> rlua::Result<Class<'lua>> {
    let class = lua.globals().get::<_, AnyUserData>(name)
        .expect("Class was not set! Did you call init?");
    assert!(class.is::<ClassState>()?,
            "This user data was not a class!");
    Ok(Class { class })
}

fn class_index<'lua>(_: &'lua Lua,
                         (class, index): (AnyUserData<'lua>, Value<'lua>))
                         -> rlua::Result<Value<'lua>> {
    let table = class.get_user_value::<Table>()?;
    let meta = table.get_metatable().expect("class had no meta table");
    match meta.raw_get("__index")? {
        Value::Function(function) => function.call((class, index)),
        Value::Table(table) => table.get(index),
        _ => panic!("Unexpected value in index")
    }
}
<commit_msg>Class: Remove unused name field<commit_after>//! Utility methods and constructors for Lua classes

use std::convert::From;
use std::default::Default;
use std::rc::Rc;
use rlua::{self, Lua, ToLua, Table, Value, UserData, AnyUserData, Function,
           UserDataMethods, MetaMethod};
use super::object::{self, Object};
use super::property::Property;

pub type Checker = Rc<Fn(Object) -> bool>;

#[derive(Clone, Debug)]
pub struct Class<'lua> {
    class: AnyUserData<'lua>
}

impl <'lua> From<AnyUserData<'lua>> for Class<'lua> {
    fn from(class: AnyUserData<'lua>) -> Self {
        Class { class }
    }
}

#[derive(Clone)]
pub struct ClassState {
    // NOTE That this is missing fields from the C version.
    // They stored in the meta table instead, to not have unsafety.
    // They are fetchable using getters.
    checker: Option<Checker>,
    instances: u32
}

unsafe impl Send for ClassState {}

pub struct ClassBuilder<'lua>{
    lua: &'lua Lua,
    class: Class<'lua>
}

impl <'lua> ClassBuilder<'lua> {
    pub fn method(self, name: String, meth: rlua::Function)
                  -> rlua::Result<Self> {
        let table = self.class.class.get_user_value::<Table>()?;
        let meta = table.get_metatable()
            .expect("Class had no meta table!");
        meta.set(name, meth)?;
        Ok(self)
    }

    pub fn property(self, prop: Property<'lua>) -> rlua::Result<Self> {
        let table = self.class.class.get_user_value::<Table>()?;
        let properties = table.get::<_, Table>("properties")?;
        let length = properties.len().unwrap_or(0) + 1;
        properties.set(length, prop)?;
        Ok(self)
    }

    // TODO remove, do right
    pub fn dummy_property(self, key: String, val: rlua::Value<'lua>) -> rlua::Result<Self> {
        let table = self.class.class.get_user_value::<Table>()?;
        let meta = table.get_metatable()
            .expect("Class had no meta table!");
        meta.set(key, val)?;
        Ok(self)
    }

    pub fn save_class(self, name: &str)
                      -> rlua::Result<Self> {
        self.lua.globals().set(name, self.class.class.clone())?;
        Ok(self)
    }

    pub fn build(self) -> rlua::Result<Class<'lua>> {
        Ok(self.class)
    }
}

impl <'lua> ToLua<'lua> for Class<'lua> {
    fn to_lua(self, lua: &'lua Lua) -> rlua::Result<Value<'lua>> {
        self.class.to_lua(lua)
    }
}

impl Default for ClassState {
    fn default() -> Self {
        ClassState {
            checker: Option::default(),
            instances: 0
        }
    }
}

impl UserData for ClassState {
    fn add_methods(methods: &mut UserDataMethods<Self>) {
        methods.add_meta_function(MetaMethod::Index, class_index);
        // TODO Class new index?
        methods.add_meta_function(MetaMethod::NewIndex, object::default_newindex);
        fn call<'lua>(lua: &'lua Lua,
                      (class, args): (AnyUserData<'lua>, rlua::MultiValue<'lua>))
                      -> rlua::Result<Value<'lua>> {
            match class_index(lua, (class, "__call".to_lua(lua)?))? {
                Value::Function(function) => function.call(args),
                v => Ok(v)
            }
        }
        methods.add_meta_function(MetaMethod::Call, call);
        methods.add_meta_function(MetaMethod::ToString, |_, class: AnyUserData| {
            let table = class.get_user_value::<Table>()?;
            table.get::<_, String>("name")
        });
    }
}

impl <'lua> Class<'lua> {
    pub fn builder(lua: &'lua Lua,
                   name: &str,
                   checker: Option<Checker>)
                   -> rlua::Result<ClassBuilder<'lua>> {
        let mut class = ClassState::default();
        class.checker = checker;
        let user_data = lua.create_userdata(class)?;
        let table = lua.create_table()?;
        // Store in not meta table so we can't index it
        table.set("name", name)?;
        table.set("properties", Vec::<Property>::new().to_lua(lua)?)?;
        let meta = lua.create_table()?;
        meta.set("signals", lua.create_table()?)?;
        meta.set("set_index_miss_handler",
                 lua.create_function(set_index_miss_handler)?.bind(user_data.clone())?)?;
        meta.set("set_newindex_miss_handler",
                 lua.create_function(set_newindex_miss_handler)?.bind(user_data.clone())?)?;
        meta.set("__index", meta.clone())?;
        table.set_metatable(Some(meta.clone()));
        user_data.set_user_value(table)?;
        Ok(ClassBuilder{
            lua: lua,
            class: Class { class: user_data }
        })
    }

    pub fn checker(&self) -> rlua::Result<Option<Checker>> {
        self.class.borrow::<ClassState>().map(|class| class.checker.clone())
    }
}

fn set_index_miss_handler<'lua>(_: &'lua Lua, (class, func): (AnyUserData, Function))
                                -> rlua::Result<()> {
    let table = class.get_user_value::<Table>()?;
    let meta = table.get_metatable()
        .expect("Object had no metatable");
    meta.set("__index_miss_handler", func)?;
    Ok(())
}
fn set_newindex_miss_handler<'lua>(_: &'lua Lua, (class, func): (AnyUserData, Function))
                                   -> rlua::Result<()> {
    let table = class.get_user_value::<Table>()?;
    let meta = table.get_metatable()
        .expect("Object had no metatable");
    meta.set("__newindex_miss_handler", func)?;
    Ok(())
}

pub fn class_setup<'lua>(lua: &'lua Lua, name: &str) -> rlua::Result<Class<'lua>> {
    let class = lua.globals().get::<_, AnyUserData>(name)
        .expect("Class was not set! Did you call init?");
    assert!(class.is::<ClassState>()?,
            "This user data was not a class!");
    Ok(Class { class })
}

fn class_index<'lua>(_: &'lua Lua,
                         (class, index): (AnyUserData<'lua>, Value<'lua>))
                         -> rlua::Result<Value<'lua>> {
    let table = class.get_user_value::<Table>()?;
    let meta = table.get_metatable().expect("class had no meta table");
    match meta.raw_get("__index")? {
        Value::Function(function) => function.call((class, index)),
        Value::Table(table) => table.get(index),
        _ => panic!("Unexpected value in index")
    }
}
<|endoftext|>


<commit_before>use engine::components::{CollisionHandle, ContactState, ContactStates, MovedFlag, Pos, Sensors};
use engine::resources::SkyCollisionWorld;
use ncollide::world::CollisionObjectHandle;
use specs::prelude::*;

#[derive(SystemData)]
pub struct CollisionSystemData<'a> {
    moved: ReadStorage<'a, MovedFlag>,
    pos: ReadStorage<'a, Pos>,
    c_handle: ReadStorage<'a, CollisionHandle>,
    sensors: ReadStorage<'a, Sensors>,
    ids: Entities<'a>,

    contact_states: WriteStorage<'a, ContactStates>,

    col_world: FetchMut<'a, SkyCollisionWorld>,
}

/// The `CollisionSystem` updates a moved entity's collisiion-related
/// marker positions (e.g. attack whiskers, rigid body, etc)
/// and registers the corresponding contact event components when collisions occur.
///
/// This is not responsible for removing no-longer happening collisions. That is the job
/// of the `CleanupSystem` which will remove `Stopped` collisions every update cycle.
///
/// In addition, other systems should verify their target has not disappeared, especially if relying on
/// `Ongoing` collision tags.
pub struct CollisionSystem;

impl<'a> System<'a> for CollisionSystem {
    type SystemData = CollisionSystemData<'a>;

    fn run(&mut self, mut sys_data: Self::SystemData) {
        use engine::resources::COLLISION_SCALE;
        use nalgebra;
        use nalgebra::{Isometry2, Vector2};
        use ncollide::events::ContactEvent;

        for (_, pos, c_handle, id) in (
            &sys_data.moved,
            &sys_data.pos,
            &sys_data.c_handle,
            &*sys_data.ids,
        ).join()
        {
            let pos = Isometry2::new(
                Vector2::new(pos.x / COLLISION_SCALE, pos.y / COLLISION_SCALE),
                nalgebra::zero(),
            );

            sys_data.col_world.set_position(c_handle.0, pos);

            if let Some(sensors) = sys_data.sensors.get(id) {
                for &sensor in sensors.0.values() {
                    let sensor_ch = sys_data
                        .c_handle
                        .get(sensor)
                        .expect("Sensor has no collision?");
                    sys_data.col_world.set_position(sensor_ch.0, pos);
                }
            }
        }

        sys_data.col_world.update();

        for event in sys_data.col_world.contact_events() {
            print!("Collision event: {:?} :::  ", event);
            match event {
                ContactEvent::Started(h1, h2) => {
                    handle_started(&sys_data.col_world, &mut sys_data.contact_states, *h1, *h2);
                }
                ContactEvent::Stopped(h1, h2) => {
                    handle_stopped(&sys_data.col_world, &mut sys_data.contact_states, *h1, *h2);
                }
            }
        }
    }
}

/// Inserts contact events into storages as they happen.
///
/// If one of the handles is a sensor, this will only flag the sensor's contact events.
///
/// If neither is a sensor, both will be flagged with a `Started` pointing
/// to the opposite entity's `Entity` handle.
///
/// During debug builds, this will verify that entities do not double-appear in
/// an entity's collision list, as well as verifying that sensors do not detect
/// each other.
fn handle_started(
    col_world: &SkyCollisionWorld,
    contact_states: &mut WriteStorage<ContactStates>,
    h1: CollisionObjectHandle,
    h2: CollisionObjectHandle,
) {
    let h1 = col_world.collision_object(h1).unwrap();
    let h2 = col_world.collision_object(h2).unwrap();

    let e1 = h1.data().e;
    let e2 = h2.data().e;

    debug_assert!(!(h1.data().sensor && h2.data().sensor));

    if h1.data().sensor {
        let states = &mut contact_states
            .entry(e1)
            .unwrap()
            .or_insert(Default::default())
            .0;
        debug_assert!(states.iter().filter(|v| v.target() == e2).count() == 0);
        states.push(ContactState::Started(e2));
    } else if h2.data().sensor {
        let states = &mut contact_states
            .entry(e2)
            .unwrap()
            .or_insert(Default::default())
            .0;
        debug_assert!(states.iter().filter(|v| v.target() == e1).count() == 0);
        states.push(ContactState::Started(e1));
    } else {
        {
            let states = &mut contact_states
                .entry(e1)
                .unwrap()
                .or_insert(Default::default())
                .0;
            debug_assert!(states.iter().filter(|v| v.target() == e2).count() == 0);
            states.push(ContactState::Started(e2));
        }

        let states = &mut contact_states
            .entry(e2)
            .unwrap()
            .or_insert(Default::default())
            .0;
        debug_assert!(states.iter().filter(|v| v.target() == e1).count() == 0);
        states.push(ContactState::Started(e1));
    }
}

/// Changes contact events into `Stopped` versions as they happen.
///
/// If one of the handles is a sensor, this will find the sensor's existing `Start`
/// or `Ongoing` corresponding to the detected entity and change it to `Stopped`
///
/// If neither is a sensor, this will symmetrically flag both with a `Stopped`
/// in the same manner.
///
/// During debug builds, this will verify that the target entity appears exactly
/// once in a list, as well as verifying that sensors do not detect
/// each other.
fn handle_stopped(
    col_world: &SkyCollisionWorld,
    contact_states: &mut WriteStorage<ContactStates>,
    h1: CollisionObjectHandle,
    h2: CollisionObjectHandle,
) {
    let h1 = col_world.collision_object(h1).unwrap();
    let h2 = col_world.collision_object(h2).unwrap();

    let e1 = h1.data().e;
    let e2 = h1.data().e;

    if e1 == e2 {
        return;
    }

    debug_assert!(!(h1.data().sensor && h2.data().sensor));

    if h1.data().sensor {
        let states = &mut contact_states
            .entry(e1)
            .unwrap()
            .or_insert(Default::default())
            .0;
        debug_assert!(states.iter().filter(|v| v.target() == e2).count() == 1);

        for v in states.iter_mut().filter(|v| v.target() == e2) {
            *v = ContactState::Stopped(v.target());
        }
    } else if h2.data().sensor {
        let states = &mut contact_states
            .entry(e2)
            .unwrap()
            .or_insert(Default::default())
            .0;
        debug_assert!(states.iter().filter(|v| v.target() == e1).count() == 1);
        for v in states.iter_mut().filter(|v| v.target() == e2) {
            *v = ContactState::Stopped(v.target());
        }
    } else {
        {
            let states = &mut contact_states
                .entry(e1)
                .unwrap()
                .or_insert(Default::default())
                .0;
            debug_assert!(states.iter().filter(|v| v.target() == e2).count() == 1);
            for v in states.iter_mut().filter(|v| v.target() == e2) {
                *v = ContactState::Stopped(v.target());
            }
        }

        let states = &mut contact_states
            .entry(e2)
            .unwrap()
            .or_insert(Default::default())
            .0;
        debug_assert!(states.iter().filter(|v| v.target() == e1).count() == 1);
        for v in states.iter_mut().filter(|v| v.target() == e2) {
            *v = ContactState::Stopped(v.target());
        }
    }
}
<commit_msg>fixup! fixup! City Attack v1 and minor fixes<commit_after>use engine::components::{CollisionHandle, ContactState, ContactStates, MovedFlag, Pos, Sensors};
use engine::resources::SkyCollisionWorld;
use ncollide::world::CollisionObjectHandle;
use specs::prelude::*;

#[derive(SystemData)]
pub struct CollisionSystemData<'a> {
    moved: ReadStorage<'a, MovedFlag>,
    pos: ReadStorage<'a, Pos>,
    c_handle: ReadStorage<'a, CollisionHandle>,
    sensors: ReadStorage<'a, Sensors>,
    ids: Entities<'a>,

    contact_states: WriteStorage<'a, ContactStates>,

    col_world: FetchMut<'a, SkyCollisionWorld>,
}

/// The `CollisionSystem` updates a moved entity's collisiion-related
/// marker positions (e.g. attack whiskers, rigid body, etc)
/// and registers the corresponding contact event components when collisions occur.
///
/// This is not responsible for removing no-longer happening collisions. That is the job
/// of the `CleanupSystem` which will remove `Stopped` collisions every update cycle.
///
/// In addition, other systems should verify their target has not disappeared, especially if relying on
/// `Ongoing` collision tags.
pub struct CollisionSystem;

impl<'a> System<'a> for CollisionSystem {
    type SystemData = CollisionSystemData<'a>;

    fn run(&mut self, mut sys_data: Self::SystemData) {
        use engine::resources::COLLISION_SCALE;
        use nalgebra;
        use nalgebra::{Isometry2, Vector2};
        use ncollide::events::ContactEvent;

        for (_, pos, c_handle, id) in (
            &sys_data.moved,
            &sys_data.pos,
            &sys_data.c_handle,
            &*sys_data.ids,
        ).join()
        {
            let pos = Isometry2::new(
                Vector2::new(pos.x / COLLISION_SCALE, pos.y / COLLISION_SCALE),
                nalgebra::zero(),
            );

            sys_data.col_world.set_position(c_handle.0, pos);

            if let Some(sensors) = sys_data.sensors.get(id) {
                for &sensor in sensors.0.values() {
                    let sensor_ch = sys_data
                        .c_handle
                        .get(sensor)
                        .expect("Sensor has no collision?");
                    sys_data.col_world.set_position(sensor_ch.0, pos);
                }
            }
        }

        sys_data.col_world.update();

        for event in sys_data.col_world.contact_events() {
            match event {
                ContactEvent::Started(h1, h2) => {
                    handle_started(&sys_data.col_world, &mut sys_data.contact_states, *h1, *h2);
                }
                ContactEvent::Stopped(h1, h2) => {
                    handle_stopped(&sys_data.col_world, &mut sys_data.contact_states, *h1, *h2);
                }
            }
        }
    }
}

/// Inserts contact events into storages as they happen.
///
/// If one of the handles is a sensor, this will only flag the sensor's contact events.
///
/// If neither is a sensor, both will be flagged with a `Started` pointing
/// to the opposite entity's `Entity` handle.
///
/// During debug builds, this will verify that entities do not double-appear in
/// an entity's collision list, as well as verifying that sensors do not detect
/// each other.
fn handle_started(
    col_world: &SkyCollisionWorld,
    contact_states: &mut WriteStorage<ContactStates>,
    h1: CollisionObjectHandle,
    h2: CollisionObjectHandle,
) {
    let h1 = col_world.collision_object(h1).unwrap();
    let h2 = col_world.collision_object(h2).unwrap();

    let e1 = h1.data().e;
    let e2 = h2.data().e;

    debug_assert!(!(h1.data().sensor && h2.data().sensor));

    if h1.data().sensor {
        let states = &mut contact_states
            .entry(e1)
            .unwrap()
            .or_insert(Default::default())
            .0;
        debug_assert!(states.iter().filter(|v| v.target() == e2).count() == 0);
        states.push(ContactState::Started(e2));
    } else if h2.data().sensor {
        let states = &mut contact_states
            .entry(e2)
            .unwrap()
            .or_insert(Default::default())
            .0;
        debug_assert!(states.iter().filter(|v| v.target() == e1).count() == 0);
        states.push(ContactState::Started(e1));
    } else {
        {
            let states = &mut contact_states
                .entry(e1)
                .unwrap()
                .or_insert(Default::default())
                .0;
            debug_assert!(states.iter().filter(|v| v.target() == e2).count() == 0);
            states.push(ContactState::Started(e2));
        }

        let states = &mut contact_states
            .entry(e2)
            .unwrap()
            .or_insert(Default::default())
            .0;
        debug_assert!(states.iter().filter(|v| v.target() == e1).count() == 0);
        states.push(ContactState::Started(e1));
    }
}

/// Changes contact events into `Stopped` versions as they happen.
///
/// If one of the handles is a sensor, this will find the sensor's existing `Start`
/// or `Ongoing` corresponding to the detected entity and change it to `Stopped`
///
/// If neither is a sensor, this will symmetrically flag both with a `Stopped`
/// in the same manner.
///
/// During debug builds, this will verify that the target entity appears exactly
/// once in a list, as well as verifying that sensors do not detect
/// each other.
fn handle_stopped(
    col_world: &SkyCollisionWorld,
    contact_states: &mut WriteStorage<ContactStates>,
    h1: CollisionObjectHandle,
    h2: CollisionObjectHandle,
) {
    let h1 = col_world.collision_object(h1).unwrap();
    let h2 = col_world.collision_object(h2).unwrap();

    let e1 = h1.data().e;
    let e2 = h1.data().e;

    if e1 == e2 {
        return;
    }

    debug_assert!(!(h1.data().sensor && h2.data().sensor));

    if h1.data().sensor {
        let states = &mut contact_states
            .entry(e1)
            .unwrap()
            .or_insert(Default::default())
            .0;
        debug_assert!(states.iter().filter(|v| v.target() == e2).count() == 1);

        for v in states.iter_mut().filter(|v| v.target() == e2) {
            *v = ContactState::Stopped(v.target());
        }
    } else if h2.data().sensor {
        let states = &mut contact_states
            .entry(e2)
            .unwrap()
            .or_insert(Default::default())
            .0;
        debug_assert!(states.iter().filter(|v| v.target() == e1).count() == 1);
        for v in states.iter_mut().filter(|v| v.target() == e2) {
            *v = ContactState::Stopped(v.target());
        }
    } else {
        {
            let states = &mut contact_states
                .entry(e1)
                .unwrap()
                .or_insert(Default::default())
                .0;
            debug_assert!(states.iter().filter(|v| v.target() == e2).count() == 1);
            for v in states.iter_mut().filter(|v| v.target() == e2) {
                *v = ContactState::Stopped(v.target());
            }
        }

        let states = &mut contact_states
            .entry(e2)
            .unwrap()
            .or_insert(Default::default())
            .0;
        debug_assert!(states.iter().filter(|v| v.target() == e1).count() == 1);
        for v in states.iter_mut().filter(|v| v.target() == e2) {
            *v = ContactState::Stopped(v.target());
        }
    }
}
<|endoftext|>


<commit_before>//! Details of the `ruma_api` procedural macro.

use std::convert::{TryFrom, TryInto as _};

use proc_macro2::{Span, TokenStream};
use quote::{quote, ToTokens};
use syn::{
    braced,
    parse::{Parse, ParseStream},
    Field, FieldValue, Ident, Token,
};

mod attribute;
mod metadata;
mod request;
mod response;

use self::{metadata::Metadata, request::Request, response::Response};

/// Removes `serde` attributes from struct fields.
pub fn strip_serde_attrs(field: &Field) -> Field {
    let mut field = field.clone();
    field.attrs.retain(|attr| attr.path.is_ident("serde"));
    field
}

/// The result of processing the `ruma_api` macro, ready for output back to source code.
pub struct Api {
    /// The `metadata` section of the macro.
    metadata: Metadata,
    /// The `request` section of the macro.
    request: Request,
    /// The `response` section of the macro.
    response: Response,
}

impl TryFrom<RawApi> for Api {
    type Error = syn::Error;

    fn try_from(raw_api: RawApi) -> syn::Result<Self> {
        let res = Self {
            metadata: raw_api.metadata.try_into()?,
            request: raw_api.request.try_into()?,
            response: raw_api.response.try_into()?,
        };

        let newtype_body_field = res.request.newtype_body_field();
        if res.metadata.method == "GET"
            && (res.request.has_body_fields() || newtype_body_field.is_some())
        {
            let mut combined_error: Option<syn::Error> = None;
            let mut add_error = |field| {
                let error = syn::Error::new_spanned(field, "GET endpoints can't have body fields");
                if let Some(combined_error_ref) = &mut combined_error {
                    combined_error_ref.combine(error);
                } else {
                    combined_error = Some(error);
                }
            };

            for field in res.request.body_fields() {
                add_error(field);
            }

            if let Some(field) = newtype_body_field {
                add_error(field);
            }

            Err(combined_error.unwrap())
        } else {
            Ok(res)
        }
    }
}

impl ToTokens for Api {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        let description = &self.metadata.description;
        let method = &self.metadata.method;
        // We don't (currently) use this literal as a literal in the generated code. Instead we just
        // put it into doc comments, for which the span information is irrelevant. So we can work
        // with only the literal's value from here on.
        let name = &self.metadata.name.value();
        let path = &self.metadata.path;
        let rate_limited = &self.metadata.rate_limited;
        let requires_authentication = &self.metadata.requires_authentication;

        let request = &self.request;
        let request_types = quote! { #request };
        let response = &self.response;
        let response_types = quote! { #response };

        let set_request_path = if self.request.has_path_fields() {
            let path_str = path.value();

            assert!(path_str.starts_with('/'), "path needs to start with '/'");
            assert!(
                path_str.chars().filter(|c| *c == ':').count() == self.request.path_field_count(),
                "number of declared path parameters needs to match amount of placeholders in path"
            );

            let request_path_init_fields = self.request.request_path_init_fields();

            let path_segments = path_str[1..].split('/');
            let path_segment_push = path_segments.map(|segment| {
                let arg = if segment.starts_with(':') {
                    let path_var = &segment[1..];
                    let path_var_ident = Ident::new(path_var, Span::call_site());
                    quote!(&request_path.#path_var_ident.to_string())
                } else {
                    quote!(#segment)
                };

                quote! {
                    path_segments.push(#arg);
                }
            });

            quote! {
                let request_path = RequestPath {
                    #request_path_init_fields
                };

                // This `unwrap()` can only fail when the url is a
                // cannot-be-base url like `mailto:` or `data:`, which is not
                // the case for our placeholder url.
                let mut path_segments = url.path_segments_mut().unwrap();
                #(#path_segment_push)*
            }
        } else {
            quote! {
                url.set_path(metadata.path);
            }
        };

        let set_request_query = if let Some(field) = self.request.query_map_field() {
            let field_name = field.ident.as_ref().expect("expected field to have identifier");
            let field_type = &field.ty;

            quote! {
                // This function exists so that the compiler will throw an
                // error when the type of the field with the query_map
                // attribute doesn't implement IntoIterator<Item = (String, String)>
                //
                // This is necessary because the serde_urlencoded::to_string
                // call will result in a runtime error when the type cannot be
                // encoded as a list key-value pairs (?key1=value1&key2=value2)
                //
                // By asserting that it implements the iterator trait, we can
                // ensure that it won't fail.
                fn assert_trait_impl<T>()
                where
                    T: std::iter::IntoIterator<Item = (std::string::String, std::string::String)>,
                {}
                assert_trait_impl::<#field_type>();

                let request_query = RequestQuery(request.#field_name);
                let query_str = ruma_api::exports::serde_urlencoded::to_string(
                    request_query,
                )?;

                let query_opt: Option<&str> = if query_str.is_empty() {
                    None
                } else {
                    Some(&query_str)
                };

                url.set_query(query_opt);
            }
        } else if self.request.has_query_fields() {
            let request_query_init_fields = self.request.request_query_init_fields();

            quote! {
                let request_query = RequestQuery {
                    #request_query_init_fields
                };

                url.set_query(Some(&ruma_api::exports::serde_urlencoded::to_string(
                    request_query,
                )?));
            }
        } else {
            TokenStream::new()
        };

        let add_headers_to_request = if self.request.has_header_fields() {
            let add_headers = self.request.add_headers_to_request();
            quote! {
                let headers = http_request.headers_mut();
                #add_headers
            }
        } else {
            TokenStream::new()
        };

        let create_http_request = if let Some(field) = self.request.newtype_body_field() {
            let field_name = field.ident.as_ref().expect("expected field to have an identifier");

            quote! {
                let request_body = RequestBody(request.#field_name);

                let mut http_request = ruma_api::exports::http::Request::new(
                    ruma_api::exports::serde_json::to_vec(&request_body)?,
                );
            }
        } else if self.request.has_body_fields() {
            let request_body_init_fields = self.request.request_body_init_fields();

            quote! {
                let request_body = RequestBody {
                    #request_body_init_fields
                };

                let mut http_request = ruma_api::exports::http::Request::new(
                    ruma_api::exports::serde_json::to_vec(&request_body)?,
                );
            }
        } else {
            quote! {
                let mut http_request = ruma_api::exports::http::Request::new(Vec::new());
            }
        };

        let try_deserialize_response_body = if let Some(field) = self.response.newtype_body_field()
        {
            let field_type = &field.ty;

            quote! {
                ruma_api::exports::serde_json::from_slice::<#field_type>(
                    http_response.into_body().as_slice(),
                )?
            }
        } else if self.response.has_body_fields() {
            quote! {
                ruma_api::exports::serde_json::from_slice::<ResponseBody>(
                    http_response.into_body().as_slice(),
                )?
            }
        } else {
            quote! {
                ()
            }
        };

        let extract_response_headers = if self.response.has_header_fields() {
            quote! {
                let mut headers = http_response.headers().clone();
            }
        } else {
            TokenStream::new()
        };

        let response_init_fields = if self.response.has_fields() {
            self.response.init_fields()
        } else {
            TokenStream::new()
        };

        let request_doc = format!(
            "Data for a request to the `{}` API endpoint.\n\n{}",
            name,
            description.value()
        );
        let response_doc = format!("Data in the response from the `{}` API endpoint.", name);

        let api = quote! {
            use ruma_api::exports::serde::de::{Error as _, IntoDeserializer as _};
            use ruma_api::exports::serde::Deserialize as _;
            use ruma_api::Endpoint as _;

            use std::convert::TryInto as _;

            #[doc = #request_doc]
            #request_types

            impl std::convert::TryFrom<Request> for ruma_api::exports::http::Request<Vec<u8>> {
                type Error = ruma_api::Error;

                #[allow(unused_mut, unused_variables)]
                fn try_from(request: Request) -> Result<Self, Self::Error> {
                    let metadata = Request::METADATA;

                    // Use dummy homeserver url which has to be overwritten in
                    // the calling code. Previously (with http::Uri) this was
                    // not required, but Url::parse only accepts absolute urls.
                    let mut url =
                        ruma_api::exports::url::Url::parse("http://invalid-host-please-change/")
                            .unwrap();

                    { #set_request_path }
                    { #set_request_query }

                    #create_http_request

                    *http_request.method_mut() = ruma_api::exports::http::Method::#method;
                    *http_request.uri_mut() = url.into_string().parse().unwrap();

                    { #add_headers_to_request }

                    Ok(http_request)
                }
            }

            #[doc = #response_doc]
            #response_types

            impl std::convert::TryFrom<ruma_api::exports::http::Response<Vec<u8>>> for Response {
                type Error = ruma_api::Error;

                #[allow(unused_variables)]
                fn try_from(
                    http_response: ruma_api::exports::http::Response<Vec<u8>>,
                ) -> Result<Self, Self::Error> {
                    if http_response.status().is_success() {
                        #extract_response_headers

                        let response_body = #try_deserialize_response_body;
                        Ok(Response {
                            #response_init_fields
                        })
                    } else {
                        Err(http_response.status().clone().into())
                    }
                }
            }

            impl ruma_api::Endpoint for Request {
                type Response = Response;

                /// Metadata for the `#name` endpoint.
                const METADATA: ruma_api::Metadata = ruma_api::Metadata {
                    description: #description,
                    method: ruma_api::exports::http::Method::#method,
                    name: #name,
                    path: #path,
                    rate_limited: #rate_limited,
                    requires_authentication: #requires_authentication,
                };
            }
        };

        api.to_tokens(tokens);
    }
}

/// Custom keyword macros for syn.
mod kw {
    use syn::custom_keyword;

    custom_keyword!(metadata);
    custom_keyword!(request);
    custom_keyword!(response);
}

/// The entire `ruma_api!` macro structure directly as it appears in the source code..
pub struct RawApi {
    /// The `metadata` section of the macro.
    pub metadata: RawMetadata,
    /// The `request` section of the macro.
    pub request: RawRequest,
    /// The `response` section of the macro.
    pub response: RawResponse,
}

impl Parse for RawApi {
    fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
        Ok(Self { metadata: input.parse()?, request: input.parse()?, response: input.parse()? })
    }
}

pub struct RawMetadata {
    pub metadata_kw: kw::metadata,
    pub field_values: Vec<FieldValue>,
}

impl Parse for RawMetadata {
    fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
        let metadata_kw = input.parse::<kw::metadata>()?;
        let field_values;
        braced!(field_values in input);

        Ok(Self {
            metadata_kw,
            field_values: field_values
                .parse_terminated::<FieldValue, Token![,]>(FieldValue::parse)?
                .into_iter()
                .collect(),
        })
    }
}

pub struct RawRequest {
    pub request_kw: kw::request,
    pub fields: Vec<Field>,
}

impl Parse for RawRequest {
    fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
        let request_kw = input.parse::<kw::request>()?;
        let fields;
        braced!(fields in input);

        Ok(Self {
            request_kw,
            fields: fields
                .parse_terminated::<Field, Token![,]>(Field::parse_named)?
                .into_iter()
                .collect(),
        })
    }
}

pub struct RawResponse {
    pub response_kw: kw::response,
    pub fields: Vec<Field>,
}

impl Parse for RawResponse {
    fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
        let response_kw = input.parse::<kw::response>()?;
        let fields;
        braced!(fields in input);

        Ok(Self {
            response_kw,
            fields: fields
                .parse_terminated::<Field, Token![,]>(Field::parse_named)?
                .into_iter()
                .collect(),
        })
    }
}
<commit_msg>Fix inverted logic in strip_serde_attrs<commit_after>//! Details of the `ruma_api` procedural macro.

use std::convert::{TryFrom, TryInto as _};

use proc_macro2::{Span, TokenStream};
use quote::{quote, ToTokens};
use syn::{
    braced,
    parse::{Parse, ParseStream},
    Field, FieldValue, Ident, Token,
};

mod attribute;
mod metadata;
mod request;
mod response;

use self::{metadata::Metadata, request::Request, response::Response};

/// Removes `serde` attributes from struct fields.
pub fn strip_serde_attrs(field: &Field) -> Field {
    let mut field = field.clone();
    field.attrs.retain(|attr| !attr.path.is_ident("serde"));
    field
}

/// The result of processing the `ruma_api` macro, ready for output back to source code.
pub struct Api {
    /// The `metadata` section of the macro.
    metadata: Metadata,
    /// The `request` section of the macro.
    request: Request,
    /// The `response` section of the macro.
    response: Response,
}

impl TryFrom<RawApi> for Api {
    type Error = syn::Error;

    fn try_from(raw_api: RawApi) -> syn::Result<Self> {
        let res = Self {
            metadata: raw_api.metadata.try_into()?,
            request: raw_api.request.try_into()?,
            response: raw_api.response.try_into()?,
        };

        let newtype_body_field = res.request.newtype_body_field();
        if res.metadata.method == "GET"
            && (res.request.has_body_fields() || newtype_body_field.is_some())
        {
            let mut combined_error: Option<syn::Error> = None;
            let mut add_error = |field| {
                let error = syn::Error::new_spanned(field, "GET endpoints can't have body fields");
                if let Some(combined_error_ref) = &mut combined_error {
                    combined_error_ref.combine(error);
                } else {
                    combined_error = Some(error);
                }
            };

            for field in res.request.body_fields() {
                add_error(field);
            }

            if let Some(field) = newtype_body_field {
                add_error(field);
            }

            Err(combined_error.unwrap())
        } else {
            Ok(res)
        }
    }
}

impl ToTokens for Api {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        let description = &self.metadata.description;
        let method = &self.metadata.method;
        // We don't (currently) use this literal as a literal in the generated code. Instead we just
        // put it into doc comments, for which the span information is irrelevant. So we can work
        // with only the literal's value from here on.
        let name = &self.metadata.name.value();
        let path = &self.metadata.path;
        let rate_limited = &self.metadata.rate_limited;
        let requires_authentication = &self.metadata.requires_authentication;

        let request = &self.request;
        let request_types = quote! { #request };
        let response = &self.response;
        let response_types = quote! { #response };

        let set_request_path = if self.request.has_path_fields() {
            let path_str = path.value();

            assert!(path_str.starts_with('/'), "path needs to start with '/'");
            assert!(
                path_str.chars().filter(|c| *c == ':').count() == self.request.path_field_count(),
                "number of declared path parameters needs to match amount of placeholders in path"
            );

            let request_path_init_fields = self.request.request_path_init_fields();

            let path_segments = path_str[1..].split('/');
            let path_segment_push = path_segments.map(|segment| {
                let arg = if segment.starts_with(':') {
                    let path_var = &segment[1..];
                    let path_var_ident = Ident::new(path_var, Span::call_site());
                    quote!(&request_path.#path_var_ident.to_string())
                } else {
                    quote!(#segment)
                };

                quote! {
                    path_segments.push(#arg);
                }
            });

            quote! {
                let request_path = RequestPath {
                    #request_path_init_fields
                };

                // This `unwrap()` can only fail when the url is a
                // cannot-be-base url like `mailto:` or `data:`, which is not
                // the case for our placeholder url.
                let mut path_segments = url.path_segments_mut().unwrap();
                #(#path_segment_push)*
            }
        } else {
            quote! {
                url.set_path(metadata.path);
            }
        };

        let set_request_query = if let Some(field) = self.request.query_map_field() {
            let field_name = field.ident.as_ref().expect("expected field to have identifier");
            let field_type = &field.ty;

            quote! {
                // This function exists so that the compiler will throw an
                // error when the type of the field with the query_map
                // attribute doesn't implement IntoIterator<Item = (String, String)>
                //
                // This is necessary because the serde_urlencoded::to_string
                // call will result in a runtime error when the type cannot be
                // encoded as a list key-value pairs (?key1=value1&key2=value2)
                //
                // By asserting that it implements the iterator trait, we can
                // ensure that it won't fail.
                fn assert_trait_impl<T>()
                where
                    T: std::iter::IntoIterator<Item = (std::string::String, std::string::String)>,
                {}
                assert_trait_impl::<#field_type>();

                let request_query = RequestQuery(request.#field_name);
                let query_str = ruma_api::exports::serde_urlencoded::to_string(
                    request_query,
                )?;

                let query_opt: Option<&str> = if query_str.is_empty() {
                    None
                } else {
                    Some(&query_str)
                };

                url.set_query(query_opt);
            }
        } else if self.request.has_query_fields() {
            let request_query_init_fields = self.request.request_query_init_fields();

            quote! {
                let request_query = RequestQuery {
                    #request_query_init_fields
                };

                url.set_query(Some(&ruma_api::exports::serde_urlencoded::to_string(
                    request_query,
                )?));
            }
        } else {
            TokenStream::new()
        };

        let add_headers_to_request = if self.request.has_header_fields() {
            let add_headers = self.request.add_headers_to_request();
            quote! {
                let headers = http_request.headers_mut();
                #add_headers
            }
        } else {
            TokenStream::new()
        };

        let create_http_request = if let Some(field) = self.request.newtype_body_field() {
            let field_name = field.ident.as_ref().expect("expected field to have an identifier");

            quote! {
                let request_body = RequestBody(request.#field_name);

                let mut http_request = ruma_api::exports::http::Request::new(
                    ruma_api::exports::serde_json::to_vec(&request_body)?,
                );
            }
        } else if self.request.has_body_fields() {
            let request_body_init_fields = self.request.request_body_init_fields();

            quote! {
                let request_body = RequestBody {
                    #request_body_init_fields
                };

                let mut http_request = ruma_api::exports::http::Request::new(
                    ruma_api::exports::serde_json::to_vec(&request_body)?,
                );
            }
        } else {
            quote! {
                let mut http_request = ruma_api::exports::http::Request::new(Vec::new());
            }
        };

        let try_deserialize_response_body = if let Some(field) = self.response.newtype_body_field()
        {
            let field_type = &field.ty;

            quote! {
                ruma_api::exports::serde_json::from_slice::<#field_type>(
                    http_response.into_body().as_slice(),
                )?
            }
        } else if self.response.has_body_fields() {
            quote! {
                ruma_api::exports::serde_json::from_slice::<ResponseBody>(
                    http_response.into_body().as_slice(),
                )?
            }
        } else {
            quote! {
                ()
            }
        };

        let extract_response_headers = if self.response.has_header_fields() {
            quote! {
                let mut headers = http_response.headers().clone();
            }
        } else {
            TokenStream::new()
        };

        let response_init_fields = if self.response.has_fields() {
            self.response.init_fields()
        } else {
            TokenStream::new()
        };

        let request_doc = format!(
            "Data for a request to the `{}` API endpoint.\n\n{}",
            name,
            description.value()
        );
        let response_doc = format!("Data in the response from the `{}` API endpoint.", name);

        let api = quote! {
            use ruma_api::exports::serde::de::{Error as _, IntoDeserializer as _};
            use ruma_api::exports::serde::Deserialize as _;
            use ruma_api::Endpoint as _;

            use std::convert::TryInto as _;

            #[doc = #request_doc]
            #request_types

            impl std::convert::TryFrom<Request> for ruma_api::exports::http::Request<Vec<u8>> {
                type Error = ruma_api::Error;

                #[allow(unused_mut, unused_variables)]
                fn try_from(request: Request) -> Result<Self, Self::Error> {
                    let metadata = Request::METADATA;

                    // Use dummy homeserver url which has to be overwritten in
                    // the calling code. Previously (with http::Uri) this was
                    // not required, but Url::parse only accepts absolute urls.
                    let mut url =
                        ruma_api::exports::url::Url::parse("http://invalid-host-please-change/")
                            .unwrap();

                    { #set_request_path }
                    { #set_request_query }

                    #create_http_request

                    *http_request.method_mut() = ruma_api::exports::http::Method::#method;
                    *http_request.uri_mut() = url.into_string().parse().unwrap();

                    { #add_headers_to_request }

                    Ok(http_request)
                }
            }

            #[doc = #response_doc]
            #response_types

            impl std::convert::TryFrom<ruma_api::exports::http::Response<Vec<u8>>> for Response {
                type Error = ruma_api::Error;

                #[allow(unused_variables)]
                fn try_from(
                    http_response: ruma_api::exports::http::Response<Vec<u8>>,
                ) -> Result<Self, Self::Error> {
                    if http_response.status().is_success() {
                        #extract_response_headers

                        let response_body = #try_deserialize_response_body;
                        Ok(Response {
                            #response_init_fields
                        })
                    } else {
                        Err(http_response.status().clone().into())
                    }
                }
            }

            impl ruma_api::Endpoint for Request {
                type Response = Response;

                /// Metadata for the `#name` endpoint.
                const METADATA: ruma_api::Metadata = ruma_api::Metadata {
                    description: #description,
                    method: ruma_api::exports::http::Method::#method,
                    name: #name,
                    path: #path,
                    rate_limited: #rate_limited,
                    requires_authentication: #requires_authentication,
                };
            }
        };

        api.to_tokens(tokens);
    }
}

/// Custom keyword macros for syn.
mod kw {
    use syn::custom_keyword;

    custom_keyword!(metadata);
    custom_keyword!(request);
    custom_keyword!(response);
}

/// The entire `ruma_api!` macro structure directly as it appears in the source code..
pub struct RawApi {
    /// The `metadata` section of the macro.
    pub metadata: RawMetadata,
    /// The `request` section of the macro.
    pub request: RawRequest,
    /// The `response` section of the macro.
    pub response: RawResponse,
}

impl Parse for RawApi {
    fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
        Ok(Self { metadata: input.parse()?, request: input.parse()?, response: input.parse()? })
    }
}

pub struct RawMetadata {
    pub metadata_kw: kw::metadata,
    pub field_values: Vec<FieldValue>,
}

impl Parse for RawMetadata {
    fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
        let metadata_kw = input.parse::<kw::metadata>()?;
        let field_values;
        braced!(field_values in input);

        Ok(Self {
            metadata_kw,
            field_values: field_values
                .parse_terminated::<FieldValue, Token![,]>(FieldValue::parse)?
                .into_iter()
                .collect(),
        })
    }
}

pub struct RawRequest {
    pub request_kw: kw::request,
    pub fields: Vec<Field>,
}

impl Parse for RawRequest {
    fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
        let request_kw = input.parse::<kw::request>()?;
        let fields;
        braced!(fields in input);

        Ok(Self {
            request_kw,
            fields: fields
                .parse_terminated::<Field, Token![,]>(Field::parse_named)?
                .into_iter()
                .collect(),
        })
    }
}

pub struct RawResponse {
    pub response_kw: kw::response,
    pub fields: Vec<Field>,
}

impl Parse for RawResponse {
    fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
        let response_kw = input.parse::<kw::response>()?;
        let fields;
        braced!(fields in input);

        Ok(Self {
            response_kw,
            fields: fields
                .parse_terminated::<Field, Token![,]>(Field::parse_named)?
                .into_iter()
                .collect(),
        })
    }
}
<|endoftext|>


<commit_before>extern crate cairo;

#[derive(Debug)]
#[derive(PartialEq)]
pub enum Segment {
    Degenerate {            // A single lone point
        x: f64,
        y: f64
    },

    LineOrCurve {
        x1: f64, y1: f64,
        x2: f64, y2: f64,
        x3: f64, y3: f64,
        x4: f64, y4: f64
    },
}

enum SegmentState {
    Start,
    End
}

/* This converts a cairo_path_t into a list of curveto-like segments.  Each segment can be:
 *
 * 1. Segment::Degenerate => the segment is actually a single point (x, y)
 *
 * 2. Segment::LineOrCurve => either a lineto or a curveto (or the effective lineto that results from a closepath).
 *    We have the following points:
 *       P1 = (x1, y1)
 *       P2 = (x2, y2)
 *       P3 = (x3, y3)
 *       P4 = (x4, y4)
 *
 *    The start and end points are P1 and P4, respectively.
 *    The tangent at the start point is given by the vector (P2 - P1).
 *    The tangent at the end point is given by the vector (P4 - P3).
 *    The tangents also work if the segment refers to a lineto (they will both just point in the same direction).
 */

const EPSILON: f64 = 1e-10;

fn double_equals (a: f64, b: f64) -> bool {
    (a - b).abs () < EPSILON
}

pub fn path_to_segments (path: cairo::Path) -> Vec<Segment> {
    let mut last_x: f64;
    let mut last_y: f64;
    let mut cur_x: f64;
    let mut cur_y: f64;
    let mut subpath_start_x: f64;
    let mut subpath_start_y: f64;
    let mut segments: Vec<Segment>;
    let mut state: SegmentState;

    cur_x = 0.0;
    cur_y = 0.0;
    subpath_start_x = 0.0;
    subpath_start_y = 0.0;

    segments = Vec::new ();
    state = SegmentState::End;

    for cairo_segment in path.iter () {
        last_x = cur_x;
        last_y = cur_y;

        let needs_new_segment: bool;
        let seg: Segment;

        match cairo_segment {
            cairo::PathSegment::MoveTo ((x, y)) => {
                cur_x = x;
                cur_y = y;

                seg = Segment::Degenerate {
                    x: cur_x,
                    y: cur_y
                };
                needs_new_segment = true;

                subpath_start_x = cur_x;
                subpath_start_y = cur_y;

                state = SegmentState::Start;
            },

            cairo::PathSegment::LineTo ((x, y)) => {
                cur_x = x;
                cur_y = y;

                seg = Segment::LineOrCurve {
                    x1: last_x,
                    y1: last_y,

                    x2: cur_x,
                    y2: cur_y,

                    x3: last_x,
                    y3: last_y,

                    x4: cur_x,
                    y4: cur_y,
                };

                match state {
                    SegmentState::Start => {
                        state = SegmentState::End;
                        needs_new_segment = false;
                    },

                    SegmentState::End => {
                        needs_new_segment = true;
                    }
                }
            },

            cairo::PathSegment::CurveTo ((mut x2, mut y2), (mut x3, mut y3), (x4, y4)) => {
                cur_x = x4;
                cur_y = y4;

                /* Fix the tangents for when the middle control points coincide with their respective endpoints */

                let x1 = last_x;
                let y1 = last_y;

                if double_equals (x2, x1) && double_equals (y2, y1) {
                    x2 = x3;
                    y2 = y3;
                }

                if double_equals (x3, x4) && double_equals (y3, y4) {
                    x3 = x2;
                    y3 = y2;
                }

                seg = Segment::LineOrCurve {
                    x1: x1,
                    y1: y1,

                    x2: x2,
                    y2: y2,

                    x3: x3,
                    y3: y3,

                    x4: x4,
                    y4: y4,
                };

                match state {
                    SegmentState::Start => {
                        state = SegmentState::End;
                        needs_new_segment = false;
                    },

                    SegmentState::End => {
                        needs_new_segment = true;
                    }
                }
            }

            cairo::PathSegment::ClosePath => {
                cur_x = subpath_start_x;
                cur_y = subpath_start_y;

                seg = Segment::LineOrCurve {
                    x1: last_x,
                    y1: last_y,

                    x2: cur_x,
                    y2: cur_y,

                    x3: last_x,
                    y3: last_y,

                    x4: cur_x,
                    y4: cur_y,
                };

                match state {
                    SegmentState::Start => {
                        state = SegmentState::End;
                        needs_new_segment = false;
                    },

                    SegmentState::End => {
                        needs_new_segment = false;
                        /* nothing; closepath after moveto (or a single lone closepath) does nothing */
                    }
                }
            }
        }

        if needs_new_segment {
            segments.push (seg);
        } else {
            let len = segments.len ();
            segments[len - 1] = seg;
        }
    }

    segments
}

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

    fn create_cr () -> cairo::Context {
        let surf = cairo::ImageSurface::create (cairo::Format::Rgb24, 1, 1);
        let cr = cairo::Context::new (&surf);

        cr
    }

    fn degenerate (x: f64, y: f64) -> Segment {
        Segment::Degenerate { x: x, y: y }
    }

    fn line_or_curve (x1: f64, y1: f64, x2: f64, y2: f64, x3: f64, y3: f64, x4: f64, y4: f64) -> Segment {
        Segment::LineOrCurve {
            x1: x1, y1: y1, x2: x2, y2: y2, x3: x3, y3: y3, x4: x4, y4: y4
        }
    }

    /* Single open path */

    fn setup_open_path () -> cairo::Path {
        let cr = create_cr ();

        cr.move_to (10.0, 10.0);
        cr.line_to (20.0, 10.0);
        cr.line_to (20.0, 20.0);

        let path = cr.copy_path ();
        path
    }

    #[test]
    fn path_to_segments_handles_open_path () {
        let expected_segments: Vec<Segment> = vec![
            line_or_curve (10.0, 10.0, 20.0, 10.0, 10.0, 10.0, 20.0, 10.0),
            line_or_curve (20.0, 10.0, 20.0, 20.0, 20.0, 10.0, 20.0, 20.0)
        ];

        let path = setup_open_path ();
        let segments = path_to_segments (path);

        assert_eq! (expected_segments, segments);
    }

    /* Multiple open subpaths */

    fn setup_multiple_open_subpaths () -> cairo::Path {
        let cr = create_cr ();

        cr.move_to (10.0, 10.0);
        cr.line_to (20.0, 10.0);
        cr.line_to (20.0, 20.0);

        cr.move_to (30.0, 30.0);
        cr.line_to (40.0, 30.0);
        cr.curve_to (50.0, 35.0, 60.0, 60.0, 70.0, 70.0);
        cr.line_to (80.0, 90.0);

        let path = cr.copy_path ();
        path
    }

    #[test]
    fn path_to_segments_handles_multiple_open_subpaths () {
        let expected_segments: Vec<Segment> = vec![
            line_or_curve (10.0, 10.0, 20.0, 10.0, 10.0, 10.0, 20.0, 10.0),
            line_or_curve (20.0, 10.0, 20.0, 20.0, 20.0, 10.0, 20.0, 20.0),

            line_or_curve (30.0, 30.0, 40.0, 30.0, 30.0, 30.0, 40.0, 30.0),
            line_or_curve (40.0, 30.0, 50.0, 35.0, 60.0, 60.0, 70.0, 70.0),
            line_or_curve (70.0, 70.0, 80.0, 90.0, 70.0, 70.0, 80.0, 90.0)
        ];

        let path = setup_multiple_open_subpaths ();
        let segments = path_to_segments (path);

        assert_eq! (expected_segments, segments);
    }
}
<commit_msg>marker.rs: Utility function to test path_to_segments()<commit_after>extern crate cairo;

#[derive(Debug)]
#[derive(PartialEq)]
pub enum Segment {
    Degenerate {            // A single lone point
        x: f64,
        y: f64
    },

    LineOrCurve {
        x1: f64, y1: f64,
        x2: f64, y2: f64,
        x3: f64, y3: f64,
        x4: f64, y4: f64
    },
}

enum SegmentState {
    Start,
    End
}

/* This converts a cairo_path_t into a list of curveto-like segments.  Each segment can be:
 *
 * 1. Segment::Degenerate => the segment is actually a single point (x, y)
 *
 * 2. Segment::LineOrCurve => either a lineto or a curveto (or the effective lineto that results from a closepath).
 *    We have the following points:
 *       P1 = (x1, y1)
 *       P2 = (x2, y2)
 *       P3 = (x3, y3)
 *       P4 = (x4, y4)
 *
 *    The start and end points are P1 and P4, respectively.
 *    The tangent at the start point is given by the vector (P2 - P1).
 *    The tangent at the end point is given by the vector (P4 - P3).
 *    The tangents also work if the segment refers to a lineto (they will both just point in the same direction).
 */

const EPSILON: f64 = 1e-10;

fn double_equals (a: f64, b: f64) -> bool {
    (a - b).abs () < EPSILON
}

pub fn path_to_segments (path: cairo::Path) -> Vec<Segment> {
    let mut last_x: f64;
    let mut last_y: f64;
    let mut cur_x: f64;
    let mut cur_y: f64;
    let mut subpath_start_x: f64;
    let mut subpath_start_y: f64;
    let mut segments: Vec<Segment>;
    let mut state: SegmentState;

    cur_x = 0.0;
    cur_y = 0.0;
    subpath_start_x = 0.0;
    subpath_start_y = 0.0;

    segments = Vec::new ();
    state = SegmentState::End;

    for cairo_segment in path.iter () {
        last_x = cur_x;
        last_y = cur_y;

        let needs_new_segment: bool;
        let seg: Segment;

        match cairo_segment {
            cairo::PathSegment::MoveTo ((x, y)) => {
                cur_x = x;
                cur_y = y;

                seg = Segment::Degenerate {
                    x: cur_x,
                    y: cur_y
                };
                needs_new_segment = true;

                subpath_start_x = cur_x;
                subpath_start_y = cur_y;

                state = SegmentState::Start;
            },

            cairo::PathSegment::LineTo ((x, y)) => {
                cur_x = x;
                cur_y = y;

                seg = Segment::LineOrCurve {
                    x1: last_x,
                    y1: last_y,

                    x2: cur_x,
                    y2: cur_y,

                    x3: last_x,
                    y3: last_y,

                    x4: cur_x,
                    y4: cur_y,
                };

                match state {
                    SegmentState::Start => {
                        state = SegmentState::End;
                        needs_new_segment = false;
                    },

                    SegmentState::End => {
                        needs_new_segment = true;
                    }
                }
            },

            cairo::PathSegment::CurveTo ((mut x2, mut y2), (mut x3, mut y3), (x4, y4)) => {
                cur_x = x4;
                cur_y = y4;

                /* Fix the tangents for when the middle control points coincide with their respective endpoints */

                let x1 = last_x;
                let y1 = last_y;

                if double_equals (x2, x1) && double_equals (y2, y1) {
                    x2 = x3;
                    y2 = y3;
                }

                if double_equals (x3, x4) && double_equals (y3, y4) {
                    x3 = x2;
                    y3 = y2;
                }

                seg = Segment::LineOrCurve {
                    x1: x1,
                    y1: y1,

                    x2: x2,
                    y2: y2,

                    x3: x3,
                    y3: y3,

                    x4: x4,
                    y4: y4,
                };

                match state {
                    SegmentState::Start => {
                        state = SegmentState::End;
                        needs_new_segment = false;
                    },

                    SegmentState::End => {
                        needs_new_segment = true;
                    }
                }
            }

            cairo::PathSegment::ClosePath => {
                cur_x = subpath_start_x;
                cur_y = subpath_start_y;

                seg = Segment::LineOrCurve {
                    x1: last_x,
                    y1: last_y,

                    x2: cur_x,
                    y2: cur_y,

                    x3: last_x,
                    y3: last_y,

                    x4: cur_x,
                    y4: cur_y,
                };

                match state {
                    SegmentState::Start => {
                        state = SegmentState::End;
                        needs_new_segment = false;
                    },

                    SegmentState::End => {
                        needs_new_segment = false;
                        /* nothing; closepath after moveto (or a single lone closepath) does nothing */
                    }
                }
            }
        }

        if needs_new_segment {
            segments.push (seg);
        } else {
            let len = segments.len ();
            segments[len - 1] = seg;
        }
    }

    segments
}

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

    fn create_cr () -> cairo::Context {
        let surf = cairo::ImageSurface::create (cairo::Format::Rgb24, 1, 1);
        let cr = cairo::Context::new (&surf);

        cr
    }

    fn degenerate (x: f64, y: f64) -> Segment {
        Segment::Degenerate { x: x, y: y }
    }

    fn line_or_curve (x1: f64, y1: f64, x2: f64, y2: f64, x3: f64, y3: f64, x4: f64, y4: f64) -> Segment {
        Segment::LineOrCurve {
            x1: x1, y1: y1, x2: x2, y2: y2, x3: x3, y3: y3, x4: x4, y4: y4
        }
    }

    fn test_path_to_segments (path: cairo::Path, expected_segments: Vec<Segment>) {
        let segments = path_to_segments (path);
        assert_eq! (segments, expected_segments);
    }

    /* Single open path */

    fn setup_open_path () -> cairo::Path {
        let cr = create_cr ();

        cr.move_to (10.0, 10.0);
        cr.line_to (20.0, 10.0);
        cr.line_to (20.0, 20.0);

        let path = cr.copy_path ();
        path
    }

    #[test]
    fn path_to_segments_handles_open_path () {
        let expected_segments: Vec<Segment> = vec![
            line_or_curve (10.0, 10.0, 20.0, 10.0, 10.0, 10.0, 20.0, 10.0),
            line_or_curve (20.0, 10.0, 20.0, 20.0, 20.0, 10.0, 20.0, 20.0)
        ];

        test_path_to_segments (setup_open_path(), expected_segments);
    }

    /* Multiple open subpaths */

    fn setup_multiple_open_subpaths () -> cairo::Path {
        let cr = create_cr ();

        cr.move_to (10.0, 10.0);
        cr.line_to (20.0, 10.0);
        cr.line_to (20.0, 20.0);

        cr.move_to (30.0, 30.0);
        cr.line_to (40.0, 30.0);
        cr.curve_to (50.0, 35.0, 60.0, 60.0, 70.0, 70.0);
        cr.line_to (80.0, 90.0);

        let path = cr.copy_path ();
        path
    }

    #[test]
    fn path_to_segments_handles_multiple_open_subpaths () {
        let expected_segments: Vec<Segment> = vec![
            line_or_curve (10.0, 10.0, 20.0, 10.0, 10.0, 10.0, 20.0, 10.0),
            line_or_curve (20.0, 10.0, 20.0, 20.0, 20.0, 10.0, 20.0, 20.0),

            line_or_curve (30.0, 30.0, 40.0, 30.0, 30.0, 30.0, 40.0, 30.0),
            line_or_curve (40.0, 30.0, 50.0, 35.0, 60.0, 60.0, 70.0, 70.0),
            line_or_curve (70.0, 70.0, 80.0, 90.0, 70.0, 70.0, 80.0, 90.0)
        ];

        test_path_to_segments (setup_multiple_open_subpaths (), expected_segments);
    }
}
<|endoftext|>


<commit_before>use byteorder::{BigEndian, ReadBytesExt};
use chrono::{TimeZone, UTC};
use std::io::{Cursor, Read, Seek, SeekFrom};
use std::string::{FromUtf8Error, FromUtf16Error};

use {Error, Result, PlistEvent, u64_to_usize};

impl From<FromUtf8Error> for Error {
    fn from(_: FromUtf8Error) -> Error {
        Error::InvalidData
    }
}

impl From<FromUtf16Error> for Error {
    fn from(_: FromUtf16Error) -> Error {
        Error::InvalidData
    }
}

struct StackItem {
    object_refs: Vec<u64>,
    ty: StackType,
}

enum StackType {
    Array,
    Dict,
    Root,
}

/// https://opensource.apple.com/source/CF/CF-550/CFBinaryPList.c
/// https://hg.python.org/cpython/file/3.4/Lib/plistlib.py
pub struct EventReader<R> {
    stack: Vec<StackItem>,
    object_offsets: Vec<u64>,
    reader: R,
    ref_size: u8,
    finished: bool,
}

impl<R: Read + Seek> EventReader<R> {
    pub fn new(reader: R) -> EventReader<R> {
        EventReader {
            stack: Vec::new(),
            object_offsets: Vec::new(),
            reader: reader,
            ref_size: 0,
            finished: false,
        }
    }

    fn read_trailer(&mut self) -> Result<()> {
        try!(self.reader.seek(SeekFrom::Start(0)));
        let mut magic = [0; 8];
        try!(self.reader.read(&mut magic));
        if &magic != b"bplist00" {
            return Err(Error::InvalidData);
        }

        // Trailer starts with 6 bytes of padding
        try!(self.reader.seek(SeekFrom::End(-32 + 6)));
        let offset_size = try!(self.reader.read_u8());
        self.ref_size = try!(self.reader.read_u8());
        let num_objects = try!(self.reader.read_u64::<BigEndian>());
        let top_object = try!(self.reader.read_u64::<BigEndian>());
        let offset_table_offset = try!(self.reader.read_u64::<BigEndian>());

        // Read offset table
        try!(self.reader.seek(SeekFrom::Start(offset_table_offset)));
        self.object_offsets = try!(self.read_ints(num_objects, offset_size));

        // Seek to top object
        self.stack.push(StackItem {
            object_refs: vec![top_object],
            ty: StackType::Root,
        });

        Ok(())
    }

    fn read_ints(&mut self, len: u64, size: u8) -> Result<Vec<u64>> {
        let len = try!(u64_to_usize(len));
        let mut ints = Vec::with_capacity(len);
        // TODO: Is the match hoisted out of the loop?
        for _ in 0..len {
            match size {
                1 => ints.push(try!(self.reader.read_u8()) as u64),
                2 => ints.push(try!(self.reader.read_u16::<BigEndian>()) as u64),
                4 => ints.push(try!(self.reader.read_u32::<BigEndian>()) as u64),
                8 => ints.push(try!(self.reader.read_u64::<BigEndian>()) as u64),
                _ => return Err(Error::InvalidData),
            }
        }
        Ok(ints)
    }

    fn read_refs(&mut self, len: u64) -> Result<Vec<u64>> {
        let ref_size = self.ref_size;
        self.read_ints(len, ref_size)
    }

    fn read_object_len(&mut self, len: u8) -> Result<u64> {
        if (len & 0x0f) == 0x0f {
            let len_power_of_two = try!(self.reader.read_u8()) & 0x03;
            Ok(match len_power_of_two {
                0 => try!(self.reader.read_u8()) as u64,
                1 => try!(self.reader.read_u16::<BigEndian>()) as u64,
                2 => try!(self.reader.read_u32::<BigEndian>()) as u64,
                3 => try!(self.reader.read_u64::<BigEndian>()),
                _ => return Err(Error::InvalidData),
            })
        } else {
            Ok(len as u64)
        }
    }

    fn read_data(&mut self, len: u64) -> Result<Vec<u8>> {
        let len = try!(u64_to_usize(len));
        let mut data = vec![0; len];
        let mut total_read = 0;

        while total_read < len {
            let read = try!(self.reader.read(&mut data[total_read..]));
            if read == 0 {
                return Err(Error::UnexpectedEof);
            }
            total_read += read;
        }

        Ok(data)
    }

    fn seek_to_object(&mut self, object_ref: u64) -> Result<u64> {
        let object_ref = try!(u64_to_usize(object_ref));
        let offset = *&self.object_offsets[object_ref];
        let pos = try!(self.reader.seek(SeekFrom::Start(offset)));
        Ok(pos)
    }

    fn read_next(&mut self) -> Result<Option<PlistEvent>> {
        if self.ref_size == 0 {
            // Initialise here rather than in new
            try!(self.read_trailer());
        }

        let object_ref = match self.stack.last_mut() {
            Some(stack_item) => stack_item.object_refs.pop(),
            // Reached the end of the plist
            None => return Ok(None),
        };

        match object_ref {
            Some(object_ref) => {
                try!(self.seek_to_object(object_ref));
            }
            None => {
                // We're at the end of an array or dict. Pop the top stack item and return
                let item = self.stack.pop().unwrap();
                match item.ty {
                    StackType::Array => return Ok(Some(PlistEvent::EndArray)),
                    StackType::Dict => return Ok(Some(PlistEvent::EndDictionary)),
                    // We're at the end of the plist
                    StackType::Root => return Ok(None),
                }
            }
        }

        let token = try!(self.reader.read_u8());
        let ty = (token & 0xf0) >> 4;
        let size = token & 0x0f;

        let result = match (ty, size) {
            (0x0, 0x00) => return Err(Error::InvalidData), // null
            (0x0, 0x08) => Some(PlistEvent::BooleanValue(false)),
            (0x0, 0x09) => Some(PlistEvent::BooleanValue(true)),
            (0x0, 0x0f) => return Err(Error::InvalidData), // fill
            (0x1, 0) => Some(PlistEvent::IntegerValue(try!(self.reader.read_u8()) as i64)),
            (0x1, 1) => {
                Some(PlistEvent::IntegerValue(try!(self.reader.read_u16::<BigEndian>()) as i64))
            }
            (0x1, 2) => {
                Some(PlistEvent::IntegerValue(try!(self.reader.read_u32::<BigEndian>()) as i64))
            }
            (0x1, 3) => Some(PlistEvent::IntegerValue(try!(self.reader.read_i64::<BigEndian>()))),
            (0x1, 4) => return Err(Error::InvalidData), // 128 bit int
            (0x1, _) => return Err(Error::InvalidData), // variable length int
            (0x2, 2) => {
                Some(PlistEvent::RealValue(try!(self.reader.read_f32::<BigEndian>()) as f64))
            }
            (0x2, 3) => Some(PlistEvent::RealValue(try!(self.reader.read_f64::<BigEndian>()))),
            (0x2, _) => return Err(Error::InvalidData), // odd length float
            (0x3, 3) => {
                // Date
                // Seconds since 1/1/2001 00:00:00
                let timestamp = try!(self.reader.read_f64::<BigEndian>());

                let secs = timestamp.floor();
                let subsecs = timestamp - secs;

                let int_secs = (secs as i64) + (31 * 365 + 8) * 86400;
                let int_nanos = (subsecs * 1_000_000_000f64) as u32;

                Some(PlistEvent::DateValue(UTC.timestamp(int_secs, int_nanos)))
            }
            (0x4, n) => {
                // Data
                let len = try!(self.read_object_len(n));
                Some(PlistEvent::DataValue(try!(self.read_data(len))))
            }
            (0x5, n) => {
                // ASCII string
                let len = try!(self.read_object_len(n));
                let raw = try!(self.read_data(len));
                let string = try!(String::from_utf8(raw));
                Some(PlistEvent::StringValue(string))
            }
            (0x6, n) => {
                // UTF-16 string
                // n is the length of 16 bit code units
                // len is the number of bytes
                let len = try!(self.read_object_len(n)) * 2;
                let raw = try!(self.read_data(len));
                let mut cursor = Cursor::new(raw);

                let len_div_2 = try!(u64_to_usize(len / 2));
                let mut raw_utf16 = Vec::with_capacity(len_div_2);
                while cursor.position() < len {
                    raw_utf16.push(try!(cursor.read_u16::<BigEndian>()))
                }

                let string = try!(String::from_utf16(&raw_utf16));
                Some(PlistEvent::StringValue(string))
            }
            (0xa, n) => {
                // Array
                let len = try!(self.read_object_len(n));
                let mut object_refs = try!(self.read_refs(len));
                // Reverse so we can pop off the end of the stack in order
                object_refs.reverse();

                self.stack.push(StackItem {
                    ty: StackType::Array,
                    object_refs: object_refs,
                });

                Some(PlistEvent::StartArray(Some(len)))
            }
            (0xd, n) => {
                // Dict
                let len = try!(self.read_object_len(n));
                let key_refs = try!(self.read_refs(len));
                let value_refs = try!(self.read_refs(len));

                let len_mul_2 = try!(u64_to_usize(len * 2));
                let len = try!(u64_to_usize(len));

                let mut object_refs = Vec::with_capacity(len_mul_2);

                for i in 1..len + 1 {
                    // Reverse so we can pop off the end of the stack in order
                    object_refs.push(value_refs[len - i]);
                    object_refs.push(key_refs[len - i]);
                }

                self.stack.push(StackItem {
                    ty: StackType::Dict,
                    object_refs: object_refs,
                });

                Some(PlistEvent::StartDictionary(Some(len as u64)))
            }
            (_, _) => return Err(Error::InvalidData),
        };

        Ok(result)
    }
}

impl<R: Read + Seek> Iterator for EventReader<R> {
    type Item = Result<PlistEvent>;

    fn next(&mut self) -> Option<Result<PlistEvent>> {
        if self.finished {
            None
        } else {
            match self.read_next() {
                Ok(Some(event)) => Some(Ok(event)),
                Err(err) => {
                    self.finished = true;
                    Some(Err(err))
                }
                Ok(None) => {
                    self.finished = true;
                    None
                }
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use chrono::{TimeZone, UTC};
    use std::fs::File;
    use std::path::Path;

    use super::*;
    use PlistEvent;

    #[test]
    fn streaming_parser() {
        use PlistEvent::*;

        let reader = File::open(&Path::new("./tests/data/binary.plist")).unwrap();
        let streaming_parser = EventReader::new(reader);
        let events: Vec<PlistEvent> = streaming_parser.map(|e| e.unwrap()).collect();

        let comparison = &[StartDictionary(Some(6)),
                           StringValue("Lines".to_owned()),
                           StartArray(Some(2)),
                           StringValue("It is a tale told by an idiot,".to_owned()),
                           StringValue("Full of sound and fury, signifying nothing.".to_owned()),
                           EndArray,
                           StringValue("Death".to_owned()),
                           IntegerValue(1564),
                           StringValue("Height".to_owned()),
                           RealValue(1.60),
                           StringValue("Birthdate".to_owned()),
                           DateValue(UTC.ymd(1981, 05, 16).and_hms(11, 32, 06)),
                           StringValue("Author".to_owned()),
                           StringValue("William Shakespeare".to_owned()),
                           StringValue("Data".to_owned()),
                           DataValue(vec![0, 0, 0, 190, 0, 0, 0, 3, 0, 0, 0, 30, 0, 0, 0]),
                           EndDictionary];

        assert_eq!(events, comparison);
    }

    #[test]
    fn utf16_plist() {
        use PlistEvent::*;

        let reader = File::open(&Path::new("./tests/data/utf16_bplist.plist")).unwrap();
        let streaming_parser = EventReader::new(reader);
        let mut events: Vec<PlistEvent> = streaming_parser.map(|e| e.unwrap()).collect();

        assert_eq!(events[2], StringValue("\u{2605} or better".to_owned()));

        let poem = if let StringValue(ref mut poem) = events[4] {
            poem
        } else {
            panic!("not a string")
        };
        assert_eq!(poem.len(), 643);
        assert_eq!(poem.pop().unwrap(), '\u{2605}');
    }
}
<commit_msg>Improve binary plist datetime decoding.<commit_after>use byteorder::{BigEndian, ReadBytesExt};
use chrono::{Duration, TimeZone, UTC};
use std::io::{Cursor, Read, Seek, SeekFrom};
use std::string::{FromUtf8Error, FromUtf16Error};

use {Error, Result, PlistEvent, u64_to_usize};

impl From<FromUtf8Error> for Error {
    fn from(_: FromUtf8Error) -> Error {
        Error::InvalidData
    }
}

impl From<FromUtf16Error> for Error {
    fn from(_: FromUtf16Error) -> Error {
        Error::InvalidData
    }
}

struct StackItem {
    object_refs: Vec<u64>,
    ty: StackType,
}

enum StackType {
    Array,
    Dict,
    Root,
}

/// https://opensource.apple.com/source/CF/CF-550/CFBinaryPList.c
/// https://hg.python.org/cpython/file/3.4/Lib/plistlib.py
pub struct EventReader<R> {
    stack: Vec<StackItem>,
    object_offsets: Vec<u64>,
    reader: R,
    ref_size: u8,
    finished: bool,
}

impl<R: Read + Seek> EventReader<R> {
    pub fn new(reader: R) -> EventReader<R> {
        EventReader {
            stack: Vec::new(),
            object_offsets: Vec::new(),
            reader: reader,
            ref_size: 0,
            finished: false,
        }
    }

    fn read_trailer(&mut self) -> Result<()> {
        try!(self.reader.seek(SeekFrom::Start(0)));
        let mut magic = [0; 8];
        try!(self.reader.read(&mut magic));
        if &magic != b"bplist00" {
            return Err(Error::InvalidData);
        }

        // Trailer starts with 6 bytes of padding
        try!(self.reader.seek(SeekFrom::End(-32 + 6)));
        let offset_size = try!(self.reader.read_u8());
        self.ref_size = try!(self.reader.read_u8());
        let num_objects = try!(self.reader.read_u64::<BigEndian>());
        let top_object = try!(self.reader.read_u64::<BigEndian>());
        let offset_table_offset = try!(self.reader.read_u64::<BigEndian>());

        // Read offset table
        try!(self.reader.seek(SeekFrom::Start(offset_table_offset)));
        self.object_offsets = try!(self.read_ints(num_objects, offset_size));

        // Seek to top object
        self.stack.push(StackItem {
            object_refs: vec![top_object],
            ty: StackType::Root,
        });

        Ok(())
    }

    fn read_ints(&mut self, len: u64, size: u8) -> Result<Vec<u64>> {
        let len = try!(u64_to_usize(len));
        let mut ints = Vec::with_capacity(len);
        // TODO: Is the match hoisted out of the loop?
        for _ in 0..len {
            match size {
                1 => ints.push(try!(self.reader.read_u8()) as u64),
                2 => ints.push(try!(self.reader.read_u16::<BigEndian>()) as u64),
                4 => ints.push(try!(self.reader.read_u32::<BigEndian>()) as u64),
                8 => ints.push(try!(self.reader.read_u64::<BigEndian>()) as u64),
                _ => return Err(Error::InvalidData),
            }
        }
        Ok(ints)
    }

    fn read_refs(&mut self, len: u64) -> Result<Vec<u64>> {
        let ref_size = self.ref_size;
        self.read_ints(len, ref_size)
    }

    fn read_object_len(&mut self, len: u8) -> Result<u64> {
        if (len & 0x0f) == 0x0f {
            let len_power_of_two = try!(self.reader.read_u8()) & 0x03;
            Ok(match len_power_of_two {
                0 => try!(self.reader.read_u8()) as u64,
                1 => try!(self.reader.read_u16::<BigEndian>()) as u64,
                2 => try!(self.reader.read_u32::<BigEndian>()) as u64,
                3 => try!(self.reader.read_u64::<BigEndian>()),
                _ => return Err(Error::InvalidData),
            })
        } else {
            Ok(len as u64)
        }
    }

    fn read_data(&mut self, len: u64) -> Result<Vec<u8>> {
        let len = try!(u64_to_usize(len));
        let mut data = vec![0; len];
        let mut total_read = 0;

        while total_read < len {
            let read = try!(self.reader.read(&mut data[total_read..]));
            if read == 0 {
                return Err(Error::UnexpectedEof);
            }
            total_read += read;
        }

        Ok(data)
    }

    fn seek_to_object(&mut self, object_ref: u64) -> Result<u64> {
        let object_ref = try!(u64_to_usize(object_ref));
        let offset = *&self.object_offsets[object_ref];
        let pos = try!(self.reader.seek(SeekFrom::Start(offset)));
        Ok(pos)
    }

    fn read_next(&mut self) -> Result<Option<PlistEvent>> {
        if self.ref_size == 0 {
            // Initialise here rather than in new
            try!(self.read_trailer());
        }

        let object_ref = match self.stack.last_mut() {
            Some(stack_item) => stack_item.object_refs.pop(),
            // Reached the end of the plist
            None => return Ok(None),
        };

        match object_ref {
            Some(object_ref) => {
                try!(self.seek_to_object(object_ref));
            }
            None => {
                // We're at the end of an array or dict. Pop the top stack item and return
                let item = self.stack.pop().unwrap();
                match item.ty {
                    StackType::Array => return Ok(Some(PlistEvent::EndArray)),
                    StackType::Dict => return Ok(Some(PlistEvent::EndDictionary)),
                    // We're at the end of the plist
                    StackType::Root => return Ok(None),
                }
            }
        }

        let token = try!(self.reader.read_u8());
        let ty = (token & 0xf0) >> 4;
        let size = token & 0x0f;

        let result = match (ty, size) {
            (0x0, 0x00) => return Err(Error::InvalidData), // null
            (0x0, 0x08) => Some(PlistEvent::BooleanValue(false)),
            (0x0, 0x09) => Some(PlistEvent::BooleanValue(true)),
            (0x0, 0x0f) => return Err(Error::InvalidData), // fill
            (0x1, 0) => Some(PlistEvent::IntegerValue(try!(self.reader.read_u8()) as i64)),
            (0x1, 1) => {
                Some(PlistEvent::IntegerValue(try!(self.reader.read_u16::<BigEndian>()) as i64))
            }
            (0x1, 2) => {
                Some(PlistEvent::IntegerValue(try!(self.reader.read_u32::<BigEndian>()) as i64))
            }
            (0x1, 3) => Some(PlistEvent::IntegerValue(try!(self.reader.read_i64::<BigEndian>()))),
            (0x1, 4) => return Err(Error::InvalidData), // 128 bit int
            (0x1, _) => return Err(Error::InvalidData), // variable length int
            (0x2, 2) => {
                Some(PlistEvent::RealValue(try!(self.reader.read_f32::<BigEndian>()) as f64))
            }
            (0x2, 3) => Some(PlistEvent::RealValue(try!(self.reader.read_f64::<BigEndian>()))),
            (0x2, _) => return Err(Error::InvalidData), // odd length float
            (0x3, 3) => {;
                // Date
                // Seconds since 1/1/2001 00:00:00.
                let timestamp = try!(self.reader.read_f64::<BigEndian>());

                let millis = timestamp * 1_000.0;
                // Chrono's Duration can only millisecond values between ::std::i64::MIN and
                // ::std::i64::MAX.
                if millis > ::std::i64::MAX as f64 || millis < ::std::i64::MIN as f64 {
                    return Err(Error::InvalidData);
                }

                let whole_millis = millis.floor();
                let submilli_nanos = ((millis - whole_millis) * 1_000_000.0).floor();

                let dur = Duration::milliseconds(whole_millis as i64);
                let dur = dur + Duration::nanoseconds(submilli_nanos as i64);

                let plist_epoch = UTC.ymd(2001, 1, 1).and_hms(0, 0, 0);
                let date = try!(plist_epoch.checked_add(dur).ok_or(Error::InvalidData));

                Some(PlistEvent::DateValue(date))
            }
            (0x4, n) => {
                // Data
                let len = try!(self.read_object_len(n));
                Some(PlistEvent::DataValue(try!(self.read_data(len))))
            }
            (0x5, n) => {
                // ASCII string
                let len = try!(self.read_object_len(n));
                let raw = try!(self.read_data(len));
                let string = try!(String::from_utf8(raw));
                Some(PlistEvent::StringValue(string))
            }
            (0x6, n) => {
                // UTF-16 string
                // n is the length of 16 bit code units
                // len is the number of bytes
                let len = try!(self.read_object_len(n)) * 2;
                let raw = try!(self.read_data(len));
                let mut cursor = Cursor::new(raw);

                let len_div_2 = try!(u64_to_usize(len / 2));
                let mut raw_utf16 = Vec::with_capacity(len_div_2);
                while cursor.position() < len {
                    raw_utf16.push(try!(cursor.read_u16::<BigEndian>()))
                }

                let string = try!(String::from_utf16(&raw_utf16));
                Some(PlistEvent::StringValue(string))
            }
            (0xa, n) => {
                // Array
                let len = try!(self.read_object_len(n));
                let mut object_refs = try!(self.read_refs(len));
                // Reverse so we can pop off the end of the stack in order
                object_refs.reverse();

                self.stack.push(StackItem {
                    ty: StackType::Array,
                    object_refs: object_refs,
                });

                Some(PlistEvent::StartArray(Some(len)))
            }
            (0xd, n) => {
                // Dict
                let len = try!(self.read_object_len(n));
                let key_refs = try!(self.read_refs(len));
                let value_refs = try!(self.read_refs(len));

                let len_mul_2 = try!(u64_to_usize(len * 2));
                let len = try!(u64_to_usize(len));

                let mut object_refs = Vec::with_capacity(len_mul_2);

                for i in 1..len + 1 {
                    // Reverse so we can pop off the end of the stack in order
                    object_refs.push(value_refs[len - i]);
                    object_refs.push(key_refs[len - i]);
                }

                self.stack.push(StackItem {
                    ty: StackType::Dict,
                    object_refs: object_refs,
                });

                Some(PlistEvent::StartDictionary(Some(len as u64)))
            }
            (_, _) => return Err(Error::InvalidData),
        };

        Ok(result)
    }
}

impl<R: Read + Seek> Iterator for EventReader<R> {
    type Item = Result<PlistEvent>;

    fn next(&mut self) -> Option<Result<PlistEvent>> {
        if self.finished {
            None
        } else {
            match self.read_next() {
                Ok(Some(event)) => Some(Ok(event)),
                Err(err) => {
                    self.finished = true;
                    Some(Err(err))
                }
                Ok(None) => {
                    self.finished = true;
                    None
                }
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use chrono::{TimeZone, UTC};
    use std::fs::File;
    use std::path::Path;

    use super::*;
    use PlistEvent;

    #[test]
    fn streaming_parser() {
        use PlistEvent::*;

        let reader = File::open(&Path::new("./tests/data/binary.plist")).unwrap();
        let streaming_parser = EventReader::new(reader);
        let events: Vec<PlistEvent> = streaming_parser.map(|e| e.unwrap()).collect();

        let comparison = &[StartDictionary(Some(6)),
                           StringValue("Lines".to_owned()),
                           StartArray(Some(2)),
                           StringValue("It is a tale told by an idiot,".to_owned()),
                           StringValue("Full of sound and fury, signifying nothing.".to_owned()),
                           EndArray,
                           StringValue("Death".to_owned()),
                           IntegerValue(1564),
                           StringValue("Height".to_owned()),
                           RealValue(1.60),
                           StringValue("Birthdate".to_owned()),
                           DateValue(UTC.ymd(1981, 05, 16).and_hms(11, 32, 06)),
                           StringValue("Author".to_owned()),
                           StringValue("William Shakespeare".to_owned()),
                           StringValue("Data".to_owned()),
                           DataValue(vec![0, 0, 0, 190, 0, 0, 0, 3, 0, 0, 0, 30, 0, 0, 0]),
                           EndDictionary];

        assert_eq!(events, comparison);
    }

    #[test]
    fn utf16_plist() {
        use PlistEvent::*;

        let reader = File::open(&Path::new("./tests/data/utf16_bplist.plist")).unwrap();
        let streaming_parser = EventReader::new(reader);
        let mut events: Vec<PlistEvent> = streaming_parser.map(|e| e.unwrap()).collect();

        assert_eq!(events[2], StringValue("\u{2605} or better".to_owned()));

        let poem = if let StringValue(ref mut poem) = events[4] {
            poem
        } else {
            panic!("not a string")
        };
        assert_eq!(poem.len(), 643);
        assert_eq!(poem.pop().unwrap(), '\u{2605}');
    }
}
<|endoftext|>