<commit_before>use std::{
    cell::{Cell, RefCell},
    collections::HashSet,
};

use crate::{prelude::*, utils::SelectionMode as SelMode};

use super::behaviors::{MouseBehavior, SelectionBehavior};

static CONTAINER: &'static str = "container";
static ITEMS_PANEL: &'static str = "items_panel";

#[derive(Default, AsAny)]
pub struct ComboBoxItemState {
    request_selection_toggle: Cell<bool>,
}

impl ComboBoxItemState {
    fn toggle_selection(&self) {
        self.request_selection_toggle.set(true);
    }
}

impl State for ComboBoxItemState {
    fn update(&mut self, _: &mut Registry, ctx: &mut Context<'_>) {
        if !ctx.widget().get::<bool>("enabled") || !self.request_selection_toggle.get() {
            return;
        }
        self.request_selection_toggle.set(false);

        let selected = *ctx.widget().get::<bool>("selected");

        let entity = ctx.entity;
        let index = ctx.index_as_child(entity).unwrap();

        if let Some(parent) = &mut ctx.try_parent_from_id(LIST_VIEW) {
            // let selection_mode = *parent.get::<SelectionMode>("selection_mode");
            // deselect item
            if selected {
                // parent
                //     .get_mut::<SelectedEntities>("selected_entities")
                //     .0
                //     .remove(&entity);
                // parent
                //     .get_mut::<SelectedIndices>("selected_indices")
                //     .0
                //     .remove(&index);
                return;
            }

            // if parent
            //     .get::<SelectedEntities>("selected_entities")
            //     .0
            //     .contains(&entity)
            //     || selection_mode == SelMode::None
            // {
            //     return;
            // }

            // if selection_mode == SelMode::Single {
            //     parent
            //         .get_mut::<SelectedEntities>("selected_entities")
            //         .0
            //         .clear();
            //     parent
            //         .get_mut::<SelectedIndices>("selected_indices")
            //         .0
            //         .clear();
            // }

            // parent
            //     .get_mut::<SelectedEntities>("selected_entities")
            //     .0
            //     .insert(entity);
            // parent
            //     .get_mut::<SelectedIndices>("selected_indices")
            //     .0
            //     .insert(index);
        }
    }
}

widget!(
    ComboBoxItem<ComboBoxItemState>: MouseHandler {
        /// Sets or shares the background property.
        background: Brush,

        /// Sets or shares the border radius property.
        border_radius: f64,

        /// Sets or shares the border thickness property.
        border_width: Thickness,

        /// Sets or shares the border brush property.
        border_brush: Brush,

        /// Sets or shares the foreground property.
        foreground: Brush,

        /// Sets or share the font size property.
        font_size: f64,

        /// Sets or shares the font property.
        font: String,

        /// Sets or shares the padding property.
        padding: Thickness,

        /// Sets or shares the css selector property.
        selector: Selector,

        /// Sets or shares the pressed property.
        pressed: bool,

        /// Sets or shares the selected property.
        selected: bool
    }
);

impl Template for ComboBoxItem {
    fn template(self, id: Entity, _: &mut BuildContext) -> Self {
        self.name("ComboBoxItem")
            .min_width(64.0)
            .height(24.0)
            .selected(false)
            .pressed(false)
            .selector("list-view-item")
            .padding(0.0)
            .background("transparent")
            .border_radius(0.0)
            .border_width(0.0)
            .border_brush("transparent")
            .foreground(colors::LINK_WATER_COLOR)
            .font_size(32.0)
            .font("Roboto Regular")
            .on_click(move |states, _| {
                states.get::<ComboBoxItemState>(id).toggle_selection();
                false
            })
    }

    fn render_object(&self) -> Box<dyn RenderObject> {
        Box::new(RectangleRenderObject)
    }

    fn layout(&self) -> Box<dyn Layout> {
        Box::new(PaddingLayout::new())
    }
}

/// The `ComboBoxState` is used to manipulate the position of the thumb of the slider widget.
#[derive(Default, AsAny)]
pub struct ComboBoxState {
    builder: WidgetBuildContext,
    count: usize,
    items_panel: Entity,
}

impl State for ComboBoxState {
    fn update(&mut self, _: &mut Registry, ctx: &mut Context<'_>) {
        let count = ctx.widget().clone_or_default::<usize>("count");
        let entity = ctx.entity;

        if count != self.count {
            if let Some(builder) = &self.builder {
                ctx.clear_children_of(self.items_panel);

                for i in 0..count {
                    let item = {
                        let build_context = &mut ctx.build_context();
                        let child = builder(build_context, i);
                        let item = ListViewItem::create().build(build_context);

                        let mouse_behavior = MouseBehavior::create().build(build_context);
                        build_context.register_shared_property::<Selector>(
                            "selector",
                            mouse_behavior,
                            item,
                        );
                        build_context.register_shared_property::<bool>(
                            "pressed",
                            mouse_behavior,
                            item,
                        );
                        build_context.append_child(item, mouse_behavior);

                        build_context.register_shared_property::<Brush>("foreground", child, item);
                        build_context.register_shared_property::<f32>("opacity", item, entity);
                        build_context.register_shared_property::<f32>("opacity", child, entity);
                        build_context.register_shared_property::<f64>("font_size", child, item);
                        build_context.append_child(self.items_panel, item);
                        build_context.append_child(mouse_behavior, child);

                        item
                    };
                    ctx.get_widget(item).update_properties_by_theme();
                }
            }

            self.count = count;
        }
    }

    fn update_post_layout(&mut self, _: &mut Registry, ctx: &mut Context<'_>) {
        // for index in ctx
        //     .widget()
        //     .get::<SelectedEntities>("selected_entities")
        //     .0
        //     .clone()
        //     .symmetric_difference(&*self.selected_entities.borrow())
        // {
        //     let mut widget = ctx.get_widget(*index);
        //     widget.set("selected", !widget.get::<bool>("selected"));

        //     widget.update_theme_by_state(false);
        // }

        // *self.selected_entities.borrow_mut() = ctx
        //     .widget()
        //     .get::<SelectedEntities>("selected_entities")
        //     .0
        //     .clone();
    }
}

// todo use code of list view item, by create combobox item insert entity of popup container

widget!(
    /// The `ComboBox` represents an selection widget with a drop-down list.
    ///
    /// **CSS element:** `ComboBox`
    ComboBox<ComboBoxState>: MouseHandler, ChangedHandler {
        /// Sets or shares the background property.
        background: Brush,

        /// Sets or shares the border radius property.
        border_radius: f64,

        /// Sets or shares the border thickness property.
        border_width: Thickness,

        /// Sets or shares the border brush property.
        border_brush: Brush,

        /// Sets or shares the foreground property.
        foreground: Brush,

        /// Sets or share the font size property.
        font_size: f64,

        /// Sets or shares the font property.
        font: String,

        /// Sets or shared the count.
        count: usize,

        /// Sets or shares the css selector property.
        selector: Selector,

        /// Sets or shares the selected index. If the value is -1 no item is selected.
        selected_index: i32,

        /// Sets or shares the padding property.
        padding: Thickness,

        /// Sets or shares the pressed property.
        pressed: bool,

        /// Sets or shares the flag if the drop down is open.
        selected: bool
    }
);

impl ComboBox {
    /// Define the template build function for the content of the ComboBoxItems.
    pub fn items_builder<F: Fn(&mut BuildContext, usize) -> Entity + 'static>(
        mut self,
        builder: F,
    ) -> Self {
        self.state_mut().builder = Some(Box::new(builder));
        self
    }
}

impl Template for ComboBox {
    fn template(mut self, id: Entity, ctx: &mut BuildContext) -> Self {
        let container = Container::create()
            .selector(Selector::default().id(CONTAINER))
            .background(id)
            .border_radius(id)
            .border_width(id)
            .border_brush(id)
            .padding(id)
            .build(ctx);

        let items_panel = Stack::create()
            .vertical_alignment("start")
            .selector(Selector::default().id(ITEMS_PANEL))
            .orientation("vertical")
            .build(ctx);

        self.state_mut().items_panel = items_panel;
        let scroll_viewer = ScrollViewer::create()
            .scroll_viewer_mode(("disabled", "auto"))
            .child(items_panel)
            .build(ctx);

        let popup = Popup::create()
            .height(200.0)
            .open(("selected", id))
            .child(scroll_viewer)
            .child(
                ScrollIndicator::create()
                    .padding(2.0)
                    .content_id(items_panel.0)
                    .scroll_offset(scroll_viewer)
                    .opacity(id)
                    .build(ctx),
            )
            .target(container.0)
            .build(ctx);

        let _ = ctx.append_child_to_overlay(popup);

        self.name("ComboBox")
            .selector("combo_box")
            .height(32.0)
            .min_width(80.0)
            .selected(false)
            .selected_index(-1)
            .child(
                MouseBehavior::create()
                    .pressed(id)
                    .enabled(id)
                    .selector(id)
                    .child(
                        SelectionBehavior::create()
                            .selected(id)
                            .enabled(id)
                            .selector(id)
                            .parent(id.0)
                            .child(container)
                            .build(ctx),
                    )
                    .build(ctx),
            )
    }
}
<commit_msg>Add ids to ComboBoxItem.<commit_after>use std::{
    cell::{Cell, RefCell},
    collections::HashSet,
};

use crate::{prelude::*, utils::SelectionMode as SelMode};

use super::behaviors::{MouseBehavior, SelectionBehavior};

static CONTAINER: &'static str = "container";
static ITEMS_PANEL: &'static str = "items_panel";

#[derive(Default, AsAny)]
pub struct ComboBoxItemState {
    request_selection_toggle: Cell<bool>,
    selected_container: Entity,
    combo_box: Entity,
}

impl ComboBoxItemState {
    fn toggle_selection(&self) {
        self.request_selection_toggle.set(true);
    }
}

impl State for ComboBoxItemState {
    fn update(&mut self, _: &mut Registry, ctx: &mut Context<'_>) {
        if !ctx.widget().get::<bool>("enabled") || !self.request_selection_toggle.get() {
            return;
        }
        self.request_selection_toggle.set(false);

        let selected = *ctx.widget().get::<bool>("selected");

        let entity = ctx.entity;
        let index = ctx.index_as_child(entity).unwrap();

        if let Some(parent) = &mut ctx.try_parent_from_id(LIST_VIEW) {
            // let selection_mode = *parent.get::<SelectionMode>("selection_mode");
            // deselect item
            if selected {
                // parent
                //     .get_mut::<SelectedEntities>("selected_entities")
                //     .0
                //     .remove(&entity);
                // parent
                //     .get_mut::<SelectedIndices>("selected_indices")
                //     .0
                //     .remove(&index);
                return;
            }

            // if parent
            //     .get::<SelectedEntities>("selected_entities")
            //     .0
            //     .contains(&entity)
            //     || selection_mode == SelMode::None
            // {
            //     return;
            // }

            // if selection_mode == SelMode::Single {
            //     parent
            //         .get_mut::<SelectedEntities>("selected_entities")
            //         .0
            //         .clear();
            //     parent
            //         .get_mut::<SelectedIndices>("selected_indices")
            //         .0
            //         .clear();
            // }

            // parent
            //     .get_mut::<SelectedEntities>("selected_entities")
            //     .0
            //     .insert(entity);
            // parent
            //     .get_mut::<SelectedIndices>("selected_indices")
            //     .0
            //     .insert(index);
        }
    }
}

widget!(
    ComboBoxItem<ComboBoxItemState>: MouseHandler {
        /// Sets or shares the background property.
        background: Brush,

        /// Sets or shares the border radius property.
        border_radius: f64,

        /// Sets or shares the border thickness property.
        border_width: Thickness,

        /// Sets or shares the border brush property.
        border_brush: Brush,

        /// Sets or shares the foreground property.
        foreground: Brush,

        /// Sets or share the font size property.
        font_size: f64,

        /// Sets or shares the font property.
        font: String,

        /// Sets or shares the padding property.
        padding: Thickness,

        /// Sets or shares the css selector property.
        selector: Selector,

        /// Sets or shares the pressed property.
        pressed: bool,

        /// Sets or shares the selected property.
        selected: bool
    }
);

impl ComboBoxItem {
    fn selected_container(mut self, selected_container: impl Into<Entity>) -> Self {
        self.state_mut().selected_container = selected_container.into();
        self
    }

    fn combo_box(mut self, combo_box: impl Into<Entity>) -> Self {
        self.state_mut().combo_box = combo_box.into();
        self
    }
}

impl Template for ComboBoxItem {
    fn template(self, id: Entity, _: &mut BuildContext) -> Self {
        self.name("ComboBoxItem")
            .min_width(64.0)
            .height(24.0)
            .selected(false)
            .pressed(false)
            .selector("list-view-item")
            .padding(0.0)
            .background("transparent")
            .border_radius(0.0)
            .border_width(0.0)
            .border_brush("transparent")
            .foreground(colors::LINK_WATER_COLOR)
            .font_size(32.0)
            .font("Roboto Regular")
            .on_click(move |states, _| {
                states.get::<ComboBoxItemState>(id).toggle_selection();
                false
            })
    }

    fn render_object(&self) -> Box<dyn RenderObject> {
        Box::new(RectangleRenderObject)
    }

    fn layout(&self) -> Box<dyn Layout> {
        Box::new(PaddingLayout::new())
    }
}

/// The `ComboBoxState` is used to manipulate the position of the thumb of the slider widget.
#[derive(Default, AsAny)]
pub struct ComboBoxState {
    builder: WidgetBuildContext,
    count: usize,
    items_panel: Entity,
    selected_container: Entity,
}

impl State for ComboBoxState {
    fn update(&mut self, _: &mut Registry, ctx: &mut Context<'_>) {
        let count = ctx.widget().clone_or_default::<usize>("count");
        let entity = ctx.entity;

        if count != self.count {
            if let Some(builder) = &self.builder {
                ctx.clear_children_of(self.items_panel);

                for i in 0..count {
                    let item = {
                        let build_context = &mut ctx.build_context();
                        let child = builder(build_context, i);
                        let item = ComboBoxItem::create()
                            .combo_box(entity)
                            .selected_container(self.selected_container)
                            .build(build_context);

                        let mouse_behavior = MouseBehavior::create().build(build_context);
                        build_context.register_shared_property::<Selector>(
                            "selector",
                            mouse_behavior,
                            item,
                        );
                        build_context.register_shared_property::<bool>(
                            "pressed",
                            mouse_behavior,
                            item,
                        );
                        build_context.append_child(item, mouse_behavior);

                        build_context.register_shared_property::<Brush>("foreground", child, item);
                        build_context.register_shared_property::<f32>("opacity", item, entity);
                        build_context.register_shared_property::<f32>("opacity", child, entity);
                        build_context.register_shared_property::<f64>("font_size", child, item);
                        build_context.append_child(self.items_panel, item);
                        build_context.append_child(mouse_behavior, child);

                        item
                    };
                    ctx.get_widget(item).update_properties_by_theme();
                }
            }

            self.count = count;
        }
    }

    fn update_post_layout(&mut self, _: &mut Registry, ctx: &mut Context<'_>) {
        // for index in ctx
        //     .widget()
        //     .get::<SelectedEntities>("selected_entities")
        //     .0
        //     .clone()
        //     .symmetric_difference(&*self.selected_entities.borrow())
        // {
        //     let mut widget = ctx.get_widget(*index);
        //     widget.set("selected", !widget.get::<bool>("selected"));

        //     widget.update_theme_by_state(false);
        // }

        // *self.selected_entities.borrow_mut() = ctx
        //     .widget()
        //     .get::<SelectedEntities>("selected_entities")
        //     .0
        //     .clone();
    }
}

// todo use code of list view item, by create combobox item insert entity of popup container

widget!(
    /// The `ComboBox` represents an selection widget with a drop-down list.
    ///
    /// **CSS element:** `ComboBox`
    ComboBox<ComboBoxState>: MouseHandler, ChangedHandler {
        /// Sets or shares the background property.
        background: Brush,

        /// Sets or shares the border radius property.
        border_radius: f64,

        /// Sets or shares the border thickness property.
        border_width: Thickness,

        /// Sets or shares the border brush property.
        border_brush: Brush,

        /// Sets or shares the foreground property.
        foreground: Brush,

        /// Sets or share the font size property.
        font_size: f64,

        /// Sets or shares the font property.
        font: String,

        /// Sets or shared the count.
        count: usize,

        /// Sets or shares the css selector property.
        selector: Selector,

        /// Sets or shares the selected index. If the value is -1 no item is selected.
        selected_index: i32,

        /// Sets or shares the padding property.
        padding: Thickness,

        /// Sets or shares the pressed property.
        pressed: bool,

        /// Sets or shares the flag if the drop down is open.
        selected: bool
    }
);

impl ComboBox {
    /// Define the template build function for the content of the ComboBoxItems.
    pub fn items_builder<F: Fn(&mut BuildContext, usize) -> Entity + 'static>(
        mut self,
        builder: F,
    ) -> Self {
        self.state_mut().builder = Some(Box::new(builder));
        self
    }
}

impl Template for ComboBox {
    fn template(mut self, id: Entity, ctx: &mut BuildContext) -> Self {
        let container = Container::create()
            .selector(Selector::default().id(CONTAINER))
            .background(id)
            .border_radius(id)
            .border_width(id)
            .border_brush(id)
            .padding(id)
            .build(ctx);
        self.state_mut().selected_container = container;

        let items_panel = Stack::create()
            .vertical_alignment("start")
            .selector(Selector::default().id(ITEMS_PANEL))
            .orientation("vertical")
            .build(ctx);

        self.state_mut().items_panel = items_panel;
        let scroll_viewer = ScrollViewer::create()
            .scroll_viewer_mode(("disabled", "auto"))
            .child(items_panel)
            .build(ctx);

        let popup = Popup::create()
            .height(200.0)
            .open(("selected", id))
            .child(scroll_viewer)
            .child(
                ScrollIndicator::create()
                    .padding(2.0)
                    .content_id(items_panel.0)
                    .scroll_offset(scroll_viewer)
                    .opacity(id)
                    .build(ctx),
            )
            .target(container.0)
            .build(ctx);

        let _ = ctx.append_child_to_overlay(popup);

        self.name("ComboBox")
            .selector("combo_box")
            .height(32.0)
            .min_width(80.0)
            .selected(false)
            .selected_index(-1)
            .child(
                MouseBehavior::create()
                    .pressed(id)
                    .enabled(id)
                    .selector(id)
                    .child(
                        SelectionBehavior::create()
                            .selected(id)
                            .enabled(id)
                            .selector(id)
                            .parent(id.0)
                            .child(container)
                            .build(ctx),
                    )
                    .build(ctx),
            )
    }
}
<|endoftext|>


<commit_before>use std::collections::hash_map::Entry;
use std::collections::HashMap;
use std::fmt;
use std::rc::Rc;

use allowed_url::AllowedUrl;
use error::ValueErrorKind;
use handle::{self, RsvgHandle};
use node::Node;
use parsers::ParseError;

pub struct Defs {
    externs: HashMap<AllowedUrl, *const RsvgHandle>,
}

impl Defs {
    pub fn new() -> Defs {
        Defs {
            externs: Default::default(),
        }
    }

    /// Returns a node referenced by a fragment ID, from an
    /// externally-loaded SVG file.
    pub fn lookup(&mut self, handle: *const RsvgHandle, fragment: &Fragment) -> Option<Rc<Node>> {
        if let Some(ref href) = fragment.uri() {
            match self.get_extern_handle(handle, href) {
                Ok(extern_handle) => handle::lookup_fragment_id(extern_handle, fragment.fragment()),
                Err(()) => None,
            }
        } else {
            unreachable!();
        }
    }

    fn get_extern_handle(
        &mut self,
        handle: *const RsvgHandle,
        href: &str,
    ) -> Result<*const RsvgHandle, ()> {
        let aurl =
            AllowedUrl::from_href(href, handle::get_base_url(handle).as_ref()).map_err(|_| ())?;

        match self.externs.entry(aurl) {
            Entry::Occupied(e) => Ok(*(e.get())),
            Entry::Vacant(e) => {
                let load_options = handle::get_load_options(handle);
                let extern_handle = handle::load_extern(&load_options, e.key())?;
                e.insert(extern_handle);
                Ok(extern_handle)
            }
        }
    }
}

/// Parsed result of an href from an SVG or CSS file
///
/// Sometimes in SVG element references (e.g. the `href` in the `<feImage>` element) we
/// must decide between referencing an external file, or using a plain fragment identifier
/// like `href="#foo"` as a reference to an SVG element in the same file as the one being
/// processed.  This enum makes that distinction.
#[derive(Debug, PartialEq)]
pub enum Href {
    PlainUri(String),
    WithFragment(Fragment),
}

/// Optional URI, mandatory fragment id
#[derive(Debug, PartialEq, Clone)]
pub struct Fragment(Option<String>, String);

impl Fragment {
    // Outside of testing, we don't want code creating Fragments by hand;
    // they should get them from Href.
    #[cfg(test)]
    pub fn new(uri: Option<String>, fragment: String) -> Fragment {
        Fragment(uri, fragment)
    }

    pub fn parse(href: &str) -> Result<Fragment, HrefError> {
        let href = Href::with_fragment(href)?;

        if let Href::WithFragment(f) = href {
            Ok(f)
        } else {
            unreachable!();
        }
    }

    pub fn uri(&self) -> Option<&str> {
        self.0.as_ref().map(|s| s.as_str())
    }

    pub fn fragment(&self) -> &str {
        &self.1
    }
}

impl fmt::Display for Fragment {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "{}#{}",
            self.0.as_ref().map(String::as_str).unwrap_or(""),
            self.1
        )
    }
}

/// Errors returned when creating an `Href` out of a string
#[derive(Debug, Clone, PartialEq)]
pub enum HrefError {
    /// The href is an invalid URI or has empty components.
    ParseError,

    /// A fragment identifier ("`#foo`") is not allowed here
    ///
    /// For example, the SVG `<image>` element only allows referencing
    /// resources without fragment identifiers like
    /// `xlink:href="foo.png"`.
    FragmentForbidden,

    /// A fragment identifier ("`#foo`") was required but not found.  For example,
    /// the SVG `<use>` element requires one, as in `<use xlink:href="foo.svg#bar">`.
    FragmentRequired,
}

impl From<HrefError> for ValueErrorKind {
    fn from(e: HrefError) -> ValueErrorKind {
        match e {
            HrefError::ParseError => ValueErrorKind::Parse(ParseError::new("url parse error")),
            HrefError::FragmentForbidden => {
                ValueErrorKind::Value("fragment identifier not allowed".to_string())
            }
            HrefError::FragmentRequired => {
                ValueErrorKind::Value("fragment identifier required".to_string())
            }
        }
    }
}

impl Href {
    /// Parses a string into an Href, or returns an error
    ///
    /// An href can come from an `xlink:href` attribute in an SVG
    /// element.  This function determines if the provided href is a
    /// plain absolute or relative URL ("`foo.png`"), or one with a
    /// fragment identifier ("`foo.svg#bar`").
    pub fn parse(href: &str) -> Result<Href, HrefError> {
        let (uri, fragment) = match href.rfind('#') {
            None => (Some(href), None),
            Some(p) if p == 0 => (None, Some(&href[1..])),
            Some(p) => (Some(&href[..p]), Some(&href[(p + 1)..])),
        };

        match (uri, fragment) {
            (None, Some(f)) if f.len() == 0 => Err(HrefError::ParseError),
            (None, Some(f)) => Ok(Href::WithFragment(Fragment(None, f.to_string()))),
            (Some(u), _) if u.len() == 0 => Err(HrefError::ParseError),
            (Some(u), None) => Ok(Href::PlainUri(u.to_string())),
            (Some(_u), Some(f)) if f.len() == 0 => Err(HrefError::ParseError),
            (Some(u), Some(f)) => Ok(Href::WithFragment(Fragment(
                Some(u.to_string()),
                f.to_string(),
            ))),
            (_, _) => Err(HrefError::ParseError),
        }
    }

    pub fn without_fragment(href: &str) -> Result<Href, HrefError> {
        use self::Href::*;

        match Href::parse(href)? {
            r @ PlainUri(_) => Ok(r),
            WithFragment(_) => Err(HrefError::FragmentForbidden),
        }
    }

    pub fn with_fragment(href: &str) -> Result<Href, HrefError> {
        use self::Href::*;

        match Href::parse(href)? {
            PlainUri(_) => Err(HrefError::FragmentRequired),
            r @ WithFragment(_) => Ok(r),
        }
    }
}

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

    #[test]
    fn parse() {
        assert_eq!(
            Href::parse("uri").unwrap(),
            Href::PlainUri("uri".to_string())
        );
        assert_eq!(
            Href::parse("#fragment").unwrap(),
            Href::WithFragment(Fragment::new(None, "fragment".to_string()))
        );
        assert_eq!(
            Href::parse("uri#fragment").unwrap(),
            Href::WithFragment(Fragment::new(
                Some("uri".to_string()),
                "fragment".to_string()
            ))
        );
    }

    #[test]
    fn parse_errors() {
        assert_eq!(Href::parse(""), Err(HrefError::ParseError));
        assert_eq!(Href::parse("#"), Err(HrefError::ParseError));
        assert_eq!(Href::parse("uri#"), Err(HrefError::ParseError));
    }

    #[test]
    fn without_fragment() {
        assert_eq!(
            Href::without_fragment("uri").unwrap(),
            Href::PlainUri("uri".to_string())
        );

        assert_eq!(
            Href::without_fragment("#foo"),
            Err(HrefError::FragmentForbidden)
        );

        assert_eq!(
            Href::without_fragment("uri#foo"),
            Err(HrefError::FragmentForbidden)
        );
    }

    #[test]
    fn with_fragment() {
        assert_eq!(
            Href::with_fragment("#foo").unwrap(),
            Href::WithFragment(Fragment::new(None, "foo".to_string()))
        );

        assert_eq!(
            Href::with_fragment("uri#foo").unwrap(),
            Href::WithFragment(Fragment::new(Some("uri".to_string()), "foo".to_string()))
        );

        assert_eq!(Href::with_fragment("uri"), Err(HrefError::FragmentRequired));
    }

    #[test]
    fn fragment_parse() {
        assert_eq!(
            Fragment::parse("#foo").unwrap(),
            Fragment::new(None, "foo".to_string())
        );

        assert_eq!(
            Fragment::parse("uri#foo").unwrap(),
            Fragment::new(Some("uri".to_string()), "foo".to_string())
        );

        assert_eq!(Fragment::parse("uri"), Err(HrefError::FragmentRequired));
    }
}
<commit_msg>Defs::get_extern_handle(): Don't take a handle<commit_after>use std::collections::hash_map::Entry;
use std::collections::HashMap;
use std::fmt;
use std::rc::Rc;

use url::Url;

use allowed_url::AllowedUrl;
use error::ValueErrorKind;
use handle::{self, LoadOptions, RsvgHandle};
use node::Node;
use parsers::ParseError;

pub struct Defs {
    externs: HashMap<AllowedUrl, *const RsvgHandle>,
}

impl Defs {
    pub fn new() -> Defs {
        Defs {
            externs: Default::default(),
        }
    }

    /// Returns a node referenced by a fragment ID, from an
    /// externally-loaded SVG file.
    pub fn lookup(&mut self, handle: *const RsvgHandle, fragment: &Fragment) -> Option<Rc<Node>> {
        if let Some(ref href) = fragment.uri() {
            let load_options = handle::get_load_options(handle);
            let base_url = handle::get_base_url(handle).clone();

            match self.get_extern_handle(&load_options, base_url, href) {
                Ok(extern_handle) => handle::lookup_fragment_id(extern_handle, fragment.fragment()),
                Err(()) => None,
            }
        } else {
            unreachable!();
        }
    }

    fn get_extern_handle(
        &mut self,
        load_options: &LoadOptions,
        base_url: Option<Url>,
        href: &str,
    ) -> Result<*const RsvgHandle, ()> {
        let aurl = AllowedUrl::from_href(href, base_url.as_ref()).map_err(|_| ())?;

        match self.externs.entry(aurl) {
            Entry::Occupied(e) => Ok(*(e.get())),
            Entry::Vacant(e) => {
                let extern_handle = handle::load_extern(load_options, e.key())?;
                e.insert(extern_handle);
                Ok(extern_handle)
            }
        }
    }
}

/// Parsed result of an href from an SVG or CSS file
///
/// Sometimes in SVG element references (e.g. the `href` in the `<feImage>` element) we
/// must decide between referencing an external file, or using a plain fragment identifier
/// like `href="#foo"` as a reference to an SVG element in the same file as the one being
/// processed.  This enum makes that distinction.
#[derive(Debug, PartialEq)]
pub enum Href {
    PlainUri(String),
    WithFragment(Fragment),
}

/// Optional URI, mandatory fragment id
#[derive(Debug, PartialEq, Clone)]
pub struct Fragment(Option<String>, String);

impl Fragment {
    // Outside of testing, we don't want code creating Fragments by hand;
    // they should get them from Href.
    #[cfg(test)]
    pub fn new(uri: Option<String>, fragment: String) -> Fragment {
        Fragment(uri, fragment)
    }

    pub fn parse(href: &str) -> Result<Fragment, HrefError> {
        let href = Href::with_fragment(href)?;

        if let Href::WithFragment(f) = href {
            Ok(f)
        } else {
            unreachable!();
        }
    }

    pub fn uri(&self) -> Option<&str> {
        self.0.as_ref().map(|s| s.as_str())
    }

    pub fn fragment(&self) -> &str {
        &self.1
    }
}

impl fmt::Display for Fragment {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "{}#{}",
            self.0.as_ref().map(String::as_str).unwrap_or(""),
            self.1
        )
    }
}

/// Errors returned when creating an `Href` out of a string
#[derive(Debug, Clone, PartialEq)]
pub enum HrefError {
    /// The href is an invalid URI or has empty components.
    ParseError,

    /// A fragment identifier ("`#foo`") is not allowed here
    ///
    /// For example, the SVG `<image>` element only allows referencing
    /// resources without fragment identifiers like
    /// `xlink:href="foo.png"`.
    FragmentForbidden,

    /// A fragment identifier ("`#foo`") was required but not found.  For example,
    /// the SVG `<use>` element requires one, as in `<use xlink:href="foo.svg#bar">`.
    FragmentRequired,
}

impl From<HrefError> for ValueErrorKind {
    fn from(e: HrefError) -> ValueErrorKind {
        match e {
            HrefError::ParseError => ValueErrorKind::Parse(ParseError::new("url parse error")),
            HrefError::FragmentForbidden => {
                ValueErrorKind::Value("fragment identifier not allowed".to_string())
            }
            HrefError::FragmentRequired => {
                ValueErrorKind::Value("fragment identifier required".to_string())
            }
        }
    }
}

impl Href {
    /// Parses a string into an Href, or returns an error
    ///
    /// An href can come from an `xlink:href` attribute in an SVG
    /// element.  This function determines if the provided href is a
    /// plain absolute or relative URL ("`foo.png`"), or one with a
    /// fragment identifier ("`foo.svg#bar`").
    pub fn parse(href: &str) -> Result<Href, HrefError> {
        let (uri, fragment) = match href.rfind('#') {
            None => (Some(href), None),
            Some(p) if p == 0 => (None, Some(&href[1..])),
            Some(p) => (Some(&href[..p]), Some(&href[(p + 1)..])),
        };

        match (uri, fragment) {
            (None, Some(f)) if f.len() == 0 => Err(HrefError::ParseError),
            (None, Some(f)) => Ok(Href::WithFragment(Fragment(None, f.to_string()))),
            (Some(u), _) if u.len() == 0 => Err(HrefError::ParseError),
            (Some(u), None) => Ok(Href::PlainUri(u.to_string())),
            (Some(_u), Some(f)) if f.len() == 0 => Err(HrefError::ParseError),
            (Some(u), Some(f)) => Ok(Href::WithFragment(Fragment(
                Some(u.to_string()),
                f.to_string(),
            ))),
            (_, _) => Err(HrefError::ParseError),
        }
    }

    pub fn without_fragment(href: &str) -> Result<Href, HrefError> {
        use self::Href::*;

        match Href::parse(href)? {
            r @ PlainUri(_) => Ok(r),
            WithFragment(_) => Err(HrefError::FragmentForbidden),
        }
    }

    pub fn with_fragment(href: &str) -> Result<Href, HrefError> {
        use self::Href::*;

        match Href::parse(href)? {
            PlainUri(_) => Err(HrefError::FragmentRequired),
            r @ WithFragment(_) => Ok(r),
        }
    }
}

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

    #[test]
    fn parse() {
        assert_eq!(
            Href::parse("uri").unwrap(),
            Href::PlainUri("uri".to_string())
        );
        assert_eq!(
            Href::parse("#fragment").unwrap(),
            Href::WithFragment(Fragment::new(None, "fragment".to_string()))
        );
        assert_eq!(
            Href::parse("uri#fragment").unwrap(),
            Href::WithFragment(Fragment::new(
                Some("uri".to_string()),
                "fragment".to_string()
            ))
        );
    }

    #[test]
    fn parse_errors() {
        assert_eq!(Href::parse(""), Err(HrefError::ParseError));
        assert_eq!(Href::parse("#"), Err(HrefError::ParseError));
        assert_eq!(Href::parse("uri#"), Err(HrefError::ParseError));
    }

    #[test]
    fn without_fragment() {
        assert_eq!(
            Href::without_fragment("uri").unwrap(),
            Href::PlainUri("uri".to_string())
        );

        assert_eq!(
            Href::without_fragment("#foo"),
            Err(HrefError::FragmentForbidden)
        );

        assert_eq!(
            Href::without_fragment("uri#foo"),
            Err(HrefError::FragmentForbidden)
        );
    }

    #[test]
    fn with_fragment() {
        assert_eq!(
            Href::with_fragment("#foo").unwrap(),
            Href::WithFragment(Fragment::new(None, "foo".to_string()))
        );

        assert_eq!(
            Href::with_fragment("uri#foo").unwrap(),
            Href::WithFragment(Fragment::new(Some("uri".to_string()), "foo".to_string()))
        );

        assert_eq!(Href::with_fragment("uri"), Err(HrefError::FragmentRequired));
    }

    #[test]
    fn fragment_parse() {
        assert_eq!(
            Fragment::parse("#foo").unwrap(),
            Fragment::new(None, "foo".to_string())
        );

        assert_eq!(
            Fragment::parse("uri#foo").unwrap(),
            Fragment::new(Some("uri".to_string()), "foo".to_string())
        );

        assert_eq!(Fragment::parse("uri"), Err(HrefError::FragmentRequired));
    }
}
<|endoftext|>


<commit_before>use cairo::{self, MatrixTrait};
use std::cell::Cell;

use crate::attributes::Attribute;
use crate::bbox::BoundingBox;
use crate::coord_units::CoordUnits;
use crate::drawing_ctx::{CompositingAffines, DrawingCtx};
use crate::error::RenderingError;
use crate::length::{LengthHorizontal, LengthVertical};
use crate::node::{NodeResult, NodeTrait, RsvgNode};
use crate::parsers::{Parse, ParseValue};
use crate::properties::Opacity;
use crate::property_bag::PropertyBag;
use crate::rect::IRect;
use crate::surface_utils::{
    iterators::Pixels, shared_surface::SharedImageSurface, shared_surface::SurfaceType,
    ImageSurfaceDataExt,
};
use crate::unit_interval::UnitInterval;

coord_units!(MaskUnits, CoordUnits::ObjectBoundingBox);
coord_units!(MaskContentUnits, CoordUnits::UserSpaceOnUse);

pub struct NodeMask {
    x: Cell<LengthHorizontal>,
    y: Cell<LengthVertical>,
    width: Cell<LengthHorizontal>,
    height: Cell<LengthVertical>,

    units: Cell<MaskUnits>,
    content_units: Cell<MaskContentUnits>,
}

impl NodeMask {
    pub fn new() -> NodeMask {
        NodeMask {
            // these values are per the spec
            x: Cell::new(LengthHorizontal::parse_str("-10%").unwrap()),
            y: Cell::new(LengthVertical::parse_str("-10%").unwrap()),

            width: Cell::new(LengthHorizontal::parse_str("120%").unwrap()),
            height: Cell::new(LengthVertical::parse_str("120%").unwrap()),

            units: Cell::new(MaskUnits::default()),
            content_units: Cell::new(MaskContentUnits::default()),
        }
    }

    pub fn generate_cairo_mask(
        &self,
        node: &RsvgNode,
        affines: &CompositingAffines,
        draw_ctx: &mut DrawingCtx,
        bbox: &BoundingBox,
    ) -> Result<(), RenderingError> {
        if bbox.rect.is_none() {
            // The node being masked is empty / doesn't have a
            // bounding box, so there's nothing to mask!
            return Ok(());
        }

        let bbox_rect = bbox.rect.as_ref().unwrap();

        let cascaded = node.get_cascaded_values();
        let values = cascaded.get();

        let mask_content_surface = draw_ctx.create_surface_for_toplevel_viewport()?;

        let mask_units = CoordUnits::from(self.units.get());
        let content_units = CoordUnits::from(self.content_units.get());

        let (x, y, w, h) = {
            let params = if mask_units == CoordUnits::ObjectBoundingBox {
                draw_ctx.push_view_box(1.0, 1.0)
            } else {
                draw_ctx.get_view_params()
            };

            let x = self.x.get().normalize(&values, &params);
            let y = self.y.get().normalize(&values, &params);
            let w = self.width.get().normalize(&values, &params);
            let h = self.height.get().normalize(&values, &params);

            (x, y, w, h)
        };

        // Use a scope because mask_cr needs to release the
        // reference to the surface before we access the pixels
        {
            let save_cr = draw_ctx.get_cairo_context();

            let mask_cr = cairo::Context::new(&mask_content_surface);
            mask_cr.set_matrix(affines.for_temporary_surface);
            mask_cr.transform(node.get_transform());

            draw_ctx.set_cairo_context(&mask_cr);

            if mask_units == CoordUnits::ObjectBoundingBox {
                draw_ctx.clip(
                    x * bbox_rect.width + bbox_rect.x,
                    y * bbox_rect.height + bbox_rect.y,
                    w * bbox_rect.width,
                    h * bbox_rect.height,
                );
            } else {
                draw_ctx.clip(x, y, w, h);
            }

            {
                let _params = if content_units == CoordUnits::ObjectBoundingBox {
                    let bbtransform = cairo::Matrix::new(
                        bbox_rect.width,
                        0.0,
                        0.0,
                        bbox_rect.height,
                        bbox_rect.x,
                        bbox_rect.y,
                    );

                    mask_cr.transform(bbtransform);

                    draw_ctx.push_view_box(1.0, 1.0)
                } else {
                    draw_ctx.get_view_params()
                };

                let res = draw_ctx.with_discrete_layer(node, values, false, &mut |dc| {
                    node.draw_children(&cascaded, dc, false)
                });

                draw_ctx.set_cairo_context(&save_cr);

                res
            }
        }?;

        let Opacity(opacity) = values.opacity;

        let mask_content_surface =
            SharedImageSurface::new(mask_content_surface, SurfaceType::SRgb)?;

        let mask_surface = compute_luminance_to_alpha(&mask_content_surface, opacity)?;

        let cr = draw_ctx.get_cairo_context();
        cr.set_matrix(affines.initial);

        cr.mask_surface(&mask_surface, 0.0, 0.0);

        Ok(())
    }
}

// Returns a surface whose alpha channel for each pixel is equal to the
// luminance of that pixel's unpremultiplied RGB values.  The resulting
// surface's RGB values are not meanignful; only the alpha channel has
// useful luminance data.
//
// This is to get a mask suitable for use with cairo_mask_surface().
fn compute_luminance_to_alpha(
    surface: &SharedImageSurface,
    opacity: UnitInterval,
) -> Result<cairo::ImageSurface, cairo::Status> {
    let width = surface.width();
    let height = surface.height();

    let bounds = IRect {
        x0: 0,
        y0: 0,
        x1: width,
        y1: height,
    };

    let opacity = u8::from(opacity);
    let mut output = cairo::ImageSurface::create(cairo::Format::ARgb32, width, height)?;
    let output_stride = output.get_stride() as usize;

    {
        let mut output_data = output.get_data().unwrap();

        for (x, y, pixel) in Pixels::new(surface, bounds) {
            output_data.set_pixel(output_stride, pixel.to_mask(opacity), x, y);
        }
    }

    Ok(output)
}

impl NodeTrait for NodeMask {
    fn set_atts(&self, _: &RsvgNode, pbag: &PropertyBag<'_>) -> NodeResult {
        for (attr, value) in pbag.iter() {
            match attr {
                Attribute::X => self.x.set(attr.parse(value)?),
                Attribute::Y => self.y.set(attr.parse(value)?),
                Attribute::Width => self
                    .width
                    .set(attr.parse_and_validate(value, LengthHorizontal::check_nonnegative)?),
                Attribute::Height => self
                    .height
                    .set(attr.parse_and_validate(value, LengthVertical::check_nonnegative)?),
                Attribute::MaskUnits => self.units.set(attr.parse(value)?),
                Attribute::MaskContentUnits => self.content_units.set(attr.parse(value)?),
                _ => (),
            }
        }

        Ok(())
    }
}
<commit_msg>Oops, use the compositing affine when setting the mask<commit_after>use cairo::{self, MatrixTrait};
use std::cell::Cell;

use crate::attributes::Attribute;
use crate::bbox::BoundingBox;
use crate::coord_units::CoordUnits;
use crate::drawing_ctx::{CompositingAffines, DrawingCtx};
use crate::error::RenderingError;
use crate::length::{LengthHorizontal, LengthVertical};
use crate::node::{NodeResult, NodeTrait, RsvgNode};
use crate::parsers::{Parse, ParseValue};
use crate::properties::Opacity;
use crate::property_bag::PropertyBag;
use crate::rect::IRect;
use crate::surface_utils::{
    iterators::Pixels, shared_surface::SharedImageSurface, shared_surface::SurfaceType,
    ImageSurfaceDataExt,
};
use crate::unit_interval::UnitInterval;

coord_units!(MaskUnits, CoordUnits::ObjectBoundingBox);
coord_units!(MaskContentUnits, CoordUnits::UserSpaceOnUse);

pub struct NodeMask {
    x: Cell<LengthHorizontal>,
    y: Cell<LengthVertical>,
    width: Cell<LengthHorizontal>,
    height: Cell<LengthVertical>,

    units: Cell<MaskUnits>,
    content_units: Cell<MaskContentUnits>,
}

impl NodeMask {
    pub fn new() -> NodeMask {
        NodeMask {
            // these values are per the spec
            x: Cell::new(LengthHorizontal::parse_str("-10%").unwrap()),
            y: Cell::new(LengthVertical::parse_str("-10%").unwrap()),

            width: Cell::new(LengthHorizontal::parse_str("120%").unwrap()),
            height: Cell::new(LengthVertical::parse_str("120%").unwrap()),

            units: Cell::new(MaskUnits::default()),
            content_units: Cell::new(MaskContentUnits::default()),
        }
    }

    pub fn generate_cairo_mask(
        &self,
        node: &RsvgNode,
        affines: &CompositingAffines,
        draw_ctx: &mut DrawingCtx,
        bbox: &BoundingBox,
    ) -> Result<(), RenderingError> {
        if bbox.rect.is_none() {
            // The node being masked is empty / doesn't have a
            // bounding box, so there's nothing to mask!
            return Ok(());
        }

        let bbox_rect = bbox.rect.as_ref().unwrap();

        let cascaded = node.get_cascaded_values();
        let values = cascaded.get();

        let mask_content_surface = draw_ctx.create_surface_for_toplevel_viewport()?;

        let mask_units = CoordUnits::from(self.units.get());
        let content_units = CoordUnits::from(self.content_units.get());

        let (x, y, w, h) = {
            let params = if mask_units == CoordUnits::ObjectBoundingBox {
                draw_ctx.push_view_box(1.0, 1.0)
            } else {
                draw_ctx.get_view_params()
            };

            let x = self.x.get().normalize(&values, &params);
            let y = self.y.get().normalize(&values, &params);
            let w = self.width.get().normalize(&values, &params);
            let h = self.height.get().normalize(&values, &params);

            (x, y, w, h)
        };

        // Use a scope because mask_cr needs to release the
        // reference to the surface before we access the pixels
        {
            let save_cr = draw_ctx.get_cairo_context();

            let mask_cr = cairo::Context::new(&mask_content_surface);
            mask_cr.set_matrix(affines.for_temporary_surface);
            mask_cr.transform(node.get_transform());

            draw_ctx.set_cairo_context(&mask_cr);

            if mask_units == CoordUnits::ObjectBoundingBox {
                draw_ctx.clip(
                    x * bbox_rect.width + bbox_rect.x,
                    y * bbox_rect.height + bbox_rect.y,
                    w * bbox_rect.width,
                    h * bbox_rect.height,
                );
            } else {
                draw_ctx.clip(x, y, w, h);
            }

            {
                let _params = if content_units == CoordUnits::ObjectBoundingBox {
                    let bbtransform = cairo::Matrix::new(
                        bbox_rect.width,
                        0.0,
                        0.0,
                        bbox_rect.height,
                        bbox_rect.x,
                        bbox_rect.y,
                    );

                    mask_cr.transform(bbtransform);

                    draw_ctx.push_view_box(1.0, 1.0)
                } else {
                    draw_ctx.get_view_params()
                };

                let res = draw_ctx.with_discrete_layer(node, values, false, &mut |dc| {
                    node.draw_children(&cascaded, dc, false)
                });

                draw_ctx.set_cairo_context(&save_cr);

                res
            }
        }?;

        let Opacity(opacity) = values.opacity;

        let mask_content_surface =
            SharedImageSurface::new(mask_content_surface, SurfaceType::SRgb)?;

        let mask_surface = compute_luminance_to_alpha(&mask_content_surface, opacity)?;

        let cr = draw_ctx.get_cairo_context();
        cr.set_matrix(affines.compositing);

        cr.mask_surface(&mask_surface, 0.0, 0.0);

        Ok(())
    }
}

// Returns a surface whose alpha channel for each pixel is equal to the
// luminance of that pixel's unpremultiplied RGB values.  The resulting
// surface's RGB values are not meanignful; only the alpha channel has
// useful luminance data.
//
// This is to get a mask suitable for use with cairo_mask_surface().
fn compute_luminance_to_alpha(
    surface: &SharedImageSurface,
    opacity: UnitInterval,
) -> Result<cairo::ImageSurface, cairo::Status> {
    let width = surface.width();
    let height = surface.height();

    let bounds = IRect {
        x0: 0,
        y0: 0,
        x1: width,
        y1: height,
    };

    let opacity = u8::from(opacity);
    let mut output = cairo::ImageSurface::create(cairo::Format::ARgb32, width, height)?;
    let output_stride = output.get_stride() as usize;

    {
        let mut output_data = output.get_data().unwrap();

        for (x, y, pixel) in Pixels::new(surface, bounds) {
            output_data.set_pixel(output_stride, pixel.to_mask(opacity), x, y);
        }
    }

    Ok(output)
}

impl NodeTrait for NodeMask {
    fn set_atts(&self, _: &RsvgNode, pbag: &PropertyBag<'_>) -> NodeResult {
        for (attr, value) in pbag.iter() {
            match attr {
                Attribute::X => self.x.set(attr.parse(value)?),
                Attribute::Y => self.y.set(attr.parse(value)?),
                Attribute::Width => self
                    .width
                    .set(attr.parse_and_validate(value, LengthHorizontal::check_nonnegative)?),
                Attribute::Height => self
                    .height
                    .set(attr.parse_and_validate(value, LengthVertical::check_nonnegative)?),
                Attribute::MaskUnits => self.units.set(attr.parse(value)?),
                Attribute::MaskContentUnits => self.content_units.set(attr.parse(value)?),
                _ => (),
            }
        }

        Ok(())
    }
}
<|endoftext|>


<commit_before>//! Read from `/dev/random`, with chunks of limited size (1040 bytes).
//! `/dev/random` uses the Hash_DRBG with SHA512 algorithm from NIST SP 800-90A.
//! `/dev/urandom` uses the FIPS 186-2 algorithm, which is considered less
//! secure. We choose to read from `/dev/random`.
//!
//! Since Solaris 11.3 the `getrandom` syscall is available. To make sure we can
//! compile on both Solaris and on OpenSolaris derivatives, that do not have the
//! function, we do a direct syscall instead of calling a library function.
//!
//! We have no way to differentiate between Solaris, illumos, SmartOS, etc.
extern crate libc;

use rand_core::{Error, ErrorKind};
use super::random_device;
use super::OsRngImpl;

use std::io;
use std::io::Read;
use std::fs::{File, OpenOptions};
use std::os::unix::fs::OpenOptionsExt;
use std::sync::atomic::{AtomicBool, ATOMIC_BOOL_INIT, Ordering};

#[derive(Clone, Debug)]
pub struct OsRng {
    method: OsRngMethod,
    initialized: bool,
}

#[derive(Clone, Debug)]
enum OsRngMethod {
    GetRandom,
    RandomDevice,
}

impl OsRngImpl for OsRng {
    fn new() -> Result<OsRng, Error> {
        if is_getrandom_available() {
            return Ok(OsRng { method: OsRngMethod::GetRandom,
                              initialized: false });
        }
        let open = |p| OpenOptions::new()
            .read(true)
            .custom_flags(libc::O_NONBLOCK)
            .open(p);
        random_device::open("/dev/random", &open)?;
        Ok(OsRng { method: OsRngMethod::RandomDevice, initialized: false })
    }

    fn fill_chunk(&mut self, dest: &mut [u8]) -> Result<(), Error> {
        match self.method {
            OsRngMethod::GetRandom => getrandom_try_fill(dest, false),
            OsRngMethod::RandomDevice => random_device::read(dest),
        }
    }

    fn test_initialized(&mut self, dest: &mut [u8], blocking: bool)
        -> Result<usize, Error>
    {
        static OS_RNG_INITIALIZED: AtomicBool = ATOMIC_BOOL_INIT;
        if !self.initialized {
            self.initialized = OS_RNG_INITIALIZED.load(Ordering::Relaxed);
        }
        if self.initialized { return Ok(0); }

        let chunk_len = ::core::cmp::min(1024, dest.len());
        let dest = &mut dest[..chunk_len];

        match self.method {
            OsRngMethod::GetRandom => getrandom_try_fill(dest, blocking)?,
            OsRngMethod::RandomDevice => {
                if blocking {
                    info!("OsRng: testing random device /dev/random");
                    // We already have a non-blocking handle, but now need a
                    // blocking one. Not much choice except opening it twice
                    let mut file = File::open("/dev/random")
                        .map_err(random_device::map_err)?;
                    file.read(dest).map_err(random_device::map_err)?;
                } else {
                    self.fill_chunk(dest)?;
                }
            }
        };
        OS_RNG_INITIALIZED.store(true, Ordering::Relaxed);
        self.initialized = true;
        Ok(chunk_len)
    }

    fn max_chunk_size(&self) -> usize {
        // The documentation says 1024 is the maximum for getrandom, but
        // 1040 for /dev/random.
        1024
    }

    fn method_str(&self) -> &'static str {
        match self.method {
            OsRngMethod::GetRandom => "getrandom",
            OsRngMethod::RandomDevice => "/dev/random",
        }
    }
}

fn getrandom(buf: &mut [u8], blocking: bool) -> libc::c_long {
    extern "C" {
        fn syscall(number: libc::c_long, ...) -> libc::c_long;
    }

    const SYS_GETRANDOM: libc::c_long = 143;
    const GRND_NONBLOCK: libc::c_uint = 0x0001;
    const GRND_RANDOM: libc::c_uint = 0x0002;

    unsafe {
        syscall(SYS_GETRANDOM, buf.as_mut_ptr(), buf.len(),
                if blocking { 0 } else { GRND_NONBLOCK } | GRND_RANDOM)
    }
}

fn getrandom_try_fill(dest: &mut [u8], blocking: bool) -> Result<(), Error> {
    let result = getrandom(dest, blocking);
    if result == -1 || result == 0 {
        let err = io::Error::last_os_error();
        let kind = err.kind();
        if kind == io::ErrorKind::WouldBlock {
            return Err(Error::with_cause(
                ErrorKind::NotReady,
                "getrandom not ready",
                err,
            ));
        } else {
            return Err(Error::with_cause(
                ErrorKind::Unavailable,
                "unexpected getrandom error",
                err,
            ));
        }
    } else if result != dest.len() as i64 {
        return Err(Error::new(ErrorKind::Unavailable,
                              "unexpected getrandom error"));
    }
    Ok(())
}

fn is_getrandom_available() -> bool {
    use std::sync::atomic::{AtomicBool, ATOMIC_BOOL_INIT, Ordering};
    use std::sync::{Once, ONCE_INIT};

    static CHECKER: Once = ONCE_INIT;
    static AVAILABLE: AtomicBool = ATOMIC_BOOL_INIT;

    CHECKER.call_once(|| {
        debug!("OsRng: testing getrandom");
        let mut buf: [u8; 0] = [];
        let result = getrandom(&mut buf, false);
        let available = if result == -1 {
            let err = io::Error::last_os_error().raw_os_error();
            err != Some(libc::ENOSYS)
        } else {
            true
        };
        AVAILABLE.store(available, Ordering::Relaxed);
        info!("OsRng: using {}", if available { "getrandom" } else { "/dev/random" });
    });

    AVAILABLE.load(Ordering::Relaxed)
}
<commit_msg>fix solaris<commit_after>//! Read from `/dev/random`, with chunks of limited size (1040 bytes).
//! `/dev/random` uses the Hash_DRBG with SHA512 algorithm from NIST SP 800-90A.
//! `/dev/urandom` uses the FIPS 186-2 algorithm, which is considered less
//! secure. We choose to read from `/dev/random`.
//!
//! Since Solaris 11.3 the `getrandom` syscall is available. To make sure we can
//! compile on both Solaris and on OpenSolaris derivatives, that do not have the
//! function, we do a direct syscall instead of calling a library function.
//!
//! We have no way to differentiate between Solaris, illumos, SmartOS, etc.
extern crate libc;

use rand_core::{Error, ErrorKind};
use super::random_device;
use super::OsRngImpl;

use std::io;
use std::io::Read;
use std::fs::{File, OpenOptions};
use std::os::unix::fs::OpenOptionsExt;
use std::sync::atomic::{AtomicBool, ATOMIC_BOOL_INIT, Ordering};
use std::cmp;

#[derive(Clone, Debug)]
pub struct OsRng {
    method: OsRngMethod,
    initialized: bool,
}

#[derive(Clone, Debug)]
enum OsRngMethod {
    GetRandom,
    RandomDevice,
}

impl OsRngImpl for OsRng {
    fn new() -> Result<OsRng, Error> {
        if is_getrandom_available() {
            return Ok(OsRng { method: OsRngMethod::GetRandom,
                              initialized: false });
        }
        let open = |p| OpenOptions::new()
            .read(true)
            .custom_flags(libc::O_NONBLOCK)
            .open(p);
        random_device::open("/dev/random", &open)?;
        Ok(OsRng { method: OsRngMethod::RandomDevice, initialized: false })
    }

    fn fill_chunk(&mut self, dest: &mut [u8]) -> Result<(), Error> {
        match self.method {
            OsRngMethod::GetRandom => getrandom_try_fill(dest, false),
            OsRngMethod::RandomDevice => random_device::read(dest),
        }
    }

    fn test_initialized(&mut self, dest: &mut [u8], blocking: bool)
        -> Result<usize, Error>
    {
        static OS_RNG_INITIALIZED: AtomicBool = ATOMIC_BOOL_INIT;
        if !self.initialized {
            self.initialized = OS_RNG_INITIALIZED.load(Ordering::Relaxed);
        }
        if self.initialized { return Ok(0); }

        let chunk_len = cmp::min(1024, dest.len());
        let dest = &mut dest[..chunk_len];

        match self.method {
            OsRngMethod::GetRandom => getrandom_try_fill(dest, blocking)?,
            OsRngMethod::RandomDevice => {
                if blocking {
                    info!("OsRng: testing random device /dev/random");
                    // We already have a non-blocking handle, but now need a
                    // blocking one. Not much choice except opening it twice
                    let mut file = File::open("/dev/random")
                        .map_err(random_device::map_err)?;
                    file.read(dest).map_err(random_device::map_err)?;
                } else {
                    self.fill_chunk(dest)?;
                }
            }
        };
        OS_RNG_INITIALIZED.store(true, Ordering::Relaxed);
        self.initialized = true;
        Ok(chunk_len)
    }

    fn max_chunk_size(&self) -> usize {
        // The documentation says 1024 is the maximum for getrandom, but
        // 1040 for /dev/random.
        1024
    }

    fn method_str(&self) -> &'static str {
        match self.method {
            OsRngMethod::GetRandom => "getrandom",
            OsRngMethod::RandomDevice => "/dev/random",
        }
    }
}

fn getrandom(buf: &mut [u8], blocking: bool) -> libc::c_long {
    extern "C" {
        fn syscall(number: libc::c_long, ...) -> libc::c_long;
    }

    const SYS_GETRANDOM: libc::c_long = 143;
    const GRND_NONBLOCK: libc::c_uint = 0x0001;
    const GRND_RANDOM: libc::c_uint = 0x0002;

    unsafe {
        syscall(SYS_GETRANDOM, buf.as_mut_ptr(), buf.len(),
                if blocking { 0 } else { GRND_NONBLOCK } | GRND_RANDOM)
    }
}

fn getrandom_try_fill(dest: &mut [u8], blocking: bool) -> Result<(), Error> {
    let result = getrandom(dest, blocking);
    if result == -1 || result == 0 {
        let err = io::Error::last_os_error();
        let kind = err.kind();
        if kind == io::ErrorKind::WouldBlock {
            return Err(Error::with_cause(
                ErrorKind::NotReady,
                "getrandom not ready",
                err,
            ));
        } else {
            return Err(Error::with_cause(
                ErrorKind::Unavailable,
                "unexpected getrandom error",
                err,
            ));
        }
    } else if result != dest.len() as i64 {
        return Err(Error::new(ErrorKind::Unavailable,
                              "unexpected getrandom error"));
    }
    Ok(())
}

fn is_getrandom_available() -> bool {
    use std::sync::atomic::{AtomicBool, ATOMIC_BOOL_INIT, Ordering};
    use std::sync::{Once, ONCE_INIT};

    static CHECKER: Once = ONCE_INIT;
    static AVAILABLE: AtomicBool = ATOMIC_BOOL_INIT;

    CHECKER.call_once(|| {
        debug!("OsRng: testing getrandom");
        let mut buf: [u8; 0] = [];
        let result = getrandom(&mut buf, false);
        let available = if result == -1 {
            let err = io::Error::last_os_error().raw_os_error();
            err != Some(libc::ENOSYS)
        } else {
            true
        };
        AVAILABLE.store(available, Ordering::Relaxed);
        info!("OsRng: using {}", if available { "getrandom" } else { "/dev/random" });
    });

    AVAILABLE.load(Ordering::Relaxed)
}
<|endoftext|>


<commit_before>extern crate libc;

use libc::{c_void, c_int};
use libc::{MS_REC, MS_PRIVATE, MS_NOSUID, MS_NODEV, MS_NOEXEC, MNT_DETACH};
use libc::{CLONE_NEWUSER, CLONE_NEWNS, CLONE_NEWPID, SIGCHLD};
use std::fs;
use std::fs::{File, OpenOptions};
use std::path::Path;
use std::io::{Read, Write};
use std::panic::catch_unwind;
use linux::{chdir, mount, umount2, pivot_root};
use stack::Stack;

// a lot copied from man USER_NAMESPACES(7)

#[no_mangle]
extern "C" fn child_func(args: *mut c_void) -> c_int {
    println!("I'm called from child_func");

    let args = args as *mut ChildArgs<bool>;
    let r_pipe_fd = unsafe { (*args).r_pipe_fd };
    let w_pipe_fd = unsafe { (*args).w_pipe_fd };
    let setup = unsafe { (*args).setup };
    let run = unsafe { (*args).run };
    println!("r_pipe_fd: {}", r_pipe_fd);

    // run everything in a new thread so exceptions bubble up to rust
    let result = catch_unwind(move || {
        // wait for parent
        println!("waiting for parent to set up mapping, ...");
        assert_eq!(unsafe { libc::close(w_pipe_fd) } , 0);
        unsafe {
            let mut buf = 0u8;
            assert_eq!(libc::read(r_pipe_fd, (&mut buf) as *mut u8 as *mut c_void, 1), 0);
        }

        {
            println!("Child status");
            let mut fstatus = File::open("/proc/self/status").expect("open status");
            let mut contents = String::new();
            fstatus.read_to_string(&mut contents).expect("read status");
            for l in contents.lines(){
                if l.starts_with("Seccomp") || l.starts_with("Cap"){
                    println!("{}", l);
                }
            }
        }

        let container_root = Path::new("./mntcont");
        if container_root.exists() {
            assert!(container_root.is_dir());
        } else {
            println!("creating {:?}", container_root.as_os_str());
            fs::create_dir(container_root).expect("mkdir mntcont");
        }

        println!("mounting container root");
        mount(None, Some(container_root), Some("tmpfs"), 0);
        mount(Some("none"), Some("/"), None, MS_REC|MS_PRIVATE);
        {
            let proc_dir = container_root.join("proc");
            println!("setting up {:?}", proc_dir.as_os_str());
            fs::create_dir(proc_dir.as_path()).expect("mkdir proc");
            mount(None, Some(proc_dir.as_path()), Some("proc"), MS_NOSUID|MS_NODEV|MS_NOEXEC);
        }

        // setup: a chance to populate root.
        let setup_result = setup(container_root);

        let old_root_name = "oldroot";
        {
            println!("Entering new root");
            let old_root = container_root.join(old_root_name);
            fs::create_dir(old_root.as_path()).expect("mkdir oldroot");
            pivot_root(container_root, old_root.as_path());
        }

        chdir("/");

        // we moved to the new root
        let container_root = Path::new("/");
        let old_root = container_root.join(old_root_name);
        let old_root = old_root.as_path();

        {
            println!("getting rid of old root");
            umount2(old_root, MNT_DETACH);
            assert!(fs::read_dir(old_root).expect("oldroot dir").next().is_none());
            fs::remove_dir(old_root).expect("rmdir oldroot");
        }

        {
            println!("mountinfo");
            let mut fstatus = File::open("/proc/self/mountinfo").expect("open mountinfo");
            let mut contents = String::new();
            fstatus.read_to_string(&mut contents).expect("read status");
            for l in contents.lines(){
                println!("{}", l);
            }
        }

        // Bare setup is done.
        // Missing:
        //  * Resource limitations with cgroups
        //  * Capability setup (process has full capabilities in its user namespace!)
        //  * Container is naked:
        //    * Full kernel attack surface, no syscall limitations with seccomp
        //    * No critical things prohibited with AppArmor or SELinux (e.g. container has full
        //      /proc access)
        setup_result
    });

    match result {
        Ok(setup_result) => run(setup_result),
        Err(_) => {
            println!("setup paniced");
            1
        }
    }
}

struct ChildArgs<T> {
    r_pipe_fd: c_int,
    w_pipe_fd: c_int,
    setup: fn(&Path) -> T, //container_root -> custom_val
    run: fn(T) -> c_int,
}

//write to file with one write call
fn write(path: &String, content: &[u8]) {
    println!("Writing to {}", path);
    let mut file = OpenOptions::new().read(false).write(true).create(false).open(path).expect("open");
    // needs to happen in one write call
    assert_eq!(file.write(content).expect("write"), content.len());
}

pub fn runcontained(setup: fn(&Path) -> bool, run: fn(bool) -> c_int) {

    let mut pipe_fd = vec![0;2];  /* Pipe used to synchronize parent and child */
    assert_eq!(unsafe { libc::pipe(pipe_fd.as_mut_ptr()) }, 0);
    println!("r pipe: {} w pipe: {}", pipe_fd[0], pipe_fd[1]);
    let ptr_child_args = {
        let child_args = Box::new(ChildArgs { r_pipe_fd: pipe_fd[0], w_pipe_fd: pipe_fd[1], setup: setup, run: run});
        Box::into_raw(child_args) as *mut c_void
    };

    let child_stack = Stack::new(512);

    println!("cloning");
    let child_pid = unsafe {
        libc::clone(child_func, child_stack.top, CLONE_NEWUSER|CLONE_NEWNS|CLONE_NEWPID|SIGCHLD, ptr_child_args)
    };
    assert!(child_pid != -1);

    let uid = unsafe { libc::getuid() };
    write(&format!("/proc/{}/uid_map", child_pid), &format!("0 {} 1", uid).into_bytes());

    //proc_setgroups_write man user_namespaces
    write(&format!("/proc/{}/setgroups", child_pid), &format!("deny").into_bytes());

    let gid = unsafe { libc::getgid() };
    write(&format!("/proc/{}/gid_map", child_pid), &format!("0 {} 1", gid).into_bytes());

    //unleash child
    assert_eq!(unsafe { libc::close(pipe_fd[1]) }, 0);

    let mut child_status: c_int = 0;
    assert!(unsafe { libc::waitpid(child_pid, &mut child_status, 0) } != -1);
    println!("Child terminated: {}", child_status);

    unsafe {
        drop(Box::from_raw(ptr_child_args));
    }

    //child_stack gets unmapped
}
<commit_msg>workaround for my legacy ubuntu rust<commit_after>extern crate libc;

use libc::{c_void, c_int};
use libc::{MS_REC, MS_PRIVATE, MS_NOSUID, MS_NODEV, MS_NOEXEC, MNT_DETACH};
use libc::{CLONE_NEWUSER, CLONE_NEWNS, CLONE_NEWPID, SIGCHLD};
use std::fs;
use std::fs::{File, OpenOptions};
use std::path::Path;
use std::io::{Read, Write};
use linux::{chdir, mount, umount2, pivot_root};
use stack::Stack;

//use std::panic::catch_unwind;
fn catch_unwind<F: FnOnce() -> R, R>(f: F) -> Result<R, ()> {
    println!("my ubuntu rust is too old. Just make sure nothing panics, mkey?");
    //rust does not support conditional compilation based on the compiler version -_-
   Ok(f())
   //panics on Error
}

// a lot copied from man USER_NAMESPACES(7)

#[no_mangle]
extern "C" fn child_func(args: *mut c_void) -> c_int {
    println!("I'm called from child_func");

    let args = args as *mut ChildArgs<bool>;
    let r_pipe_fd = unsafe { (*args).r_pipe_fd };
    let w_pipe_fd = unsafe { (*args).w_pipe_fd };
    let setup = unsafe { (*args).setup };
    let run = unsafe { (*args).run };
    println!("r_pipe_fd: {}", r_pipe_fd);

    // run everything in a new thread so exceptions bubble up to rust
    let result = catch_unwind(move || {
        // wait for parent
        println!("waiting for parent to set up mapping, ...");
        assert_eq!(unsafe { libc::close(w_pipe_fd) } , 0);
        unsafe {
            let mut buf = 0u8;
            assert_eq!(libc::read(r_pipe_fd, (&mut buf) as *mut u8 as *mut c_void, 1), 0);
        }

        {
            println!("Child status");
            let mut fstatus = File::open("/proc/self/status").expect("open status");
            let mut contents = String::new();
            fstatus.read_to_string(&mut contents).expect("read status");
            for l in contents.lines(){
                if l.starts_with("Seccomp") || l.starts_with("Cap"){
                    println!("{}", l);
                }
            }
        }

        let container_root = Path::new("./mntcont");
        if container_root.exists() {
            assert!(container_root.is_dir());
        } else {
            println!("creating {:?}", container_root.as_os_str());
            fs::create_dir(container_root).expect("mkdir mntcont");
        }

        println!("mounting container root");
        mount(None, Some(container_root), Some("tmpfs"), 0);
        mount(Some("none"), Some("/"), None, MS_REC|MS_PRIVATE);
        {
            let proc_dir = container_root.join("proc");
            println!("setting up {:?}", proc_dir.as_os_str());
            fs::create_dir(proc_dir.as_path()).expect("mkdir proc");
            mount(None, Some(proc_dir.as_path()), Some("proc"), MS_NOSUID|MS_NODEV|MS_NOEXEC);
        }

        // setup: a chance to populate root.
        let setup_result = setup(container_root);

        let old_root_name = "oldroot";
        {
            println!("Entering new root");
            let old_root = container_root.join(old_root_name);
            fs::create_dir(old_root.as_path()).expect("mkdir oldroot");
            pivot_root(container_root, old_root.as_path());
        }

        chdir("/");

        // we moved to the new root
        let container_root = Path::new("/");
        let old_root = container_root.join(old_root_name);
        let old_root = old_root.as_path();

        {
            println!("getting rid of old root");
            umount2(old_root, MNT_DETACH);
            assert!(fs::read_dir(old_root).expect("oldroot dir").next().is_none());
            fs::remove_dir(old_root).expect("rmdir oldroot");
        }

        {
            println!("mountinfo");
            let mut fstatus = File::open("/proc/self/mountinfo").expect("open mountinfo");
            let mut contents = String::new();
            fstatus.read_to_string(&mut contents).expect("read status");
            for l in contents.lines(){
                println!("{}", l);
            }
        }

        // Bare setup is done.
        // Missing:
        //  * Resource limitations with cgroups
        //  * Capability setup (process has full capabilities in its user namespace!)
        //  * Container is naked:
        //    * Full kernel attack surface, no syscall limitations with seccomp
        //    * No critical things prohibited with AppArmor or SELinux (e.g. container has full
        //      /proc access)
        setup_result
    });

    match result {
        Ok(setup_result) => run(setup_result),
        Err(_) => {
            println!("setup paniced");
            1
        }
    }
}

struct ChildArgs<T> {
    r_pipe_fd: c_int,
    w_pipe_fd: c_int,
    setup: fn(&Path) -> T, //container_root -> custom_val
    run: fn(T) -> c_int,
}

//write to file with one write call
fn write(path: &String, content: &[u8]) {
    println!("Writing to {}", path);
    let mut file = OpenOptions::new().read(false).write(true).create(false).open(path).expect("open");
    // needs to happen in one write call
    assert_eq!(file.write(content).expect("write"), content.len());
}

pub fn runcontained(setup: fn(&Path) -> bool, run: fn(bool) -> c_int) {

    let mut pipe_fd = vec![0;2];  /* Pipe used to synchronize parent and child */
    assert_eq!(unsafe { libc::pipe(pipe_fd.as_mut_ptr()) }, 0);
    println!("r pipe: {} w pipe: {}", pipe_fd[0], pipe_fd[1]);
    let ptr_child_args = {
        let child_args = Box::new(ChildArgs { r_pipe_fd: pipe_fd[0], w_pipe_fd: pipe_fd[1], setup: setup, run: run});
        Box::into_raw(child_args) as *mut c_void
    };

    let child_stack = Stack::new(512);

    println!("cloning");
    let child_pid = unsafe {
        libc::clone(child_func, child_stack.top, CLONE_NEWUSER|CLONE_NEWNS|CLONE_NEWPID|SIGCHLD, ptr_child_args)
    };
    assert!(child_pid != -1);

    let uid = unsafe { libc::getuid() };
    write(&format!("/proc/{}/uid_map", child_pid), &format!("0 {} 1", uid).into_bytes());

    //proc_setgroups_write man user_namespaces
    write(&format!("/proc/{}/setgroups", child_pid), &format!("deny").into_bytes());

    let gid = unsafe { libc::getgid() };
    write(&format!("/proc/{}/gid_map", child_pid), &format!("0 {} 1", gid).into_bytes());

    //unleash child
    assert_eq!(unsafe { libc::close(pipe_fd[1]) }, 0);

    let mut child_status: c_int = 0;
    assert!(unsafe { libc::waitpid(child_pid, &mut child_status, 0) } != -1);
    println!("Child terminated: {}", child_status);

    unsafe {
        drop(Box::from_raw(ptr_child_args));
    }

    //child_stack gets unmapped
}
<|endoftext|>


<commit_before>// The From trait is used for value-to-value conversions.
// If From is implemented correctly for a type, the Into trait should work conversely.
// You can read more about it at https://doc.rust-lang.org/std/convert/trait.From.html
#[derive(Debug)]
struct Person {
    name: String,
    age: usize,
}

// We implement the Default trait to use it as a fallback
// when the provided string is not convertible into a Person object
impl Default for Person {
    fn default() -> Person {
        Person {
            name: String::from("John"),
            age: 30,
        }
    }
}

// I AM NOT DONE
// Your task is to complete this implementation
// in order for the line `let p = Person::from("Mark,20")` to compile
// Please note that you'll need to parse the age component into a `usize`
// with something like `"4".parse::<usize>()`. The outcome of this needs to
// be handled appropriately.
//
// Steps:
// 1. If the length of the provided string is 0, then return the default of Person
// 2. Split the given string on the commas present in it
// 3. Extract the first element from the split operation and use it as the name
// 4. Extract the other element from the split operation and parse it into a `usize` as the age
// If while parsing the age, something goes wrong, then return the default of Person
// Otherwise, then return an instantiated Person onject with the results
impl From<&str> for Person {
    fn from(s: &str) -> Person {
    }
}

fn main() {
    // Use the `from` function
    let p1 = Person::from("Mark,20");
    // Since From is implemented for Person, we should be able to use Into
    let p2: Person = "Gerald,70".into();
    println!("{:?}", p1);
    println!("{:?}", p2);
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn test_default() {
        // Test that the default person is 30 year old John
        let dp = Person::default();
        assert_eq!(dp.name, "John");
        assert_eq!(dp.age, 30);
    }
    #[test]
    fn test_bad_convert() {
        // Test that John is returned when bad string is provided
        let p = Person::from("");
        assert_eq!(p.name, "John");
        assert_eq!(p.age, 30);
    }
    #[test]
    fn test_good_convert() {
        // Test that "Mark,20" works
        let p = Person::from("Mark,20");
        assert_eq!(p.name, "Mark");
        assert_eq!(p.age, 20);
    }
}
<commit_msg>adds additional test to meet exercise rules<commit_after>// The From trait is used for value-to-value conversions.
// If From is implemented correctly for a type, the Into trait should work conversely.
// You can read more about it at https://doc.rust-lang.org/std/convert/trait.From.html
#[derive(Debug)]
struct Person {
    name: String,
    age: usize,
}

// We implement the Default trait to use it as a fallback
// when the provided string is not convertible into a Person object
impl Default for Person {
    fn default() -> Person {
        Person {
            name: String::from("John"),
            age: 30,
        }
    }
}

// I AM NOT DONE
// Your task is to complete this implementation
// in order for the line `let p = Person::from("Mark,20")` to compile
// Please note that you'll need to parse the age component into a `usize`
// with something like `"4".parse::<usize>()`. The outcome of this needs to
// be handled appropriately.
//
// Steps:
// 1. If the length of the provided string is 0, then return the default of Person
// 2. Split the given string on the commas present in it
// 3. Extract the first element from the split operation and use it as the name
// 4. Extract the other element from the split operation and parse it into a `usize` as the age
// If while parsing the age, something goes wrong, then return the default of Person
// Otherwise, then return an instantiated Person onject with the results
impl From<&str> for Person {
    fn from(s: &str) -> Person {
    }
}

fn main() {
    // Use the `from` function
    let p1 = Person::from("Mark,20");
    // Since From is implemented for Person, we should be able to use Into
    let p2: Person = "Gerald,70".into();
    println!("{:?}", p1);
    println!("{:?}", p2);
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn test_default() {
        // Test that the default person is 30 year old John
        let dp = Person::default();
        assert_eq!(dp.name, "John");
        assert_eq!(dp.age, 30);
    }
    #[test]
    fn test_bad_convert() {
        // Test that John is returned when bad string is provided
        let p = Person::from("");
        assert_eq!(p.name, "John");
        assert_eq!(p.age, 30);
    }
    #[test]
    fn test_good_convert() {
        // Test that "Mark,20" works
        let p = Person::from("Mark,20");
        assert_eq!(p.name, "Mark");
        assert_eq!(p.age, 20);
    }
    #[test]
    fn test_bad_age() {
        // Test that "Mark.twenty" will return the default person due to an error in parsing age
        let p = Person::from("Mark,twenty");
        assert_eq!(p.name, "John");
        assert_eq!(p.age, 30);
    }
}
<|endoftext|>


<commit_before>use term::*;
use term::Term::*;

// TRUE := λxy.x
pub fn tru() -> Term { abs(abs(Var(2))) }

// FALSE := λxy.y
pub fn fls() -> Term { abs(abs(Var(1))) }

// AND := λpq.p q p
pub fn and() -> Term { abs(abs(Var(2).app(Var(1)).app(Var(2)))) }

// OR := λpq.p p q
pub fn or() -> Term { abs(abs(Var(2).app(Var(2)).app(Var(1)))) }

// NOT := λp.p FALSE TRUE
pub fn not() -> Term { abs(Var(1).app(fls()).app(tru())) }

// XOR := λpab.p a b
pub fn xor() -> Term { abs(abs(Var(2).app(not().app(Var(1))).app(Var(1)))) }

// IFELSE := λpab.p a b
pub fn if_else() -> Term { abs(abs(abs(Var(3).app(Var(2)).app(Var(1))))) }

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

	#[test]
	fn chuch_and() {
		let and_true_true	= and().app(tru()).app(tru());
		let and_true_false	= and().app(tru()).app(fls());
		let and_false_true	= and().app(fls()).app(tru());
		let and_false_false = and().app(fls()).app(fls());

		assert_eq!(normalize(and_true_true), tru());
		assert_eq!(normalize(and_true_false), fls());
		assert_eq!(normalize(and_false_true), fls());
		assert_eq!(normalize(and_false_false), fls());
	}

	#[test]
	fn chuch_or() {
		let or_true_true   = or().app(tru()).app(tru());
		let or_true_false  = or().app(tru()).app(fls());
		let or_false_true  = or().app(fls()).app(tru());
		let or_false_false = or().app(fls()).app(fls());

		assert_eq!(normalize(or_true_true), tru());
		assert_eq!(normalize(or_true_false), tru());
		assert_eq!(normalize(or_false_true), tru());
		assert_eq!(normalize(or_false_false), fls());
	}

	#[test]
	fn chuch_not() {
		let not_true  = not().app(tru());
		let not_false = not().app(fls());

		assert_eq!(normalize(not_true), fls());
		assert_eq!(normalize(not_false), tru());
	}

	#[test]
	fn chuch_xor() {
		let xor_true_true   = xor().app(tru()).app(tru());
		let xor_true_false  = xor().app(tru()).app(fls());
		let xor_false_true  = xor().app(fls()).app(tru());
		let xor_false_false = xor().app(fls()).app(fls());

		assert_eq!(normalize(xor_true_true), fls());
		assert_eq!(normalize(xor_true_false), tru());
		assert_eq!(normalize(xor_false_true), tru());
		assert_eq!(normalize(xor_false_false), fls());
	}

	#[test]
	fn church_if_else() {
		let if_true_one_else_zero  = if_else().app(tru()).app(one()).app(zero());
		let if_false_one_else_zero = if_else().app(fls()).app(one()).app(zero());

		assert_eq!(normalize(if_true_one_else_zero), one());
		assert_eq!(normalize(if_false_one_else_zero), zero());
	}
}<commit_msg>Delete booleans.rs<commit_after><|endoftext|>