<commit_before>use std::collections::HashMap;
use std::io::Write;

use serialize::json::ToJson;

use template::{Template};
use render::{Renderable, RenderError, RenderContext};
use helpers::{HelperDef};
use context::{Context};
use helpers;
use support::str::StringWriter;
use TemplateError;

pub struct Registry {
    templates: HashMap<String, Template>,
    helpers: HashMap<String, Box<HelperDef + 'static>>
}

impl Registry {
    pub fn new() -> Registry {
        let mut r = Registry {
            templates: HashMap::new(),
            helpers: HashMap::new()
        };

        r.register_helper("if", Box::new(helpers::IF_HELPER));
        r.register_helper("unless", Box::new(helpers::UNLESS_HELPER));
        r.register_helper("each", Box::new(helpers::EACH_HELPER));
        r.register_helper("with", Box::new(helpers::WITH_HELPER));
        r.register_helper("lookup", Box::new(helpers::LOOKUP_HELPER));
        r.register_helper("raw", Box::new(helpers::RAW_HELPER));
        r.register_helper(">", Box::new(helpers::INCLUDE_HELPER));
        r.register_helper("block", Box::new(helpers::BLOCK_HELPER));
        r.register_helper("partial", Box::new(helpers::PARTIAL_HELPER));
        r.register_helper("log", Box::new(helpers::LOG_HELPER));

        r
    }

    pub fn register_template(&mut self, name: &str, template: Template) {
        self.templates.insert(name.to_string(), template);
    }

    pub fn register_template_string(&mut self, name: &str, tpl_str: String) -> Result<(), TemplateError>{
        try!(Template::compile(tpl_str).and_then(|t| Ok(self.templates.insert(name.to_string(), t))));
        Ok(())
    }

    pub fn unregister_template(&mut self, name: &String) {
        self.templates.remove(name);
    }

    pub fn register_helper(&mut self, name: &str, def: Box<HelperDef + 'static>) -> Option<Box<HelperDef + 'static>> {
        self.helpers.insert(name.to_string(), def)
    }

    pub fn get_template(&self, name: &String) -> Option<&Template> {
        self.templates.get(name)
    }

    pub fn get_helper(&self, name: &String) -> Option<&Box<HelperDef + 'static>> {
        self.helpers.get(name)
    }

    pub fn get_templates(&self) -> &HashMap<String, Template> {
        &self.templates
    }

    pub fn clear_templates(&mut self) {
        self.templates.clear();
    }

    pub fn render<T>(&self, name: &str, ctx: &T) -> Result<String, RenderError> where T: ToJson {
        let mut writer = StringWriter::new();
        let context = Context::wraps(ctx);
        {
            try!(self.renderw(name, &context, &mut writer));
        }
        Ok(writer.to_string())
    }

    pub fn renderw(&self, name: &str, context: &Context, writer: &mut Write) -> Result<(), RenderError> {
        let template = self.get_template(&name.to_string());

        if let Some(t) = template {
            let mut render_context = RenderContext::new(writer);
            (*t).render(context, self, &mut render_context)
        } else {
            Err(RenderError{
                desc: "Template not found."
            })
        }
    }
}

#[cfg(test)]
mod test {
    use template::{Template};
    use registry::{Registry};
    use render::{RenderContext, Renderable, RenderError, Helper};
    use helpers::{HelperDef};
    use context::{Context};
    use support::str::StringWriter;

    #[derive(Clone, Copy)]
    struct DummyHelper;

    impl HelperDef for DummyHelper {
        fn call(&self, c: &Context, h: &Helper, r: &Registry, rc: &mut RenderContext) -> Result<(), RenderError> {
            try!(h.template().unwrap().render(c, r, rc));
            Ok(())
        }
    }

    static DUMMY_HELPER: DummyHelper = DummyHelper;

    #[test]
    fn test_registry_operations() {
        let mut r = Registry::new();

        let t = Template::compile("

".to_string()).ok().unwrap();
        r.register_template("index", t.clone());

        let t2 = Template::compile("

".to_string()).ok().unwrap();
        r.register_template("index2", t2.clone());

        assert_eq!((*r.get_template(&("index".to_string())).unwrap()).to_string(),
                   t.to_string());
        assert_eq!(r.templates.len(), 2);

        r.unregister_template(&("index".to_string()));
        assert_eq!(r.templates.len(), 1);

        r.clear_templates();
        assert_eq!(r.templates.len(), 0);

        r.register_helper("dummy", Box::new(DUMMY_HELPER));

        // built-in helpers plus 1
        assert_eq!(r.helpers.len(), 10+1);
    }

    #[test]
    fn test_renderw() {
        let mut r = Registry::new();

        let t = Template::compile("

".to_string()).ok().unwrap();
        r.register_template("index", t.clone());

        let mut sw = StringWriter::new();
        let context = Context::null();

        {
            r.renderw("index", &context, &mut sw).ok().unwrap();
        }

        assert_eq!("

".to_string(), sw.to_string());

    }
}
<commit_msg>(fix) &String -> &str<commit_after>use std::collections::HashMap;
use std::io::Write;

use serialize::json::ToJson;

use template::{Template};
use render::{Renderable, RenderError, RenderContext};
use helpers::{HelperDef};
use context::{Context};
use helpers;
use support::str::StringWriter;
use TemplateError;

pub struct Registry {
    templates: HashMap<String, Template>,
    helpers: HashMap<String, Box<HelperDef + 'static>>
}

impl Registry {
    pub fn new() -> Registry {
        let mut r = Registry {
            templates: HashMap::new(),
            helpers: HashMap::new()
        };

        r.register_helper("if", Box::new(helpers::IF_HELPER));
        r.register_helper("unless", Box::new(helpers::UNLESS_HELPER));
        r.register_helper("each", Box::new(helpers::EACH_HELPER));
        r.register_helper("with", Box::new(helpers::WITH_HELPER));
        r.register_helper("lookup", Box::new(helpers::LOOKUP_HELPER));
        r.register_helper("raw", Box::new(helpers::RAW_HELPER));
        r.register_helper(">", Box::new(helpers::INCLUDE_HELPER));
        r.register_helper("block", Box::new(helpers::BLOCK_HELPER));
        r.register_helper("partial", Box::new(helpers::PARTIAL_HELPER));
        r.register_helper("log", Box::new(helpers::LOG_HELPER));

        r
    }

    pub fn register_template(&mut self, name: &str, template: Template) {
        self.templates.insert(name.to_string(), template);
    }

    pub fn register_template_string(&mut self, name: &str, tpl_str: String) -> Result<(), TemplateError>{
        try!(Template::compile(tpl_str).and_then(|t| Ok(self.templates.insert(name.to_string(), t))));
        Ok(())
    }

    pub fn unregister_template(&mut self, name: &str) {
        self.templates.remove(name);
    }

    pub fn register_helper(&mut self, name: &str, def: Box<HelperDef + 'static>) -> Option<Box<HelperDef + 'static>> {
        self.helpers.insert(name.to_string(), def)
    }

    pub fn get_template(&self, name: &str) -> Option<&Template> {
        self.templates.get(name)
    }

    pub fn get_helper(&self, name: &str) -> Option<&Box<HelperDef + 'static>> {
        self.helpers.get(name)
    }

    pub fn get_templates(&self) -> &HashMap<String, Template> {
        &self.templates
    }

    pub fn clear_templates(&mut self) {
        self.templates.clear();
    }

    pub fn render<T>(&self, name: &str, ctx: &T) -> Result<String, RenderError> where T: ToJson {
        let mut writer = StringWriter::new();
        let context = Context::wraps(ctx);
        {
            try!(self.renderw(name, &context, &mut writer));
        }
        Ok(writer.to_string())
    }

    pub fn renderw(&self, name: &str, context: &Context, writer: &mut Write) -> Result<(), RenderError> {
        let template = self.get_template(&name.to_string());

        if let Some(t) = template {
            let mut render_context = RenderContext::new(writer);
            (*t).render(context, self, &mut render_context)
        } else {
            Err(RenderError{
                desc: "Template not found."
            })
        }
    }
}

#[cfg(test)]
mod test {
    use template::{Template};
    use registry::{Registry};
    use render::{RenderContext, Renderable, RenderError, Helper};
    use helpers::{HelperDef};
    use context::{Context};
    use support::str::StringWriter;

    #[derive(Clone, Copy)]
    struct DummyHelper;

    impl HelperDef for DummyHelper {
        fn call(&self, c: &Context, h: &Helper, r: &Registry, rc: &mut RenderContext) -> Result<(), RenderError> {
            try!(h.template().unwrap().render(c, r, rc));
            Ok(())
        }
    }

    static DUMMY_HELPER: DummyHelper = DummyHelper;

    #[test]
    fn test_registry_operations() {
        let mut r = Registry::new();

        let t = Template::compile("

".to_string()).ok().unwrap();
        r.register_template("index", t.clone());

        let t2 = Template::compile("

".to_string()).ok().unwrap();
        r.register_template("index2", t2.clone());

        assert_eq!((*r.get_template(&("index".to_string())).unwrap()).to_string(),
                   t.to_string());
        assert_eq!(r.templates.len(), 2);

        r.unregister_template(&("index".to_string()));
        assert_eq!(r.templates.len(), 1);

        r.clear_templates();
        assert_eq!(r.templates.len(), 0);

        r.register_helper("dummy", Box::new(DUMMY_HELPER));

        // built-in helpers plus 1
        assert_eq!(r.helpers.len(), 10+1);
    }

    #[test]
    fn test_renderw() {
        let mut r = Registry::new();

        let t = Template::compile("

".to_string()).ok().unwrap();
        r.register_template("index", t.clone());

        let mut sw = StringWriter::new();
        let context = Context::null();

        {
            r.renderw("index", &context, &mut sw).ok().unwrap();
        }

        assert_eq!("

".to_string(), sw.to_string());

    }
}
<|endoftext|>


<commit_before>// This is a part of Chrono.
// See README.md and LICENSE.txt for details.

use Timelike;
use std::ops::{Add, Sub};
use oldtime::Duration;

/// Extension trait for subsecond rounding or truncation to a maximum number
/// of digits. Rounding can be used to decrease the error variance when
/// serializing/persisting to lower precision. Truncation is the default
/// behavior in Chrono display formatting.  Either can be used to guarantee
/// equality (e.g. for testing) when round-tripping through a lower precision
/// format.
pub trait SubSecondRound {
    /// Return a copy rounded to the specified number of subsecond digits. With
    /// 9 or more digits, self is returned unmodified. Halfway values are
    /// rounded up (away from zero).
    fn round(self, digits: u16) -> Self;

    /// Return a copy truncated to the specified number of subsecond
    /// digits. With 9 or more digits, self is returned unmodified.
    fn trunc(self, digits: u16) -> Self;
}

impl<T> SubSecondRound for T
where T: Timelike + Add<Duration, Output=T> + Sub<Duration, Output=T>
{
    fn round(self, digits: u16) -> T {
        let span = span_for_digits(digits);
        let rem = self.nanosecond() % span;
        if rem > 0 {
            let rev = span - rem;
            if rev <= rem {
                self + Duration::nanoseconds(rev.into()) // up
            } else {
                self - Duration::nanoseconds(rem.into()) // down
            }
        } else {
            self // unchanged
        }
    }

    fn trunc(self, digits: u16) -> T {
        let span = span_for_digits(digits);
        let rem = self.nanosecond() % span;
        if rem > 0 {
            self - Duration::nanoseconds(rem.into()) // truncate
        } else {
            self // unchanged
        }
    }
}

// Return the maximum span in nanoseconds for the target number of digits.
fn span_for_digits(digits: u16) -> u32 {
    // fast lookup form of: 10^(9-min(9,digits))
    match digits {
        0 => 1_000_000_000,
        1 =>   100_000_000,
        2 =>    10_000_000,
        3 =>     1_000_000,
        4 =>       100_000,
        5 =>        10_000,
        6 =>         1_000,
        7 =>           100,
        8 =>            10,
        _ =>             1
    }
}

#[cfg(test)]
mod tests {
    use Timelike;
    use offset::{FixedOffset, TimeZone, Utc};
    use super::SubSecondRound;

    #[test]
    fn test_round() {
        let pst = FixedOffset::east(8 * 60 * 60);
        let dt = pst.ymd(2018, 1, 11).and_hms_nano(10, 5, 13, 084_660_684);

        assert_eq!(dt.round(10), dt);
        assert_eq!(dt.round(9), dt);
        assert_eq!(dt.round(8).nanosecond(), 084_660_680);
        assert_eq!(dt.round(7).nanosecond(), 084_660_700);
        assert_eq!(dt.round(6).nanosecond(), 084_661_000);
        assert_eq!(dt.round(5).nanosecond(), 084_660_000);
        assert_eq!(dt.round(4).nanosecond(), 084_700_000);
        assert_eq!(dt.round(3).nanosecond(), 085_000_000);
        assert_eq!(dt.round(2).nanosecond(), 080_000_000);
        assert_eq!(dt.round(1).nanosecond(), 100_000_000);

        assert_eq!(dt.round(0).nanosecond(), 0);
        assert_eq!(dt.round(0).second(), 13);

        let dt = Utc.ymd(2018, 1, 11).and_hms_nano(10, 5, 27, 750_500_000);
        assert_eq!(dt.round(9), dt);
        assert_eq!(dt.round(4), dt);
        assert_eq!(dt.round(3).nanosecond(), 751_000_000);
        assert_eq!(dt.round(2).nanosecond(), 750_000_000);
        assert_eq!(dt.round(1).nanosecond(), 800_000_000);

        assert_eq!(dt.round(0).nanosecond(), 0);
        assert_eq!(dt.round(0).second(), 28);
    }

    #[test]
    fn test_round_leap_nanos() {
        let dt = Utc.ymd(2016, 12, 31).and_hms_nano(23, 59, 59, 1_750_500_000);
        assert_eq!(dt.round(9), dt);
        assert_eq!(dt.round(4), dt);
        assert_eq!(dt.round(2).nanosecond(), 1_750_000_000);
        assert_eq!(dt.round(1).nanosecond(), 1_800_000_000);
        assert_eq!(dt.round(1).second(), 59);

        assert_eq!(dt.round(0).nanosecond(), 0);
        assert_eq!(dt.round(0).second(), 0);
    }

    #[test]
    fn test_trunc() {
        let pst = FixedOffset::east(8 * 60 * 60);
        let dt = pst.ymd(2018, 1, 11).and_hms_nano(10, 5, 13, 084_660_684);

        assert_eq!(dt.trunc(10), dt);
        assert_eq!(dt.trunc(9), dt);
        assert_eq!(dt.trunc(8).nanosecond(), 084_660_680);
        assert_eq!(dt.trunc(7).nanosecond(), 084_660_600);
        assert_eq!(dt.trunc(6).nanosecond(), 084_660_000);
        assert_eq!(dt.trunc(5).nanosecond(), 084_660_000);
        assert_eq!(dt.trunc(4).nanosecond(), 084_600_000);
        assert_eq!(dt.trunc(3).nanosecond(), 084_000_000);
        assert_eq!(dt.trunc(2).nanosecond(), 080_000_000);
        assert_eq!(dt.trunc(1).nanosecond(), 0);

        assert_eq!(dt.trunc(0).nanosecond(), 0);
        assert_eq!(dt.trunc(0).second(), 13);

        let dt = pst.ymd(2018, 1, 11).and_hms_nano(10, 5, 27, 750_500_000);
        assert_eq!(dt.trunc(9), dt);
        assert_eq!(dt.trunc(4), dt);
        assert_eq!(dt.trunc(3).nanosecond(), 750_000_000);
        assert_eq!(dt.trunc(2).nanosecond(), 750_000_000);
        assert_eq!(dt.trunc(1).nanosecond(), 700_000_000);

        assert_eq!(dt.trunc(0).nanosecond(), 0);
        assert_eq!(dt.trunc(0).second(), 27);
    }

    #[test]
    fn test_trunc_leap_nanos() {
        let dt = Utc.ymd(2016, 12, 31).and_hms_nano(23, 59, 59, 1_750_500_000);
        assert_eq!(dt.trunc(9), dt);
        assert_eq!(dt.trunc(4), dt);
        assert_eq!(dt.trunc(2).nanosecond(), 1_750_000_000);
        assert_eq!(dt.trunc(1).nanosecond(), 1_700_000_000);
        assert_eq!(dt.trunc(1).second(), 59);

        assert_eq!(dt.trunc(0).nanosecond(), 1_000_000_000);
        assert_eq!(dt.trunc(0).second(), 59);
    }
}
<commit_msg>Improve up/down variable naming<commit_after>// This is a part of Chrono.
// See README.md and LICENSE.txt for details.

use Timelike;
use std::ops::{Add, Sub};
use oldtime::Duration;

/// Extension trait for subsecond rounding or truncation to a maximum number
/// of digits. Rounding can be used to decrease the error variance when
/// serializing/persisting to lower precision. Truncation is the default
/// behavior in Chrono display formatting.  Either can be used to guarantee
/// equality (e.g. for testing) when round-tripping through a lower precision
/// format.
pub trait SubSecondRound {
    /// Return a copy rounded to the specified number of subsecond digits. With
    /// 9 or more digits, self is returned unmodified. Halfway values are
    /// rounded up (away from zero).
    fn round(self, digits: u16) -> Self;

    /// Return a copy truncated to the specified number of subsecond
    /// digits. With 9 or more digits, self is returned unmodified.
    fn trunc(self, digits: u16) -> Self;
}

impl<T> SubSecondRound for T
where T: Timelike + Add<Duration, Output=T> + Sub<Duration, Output=T>
{
    fn round(self, digits: u16) -> T {
        let span = span_for_digits(digits);
        let delta_down = self.nanosecond() % span;
        if delta_down > 0 {
            let delta_up = span - delta_down;
            if delta_up <= delta_down {
                self + Duration::nanoseconds(delta_up.into())
            } else {
                self - Duration::nanoseconds(delta_down.into())
            }
        } else {
            self // unchanged
        }
    }

    fn trunc(self, digits: u16) -> T {
        let span = span_for_digits(digits);
        let delta_down = self.nanosecond() % span;
        if delta_down > 0 {
            self - Duration::nanoseconds(delta_down.into())
        } else {
            self // unchanged
        }
    }
}

// Return the maximum span in nanoseconds for the target number of digits.
fn span_for_digits(digits: u16) -> u32 {
    // fast lookup form of: 10^(9-min(9,digits))
    match digits {
        0 => 1_000_000_000,
        1 =>   100_000_000,
        2 =>    10_000_000,
        3 =>     1_000_000,
        4 =>       100_000,
        5 =>        10_000,
        6 =>         1_000,
        7 =>           100,
        8 =>            10,
        _ =>             1
    }
}

#[cfg(test)]
mod tests {
    use Timelike;
    use offset::{FixedOffset, TimeZone, Utc};
    use super::SubSecondRound;

    #[test]
    fn test_round() {
        let pst = FixedOffset::east(8 * 60 * 60);
        let dt = pst.ymd(2018, 1, 11).and_hms_nano(10, 5, 13, 084_660_684);

        assert_eq!(dt.round(10), dt);
        assert_eq!(dt.round(9), dt);
        assert_eq!(dt.round(8).nanosecond(), 084_660_680);
        assert_eq!(dt.round(7).nanosecond(), 084_660_700);
        assert_eq!(dt.round(6).nanosecond(), 084_661_000);
        assert_eq!(dt.round(5).nanosecond(), 084_660_000);
        assert_eq!(dt.round(4).nanosecond(), 084_700_000);
        assert_eq!(dt.round(3).nanosecond(), 085_000_000);
        assert_eq!(dt.round(2).nanosecond(), 080_000_000);
        assert_eq!(dt.round(1).nanosecond(), 100_000_000);

        assert_eq!(dt.round(0).nanosecond(), 0);
        assert_eq!(dt.round(0).second(), 13);

        let dt = Utc.ymd(2018, 1, 11).and_hms_nano(10, 5, 27, 750_500_000);
        assert_eq!(dt.round(9), dt);
        assert_eq!(dt.round(4), dt);
        assert_eq!(dt.round(3).nanosecond(), 751_000_000);
        assert_eq!(dt.round(2).nanosecond(), 750_000_000);
        assert_eq!(dt.round(1).nanosecond(), 800_000_000);

        assert_eq!(dt.round(0).nanosecond(), 0);
        assert_eq!(dt.round(0).second(), 28);
    }

    #[test]
    fn test_round_leap_nanos() {
        let dt = Utc.ymd(2016, 12, 31).and_hms_nano(23, 59, 59, 1_750_500_000);
        assert_eq!(dt.round(9), dt);
        assert_eq!(dt.round(4), dt);
        assert_eq!(dt.round(2).nanosecond(), 1_750_000_000);
        assert_eq!(dt.round(1).nanosecond(), 1_800_000_000);
        assert_eq!(dt.round(1).second(), 59);

        assert_eq!(dt.round(0).nanosecond(), 0);
        assert_eq!(dt.round(0).second(), 0);
    }

    #[test]
    fn test_trunc() {
        let pst = FixedOffset::east(8 * 60 * 60);
        let dt = pst.ymd(2018, 1, 11).and_hms_nano(10, 5, 13, 084_660_684);

        assert_eq!(dt.trunc(10), dt);
        assert_eq!(dt.trunc(9), dt);
        assert_eq!(dt.trunc(8).nanosecond(), 084_660_680);
        assert_eq!(dt.trunc(7).nanosecond(), 084_660_600);
        assert_eq!(dt.trunc(6).nanosecond(), 084_660_000);
        assert_eq!(dt.trunc(5).nanosecond(), 084_660_000);
        assert_eq!(dt.trunc(4).nanosecond(), 084_600_000);
        assert_eq!(dt.trunc(3).nanosecond(), 084_000_000);
        assert_eq!(dt.trunc(2).nanosecond(), 080_000_000);
        assert_eq!(dt.trunc(1).nanosecond(), 0);

        assert_eq!(dt.trunc(0).nanosecond(), 0);
        assert_eq!(dt.trunc(0).second(), 13);

        let dt = pst.ymd(2018, 1, 11).and_hms_nano(10, 5, 27, 750_500_000);
        assert_eq!(dt.trunc(9), dt);
        assert_eq!(dt.trunc(4), dt);
        assert_eq!(dt.trunc(3).nanosecond(), 750_000_000);
        assert_eq!(dt.trunc(2).nanosecond(), 750_000_000);
        assert_eq!(dt.trunc(1).nanosecond(), 700_000_000);

        assert_eq!(dt.trunc(0).nanosecond(), 0);
        assert_eq!(dt.trunc(0).second(), 27);
    }

    #[test]
    fn test_trunc_leap_nanos() {
        let dt = Utc.ymd(2016, 12, 31).and_hms_nano(23, 59, 59, 1_750_500_000);
        assert_eq!(dt.trunc(9), dt);
        assert_eq!(dt.trunc(4), dt);
        assert_eq!(dt.trunc(2).nanosecond(), 1_750_000_000);
        assert_eq!(dt.trunc(1).nanosecond(), 1_700_000_000);
        assert_eq!(dt.trunc(1).second(), 59);

        assert_eq!(dt.trunc(0).nanosecond(), 1_000_000_000);
        assert_eq!(dt.trunc(0).second(), 59);
    }
}
<|endoftext|>


<commit_before>
use std::collections::HashMap;
use uuid::Uuid;

use graphics::*;

use event::GenericEvent;
use ai_behavior::{
    Behavior,
    State,
    Running,
};

use sprite::Sprite;

use animation::{
    Animation,
    AnimationState,
};

/// A scene is used to manage sprite's life and run animation with sprite
pub struct Scene<I: ImageSize> {
    children: Vec<Sprite<I>>,
    children_index: HashMap<Uuid, uint>,
    running: HashMap<Uuid,
        Vec<(Behavior<Animation>, State<Animation, AnimationState>, bool)>>,
}

impl<I: ImageSize> Scene<I> {
    /// Create a new scene
    pub fn new() -> Scene<I> {
        Scene {
            children: Vec::new(),
            children_index: HashMap::new(),
            running: HashMap::new(),
        }
    }

    /// Update animation's state
    pub fn event<E: GenericEvent>(&mut self, e: &E) {
        // regenerate the animations and their states
        let running = self.running.clone();
        self.running.clear();

        for (id, animations) in running.into_iter() {
            let mut new_animations = Vec::new();

            for (b, mut a, paused) in animations.into_iter() {
                if paused {
                    new_animations.push((b, a, paused));
                    continue;
                }

                let sprite = self.child_mut(id.clone()).unwrap();
                let (status, _) = a.event(e, |_, dt, animation, s| {
                    let (state, status, remain) = {
                        let start_state;
                        let state = match *s {
                            None => { start_state = animation.to_state(sprite); &start_state },
                            Some(ref state) => state,
                        };
                        state.update(sprite, dt)
                    };
                    *s = state;
                    (status, remain)
                });

                match status {
                    // the behavior is still running, add it for next update
                    Running => {
                        new_animations.push((b, a, paused));
                    },
                    _ => {},
                }
            }

            if new_animations.len() > 0 {
                self.running.insert(id, new_animations);
            }
        }
    }

    /// Render this scene
    pub fn draw<B: BackEnd<I>>(&self, c: &Context, b: &mut B) {
        for child in self.children.iter() {
            child.draw(c, b);
        }
    }

    /// Register animation with sprite
    pub fn run(&mut self, sprite_id: Uuid, animation: &Behavior<Animation>) {
        use std::collections::hash_map::{ Vacant, Occupied };
        let animations = match self.running.entry(sprite_id) {
            Vacant(entry) => entry.set(Vec::new()),
            Occupied(entry) => entry.into_mut()
        };
        let state = State::new(animation.clone());
        animations.push((animation.clone(), state, false));
    }

    fn find(&self, sprite_id: Uuid, animation: &Behavior<Animation>) -> Option<uint> {
        let mut index = None;
        match self.running.get(&sprite_id) {
            Some(animations) => {
                for i in range(0, animations.len()) {
                    let (ref b, _, _) = animations[i];
                    if b == animation {
                        index = Some(i);
                        break;
                    }
                }
            },
            _ => {},
        }
        index
    }

    /// Pause a running animation of the sprite
    pub fn pause(&mut self, sprite_id: Uuid, animation: &Behavior<Animation>) {
        let index = self.find(sprite_id.clone(), animation);
        if index.is_some() {
            println!("found");
            let i = index.unwrap();
            let animations = &mut self.running[sprite_id];
            let (b, s, _) = animations.remove(i).unwrap();
            animations.push((b, s, true));
        }
    }

    /// Resume a paused animation of the sprite
    pub fn resume(&mut self, sprite_id: Uuid, animation: &Behavior<Animation>) {
        let index = self.find(sprite_id.clone(), animation);
        if index.is_some() {
            println!("found");
            let i = index.unwrap();
            let animations = &mut self.running[sprite_id];
            let (b, s, _) = animations.remove(i).unwrap();
            animations.push((b, s, false));
        }
    }

    /// Toggle an animation of the sprite
    pub fn toggle(&mut self, sprite_id: Uuid, animation: &Behavior<Animation>) {
        let index = self.find(sprite_id.clone(), animation);
        if index.is_some() {
            let i = index.unwrap();
            let animations = &mut self.running[sprite_id];
            let (b, s, paused) = animations.remove(i).unwrap();
            animations.push((b, s, !paused));
        }
    }

    /// Stop a running animation of the sprite
    pub fn stop(&mut self, sprite_id: Uuid, animation: &Behavior<Animation>) {
        let index = self.find(sprite_id.clone(), animation);
        if index.is_some() {
            let i = index.unwrap();
            &mut self.running[sprite_id].remove(i);
        }
    }

    /// Stop all running animations of the sprite
    pub fn stop_all(&mut self, sprite_id: Uuid) {
        self.running.remove(&sprite_id);
    }

    /// Get all the running animations in the scene
    pub fn running(&self) -> uint {
        let mut total = 0;
        for (_, animations) in self.running.iter() {
            total += animations.len();
        }
        total
    }

    /// Add sprite to scene
    pub fn add_child(&mut self, sprite: Sprite<I>) -> Uuid {
        let id = sprite.id();
        self.children.push(sprite);
        self.children_index.insert(id.clone(), self.children.len() - 1);
        id
    }

    fn stop_all_including_children(&mut self, sprite: &Sprite<I>) {
        self.stop_all(sprite.id());
        for child in sprite.children().iter() {
            self.stop_all_including_children(child);
        }
    }

    /// Remove the child by `id` from the scene's children or grandchild
    /// will stop all the animations run by this child
    pub fn remove_child(&mut self, id: Uuid) -> Option<Sprite<I>> {
        let removed = match self.children_index.remove(&id) {
            Some(i) => {
                let removed = self.children.remove(i).unwrap();
                // Removing a element of vector will alter the index,
                // update the mapping from uuid to index.
                for index in range(i, self.children.len()) {
                    let uuid = self.children[index].id();
                    self.children_index.insert(uuid, index);
                }
                Some(removed)
            },
            None => {
                for child in self.children.iter_mut() {
                    match child.remove_child(id.clone()) {
                        Some(c) => {
                            return Some(c);
                        }
                        _ => {}
                    }
                }

                None
            }
        };

        if removed.is_some() {
            self.stop_all_including_children(removed.as_ref().unwrap());
        }

        removed
    }

    /// Find the child by `id` from the scene's children or grandchild
    pub fn child(&self, id: Uuid) -> Option<&Sprite<I>> {
        match self.children_index.get(&id) {
            Some(i) => { Some(&self.children[*i]) },
            None => {
                for child in self.children.iter() {
                    match child.child(id.clone()) {
                        Some(c) => {
                            return Some(c);
                        }
                        _ => {}
                    }
                }

                None
            }
        }
    }

    /// Find the child by `id` from this sprite's children or grandchild, mutability
    pub fn child_mut(&mut self, id: Uuid) -> Option<&mut Sprite<I>> {
        match self.children_index.get(&id) {
            Some(i) => { Some(&mut self.children[*i]) },
            None => {
                for child in self.children.iter_mut() {
                    match child.child_mut(id.clone()) {
                        Some(c) => {
                            return Some(c);
                        }
                        _ => {}
                    }
                }

                None
            }
        }
    }
}
<commit_msg>Fix Vacant/Occupied imports<commit_after>
use std::collections::HashMap;
use uuid::Uuid;

use graphics::*;

use event::GenericEvent;
use ai_behavior::{
    Behavior,
    State,
    Running,
};

use sprite::Sprite;

use animation::{
    Animation,
    AnimationState,
};

/// A scene is used to manage sprite's life and run animation with sprite
pub struct Scene<I: ImageSize> {
    children: Vec<Sprite<I>>,
    children_index: HashMap<Uuid, uint>,
    running: HashMap<Uuid,
        Vec<(Behavior<Animation>, State<Animation, AnimationState>, bool)>>,
}

impl<I: ImageSize> Scene<I> {
    /// Create a new scene
    pub fn new() -> Scene<I> {
        Scene {
            children: Vec::new(),
            children_index: HashMap::new(),
            running: HashMap::new(),
        }
    }

    /// Update animation's state
    pub fn event<E: GenericEvent>(&mut self, e: &E) {
        // regenerate the animations and their states
        let running = self.running.clone();
        self.running.clear();

        for (id, animations) in running.into_iter() {
            let mut new_animations = Vec::new();

            for (b, mut a, paused) in animations.into_iter() {
                if paused {
                    new_animations.push((b, a, paused));
                    continue;
                }

                let sprite = self.child_mut(id.clone()).unwrap();
                let (status, _) = a.event(e, |_, dt, animation, s| {
                    let (state, status, remain) = {
                        let start_state;
                        let state = match *s {
                            None => { start_state = animation.to_state(sprite); &start_state },
                            Some(ref state) => state,
                        };
                        state.update(sprite, dt)
                    };
                    *s = state;
                    (status, remain)
                });

                match status {
                    // the behavior is still running, add it for next update
                    Running => {
                        new_animations.push((b, a, paused));
                    },
                    _ => {},
                }
            }

            if new_animations.len() > 0 {
                self.running.insert(id, new_animations);
            }
        }
    }

    /// Render this scene
    pub fn draw<B: BackEnd<I>>(&self, c: &Context, b: &mut B) {
        for child in self.children.iter() {
            child.draw(c, b);
        }
    }

    /// Register animation with sprite
    pub fn run(&mut self, sprite_id: Uuid, animation: &Behavior<Animation>) {
        use std::collections::hash_map::Entry::{ Vacant, Occupied };
        let animations = match self.running.entry(sprite_id) {
            Vacant(entry) => entry.set(Vec::new()),
            Occupied(entry) => entry.into_mut()
        };
        let state = State::new(animation.clone());
        animations.push((animation.clone(), state, false));
    }

    fn find(&self, sprite_id: Uuid, animation: &Behavior<Animation>) -> Option<uint> {
        let mut index = None;
        match self.running.get(&sprite_id) {
            Some(animations) => {
                for i in range(0, animations.len()) {
                    let (ref b, _, _) = animations[i];
                    if b == animation {
                        index = Some(i);
                        break;
                    }
                }
            },
            _ => {},
        }
        index
    }

    /// Pause a running animation of the sprite
    pub fn pause(&mut self, sprite_id: Uuid, animation: &Behavior<Animation>) {
        let index = self.find(sprite_id.clone(), animation);
        if index.is_some() {
            println!("found");
            let i = index.unwrap();
            let animations = &mut self.running[sprite_id];
            let (b, s, _) = animations.remove(i).unwrap();
            animations.push((b, s, true));
        }
    }

    /// Resume a paused animation of the sprite
    pub fn resume(&mut self, sprite_id: Uuid, animation: &Behavior<Animation>) {
        let index = self.find(sprite_id.clone(), animation);
        if index.is_some() {
            println!("found");
            let i = index.unwrap();
            let animations = &mut self.running[sprite_id];
            let (b, s, _) = animations.remove(i).unwrap();
            animations.push((b, s, false));
        }
    }

    /// Toggle an animation of the sprite
    pub fn toggle(&mut self, sprite_id: Uuid, animation: &Behavior<Animation>) {
        let index = self.find(sprite_id.clone(), animation);
        if index.is_some() {
            let i = index.unwrap();
            let animations = &mut self.running[sprite_id];
            let (b, s, paused) = animations.remove(i).unwrap();
            animations.push((b, s, !paused));
        }
    }

    /// Stop a running animation of the sprite
    pub fn stop(&mut self, sprite_id: Uuid, animation: &Behavior<Animation>) {
        let index = self.find(sprite_id.clone(), animation);
        if index.is_some() {
            let i = index.unwrap();
            &mut self.running[sprite_id].remove(i);
        }
    }

    /// Stop all running animations of the sprite
    pub fn stop_all(&mut self, sprite_id: Uuid) {
        self.running.remove(&sprite_id);
    }

    /// Get all the running animations in the scene
    pub fn running(&self) -> uint {
        let mut total = 0;
        for (_, animations) in self.running.iter() {
            total += animations.len();
        }
        total
    }

    /// Add sprite to scene
    pub fn add_child(&mut self, sprite: Sprite<I>) -> Uuid {
        let id = sprite.id();
        self.children.push(sprite);
        self.children_index.insert(id.clone(), self.children.len() - 1);
        id
    }

    fn stop_all_including_children(&mut self, sprite: &Sprite<I>) {
        self.stop_all(sprite.id());
        for child in sprite.children().iter() {
            self.stop_all_including_children(child);
        }
    }

    /// Remove the child by `id` from the scene's children or grandchild
    /// will stop all the animations run by this child
    pub fn remove_child(&mut self, id: Uuid) -> Option<Sprite<I>> {
        let removed = match self.children_index.remove(&id) {
            Some(i) => {
                let removed = self.children.remove(i).unwrap();
                // Removing a element of vector will alter the index,
                // update the mapping from uuid to index.
                for index in range(i, self.children.len()) {
                    let uuid = self.children[index].id();
                    self.children_index.insert(uuid, index);
                }
                Some(removed)
            },
            None => {
                for child in self.children.iter_mut() {
                    match child.remove_child(id.clone()) {
                        Some(c) => {
                            return Some(c);
                        }
                        _ => {}
                    }
                }

                None
            }
        };

        if removed.is_some() {
            self.stop_all_including_children(removed.as_ref().unwrap());
        }

        removed
    }

    /// Find the child by `id` from the scene's children or grandchild
    pub fn child(&self, id: Uuid) -> Option<&Sprite<I>> {
        match self.children_index.get(&id) {
            Some(i) => { Some(&self.children[*i]) },
            None => {
                for child in self.children.iter() {
                    match child.child(id.clone()) {
                        Some(c) => {
                            return Some(c);
                        }
                        _ => {}
                    }
                }

                None
            }
        }
    }

    /// Find the child by `id` from this sprite's children or grandchild, mutability
    pub fn child_mut(&mut self, id: Uuid) -> Option<&mut Sprite<I>> {
        match self.children_index.get(&id) {
            Some(i) => { Some(&mut self.children[*i]) },
            None => {
                for child in self.children.iter_mut() {
                    match child.child_mut(id.clone()) {
                        Some(c) => {
                            return Some(c);
                        }
                        _ => {}
                    }
                }

                None
            }
        }
    }
}
<|endoftext|>


<commit_before>//
// Mnemosyne: a functional systems programming language.
// (c) 2015 Hawk Weisman
//
// Mnemosyne is released under the MIT License. Please refer to
// the LICENSE file at the top-level directory of this distribution
// or at https://github.com/hawkw/mnemosyne/.
//
#![crate_name = "mnemosyne"]
#![crate_type = "lib"]
#![feature(rustc_private)]
#![feature(static_recursion)]
#![feature(box_syntax, box_patterns)]

//! # Mnemosyne core
//!
//! This crate contains the core Mnemosyne programming language components.
//! This includes the mnemosyne abstract syntax tree (`semantic::ast`),
//! functions for performing semantic analysis (`semantic`), functions
//! for compiling abstract syntax trees to LLVM bytecode (`compile`), and
//! assorted utility code such as a positional reference type and a
//! `ForkTable` data structure for use as a symbol table.
//!
//! The Mnemosyne parser is contained in a separate crate in order to improve
//! compile times.

extern crate rustc;
extern crate libc;
extern crate combine;
extern crate iron_llvm;
extern crate llvm_sys;
#[macro_use] extern crate itertools;

use rustc::lib::llvm::{LLVMVersionMajor, LLVMVersionMinor};

use std::fmt::Debug;

const VERSION_MAJOR: u32 = 0;
const VERSION_MINOR: u32 = 1;

include!(concat!(env!("OUT_DIR"), "/gen.rs"));

/// Returns the Mnemosyne version as a String
pub fn mnemosyne_version() -> String {
    format!("Mnemosyne v{}.{}", VERSION_MAJOR, VERSION_MINOR)
}

/// Macro for formatting an internal compiler error panic.
///
/// This should be used instead of the Rust standard library's `panic!()`
/// macro in the event of an unrecoverable internal compiler error.
#[macro_export]
macro_rules! ice {
    ($msg:expr) => (
        panic!( "[internal error] {}\n \
                 [internal error] Something has gone horribly wrong.\n \
                 [internal error] Please contact the Mnemosyne implementors.\n\
                 {}, {}"
              , $msg
              , $crate::mnemosyne_version(), $crate::llvm_version()
              )
          );
    ($fmt:expr, $($arg:tt)+) => (
        panic!( "[internal error] {}\n \
                 [internal error] Something has gone horribly wrong.\n \
                 [internal error] Please contact the Mnemosyne implementors.\n\
                 {}, {}"
              , format_args!($fmt, $($arg)+)
              , $crate::mnemosyne_version(), $crate::llvm_version()
              )
          )
}

pub mod position;
pub mod semantic;
pub mod compile;
pub mod forktable;
pub mod chars;
pub mod errors;

pub use semantic::ast;
<commit_msg>Nicer way of managing versions for crate<commit_after>//
// Mnemosyne: a functional systems programming language.
// (c) 2015 Hawk Weisman
//
// Mnemosyne is released under the MIT License. Please refer to
// the LICENSE file at the top-level directory of this distribution
// or at https://github.com/hawkw/mnemosyne/.
//
#![crate_name = "mnemosyne"]
#![crate_type = "lib"]
#![feature(rustc_private)]
#![feature(static_recursion)]
#![feature(box_syntax, box_patterns)]

//! # Mnemosyne core
//!
//! This crate contains the core Mnemosyne programming language components.
//! This includes the mnemosyne abstract syntax tree (`semantic::ast`),
//! functions for performing semantic analysis (`semantic`), functions
//! for compiling abstract syntax trees to LLVM bytecode (`compile`), and
//! assorted utility code such as a positional reference type and a
//! `ForkTable` data structure for use as a symbol table.
//!
//! The Mnemosyne parser is contained in a separate crate in order to improve
//! compile times.

extern crate rustc;
extern crate libc;
extern crate combine;
extern crate iron_llvm;
extern crate llvm_sys;
#[macro_use] extern crate itertools;

use rustc::lib::llvm::{LLVMVersionMajor, LLVMVersionMinor};

use std::fmt::Debug;

include!(concat!(env!("OUT_DIR"), "/gen.rs"));

/// Returns the Mnemosyne version as a String
pub fn mnemosyne_version() -> String {
    format!("Mnemosyne {}", env!("CARGO_PKG_VERSION"))
}

/// Macro for formatting an internal compiler error panic.
///
/// This should be used instead of the Rust standard library's `panic!()`
/// macro in the event of an unrecoverable internal compiler error.
#[macro_export]
macro_rules! ice {
    ($msg:expr) => (
        panic!( "[internal error] {}\n \
                 [internal error] Something has gone horribly wrong.\n \
                 [internal error] Please contact the Mnemosyne implementors.\n\
                 {}, {}"
              , $msg
              , $crate::mnemosyne_version(), $crate::llvm_version()
              )
          );
    ($fmt:expr, $($arg:tt)+) => (
        panic!( "[internal error] {}\n \
                 [internal error] Something has gone horribly wrong.\n \
                 [internal error] Please contact the Mnemosyne implementors.\n\
                 {}, {}"
              , format_args!($fmt, $($arg)+)
              , $crate::mnemosyne_version(), $crate::llvm_version()
              )
          )
}

pub mod position;
pub mod semantic;
pub mod compile;
pub mod forktable;
pub mod chars;
pub mod errors;

pub use semantic::ast;
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use app_units::Au;
use webrender_traits::{FontKey, FontRenderMode, GlyphDimensions};
use webrender_traits::{NativeFontHandle, GlyphOptions};
use webrender_traits::{GlyphKey};

use freetype::freetype::{FT_Render_Mode, FT_Pixel_Mode};
use freetype::freetype::{FT_Done_FreeType, FT_Library_SetLcdFilter};
use freetype::freetype::{FT_Library, FT_Set_Char_Size};
use freetype::freetype::{FT_Face, FT_Long, FT_UInt, FT_F26Dot6};
use freetype::freetype::{FT_Init_FreeType, FT_Load_Glyph, FT_Render_Glyph};
use freetype::freetype::{FT_New_Memory_Face, FT_GlyphSlot, FT_LcdFilter};
use freetype::freetype::{FT_Done_Face, FT_Error};

use std::{cmp, mem, ptr, slice};
use std::collections::HashMap;

struct Face {
    face: FT_Face,
}

pub struct FontContext {
    lib: FT_Library,
    faces: HashMap<FontKey, Face>,
}

pub struct RasterizedGlyph {
    pub width: u32,
    pub height: u32,
    pub bytes: Vec<u8>,
}

fn float_to_fixed(before: usize, f: f64) -> i32 {
    ((1i32 << before) as f64 * f) as i32
}

fn float_to_fixed_ft(f: f64) -> i32 {
    float_to_fixed(6, f)
}

const SUCCESS: FT_Error = FT_Error(0);

impl FontContext {
    pub fn new() -> FontContext {
        let mut lib: FT_Library = ptr::null_mut();
        unsafe {
            let result = FT_Init_FreeType(&mut lib);
            assert!(result.succeeded(), "Unable to initialize FreeType library {:?}", result);

            // TODO(gw): Check result of this to determine if freetype build supports subpixel.
            let result = FT_Library_SetLcdFilter(lib, FT_LcdFilter::FT_LCD_FILTER_DEFAULT);
            if !result.succeeded() {
                println!("WARN: Initializing a FreeType library build without subpixel AA enabled!");
            }
        }

        FontContext {
            lib: lib,
            faces: HashMap::new(),
        }
    }

    pub fn add_raw_font(&mut self, font_key: &FontKey, bytes: &[u8], index: u32) {
        if !self.faces.contains_key(&font_key) {
            let mut face: FT_Face = ptr::null_mut();
            let result = unsafe {
                FT_New_Memory_Face(self.lib,
                                   bytes.as_ptr(),
                                   bytes.len() as FT_Long,
                                   index as FT_Long,
                                   &mut face)
            };
            if result.succeeded() && !face.is_null() {
                self.faces.insert(*font_key, Face {
                    face: face,
                    //_bytes: bytes
                });
            } else {
                println!("WARN: webrender failed to load font {:?}", font_key);
            }
        }
    }

    pub fn add_native_font(&mut self, _font_key: &FontKey, _native_font_handle: NativeFontHandle) {
        panic!("TODO: Not supported on Linux");
    }

    pub fn delete_font(&mut self, font_key: &FontKey) {
        if let Some(face) = self.faces.remove(font_key) {
            let result = unsafe {
                FT_Done_Face(face.face)
            };
            assert!(result.succeeded());
        }
    }

    fn load_glyph(&self,
                  font_key: FontKey,
                  size: Au,
                  character: u32) -> Option<FT_GlyphSlot> {

        debug_assert!(self.faces.contains_key(&font_key));
        let face = self.faces.get(&font_key).unwrap();
        let char_size = float_to_fixed_ft(size.to_f64_px());

        assert_eq!(SUCCESS, unsafe {
            FT_Set_Char_Size(face.face, char_size as FT_F26Dot6, 0, 0, 0)
        });

        let result = unsafe {
            FT_Load_Glyph(face.face, character as FT_UInt, 0)
        };

        if result == SUCCESS {
            let slot = unsafe { (*face.face).glyph };
            assert!(slot != ptr::null_mut());
            Some(slot)
        } else {
            error!("Unable to load glyph for {} of size {:?} from font {:?}, {:?}",
                character, size, font_key, result);
            None
        }
    }

    fn get_glyph_dimensions_impl(slot: FT_GlyphSlot) -> Option<GlyphDimensions> {
        let metrics = unsafe { &(*slot).metrics };
        if metrics.width == 0 || metrics.height == 0 {
            None
        } else {
            let left = metrics.horiBearingX >> 6;
            let top = metrics.horiBearingY >> 6;
            let right = (metrics.horiBearingX + metrics.width + 0x3f) >> 6;
            let bottom = (metrics.horiBearingY + metrics.height + 0x3f) >> 6;
            Some(GlyphDimensions {
                left: left as i32,
                top: top as i32,
                width: (right - left) as u32,
                height: (bottom - top) as u32,
            })
        }
    }

    pub fn get_glyph_dimensions(&mut self,
                                key: &GlyphKey) -> Option<GlyphDimensions> {
        self.load_glyph(key.font_key, key.size, key.index)
            .and_then(Self::get_glyph_dimensions_impl)
    }

    pub fn rasterize_glyph(&mut self,
                           key: &GlyphKey,
                           render_mode: FontRenderMode,
                           _glyph_options: Option<GlyphOptions>)
                           -> Option<RasterizedGlyph> {

        let slot = match self.load_glyph(key.font_key, key.size, key.index) {
            Some(slot) => slot,
            None => return None,
        };
        let render_mode = match render_mode {
            FontRenderMode::Mono => FT_Render_Mode::FT_RENDER_MODE_MONO,
            FontRenderMode::Alpha => FT_Render_Mode::FT_RENDER_MODE_NORMAL,
            FontRenderMode::Subpixel => FT_Render_Mode::FT_RENDER_MODE_LCD,
        };

        let result = unsafe { FT_Render_Glyph(slot, render_mode) };
        if result != SUCCESS {
            error!("Unable to rasterize {:?} with {:?}, {:?}", key, render_mode, result);
            return None;
        }

        let dimensions = Self::get_glyph_dimensions_impl(slot).unwrap();
        let bitmap = unsafe { &(*slot).bitmap };
        let pixel_mode = unsafe { mem::transmute(bitmap.pixel_mode as u32) };
        info!("Rasterizing {:?} as {:?} with dimensions {:?}", key, render_mode, dimensions);
        // we may be filling only a part of the buffer, so initialize the whole thing with 0
        let mut final_buffer = Vec::with_capacity(dimensions.width as usize *
                                                  dimensions.height as usize *
                                                  4);

        let offset_x = dimensions.left as i32 - unsafe { (*slot).bitmap_left };

        for y in 0 .. dimensions.height {
            let src_y = y as i32 + dimensions.top - unsafe { (*slot).bitmap_top };
            if src_y < 0 || src_y >= bitmap.rows as i32 {
                for _x in 0 .. dimensions.width {
                    final_buffer.extend_from_slice(&[0xff, 0xff, 0xff, 0]);
                }
                continue
            }
            let row = unsafe {
                let base = bitmap.buffer.offset((src_y * bitmap.pitch) as isize);
                slice::from_raw_parts(base, bitmap.width as usize)
            };
            match pixel_mode {
                FT_Pixel_Mode::FT_PIXEL_MODE_MONO => {
                    for x in 0 .. dimensions.width {
                        let src_x = x as i32 + offset_x;
                        let mask = 0x80 >> (src_x & 0x7);
                        let alpha = if src_x >= 0 && src_x < 8 * bitmap.width as i32 &&
                                       row[(src_x >> 3) as usize] & mask != 0 {
                            0xff
                        } else {
                            0
                        };
                        final_buffer.extend_from_slice(&[0xff, 0xff, 0xff, alpha]);
                    }
                }
                FT_Pixel_Mode::FT_PIXEL_MODE_GRAY => {
                    for _x in 0 .. -offset_x {
                        final_buffer.extend_from_slice(&[0xff, 0xff, 0xff, 0]);
                    }
                    let cut_length = cmp::max(0, (offset_x + dimensions.width as i32));
                    for &alpha in &row[.. cmp::min(row.len(), cut_length as usize)] {
                        final_buffer.extend_from_slice(&[0xff, 0xff, 0xff, alpha]);
                    }
                    for _x in bitmap.width as i32 - offset_x .. dimensions.width as i32 {
                        final_buffer.extend_from_slice(&[0xff, 0xff, 0xff, 0]);
                    }
                }
                FT_Pixel_Mode::FT_PIXEL_MODE_LCD => {
                    assert_eq!(bitmap.width % 3, 0);
                    for x in 0 .. dimensions.width {
                        let src_x = (x as i32 + offset_x) * 3;
                        let slice = if src_x >= 0 && src_x < bitmap.width as i32 {
                            let t = &row[src_x as usize ..];
                            [t[2], t[1], t[0], 0xff]
                        } else {
                            [0xff, 0xff, 0xff, 0]
                        };
                        final_buffer.extend_from_slice(&slice);
                    }
                }
                _ => panic!("Unsupported {:?}", pixel_mode)
            }
            assert_eq!(final_buffer.len(), ((y+1) * dimensions.width * 4) as usize);
        }

        Some(RasterizedGlyph {
            width: dimensions.width as u32,
            height: dimensions.height as u32,
            bytes: final_buffer,
        })
    }
}

impl Drop for FontContext {
    fn drop(&mut self) {
        unsafe {
            FT_Done_FreeType(self.lib);
        }
    }
}
<commit_msg>Reworked the rasterization code with the focus on safety<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use app_units::Au;
use webrender_traits::{FontKey, FontRenderMode, GlyphDimensions};
use webrender_traits::{NativeFontHandle, GlyphOptions};
use webrender_traits::{GlyphKey};

use freetype::freetype::{FT_Render_Mode, FT_Pixel_Mode};
use freetype::freetype::{FT_Done_FreeType, FT_Library_SetLcdFilter};
use freetype::freetype::{FT_Library, FT_Set_Char_Size};
use freetype::freetype::{FT_Face, FT_Long, FT_UInt, FT_F26Dot6};
use freetype::freetype::{FT_Init_FreeType, FT_Load_Glyph, FT_Render_Glyph};
use freetype::freetype::{FT_New_Memory_Face, FT_GlyphSlot, FT_LcdFilter};
use freetype::freetype::{FT_Done_Face, FT_Error};

use std::{cmp, mem, ptr, slice};
use std::collections::HashMap;

struct Face {
    face: FT_Face,
}

pub struct FontContext {
    lib: FT_Library,
    faces: HashMap<FontKey, Face>,
}

pub struct RasterizedGlyph {
    pub width: u32,
    pub height: u32,
    pub bytes: Vec<u8>,
}

fn float_to_fixed(before: usize, f: f64) -> i32 {
    ((1i32 << before) as f64 * f) as i32
}

fn float_to_fixed_ft(f: f64) -> i32 {
    float_to_fixed(6, f)
}

const SUCCESS: FT_Error = FT_Error(0);

impl FontContext {
    pub fn new() -> FontContext {
        let mut lib: FT_Library = ptr::null_mut();
        unsafe {
            let result = FT_Init_FreeType(&mut lib);
            assert!(result.succeeded(), "Unable to initialize FreeType library {:?}", result);

            // TODO(gw): Check result of this to determine if freetype build supports subpixel.
            let result = FT_Library_SetLcdFilter(lib, FT_LcdFilter::FT_LCD_FILTER_DEFAULT);
            if !result.succeeded() {
                println!("WARN: Initializing a FreeType library build without subpixel AA enabled!");
            }
        }

        FontContext {
            lib: lib,
            faces: HashMap::new(),
        }
    }

    pub fn add_raw_font(&mut self, font_key: &FontKey, bytes: &[u8], index: u32) {
        if !self.faces.contains_key(&font_key) {
            let mut face: FT_Face = ptr::null_mut();
            let result = unsafe {
                FT_New_Memory_Face(self.lib,
                                   bytes.as_ptr(),
                                   bytes.len() as FT_Long,
                                   index as FT_Long,
                                   &mut face)
            };
            if result.succeeded() && !face.is_null() {
                self.faces.insert(*font_key, Face {
                    face: face,
                    //_bytes: bytes
                });
            } else {
                println!("WARN: webrender failed to load font {:?}", font_key);
            }
        }
    }

    pub fn add_native_font(&mut self, _font_key: &FontKey, _native_font_handle: NativeFontHandle) {
        panic!("TODO: Not supported on Linux");
    }

    pub fn delete_font(&mut self, font_key: &FontKey) {
        if let Some(face) = self.faces.remove(font_key) {
            let result = unsafe {
                FT_Done_Face(face.face)
            };
            assert!(result.succeeded());
        }
    }

    fn load_glyph(&self,
                  font_key: FontKey,
                  size: Au,
                  character: u32) -> Option<FT_GlyphSlot> {

        debug_assert!(self.faces.contains_key(&font_key));
        let face = self.faces.get(&font_key).unwrap();
        let char_size = float_to_fixed_ft(size.to_f64_px());

        assert_eq!(SUCCESS, unsafe {
            FT_Set_Char_Size(face.face, char_size as FT_F26Dot6, 0, 0, 0)
        });

        let result = unsafe {
            FT_Load_Glyph(face.face, character as FT_UInt, 0)
        };

        if result == SUCCESS {
            let slot = unsafe { (*face.face).glyph };
            assert!(slot != ptr::null_mut());
            Some(slot)
        } else {
            error!("Unable to load glyph for {} of size {:?} from font {:?}, {:?}",
                character, size, font_key, result);
            None
        }
    }

    fn get_glyph_dimensions_impl(slot: FT_GlyphSlot) -> Option<GlyphDimensions> {
        let metrics = unsafe { &(*slot).metrics };
        if metrics.width == 0 || metrics.height == 0 {
            None
        } else {
            let left = metrics.horiBearingX >> 6;
            let top = metrics.horiBearingY >> 6;
            let right = (metrics.horiBearingX + metrics.width + 0x3f) >> 6;
            let bottom = (metrics.horiBearingY + metrics.height + 0x3f) >> 6;
            Some(GlyphDimensions {
                left: left as i32,
                top: top as i32,
                width: (right - left) as u32,
                height: (bottom - top) as u32,
            })
        }
    }

    pub fn get_glyph_dimensions(&mut self,
                                key: &GlyphKey) -> Option<GlyphDimensions> {
        self.load_glyph(key.font_key, key.size, key.index)
            .and_then(Self::get_glyph_dimensions_impl)
    }

    pub fn rasterize_glyph(&mut self,
                           key: &GlyphKey,
                           render_mode: FontRenderMode,
                           _glyph_options: Option<GlyphOptions>)
                           -> Option<RasterizedGlyph> {

        let slot = match self.load_glyph(key.font_key, key.size, key.index) {
            Some(slot) => slot,
            None => return None,
        };
        let render_mode = match render_mode {
            FontRenderMode::Mono => FT_Render_Mode::FT_RENDER_MODE_MONO,
            FontRenderMode::Alpha => FT_Render_Mode::FT_RENDER_MODE_NORMAL,
            FontRenderMode::Subpixel => FT_Render_Mode::FT_RENDER_MODE_LCD,
        };

        let result = unsafe { FT_Render_Glyph(slot, render_mode) };
        if result != SUCCESS {
            error!("Unable to rasterize {:?} with {:?}, {:?}", key, render_mode, result);
            return None;
        }

        let dimensions = Self::get_glyph_dimensions_impl(slot).unwrap();
        let bitmap = unsafe { &(*slot).bitmap };
        let pixel_mode = unsafe { mem::transmute(bitmap.pixel_mode as u32) };
        info!("Rasterizing {:?} as {:?} with dimensions {:?}", key, render_mode, dimensions);
        // we may be filling only a part of the buffer, so initialize the whole thing with 0
        let mut final_buffer = Vec::with_capacity(dimensions.width as usize *
                                                  dimensions.height as usize *
                                                  4);

        let offset_x = dimensions.left - unsafe { (*slot).bitmap_left };

        for y in 0 .. dimensions.height {
            let src_y = y as i32 + dimensions.top - unsafe { (*slot).bitmap_top };
            if src_y < 0 || src_y >= bitmap.rows as i32 {
                for _x in 0 .. dimensions.width {
                    final_buffer.extend_from_slice(&[0xff, 0xff, 0xff, 0]);
                }
                continue
            }
            let base = unsafe {
                bitmap.buffer.offset((src_y * bitmap.pitch) as isize)
            };
            // determine the destination range of texels that `bitmap` provides data for
            let dst_start = cmp::max(0, -offset_x);
            let dst_end = cmp::min(dimensions.width as i32, bitmap.width as i32 - offset_x);
            for _x in 0 .. dst_start {
                final_buffer.extend_from_slice(&[0xff, 0xff, 0xff, 0]);
            }
            match pixel_mode {
                FT_Pixel_Mode::FT_PIXEL_MODE_MONO => {
                    for x in dst_start .. dst_end {
                        let src_x = x + offset_x;
                        let mask = 0x80 >> (src_x & 0x7);
                        let byte = unsafe {
                            *base.offset((src_x >> 3) as isize)
                        };
                        let alpha = if byte & mask != 0 { 0xff } else { 0 };
                        final_buffer.extend_from_slice(&[0xff, 0xff, 0xff, alpha]);
                    }
                }
                FT_Pixel_Mode::FT_PIXEL_MODE_GRAY => {
                    for x in dst_start .. dst_end {
                        let alpha = unsafe {
                            *base.offset((x + offset_x) as isize)
                        };
                        final_buffer.extend_from_slice(&[0xff, 0xff, 0xff, alpha]);
                    }
                }
                FT_Pixel_Mode::FT_PIXEL_MODE_LCD => {
                    for x in dst_start .. dst_end {
                        let src_x = ((x + offset_x) * 3) as isize;
                        assert!(src_x+2 < bitmap.pitch as isize);
                        let t = unsafe {
                            slice::from_raw_parts(base.offset(src_x), 3)
                        };
                        final_buffer.extend_from_slice(&[t[2], t[1], t[0], 0xff]);
                    }
                }
                _ => panic!("Unsupported {:?}", pixel_mode)
            }
            for _x in dst_end .. dimensions.width as i32 {
                final_buffer.extend_from_slice(&[0xff, 0xff, 0xff, 0]);
            }
            assert_eq!(final_buffer.len(), ((y+1) * dimensions.width * 4) as usize);
        }

        Some(RasterizedGlyph {
            width: dimensions.width as u32,
            height: dimensions.height as u32,
            bytes: final_buffer,
        })
    }
}

impl Drop for FontContext {
    fn drop(&mut self) {
        unsafe {
            FT_Done_FreeType(self.lib);
        }
    }
}
<|endoftext|>


<commit_before>// Copyright (C) 2018 O.S. Systems Sofware LTDA
//
// SPDX-License-Identifier: GPL-2.0
//

use serde_ini;

use std::io;
use std::path::PathBuf;
use std::time::Duration;

use serde_helpers::de;

const SYSTEM_SETTINGS_PATH: &str = "/etc/updatehub.conf";

#[derive(Default, PartialEq, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct Settings {
    pub polling: Polling,
    pub storage: Storage,
    pub update: Update,
    pub network: Network,
    pub firmware: Firmware,
}

impl Settings {
    pub fn new() -> Self {
        Settings::default()
    }

    pub fn load(self) -> Result<Self, SettingsError> {
        use std::fs::File;
        use std::io::Read;
        use std::path::Path;

        let path = Path::new(SYSTEM_SETTINGS_PATH);

        if path.exists() {
            info!("Loading system settings from '{}'...",
                  path.to_string_lossy());

            let mut content = String::new();
            File::open(path)?.read_to_string(&mut content)?;

            Ok(Settings::parse(&content)?)
        } else {
            debug!("System settings file {} does not exists.",
                   path.to_string_lossy());
            info!("Using default system settings...");
            Ok(self)
        }
    }

    fn parse(content: &str) -> Result<Self, SettingsError> {
        fn has_protocol_prefix(server: &str) -> bool {
            server.starts_with("http://") || server.starts_with("https://")
        }

        fn has_valid_polling_interval(interval: &Duration) -> bool {
            (*interval >= Duration::new(60, 0))
        }

        let settings = serde_ini::from_str::<Settings>(content)?;

        if !has_valid_polling_interval(&settings.polling.interval) {
            error!("Invalid setting for polling interval. The interval cannot be less than 60 seconds");
            return Err(SettingsError::InvalidInterval);
        }

        if !has_protocol_prefix(&settings.network.server_address) {
            error!("Invalid setting for server address. The server address must use the protocol prefix");
            return Err(SettingsError::InvalidServerAddress);
        }

        Ok(settings)
    }
}

#[derive(Debug)]
pub enum SettingsError {
    Io(io::Error),
    Ini(serde_ini::de::Error),
    InvalidInterval,
    InvalidServerAddress,
}

impl From<io::Error> for SettingsError {
    fn from(err: io::Error) -> SettingsError {
        SettingsError::Io(err)
    }
}

impl From<serde_ini::de::Error> for SettingsError {
    fn from(err: serde_ini::de::Error) -> SettingsError {
        SettingsError::Ini(err)
    }
}

#[derive(Deserialize, PartialEq)]
#[serde(rename_all = "PascalCase")]
pub struct Polling {
    #[serde(deserialize_with = "de::duration_from_str")] pub interval: Duration,
    #[serde(deserialize_with = "de::bool_from_str")] pub enabled: bool,
}

impl Default for Polling {
    fn default() -> Self {
        Polling { interval: Duration::new(86_400, 0), // 1 day
                  enabled: true, }
    }
}

#[derive(Deserialize, PartialEq)]
#[serde(rename_all = "PascalCase")]
pub struct Storage {
    #[serde(deserialize_with = "de::bool_from_str")] pub read_only: bool,
    pub runtime_settings: String,
}

impl Default for Storage {
    fn default() -> Self {
        Storage { read_only: false,
                  runtime_settings: "/var/lib/updatehub.conf".into(), }
    }
}

#[derive(Deserialize, PartialEq)]
#[serde(rename_all = "PascalCase")]
pub struct Update {
    pub download_dir: String,
    #[serde(rename = "SupportedInstallModes")]
    #[serde(deserialize_with = "de::vec_from_str")]
    pub install_modes: Vec<String>,
}

impl Default for Update {
    fn default() -> Self {
        Update { download_dir: "/tmp/updatehub".into(),
                 install_modes: ["dry-run", "copy", "flash", "imxkobs", "raw", "tarball", "ubifs"].iter()
                                                                                .map(|i| {
                                                                                i.to_string()
                                                                                })
                                                                                .collect(), }
    }
}

#[derive(Deserialize, PartialEq)]
#[serde(rename_all = "PascalCase")]
pub struct Network {
    pub server_address: String,
}

impl Default for Network {
    fn default() -> Self {
        Network { server_address: "https://api.updatehub.io".into(), }
    }
}

#[derive(Deserialize, PartialEq)]
#[serde(rename_all = "PascalCase")]
pub struct Firmware {
    pub metadata_path: PathBuf,
}

impl Default for Firmware {
    fn default() -> Self {
        Firmware { metadata_path: "/usr/share/updatehub".into(), }
    }
}

#[test]
fn ok() {
    let ini = r"
[Polling]
Interval=60s
Enabled=false

[Storage]
ReadOnly=true
RuntimeSettings=/run/updatehub/state

[Update]
DownloadDir=/tmp/download
SupportedInstallModes=mode1,mode2

[Network]
ServerAddress=http://localhost

[Firmware]
MetadataPath=/tmp/metadata
";

    let expected =
        Settings { polling: Polling { interval: Duration::new(60, 0),
                                      enabled: false, },
                   storage: Storage { read_only: true,
                                      runtime_settings: "/run/updatehub/state".into(), },
                   update: Update { download_dir: "/tmp/download".into(),
                                    install_modes: ["mode1", "mode2"].iter().map(|i| i.to_string()).collect(), },
                   network: Network { server_address: "http://localhost".into(), },
                   firmware: Firmware { metadata_path: "/tmp/metadata".into(), }, };

    assert!(serde_ini::from_str::<Settings>(ini).map_err(|e| println!("{}", e))
                                                .ok() == Some(expected));
}

#[test]
fn invalid_polling_interval() {
    let ini = r"
[Polling]
Interval=59s
Enabled=false

[Storage]
ReadOnly=true
RuntimeSettings=/run/updatehub/state

[Update]
DownloadDir=/tmp/download
SupportedInstallModes=mode1,mode2

[Network]
ServerAddress=http://localhost

[Firmware]
MetadataPath=/tmp/metadata
";
    assert!(Settings::parse(ini).is_err());
}

#[test]
fn invalid_network_server_address() {
    let ini = r"
[Polling]
Interval=60s
Enabled=false

[Storage]
ReadOnly=true
RuntimeSettings=/run/updatehub/state

[Update]
DownloadDir=/tmp/download
SupportedInstallModes=mode1,mode2

[Network]
ServerAddress=localhost

[Firmware]
MetadataPath=/tmp/metadata
";
    assert!(Settings::parse(ini).is_err());
}

#[test]
fn default() {
    let settings = Settings::new();
    let expected = Settings {
        polling: Polling {
            interval: Duration::new(86_400, 0),
            enabled: true,
        },
        storage: Storage {
            read_only: false,
            runtime_settings: "/var/lib/updatehub.conf".into(),
        },
        update: Update {
            download_dir: "/tmp/updatehub".into(),
            install_modes: [
                "dry-run", "copy", "flash", "imxkobs", "raw", "tarball", "ubifs"
            ].iter()
                .map(|i| i.to_string())
                .collect(),
        },
        network: Network {
            server_address: "https://api.updatehub.io".into(),
        },
        firmware: Firmware {
            metadata_path: "/usr/share/updatehub".into(),
        },
    };

    assert!(Some(settings) == Some(expected));
}
<commit_msg>settings: Simplify code removing inner functions<commit_after>// Copyright (C) 2018 O.S. Systems Sofware LTDA
//
// SPDX-License-Identifier: GPL-2.0
//

use serde_ini;

use std::io;
use std::path::PathBuf;
use std::time::Duration;

use serde_helpers::de;

const SYSTEM_SETTINGS_PATH: &str = "/etc/updatehub.conf";

#[derive(Default, PartialEq, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct Settings {
    pub polling: Polling,
    pub storage: Storage,
    pub update: Update,
    pub network: Network,
    pub firmware: Firmware,
}

impl Settings {
    pub fn new() -> Self {
        Settings::default()
    }

    pub fn load(self) -> Result<Self, SettingsError> {
        use std::fs::File;
        use std::io::Read;
        use std::path::Path;

        let path = Path::new(SYSTEM_SETTINGS_PATH);

        if path.exists() {
            info!("Loading system settings from '{}'...",
                  path.to_string_lossy());

            let mut content = String::new();
            File::open(path)?.read_to_string(&mut content)?;

            Ok(Settings::parse(&content)?)
        } else {
            debug!("System settings file {} does not exists.",
                   path.to_string_lossy());
            info!("Using default system settings...");
            Ok(self)
        }
    }

    fn parse(content: &str) -> Result<Self, SettingsError> {
        let settings = serde_ini::from_str::<Settings>(content)?;

        if &settings.polling.interval < &Duration::new(60, 0) {
            error!("Invalid setting for polling interval. The interval cannot be less than 60 seconds");
            return Err(SettingsError::InvalidInterval);
        }

        if !&settings.network.server_address.starts_with("http://")
           && !&settings.network.server_address.starts_with("https://")
        {
            error!("Invalid setting for server address. The server address must use the protocol prefix");
            return Err(SettingsError::InvalidServerAddress);
        }

        Ok(settings)
    }
}

#[derive(Debug)]
pub enum SettingsError {
    Io(io::Error),
    Ini(serde_ini::de::Error),
    InvalidInterval,
    InvalidServerAddress,
}

impl From<io::Error> for SettingsError {
    fn from(err: io::Error) -> SettingsError {
        SettingsError::Io(err)
    }
}

impl From<serde_ini::de::Error> for SettingsError {
    fn from(err: serde_ini::de::Error) -> SettingsError {
        SettingsError::Ini(err)
    }
}

#[derive(Deserialize, PartialEq)]
#[serde(rename_all = "PascalCase")]
pub struct Polling {
    #[serde(deserialize_with = "de::duration_from_str")] pub interval: Duration,
    #[serde(deserialize_with = "de::bool_from_str")] pub enabled: bool,
}

impl Default for Polling {
    fn default() -> Self {
        Polling { interval: Duration::new(86_400, 0), // 1 day
                  enabled: true, }
    }
}

#[derive(Deserialize, PartialEq)]
#[serde(rename_all = "PascalCase")]
pub struct Storage {
    #[serde(deserialize_with = "de::bool_from_str")] pub read_only: bool,
    pub runtime_settings: String,
}

impl Default for Storage {
    fn default() -> Self {
        Storage { read_only: false,
                  runtime_settings: "/var/lib/updatehub.conf".into(), }
    }
}

#[derive(Deserialize, PartialEq)]
#[serde(rename_all = "PascalCase")]
pub struct Update {
    pub download_dir: String,
    #[serde(rename = "SupportedInstallModes")]
    #[serde(deserialize_with = "de::vec_from_str")]
    pub install_modes: Vec<String>,
}

impl Default for Update {
    fn default() -> Self {
        Update { download_dir: "/tmp/updatehub".into(),
                 install_modes: ["dry-run", "copy", "flash", "imxkobs", "raw", "tarball", "ubifs"].iter()
                                                                                .map(|i| {
                                                                                i.to_string()
                                                                                })
                                                                                .collect(), }
    }
}

#[derive(Deserialize, PartialEq)]
#[serde(rename_all = "PascalCase")]
pub struct Network {
    pub server_address: String,
}

impl Default for Network {
    fn default() -> Self {
        Network { server_address: "https://api.updatehub.io".into(), }
    }
}

#[derive(Deserialize, PartialEq)]
#[serde(rename_all = "PascalCase")]
pub struct Firmware {
    pub metadata_path: PathBuf,
}

impl Default for Firmware {
    fn default() -> Self {
        Firmware { metadata_path: "/usr/share/updatehub".into(), }
    }
}

#[test]
fn ok() {
    let ini = r"
[Polling]
Interval=60s
Enabled=false

[Storage]
ReadOnly=true
RuntimeSettings=/run/updatehub/state

[Update]
DownloadDir=/tmp/download
SupportedInstallModes=mode1,mode2

[Network]
ServerAddress=http://localhost

[Firmware]
MetadataPath=/tmp/metadata
";

    let expected =
        Settings { polling: Polling { interval: Duration::new(60, 0),
                                      enabled: false, },
                   storage: Storage { read_only: true,
                                      runtime_settings: "/run/updatehub/state".into(), },
                   update: Update { download_dir: "/tmp/download".into(),
                                    install_modes: ["mode1", "mode2"].iter().map(|i| i.to_string()).collect(), },
                   network: Network { server_address: "http://localhost".into(), },
                   firmware: Firmware { metadata_path: "/tmp/metadata".into(), }, };

    assert!(serde_ini::from_str::<Settings>(ini).map_err(|e| println!("{}", e))
                                                .ok() == Some(expected));
}

#[test]
fn invalid_polling_interval() {
    let ini = r"
[Polling]
Interval=59s
Enabled=false

[Storage]
ReadOnly=true
RuntimeSettings=/run/updatehub/state

[Update]
DownloadDir=/tmp/download
SupportedInstallModes=mode1,mode2

[Network]
ServerAddress=http://localhost

[Firmware]
MetadataPath=/tmp/metadata
";
    assert!(Settings::parse(ini).is_err());
}

#[test]
fn invalid_network_server_address() {
    let ini = r"
[Polling]
Interval=60s
Enabled=false

[Storage]
ReadOnly=true
RuntimeSettings=/run/updatehub/state

[Update]
DownloadDir=/tmp/download
SupportedInstallModes=mode1,mode2

[Network]
ServerAddress=localhost

[Firmware]
MetadataPath=/tmp/metadata
";
    assert!(Settings::parse(ini).is_err());
}

#[test]
fn default() {
    let settings = Settings::new();
    let expected = Settings {
        polling: Polling {
            interval: Duration::new(86_400, 0),
            enabled: true,
        },
        storage: Storage {
            read_only: false,
            runtime_settings: "/var/lib/updatehub.conf".into(),
        },
        update: Update {
            download_dir: "/tmp/updatehub".into(),
            install_modes: [
                "dry-run", "copy", "flash", "imxkobs", "raw", "tarball", "ubifs"
            ].iter()
                .map(|i| i.to_string())
                .collect(),
        },
        network: Network {
            server_address: "https://api.updatehub.io".into(),
        },
        firmware: Firmware {
            metadata_path: "/usr/share/updatehub".into(),
        },
    };

    assert!(Some(settings) == Some(expected));
}
<|endoftext|>