<commit_before>/** Creates CSS boxes from a DOM. */
use au = gfx::geometry;
use core::dvec::DVec;
use css::values::{CSSDisplay, DisplayBlock, DisplayInline, DisplayInlineBlock, DisplayNone};
use css::values::{Inherit, Initial, Specified};
use dom::base::{ElementData, HTMLDivElement, HTMLImageElement};
use dom::base::{Element, Text, Node, Doctype, Comment, NodeTree};
use layout::base::{RenderBox, BoxData, GenericBox, ImageBox, TextBox, RenderBoxTree};
use layout::base::{FlowContext, FlowContextData, BlockFlow, InlineFlow, InlineBlockFlow, RootFlow, FlowTree};
use layout::block::BlockFlowData;
use layout::context::LayoutContext;
use layout::inline::InlineFlowData;
use layout::root::RootFlowData;
use layout::text::TextBoxData;
use option::is_none;
use util::tree;
use servo_text::text_run::TextRun;
use servo_text::font_cache::FontCache;

export LayoutTreeBuilder;

struct LayoutTreeBuilder {
    mut root_box: Option<@RenderBox>,
    mut root_ctx: Option<@FlowContext>,
    mut next_bid: int,
    mut next_cid: int
}

fn LayoutTreeBuilder() -> LayoutTreeBuilder {
    LayoutTreeBuilder {
        root_box: None,
        root_ctx: None,
        next_bid: -1,
        next_cid: -1
    }
}

impl LayoutTreeBuilder {
    /* Debug-only ids */
    fn next_box_id() -> int { self.next_bid += 1; self.next_bid }
    fn next_ctx_id() -> int { self.next_cid += 1; self.next_cid }

    /** Creates necessary box(es) and flow context(s) for the current DOM node,
    and recurses on its children. */
    fn construct_recursively(layout_ctx: &LayoutContext, cur_node: Node, parent_ctx: @FlowContext, parent_box: @RenderBox) {
        let style = cur_node.style();

        // DEBUG
        let n_str = fmt!("%?", cur_node.read(|n| copy n.kind ));
        debug!("Considering node: %?", n_str);

        // TODO: handle interactions with 'float', 'position' (CSS 2.1, Section 9.7)
        let display = match style.display_type {
            Specified(v) => match v {
                // tree ends here if 'display: none'
                DisplayNone => return,
                _ => v
            },
            Inherit | Initial => DisplayInline // TODO: fail instead once resolve works
            //fail ~"Node should have resolved value for 'display', but was initial or inherit"
        };

        // first, create the proper box kind, based on node characteristics
        let box_data = match cur_node.create_box_data(layout_ctx, display) {
            None => return,
            Some(data) => data
        };

        // then, figure out its proper context, possibly reorganizing.
        let next_ctx: @FlowContext = match box_data {
            /* Text box is always an inline flow. create implicit inline
            flow ctx if we aren't inside one already. */
            TextBox(*) => {
                if (parent_ctx.starts_inline_flow()) {
                    parent_ctx
                } else {
                    self.make_ctx(InlineFlow(InlineFlowData()), tree::empty())
                }
            },
            ImageBox(*) | GenericBox => {
                match display {
                    DisplayInline | DisplayInlineBlock => {
                        /* if inline, try to put into inline context,
                        making a new one if necessary */
                        if (parent_ctx.starts_inline_flow()) {
                            parent_ctx
                        } else {
                            self.make_ctx(InlineFlow(InlineFlowData()), tree::empty())
                        }
                    },
                    /* block boxes always create a new context */
                    DisplayBlock => {
                        self.make_ctx(BlockFlow(BlockFlowData()), tree::empty())
                    },
                    _ => fail fmt!("unsupported display type in box generation: %?", display)
                }
            }
        };

        // make box, add box to any context-specific list.
        let mut new_box = self.make_box(cur_node, parent_ctx, box_data);
        debug!("Assign ^box to flow: %?", next_ctx.debug_str());

        match next_ctx.kind {
            InlineFlow(d) => { d.boxes.push(new_box) }
            BlockFlow(d) => { d.box = Some(new_box) }
            _ => {} // TODO: float lists, etc.
        };

        // connect the box to its parent box
        debug!("Adding child box b%? of b%?", parent_box.id, new_box.id);
        RenderBoxTree.add_child(parent_box, new_box);

        if (!next_ctx.eq(&parent_ctx)) {
            debug!("Adding child flow f%? of f%?", parent_ctx.id, next_ctx.id);
            FlowTree.add_child(parent_ctx, next_ctx);
        }
        // recurse
        do NodeTree.each_child(cur_node) |child_node| {
            self.construct_recursively(layout_ctx, child_node, next_ctx, new_box); true
        }

        // Fixup any irregularities, such as split inlines (CSS 2.1 Section 9.2.1.1)
        if (next_ctx.starts_inline_flow()) {
            let mut found_child_inline = false;
            let mut found_child_block = false;

            do FlowTree.each_child(next_ctx) |child_ctx| {
                match child_ctx.kind {
                    InlineFlow(*) | InlineBlockFlow => found_child_inline = true,
                    BlockFlow(*) => found_child_block = true,
                    _ => {}
                }; true
            }

            if found_child_block && found_child_inline {
                self.fixup_split_inline(next_ctx)
            }
        }
    }

    fn fixup_split_inline(_foo: @FlowContext) {
        // TODO: finish me.
        fail ~"TODO: handle case where an inline is split by a block"
    }

    /** entry point for box creation. Should only be
    called on root DOM element. */
    fn construct_trees(layout_ctx: &LayoutContext, root: Node) -> Result<@RenderBox, ()> {
        self.root_ctx = Some(self.make_ctx(RootFlow(RootFlowData()), tree::empty()));
        self.root_box = Some(self.make_box(root, self.root_ctx.get(), GenericBox));

        self.construct_recursively(layout_ctx, root, self.root_ctx.get(), self.root_box.get());
        return Ok(self.root_box.get())
    }

    fn make_ctx(kind : FlowContextData, tree: tree::Tree<@FlowContext>) -> @FlowContext {
        let ret = @FlowContext(self.next_ctx_id(), kind, tree);
        debug!("Created context: %s", ret.debug_str());
        ret
    }

    fn make_box(node : Node, ctx: @FlowContext, data: BoxData) -> @RenderBox {
        let ret = @RenderBox(self.next_box_id(), node, ctx, data);
        debug!("Created box: %s", ret.debug_str());
        ret
    }
}

trait PrivateBuilderMethods {
    fn create_box_data(layout_ctx: &LayoutContext, display: CSSDisplay) -> Option<BoxData>;
}

impl Node : PrivateBuilderMethods {
    fn create_box_data(layout_ctx: &LayoutContext, display: CSSDisplay) -> Option<BoxData> {
        do self.read |node| {
            match node.kind {
                ~Doctype(*) | ~Comment(*) => None,
                ~Text(string) => {
                    // TODO: clean this up. Fonts should not be created here.
                    let font = layout_ctx.font_cache.get_test_font();
                    let run = TextRun(font, string);
                    Some(TextBox(TextBoxData(copy string, ~[move run])))
                }
                ~Element(element) => {
                    match (element.kind, display) {
                        (~HTMLImageElement({size}), _) => Some(ImageBox(size)),
//                      (_, Specified(_)) => Some(GenericBox),
                        (_, _) => Some(GenericBox) // TODO: replace this with the commented lines
//                      (_, _) => fail ~"Can't create box for Node with non-specified 'display' type"
                    }
                }
            }
        }
    }
}

<commit_msg>Move create_box_data to be a method of LayoutTreeBuilder; add an explicit UA stylesheet simulation method<commit_after>/** Creates CSS boxes from a DOM. */
use au = gfx::geometry;
use core::dvec::DVec;
use css::styles::SpecifiedStyle;
use css::values::{CSSDisplay, DisplayBlock, DisplayInline, DisplayInlineBlock, DisplayNone};
use css::values::{Inherit, Initial, Specified};
use dom::base::*;
use layout::base::{RenderBox, BoxData, GenericBox, ImageBox, TextBox, RenderBoxTree};
use layout::base::{FlowContext, FlowContextData, BlockFlow, InlineFlow, InlineBlockFlow, RootFlow, FlowTree};
use layout::block::BlockFlowData;
use layout::context::LayoutContext;
use layout::inline::InlineFlowData;
use layout::root::RootFlowData;
use layout::text::TextBoxData;
use option::is_none;
use util::tree;
use servo_text::text_run::TextRun;
use servo_text::font_cache::FontCache;

export LayoutTreeBuilder;

struct LayoutTreeBuilder {
    mut root_box: Option<@RenderBox>,
    mut root_ctx: Option<@FlowContext>,
    mut next_bid: int,
    mut next_cid: int
}

fn LayoutTreeBuilder() -> LayoutTreeBuilder {
    LayoutTreeBuilder {
        root_box: None,
        root_ctx: None,
        next_bid: -1,
        next_cid: -1
    }
}

impl LayoutTreeBuilder {
    /* Debug-only ids */
    fn next_box_id() -> int { self.next_bid += 1; self.next_bid }
    fn next_ctx_id() -> int { self.next_cid += 1; self.next_cid }

    /** Creates necessary box(es) and flow context(s) for the current DOM node,
    and recurses on its children. */
    fn construct_recursively(layout_ctx: &LayoutContext, cur_node: Node, parent_ctx: @FlowContext, parent_box: @RenderBox) {
        let style = cur_node.style();

        // DEBUG
        let n_str = fmt!("%?", cur_node.read(|n| copy n.kind ));
        debug!("Considering node: %?", n_str);

        // TODO: remove this once UA styles work
        // TODO: handle interactions with 'float', 'position' (CSS 2.1, Section 9.7)
        let simulated_display = match self.simulate_UA_display_rules(cur_node, style) {
            DisplayNone => return, // tree ends here if 'display: none'
            v => v
        };

        // first, create the proper box kind, based on node characteristics
        let box_data = match self.create_box_data(layout_ctx, cur_node, simulated_display) {
            None => return,
            Some(data) => data
        };

        // then, figure out its proper context, possibly reorganizing.
        let next_ctx: @FlowContext = match box_data {
            /* Text box is always an inline flow. create implicit inline
            flow ctx if we aren't inside one already. */
            TextBox(*) => {
                if (parent_ctx.starts_inline_flow()) {
                    parent_ctx
                } else {
                    self.make_ctx(InlineFlow(InlineFlowData()), tree::empty())
                }
            },
            ImageBox(*) | GenericBox => {
                match simulated_display {
                    DisplayInline | DisplayInlineBlock => {
                        /* if inline, try to put into inline context,
                        making a new one if necessary */
                        if (parent_ctx.starts_inline_flow()) {
                            parent_ctx
                        } else {
                            self.make_ctx(InlineFlow(InlineFlowData()), tree::empty())
                        }
                    },
                    /* block boxes always create a new context */
                    DisplayBlock => {
                        self.make_ctx(BlockFlow(BlockFlowData()), tree::empty())
                    },
                    _ => fail fmt!("unsupported display type in box generation: %?", simulated_display)
                }
            }
        };

        // make box, add box to any context-specific list.
        let mut new_box = self.make_box(cur_node, parent_ctx, box_data);
        debug!("Assign ^box to flow: %?", next_ctx.debug_str());

        match next_ctx.kind {
            InlineFlow(d) => { d.boxes.push(new_box) }
            BlockFlow(d) => { d.box = Some(new_box) }
            _ => {} // TODO: float lists, etc.
        };

        // connect the box to its parent box
        debug!("Adding child box b%? of b%?", parent_box.id, new_box.id);
        RenderBoxTree.add_child(parent_box, new_box);

        if (!next_ctx.eq(&parent_ctx)) {
            debug!("Adding child flow f%? of f%?", parent_ctx.id, next_ctx.id);
            FlowTree.add_child(parent_ctx, next_ctx);
        }
        // recurse
        do NodeTree.each_child(cur_node) |child_node| {
            self.construct_recursively(layout_ctx, child_node, next_ctx, new_box); true
        }

        // Fixup any irregularities, such as split inlines (CSS 2.1 Section 9.2.1.1)
        if (next_ctx.starts_inline_flow()) {
            let mut found_child_inline = false;
            let mut found_child_block = false;

            do FlowTree.each_child(next_ctx) |child_ctx| {
                match child_ctx.kind {
                    InlineFlow(*) | InlineBlockFlow => found_child_inline = true,
                    BlockFlow(*) => found_child_block = true,
                    _ => {}
                }; true
            }

            if found_child_block && found_child_inline {
                self.fixup_split_inline(next_ctx)
            }
        }
    }

    fn fixup_split_inline(_foo: @FlowContext) {
        // TODO: finish me.
        fail ~"TODO: handle case where an inline is split by a block"
    }

    priv fn simulate_UA_display_rules(node: Node, style: SpecifiedStyle) -> CSSDisplay {
        let resolved = match style.display_type {
            Inherit | Initial => DisplayInline, // TODO: remove once resolve works
            Specified(v) => v
        };

        if (resolved == DisplayNone) { return resolved; }

        do node.read |n| {
            match n.kind {
                ~Doctype(*) | ~Comment(*) => DisplayNone,
                ~Text(*) => DisplayInline,
                ~Element(e) => match e.kind {
                    ~HTMLHeadElement(*) => DisplayNone,
                    ~HTMLScriptElement(*) => DisplayNone,
                    _ => resolved
                }
            }
        }
    }

    /** entry point for box creation. Should only be
    called on root DOM element. */
    fn construct_trees(layout_ctx: &LayoutContext, root: Node) -> Result<@RenderBox, ()> {
        self.root_ctx = Some(self.make_ctx(RootFlow(RootFlowData()), tree::empty()));
        self.root_box = Some(self.make_box(root, self.root_ctx.get(), GenericBox));

        self.construct_recursively(layout_ctx, root, self.root_ctx.get(), self.root_box.get());
        return Ok(self.root_box.get())
    }

    fn make_ctx(kind : FlowContextData, tree: tree::Tree<@FlowContext>) -> @FlowContext {
        let ret = @FlowContext(self.next_ctx_id(), kind, tree);
        debug!("Created context: %s", ret.debug_str());
        ret
    }

    fn make_box(node : Node, ctx: @FlowContext, data: BoxData) -> @RenderBox {
        let ret = @RenderBox(self.next_box_id(), node, ctx, data);
        debug!("Created box: %s", ret.debug_str());
        ret
    }

    /* Based on the DOM node type, create a specific type of box */
    fn create_box_data(layout_ctx: &LayoutContext, node: Node, display: CSSDisplay) -> Option<BoxData> {
        // TODO: handle more types of nodes.
        do node.read |n| {
            match n.kind {
                ~Doctype(*) | ~Comment(*) => None,
                ~Text(string) => {
                    // TODO: clean this up. Fonts should not be created here.
                    let font = layout_ctx.font_cache.get_test_font();
                    let run = TextRun(font, string);
                    Some(TextBox(TextBoxData(copy string, ~[move run])))
                }
                ~Element(element) => {
                    match (element.kind, display) {
                        (~HTMLImageElement({size}), _) => Some(ImageBox(size)),
//                      (_, Specified(_)) => Some(GenericBox),
                        (_, _) => Some(GenericBox) // TODO: replace this with the commented lines
//                      (_, _) => fail ~"Can't create box for Node with non-specified 'display' type"
                    }
                }
            }
        }
    }
}

<|endoftext|>


<commit_before>
use clock_ticks::precise_time_ns;
use color::Color;
use graphics::character::CharacterCache;
use graphics::math::Scalar;
use label::FontSize;
use mouse::Mouse;
use position::{self, Depth, Dimensions, Horizontal, HorizontalAlign, Place, Point, Position,
               VerticalAlign};
use theme::Theme;
use ui::{self, GlyphCache, Ui, UiId};

//use super::{CanvasId, Kind};
//use super::split::Split;

/// The current state of a Floating.
#[derive(Clone, Debug, PartialEq)]
pub struct State {
    interaction: Interaction,
    xy: Point,
    dim: Dimensions,
    pub time_last_clicked: u64,
}

/// Describes an interaction with the Floating Canvas.
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum Interaction {
    Normal,
    Highlighted(Elem),
    Clicked(Elem, Point),
}

/// Describes the different Elements that make up the Canvas.
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum Elem {
    TitleBar,
    WidgetArea,
}

/// A type of Canvas for flexibly designing and guiding widget layout as splits of a window.
pub struct Floating<'a> {
    init_dim: Dimensions,
    init_pos: Position,
    depth: Depth,
    maybe_h_align: Option<HorizontalAlign>,
    maybe_v_align: Option<VerticalAlign>,
    //maybe_splits: Option<(Direction, &'a [Split<'a>])>,
    maybe_title_bar_label: Option<&'a str>,
    show_title_bar: bool,
    style: Style,
//     maybe_adjustable: Option<Adjustable>,
}

/// Describes the style of a Canvas Floating.
#[derive(Clone, Debug, PartialEq, RustcDecodable, RustcEncodable)]
pub struct Style {
    maybe_color: Option<Color>,
    maybe_frame: Option<f64>,
    maybe_frame_color: Option<Color>,
    maybe_title_bar_font_size: Option<FontSize>,
    maybe_title_bar_label_align: Option<Horizontal>,
    maybe_title_bar_label_color: Option<Color>,
    padding: Padding,
}

// /// Adjustable dimensions within these bounds.
// #[derive(Clone, Copy, Debug, PartialEq)]
// pub struct Adjustable {
//     width: Bounds,
//     height: Bounds,
// }
//
// /// The minimum and maximum for a dimension of a Floating.
// #[derive(Clone, Copy, Debug, PartialEq)]
// pub struct Bounds {
//     pub min: f64,
//     pub max: f64,
// }

/// The distance between the edge of a widget and the inner edge of a Canvas' frame.
#[derive(Clone, Debug, PartialEq, RustcDecodable, RustcEncodable)]
pub struct Padding {
    maybe_top: Option<f64>,
    maybe_bottom: Option<f64>,
    maybe_left: Option<f64>,
    maybe_right: Option<f64>,
}

impl<'a> Floating<'a> {

    /// Construct a default Floating canvas.
    pub fn new() -> Floating<'a> {
        Floating {
            init_dim: [160.0, 80.0],
            init_pos: Position::Place(Place::Middle, None),
            depth: 0.0,
            maybe_h_align: None,
            maybe_v_align: None,
            //maybe_splits: None,
            maybe_title_bar_label: None,
            show_title_bar: true,
            style: Style::new(),
        }
    }

    /// Show or hide the title bar.
    pub fn show_title_bar(mut self, show: bool) -> Floating<'a> {
        self.show_title_bar = show;
        self
    }

    /// Set the padding from the left edge.
    pub fn pad_left(mut self, pad: Scalar) -> Floating<'a> {
        self.style.padding.maybe_left = Some(pad);
        self
    }

    /// Set the padding from the right edge.
    pub fn pad_right(mut self, pad: Scalar) -> Floating<'a> {
        self.style.padding.maybe_right = Some(pad);
        self
    }

    /// Set the padding from the top edge.
    pub fn pad_top(mut self, pad: Scalar) -> Floating<'a> {
        self.style.padding.maybe_top = Some(pad);
        self
    }

    /// Set the padding from the bottom edge.
    pub fn pad_bottom(mut self, pad: Scalar) -> Floating<'a> {
        self.style.padding.maybe_bottom = Some(pad);
        self
    }

    /// Set the padding for all edges.
    pub fn pad(self, pad: Scalar) -> Floating<'a> {
        self.pad_left(pad).pad_right(pad).pad_top(pad).pad_bottom(pad)
    }

    /// Register the Canvas within the Ui.
    pub fn set<C>(self, id: CanvasId, ui: &mut Ui<C>)
        where
            C: CharacterCache
    {
        use elmesque::form::{collage, rect, text};

        let Floating {
            show_title_bar,
            //ref maybe_splits,
            ref style,
            ref maybe_title_bar_label,
            ..
        } = self;

        let State { interaction, xy, dim, time_last_clicked } = match ui::get_canvas_state(ui, id) {
            Some(Kind::Floating(state)) => state,
            _ => {
                let init_dim = self.init_dim;
                let h_align = self.maybe_h_align.unwrap_or(ui.theme.align.horizontal);
                let v_align = self.maybe_v_align.unwrap_or(ui.theme.align.vertical);
                let init_xy = ui.get_xy(self.init_pos, init_dim, h_align, v_align);
                State {
                    interaction: Interaction::Normal,
                    xy: init_xy,
                    dim: init_dim,
                    time_last_clicked: precise_time_ns(),
                }
            },
        };

        let maybe_mouse = ui::get_mouse_state(ui, UiId::Canvas(id), Some(id));
        let pad = style.padding(&ui.theme);
        let title_bar_font_size = style.title_bar_font_size(&ui.theme);
        const TITLE_BAR_LABEL_PADDING: f64 = 4.0;
        let (title_bar_h, title_bar_y) = {
            if show_title_bar {
                let h = title_bar_font_size as f64 + TITLE_BAR_LABEL_PADDING * 2.0;
                let y = position::align_top_of(dim[1], h);
                (h, y)
            } else {
                (0.0, 0.0)
            }
        };

        let widget_area_xy = [xy[0], xy[1] - title_bar_h / 2.0];
        let widget_area_dim = [dim[0], dim[1] - title_bar_h];

        // If there is new mouse state, check for a new interaction.
        let new_interaction = if let Some(mouse) = maybe_mouse {
            let is_over_elem = is_over(mouse.relative_to(xy).xy, dim, title_bar_y, title_bar_h);
            get_new_interaction(is_over_elem, interaction, mouse)
        } else {
            interaction
        };

        // Drag the Canvas if the TitleBar remains clicked.
        let new_xy = match (interaction, new_interaction) {
            (Interaction::Clicked(Elem::TitleBar, a), Interaction::Clicked(Elem::TitleBar, b)) =>
                ::vecmath::vec2_add(xy, ::vecmath::vec2_sub(b, a)),
            _ => xy,
        };

        // Check whether or not we need to capture or uncapture the mouse.
        match (interaction, new_interaction) {
            (Interaction::Highlighted(Elem::TitleBar), Interaction::Clicked(Elem::TitleBar, _)) =>
                ui::mouse_captured_by(ui, UiId::Canvas(id)),
            (Interaction::Clicked(Elem::TitleBar, _), Interaction::Highlighted(_)) |
            (Interaction::Clicked(Elem::TitleBar, _), Interaction::Normal)         =>
                ui::mouse_uncaptured_by(ui, UiId::Canvas(id)),
            _ => (),
        }

        // If the canvas was clicked, dragged or released, update the time_last_clicked.
        let new_time_last_clicked = match (interaction, new_interaction) {
            (Interaction::Highlighted(_), Interaction::Clicked(_, _)) |
            (Interaction::Clicked(_, _), Interaction::Highlighted(_)) |
            (Interaction::Clicked(_, _), Interaction::Clicked(_, _))  => precise_time_ns(),
            _ => time_last_clicked,
        };

        // Draw.
        let frame = style.frame(&ui.theme);
        let inner_dim = ::vecmath::vec2_sub(dim, [frame * 2.0; 2]);
        let color = style.color(&ui.theme);
        let frame_color = style.frame_color(&ui.theme);
        let frame_form = rect(dim[0], dim[1]).filled(frame_color.alpha(0.7));
        let rect_form = rect(inner_dim[0], inner_dim[1]).filled(color.alpha(0.7));
        let maybe_title_bar_form = if show_title_bar {
            let inner_dim = ::vecmath::vec2_sub([dim[0], title_bar_h], [frame * 2.0; 2]);
            let title_bar_frame_form = rect(dim[0], title_bar_h).filled(frame_color);
            let title_bar_rect_form = rect(inner_dim[0], inner_dim[1]).filled(color);
            let maybe_label_form = if let Some(label) = *maybe_title_bar_label {
                use elmesque::text::Text;
                let label_color = style.title_bar_label_color(&ui.theme);
                let align = style.title_bar_label_align(&ui.theme);
                let label_width = ui.glyph_cache.width(title_bar_font_size, label);
                let label_x = align.to(inner_dim[0], label_width) + TITLE_BAR_LABEL_PADDING;
                Some(text(Text::from_string(label.to_string())
                             .color(label_color)
                             .height(title_bar_font_size as f64)).shift_x(label_x))
            } else {
                None
            };
            Some(Some(title_bar_frame_form).into_iter()
                .chain(Some(title_bar_rect_form).into_iter())
                .chain(maybe_label_form)
                .map(|form| form.shift_y(title_bar_y)))
        } else {
            None
        };

        // Chain the Canvas' Forms together.
        let form_chain = Some(frame_form).into_iter()
            .chain(Some(rect_form).into_iter())
            .chain(maybe_title_bar_form.into_iter().flat_map(|it| it))
            .map(|form| form.shift(new_xy[0], new_xy[1]));

        // Construct the renderable element.
        let element = collage(dim[0] as i32, dim[1] as i32, form_chain.collect());

        // Construct the new Canvas state.
        let new_state = State {
            interaction: new_interaction,
            xy: new_xy,
            dim: dim,
            time_last_clicked: new_time_last_clicked,
        };

        // Update the canvas within the `Ui`'s `canvas_cache`.
        ui::update_canvas(ui, id, Kind::Floating(new_state), new_xy, widget_area_xy,
                          widget_area_dim, pad, Some(element));
    }

}

/// Is the mouse over the canvas, if so which Elem.
fn is_over(mouse_xy: Point,
           dim: Dimensions,
           title_bar_y: f64,
           title_bar_h: f64) -> Option<Elem> {
    use utils::is_over_rect;
    if is_over_rect([0.0, 0.0], mouse_xy, dim) {
        if is_over_rect([0.0, title_bar_y], mouse_xy, [dim[0], title_bar_h]) {
            Some(Elem::TitleBar)
        } else {
            Some(Elem::WidgetArea)
        }
    } else {
        None
    }
}

/// Determine the new interaction given the mouse state and previous interaction.
fn get_new_interaction(is_over_elem: Option<Elem>, prev: Interaction, mouse: Mouse) -> Interaction {
    use mouse::ButtonState::{Down, Up};
    use self::Interaction::{Normal, Highlighted, Clicked};
    match (is_over_elem, prev, mouse.left) {
        (Some(_),    Normal,          Down)  => Normal,
        (Some(elem), _,               Up)    => Highlighted(elem),
        (Some(elem), Highlighted(_),  Down)  => Clicked(elem, mouse.xy),
        (Some(_),    Clicked(elem, _), Down) => Clicked(elem, mouse.xy),
        (None,       Clicked(elem, _), Down) => Clicked(elem, mouse.xy),
        _                                    => Normal,
    }
}

impl Style {

    /// Construct the default Style.
    pub fn new() -> Style {
        Style {
            maybe_color: None,
            maybe_frame: None,
            maybe_frame_color: None,
            maybe_title_bar_label_color: None,
            maybe_title_bar_font_size: None,
            maybe_title_bar_label_align: None,
            padding: Padding::new()
        }
    }

    /// Get the color for the Floating's Element.
    pub fn color(&self, theme: &Theme) -> Color {
        self.maybe_color.or(theme.maybe_canvas_floating.as_ref().map(|style| {
            style.maybe_color.unwrap_or(theme.background_color)
        })).unwrap_or(theme.background_color)
    }

    /// Get the frame for an Element.
    pub fn frame(&self, theme: &Theme) -> f64 {
        self.maybe_frame.or(theme.maybe_canvas_floating.as_ref().map(|style| {
            style.maybe_frame.unwrap_or(theme.frame_width)
        })).unwrap_or(theme.frame_width)
    }

    /// Get the frame Color for an Element.
    pub fn frame_color(&self, theme: &Theme) -> Color {
        self.maybe_frame_color.or(theme.maybe_canvas_floating.as_ref().map(|style| {
            style.maybe_frame_color.unwrap_or(theme.frame_color)
        })).unwrap_or(theme.frame_color)
    }

    /// Get the Padding for the Canvas Floating.
    pub fn padding(&self, theme: &Theme) -> position::Padding {
        position::Padding {
            top: self.padding.maybe_top.or(theme.maybe_canvas_floating.as_ref().map(|style| {
                style.padding.maybe_top.unwrap_or(theme.padding.top)
            })).unwrap_or(theme.padding.top),
            bottom: self.padding.maybe_bottom.or(theme.maybe_canvas_floating.as_ref().map(|style| {
                style.padding.maybe_bottom.unwrap_or(theme.padding.bottom)
            })).unwrap_or(theme.padding.bottom),
            left: self.padding.maybe_left.or(theme.maybe_canvas_floating.as_ref().map(|style| {
                style.padding.maybe_left.unwrap_or(theme.padding.left)
            })).unwrap_or(theme.padding.left),
            right: self.padding.maybe_right.or(theme.maybe_canvas_floating.as_ref().map(|style| {
                style.padding.maybe_right.unwrap_or(theme.padding.right)
            })).unwrap_or(theme.padding.right),
        }
    }

    /// Get the font size of the title bar.
    pub fn title_bar_font_size(&self, theme: &Theme) -> FontSize {
        self.maybe_title_bar_font_size.or(theme.maybe_canvas_floating.as_ref().map(|style| {
            style.maybe_title_bar_font_size.unwrap_or(theme.font_size_medium)
        })).unwrap_or(theme.font_size_medium)
    }

    /// Get the alignment of the title bar label.
    pub fn title_bar_label_align(&self, theme: &Theme) -> Horizontal {
        const DEFAULT_ALIGN: Horizontal = Horizontal::Middle;
        self.maybe_title_bar_label_align.or(theme.maybe_canvas_floating.as_ref().map(|style| {
            style.maybe_title_bar_label_align.unwrap_or(DEFAULT_ALIGN)
        })).unwrap_or(DEFAULT_ALIGN)
    }

    /// Get the color of the title bar label.
    pub fn title_bar_label_color(&self, theme: &Theme) -> Color {
        self.maybe_title_bar_label_color.or(theme.maybe_canvas_floating.as_ref().map(|style| {
            style.maybe_title_bar_label_color.unwrap_or(theme.label_color)
        })).unwrap_or(theme.label_color)
    }

}

impl Padding {
    /// Construct a defualt Padding.
    pub fn new() -> Padding {
        Padding {
            maybe_top: None,
            maybe_bottom: None,
            maybe_left: None,
            maybe_right: None,
        }
    }
}

impl<'a> ::color::Colorable for Floating<'a> {
    fn color(mut self, color: Color) -> Self {
        self.style.maybe_color = Some(color);
        self
    }
}

impl<'a> ::frame::Frameable for Floating<'a> {
    fn frame(mut self, width: f64) -> Self {
        self.style.maybe_frame = Some(width);
        self
    }
    fn frame_color(mut self, color: Color) -> Self {
        self.style.maybe_frame_color = Some(color);
        self
    }
}

impl<'a> ::position::Positionable for Floating<'a> {
    fn position(mut self, pos: Position) -> Self {
        self.init_pos = pos;
        self
    }
    fn get_position(&self) -> Position { self.init_pos }
    #[inline]
    fn horizontal_align(self, h_align: HorizontalAlign) -> Self {
        Floating { maybe_h_align: Some(h_align), ..self }
    }
    #[inline]
    fn vertical_align(self, v_align: VerticalAlign) -> Self {
        Floating { maybe_v_align: Some(v_align), ..self }
    }
    fn get_horizontal_align(&self, theme: &Theme) -> HorizontalAlign {
        self.maybe_h_align.unwrap_or(theme.align.horizontal)
    }
    fn get_vertical_align(&self, theme: &Theme) -> VerticalAlign {
        self.maybe_v_align.unwrap_or(theme.align.vertical)
    }
    fn depth(mut self, depth: Depth) -> Self {
        self.depth = depth;
        self
    }
    fn get_depth(&self) -> Depth { self.depth }
}

impl<'a> ::position::Sizeable for Floating<'a> {
    #[inline]
    fn width(self, w: f64) -> Self {
        let h = self.init_dim[1];
        Floating { init_dim: [w, h], ..self }
    }
    #[inline]
    fn height(self, h: f64) -> Self {
        let w = self.init_dim[0];
        Floating { init_dim: [w, h], ..self }
    }
    fn get_width<C: CharacterCache>(&self, _theme: &Theme, _: &GlyphCache<C>) -> f64 { self.init_dim[0] }
    fn get_height(&self, _theme: &Theme) -> f64 { self.init_dim[1] }
}

impl<'a> ::label::Labelable<'a> for Floating<'a> {
    fn label(mut self, text: &'a str) -> Self {
        self.maybe_title_bar_label = Some(text);
        self
    }
    fn label_color(mut self, color: Color) -> Self {
        self.style.maybe_title_bar_label_color = Some(color);
        self
    }
    fn label_font_size(mut self, size: FontSize) -> Self {
        self.style.maybe_title_bar_font_size = Some(size);
        self
    }
}

<commit_msg>Removed unnecessary copy of old floating module<commit_after><|endoftext|>


<commit_before>
use serialize::json::{Json, JsonObject, ToJson};

use valico::{
    Builder,
    Coercer
};

use helpers::{
    test_error,
    assert_str_eq,
    assert_result_key,
    assert_error_key
};

#[test]
fn is_process_simple_require() {

    let params = Builder::build(|params| {
        params.req_defined("a");
    });

    assert_str_eq(&params, r#"{"a":1}"#, r#"{"a":1}"#);
    assert_error_key(&params, r#"{}"#, ["a", "validation"]);
}

#[test]
fn is_process_i64_require() {

    let params = Builder::build(|params| {
        params.req_typed("a", Builder::i64());
    });

    assert_str_eq(&params, r#"{"a":"1"}"#, r#"{"a":1}"#);

    // truncate!
    assert_str_eq(&params, r#"{"a": 1.112}"#, r#"{"a":1}"#);

    // error because "a" is string that we can't convert
    assert_error_key(&params, r#"{"a": "not-int"}"#, ["a", "coercion"]);
    // error because "a" is object
    assert_error_key(&params, r#"{"a": {"a": 1}}"#, ["a", "coercion"]);
}

#[test]
fn is_process_string_require() {

    let params = Builder::build(|params| {
        params.req_typed("a", Builder::string());
    });

    assert_str_eq(&params, r#"{"a":"1"}"#, r#"{"a":"1"}"#);
    assert_str_eq(&params, r#"{"a":1}"#, r#"{"a":"1"}"#);
    assert_str_eq(&params, r#"{"a":1.112}"#, r#"{"a":"1.112"}"#);

    // error because "a" is object
    assert_error_key(&params, r#"{"a": {}}"#, ["a", "coercion"]);

    // error because "a" is null
    assert_error_key(&params, r#"{"a": null}"#, ["a", "coercion"]);
}

#[test]
fn is_process_boolean_require() {

    let params = Builder::build(|params| {
        params.req_typed("a", Builder::boolean());
    });

    assert_str_eq(&params, r#"{"a":true}"#, r#"{"a":true}"#);
    assert_str_eq(&params, r#"{"a":false}"#, r#"{"a":false}"#);
    assert_str_eq(&params, r#"{"a":"true"}"#, r#"{"a":true}"#);
    assert_str_eq(&params, r#"{"a":"false"}"#, r#"{"a":false}"#);

    assert_error_key(&params, r#"{"a": null}"#, ["a", "coercion"]);
    assert_error_key(&params, r#"{"a": 1}"#, ["a", "coercion"]);
    assert_error_key(&params, r#"{"a": "not-bool"}"#, ["a", "coercion"]);
}

#[test]
fn is_process_simple_list_require() {

    let params = Builder::build(|params| {
        params.req_typed("a", Builder::list());
    });

    assert_str_eq(&params, r#"{"a":[1,"2",[3]]}"#, r#"{"a":[1,"2",[3]]}"#);

    // error because "a" is object
    assert_error_key(&params, r#"{"a": {}}"#, ["a", "coercion"]);

    // error because "a" is string
    assert_error_key(&params, r#"{"a": "test"}"#, ["a", "coercion"]);
}

#[test]
fn is_process_typed_list_require() {

    let params = Builder::build(|params| {
        params.req_typed("a", Builder::list_of(Builder::string()));
    });

    // convert all to string
    assert_str_eq(&params, r#"{"a":[1,"2",3.1]}"#, r#"{"a":["1","2","3.1"]}"#);

    // error because "a" is object
    assert_error_key(&params, r#"{"a": {}}"#, ["a", "coercion"]);

    // error because element at index(2) is not coersible to string
    assert_error_key(&params, r#"{"a": [1,2,{}]}"#, ["a", "2", "coercion"]);

}

#[test]
fn is_process_list_with_nested_require() {

    let params = Builder::build(|params| {
        params.req_nested("a", Builder::list(), |params| {
            params.req_typed("b", Builder::string());
            params.req_typed("c", Builder::list_of(Builder::u64()))
        });
    });

    assert_str_eq(&params, r#"{"a":[{"b":1,"c":["1"]}]}"#, r#"{"a":[{"b":"1","c":[1]}]}"#);

    // error because element in "a" at index(0) is not coersible to string
    assert_error_key(&params, r#"{"a":[{"b":{},"c":["1"]}]}"#, ["a", "0", "b", "coercion"]);

    // error because element in "a":0:"c":0 is not coersible to string
    assert_error_key(&params, r#"{"a":[{"b":1,"c":[{}]}]}"#, ["a", "0", "c", "0", "coercion"]);

}

#[test]
fn is_process_object_require() {

    let params = Builder::build(|params| {
        params.req_typed("a", Builder::object());
    });

    assert_str_eq(&params, r#"{"a":{}}"#, r#"{"a":{}}"#);

    // error because "a" is array, not object
    assert_error_key(&params, r#"{"a":[]}"#, ["a", "coercion"]);

    // error because "a" is string, not object
    assert_error_key(&params, r#"{"a":""}"#, ["a", "coercion"]);

}

#[test]
fn is_process_object_with_nested_require() {

    let params = Builder::build(|params| {
        params.req_nested("a", Builder::object(), |params| {
            params.req_typed("b", Builder::f64());
            params.req_typed("c", Builder::list_of(Builder::string()));
        });
    });

    assert_str_eq(&params, r#"{"a":{"b":"1.22","c":[1.112,""]}}"#, r#"{"a":{"b":1.22,"c":["1.112",""]}}"#);

    // error because "a":"b" is not a f64
    assert_error_key(&params, r#"{"a":{"b":"not-f64"},"c":[1.112,""]}"#, ["a", "b", "coercion"]);

    // error because "a":"c":"1" is object and can't be coerced to string
    assert_error_key(&params, r#"{"a":{"b":"1.22","c":[1.112,{}]}}"#, ["a", "c", "1", "coercion"]);

}

#[test]
fn is_process_require_allows_null() {

    let params = Builder::build(|params| {
        params.req("a", |a| {
            a.coerce(Builder::string());
        })
    });

    // error because a is not allow null explicitly
    assert_error_key(&params, r#"{"a":null}"#, ["a", "coercion"]);

    let params = Builder::build(|params| {
        params.req("a", |a| {
            a.coerce(Builder::string());
            a.allow_null();
        })
    });

    // ok because "a" allows null explicitly
    assert_str_eq(&params, r#"{"a":null}"#, r#"{"a":null}"#);
}

#[test]
fn is_validate_allow_values() {

    let params = Builder::build(|params| {
        params.req("a", |a| {
            a.coerce(Builder::string());
            a.allow_values(["allowed1".to_string(), "allowed2".to_string()])
        })
    });

    assert_str_eq(&params, r#"{"a":"allowed1"}"#, r#"{"a":"allowed1"}"#);
    assert_str_eq(&params, r#"{"a":"allowed2"}"#, r#"{"a":"allowed2"}"#);

    // error because "a" is not in allowed list
    assert_error_key(&params, r#"{"a":"not in allowed"}"#, ["a", "validation"]);

}

#[test]
fn is_validate_reject_values() {

    let params = Builder::build(|params| {
        params.req("a", |a| {
            a.coerce(Builder::string());
            a.reject_values(["rejected1".to_string(), "rejected2".to_string()])
        })
    });

    assert_str_eq(&params, r#"{"a":"some"}"#, r#"{"a":"some"}"#);

    // errors because "a" is in reject list
    assert_error_key(&params, r#"{"a":"rejected1"}"#, ["a", "validation"]);
    assert_error_key(&params, r#"{"a":"rejected2"}"#, ["a", "validation"]);

}

#[test]
fn is_validate_with_function_validator() {

    let params = Builder::build(|params| {
        params.req("a", |a| {
            a.coerce(Builder::u64());

            fn validate(val: &Json) -> Result<(), String> {
                if *val == 2u.to_json() {
                    Ok(())
                } else {
                    Err("Value is not exactly 2".to_string())
                }
            }

            a.validate_with(validate);
        })
    });

    assert_str_eq(&params, r#"{"a":"2"}"#, r#"{"a":2}"#);
    assert_error_key(&params, r#"{"a":3}"#, ["a", "validation"]);
    assert_error_key(&params, r#"{"a":"3"}"#, ["a", "validation"]);

}

#[test]
fn is_validate_with_regex() {

    let params = Builder::build(|params| {
        params.req("a", |a| {
            a.coerce(Builder::string());
            a.regex(regex!("^test$"));
        })
    });

    assert_str_eq(&params, r#"{"a":"test"}"#, r#"{"a":"test"}"#);

    // error because "a" is not match regex
    assert_error_key(&params, r#"{"a":"2"}"#, ["a", "validation"]);
    assert_error_key(&params, r#"{"a":"test "}"#, ["a", "validation"]);

    let params = Builder::build(|params| {
        params.req("a", |a| {
            // regex can't be applied to list, so it will never be valid
            a.coerce(Builder::list());
            a.regex(regex!("^test$"));
        })
    });

    // "a" is valid list but it can't pass regex validation
    assert_error_key(&params, r#"{"a":[]}"#, ["a", "validation"]);

}

#[test]
fn is_validate_opt() {

    let params = Builder::build(|params| {
        params.req_defined("a");
        params.opt_typed("b", Builder::u64());
    });

    // ok because a is optional
    assert_str_eq(&params, r#"{"a":"test"}"#, r#"{"a":"test"}"#);
    assert_str_eq(&params, r#"{"a":"test","b":"1"}"#, r#"{"a":"test","b":1}"#);

}

#[test]
fn is_validate_opt_with_default() {

    let params = Builder::build(|params| {
        params.opt("a", |a| {
            a.default("default".to_string());
        });
    });

    assert_str_eq(&params, r#"{"a":"test"}"#, r#"{"a":"test"}"#);
    assert_str_eq(&params, r#"{}"#, r#"{"a":"default"}"#);

}

#[test]
fn is_validate_mutually_exclusive() {

    let params = Builder::build(|params| {
        params.opt_defined("a");
        params.opt_defined("b");

        params.mutually_exclusive(["a", "b"])
    });

    assert_str_eq(&params, r#"{"a":1}"#, r#"{"a":1}"#);
    assert_str_eq(&params, r#"{"b":1}"#, r#"{"b":1}"#);
    assert_str_eq(&params, r#"{}"#, r#"{}"#);

    assert_error_key(&params, r#"{"a":1,"b":1}"#, ["$$0", "validation"]);

}

#[test]
fn is_validate_exactly_one_of() {

    let params = Builder::build(|params| {
        params.opt_defined("a");
        params.opt_defined("b");

        params.exactly_one_of(["a", "b"])
    });

    assert_str_eq(&params, r#"{"a":1}"#, r#"{"a":1}"#);
    assert_str_eq(&params, r#"{"b":1}"#, r#"{"b":1}"#);

    assert_error_key(&params, r#"{}"#, ["$$0", "validation"]);
    assert_error_key(&params, r#"{"a":1,"b":1}"#, ["$$0", "validation"]);

}

#[test]
fn is_validate_at_least_one_of() {

    let params = Builder::build(|params| {
        params.opt_defined("a");
        params.opt_defined("b");

        params.at_least_one_of(["a", "b"])
    });

    assert_str_eq(&params, r#"{"a":1}"#, r#"{"a":1}"#);
    assert_str_eq(&params, r#"{"b":1}"#, r#"{"b":1}"#);
    assert_str_eq(&params, r#"{"a":1,"b":1}"#, r#"{"a":1,"b":1}"#);

    assert_error_key(&params, r#"{}"#, ["$$0", "validation"]);

}

#[test]
fn is_validate_with_function() {

    let params = Builder::build(|params| {
        params.opt_defined("a");
        params.opt_defined("b");

        fn validate_params(_: &JsonObject) -> Result<(),String> {
            Err("YOU SHALL NOT PASS".to_string())
        }

        params.validate_with(validate_params);
    });

    assert_error_key(&params, r#"{}"#, ["$$0", "validation"]);

}

<commit_msg>test: add specs to empty builder<commit_after>
use serialize::json::{Json, JsonObject, ToJson};

use valico::{
    Builder,
    Coercer
};

use helpers::{
    test_error,
    assert_str_eq,
    assert_result_key,
    assert_error_key
};

#[test]
fn is_process_empty_builder() {

    let params = Builder::build(|params| { });

    assert_str_eq(&params, r#"{"a":1}"#, r#"{"a":1}"#);
}

#[test]
fn is_process_simple_require() {

    let params = Builder::build(|params| {
        params.req_defined("a");
    });

    assert_str_eq(&params, r#"{"a":1}"#, r#"{"a":1}"#);
    assert_error_key(&params, r#"{}"#, ["a", "validation"]);
}

#[test]
fn is_process_i64_require() {

    let params = Builder::build(|params| {
        params.req_typed("a", Builder::i64());
    });

    assert_str_eq(&params, r#"{"a":"1"}"#, r#"{"a":1}"#);

    // truncate!
    assert_str_eq(&params, r#"{"a": 1.112}"#, r#"{"a":1}"#);

    // error because "a" is string that we can't convert
    assert_error_key(&params, r#"{"a": "not-int"}"#, ["a", "coercion"]);
    // error because "a" is object
    assert_error_key(&params, r#"{"a": {"a": 1}}"#, ["a", "coercion"]);
}

#[test]
fn is_process_string_require() {

    let params = Builder::build(|params| {
        params.req_typed("a", Builder::string());
    });

    assert_str_eq(&params, r#"{"a":"1"}"#, r#"{"a":"1"}"#);
    assert_str_eq(&params, r#"{"a":1}"#, r#"{"a":"1"}"#);
    assert_str_eq(&params, r#"{"a":1.112}"#, r#"{"a":"1.112"}"#);

    // error because "a" is object
    assert_error_key(&params, r#"{"a": {}}"#, ["a", "coercion"]);

    // error because "a" is null
    assert_error_key(&params, r#"{"a": null}"#, ["a", "coercion"]);
}

#[test]
fn is_process_boolean_require() {

    let params = Builder::build(|params| {
        params.req_typed("a", Builder::boolean());
    });

    assert_str_eq(&params, r#"{"a":true}"#, r#"{"a":true}"#);
    assert_str_eq(&params, r#"{"a":false}"#, r#"{"a":false}"#);
    assert_str_eq(&params, r#"{"a":"true"}"#, r#"{"a":true}"#);
    assert_str_eq(&params, r#"{"a":"false"}"#, r#"{"a":false}"#);

    assert_error_key(&params, r#"{"a": null}"#, ["a", "coercion"]);
    assert_error_key(&params, r#"{"a": 1}"#, ["a", "coercion"]);
    assert_error_key(&params, r#"{"a": "not-bool"}"#, ["a", "coercion"]);
}

#[test]
fn is_process_simple_list_require() {

    let params = Builder::build(|params| {
        params.req_typed("a", Builder::list());
    });

    assert_str_eq(&params, r#"{"a":[1,"2",[3]]}"#, r#"{"a":[1,"2",[3]]}"#);

    // error because "a" is object
    assert_error_key(&params, r#"{"a": {}}"#, ["a", "coercion"]);

    // error because "a" is string
    assert_error_key(&params, r#"{"a": "test"}"#, ["a", "coercion"]);
}

#[test]
fn is_process_typed_list_require() {

    let params = Builder::build(|params| {
        params.req_typed("a", Builder::list_of(Builder::string()));
    });

    // convert all to string
    assert_str_eq(&params, r#"{"a":[1,"2",3.1]}"#, r#"{"a":["1","2","3.1"]}"#);

    // error because "a" is object
    assert_error_key(&params, r#"{"a": {}}"#, ["a", "coercion"]);

    // error because element at index(2) is not coersible to string
    assert_error_key(&params, r#"{"a": [1,2,{}]}"#, ["a", "2", "coercion"]);

}

#[test]
fn is_process_list_with_nested_require() {

    let params = Builder::build(|params| {
        params.req_nested("a", Builder::list(), |params| {
            params.req_typed("b", Builder::string());
            params.req_typed("c", Builder::list_of(Builder::u64()))
        });
    });

    assert_str_eq(&params, r#"{"a":[{"b":1,"c":["1"]}]}"#, r#"{"a":[{"b":"1","c":[1]}]}"#);

    // error because element in "a" at index(0) is not coersible to string
    assert_error_key(&params, r#"{"a":[{"b":{},"c":["1"]}]}"#, ["a", "0", "b", "coercion"]);

    // error because element in "a":0:"c":0 is not coersible to string
    assert_error_key(&params, r#"{"a":[{"b":1,"c":[{}]}]}"#, ["a", "0", "c", "0", "coercion"]);

}

#[test]
fn is_process_object_require() {

    let params = Builder::build(|params| {
        params.req_typed("a", Builder::object());
    });

    assert_str_eq(&params, r#"{"a":{}}"#, r#"{"a":{}}"#);

    // error because "a" is array, not object
    assert_error_key(&params, r#"{"a":[]}"#, ["a", "coercion"]);

    // error because "a" is string, not object
    assert_error_key(&params, r#"{"a":""}"#, ["a", "coercion"]);

}

#[test]
fn is_process_object_with_nested_require() {

    let params = Builder::build(|params| {
        params.req_nested("a", Builder::object(), |params| {
            params.req_typed("b", Builder::f64());
            params.req_typed("c", Builder::list_of(Builder::string()));
        });
    });

    assert_str_eq(&params, r#"{"a":{"b":"1.22","c":[1.112,""]}}"#, r#"{"a":{"b":1.22,"c":["1.112",""]}}"#);

    // error because "a":"b" is not a f64
    assert_error_key(&params, r#"{"a":{"b":"not-f64"},"c":[1.112,""]}"#, ["a", "b", "coercion"]);

    // error because "a":"c":"1" is object and can't be coerced to string
    assert_error_key(&params, r#"{"a":{"b":"1.22","c":[1.112,{}]}}"#, ["a", "c", "1", "coercion"]);

}

#[test]
fn is_process_require_allows_null() {

    let params = Builder::build(|params| {
        params.req("a", |a| {
            a.coerce(Builder::string());
        })
    });

    // error because a is not allow null explicitly
    assert_error_key(&params, r#"{"a":null}"#, ["a", "coercion"]);

    let params = Builder::build(|params| {
        params.req("a", |a| {
            a.coerce(Builder::string());
            a.allow_null();
        })
    });

    // ok because "a" allows null explicitly
    assert_str_eq(&params, r#"{"a":null}"#, r#"{"a":null}"#);
}

#[test]
fn is_validate_allow_values() {

    let params = Builder::build(|params| {
        params.req("a", |a| {
            a.coerce(Builder::string());
            a.allow_values(["allowed1".to_string(), "allowed2".to_string()])
        })
    });

    assert_str_eq(&params, r#"{"a":"allowed1"}"#, r#"{"a":"allowed1"}"#);
    assert_str_eq(&params, r#"{"a":"allowed2"}"#, r#"{"a":"allowed2"}"#);

    // error because "a" is not in allowed list
    assert_error_key(&params, r#"{"a":"not in allowed"}"#, ["a", "validation"]);

}

#[test]
fn is_validate_reject_values() {

    let params = Builder::build(|params| {
        params.req("a", |a| {
            a.coerce(Builder::string());
            a.reject_values(["rejected1".to_string(), "rejected2".to_string()])
        })
    });

    assert_str_eq(&params, r#"{"a":"some"}"#, r#"{"a":"some"}"#);

    // errors because "a" is in reject list
    assert_error_key(&params, r#"{"a":"rejected1"}"#, ["a", "validation"]);
    assert_error_key(&params, r#"{"a":"rejected2"}"#, ["a", "validation"]);

}

#[test]
fn is_validate_with_function_validator() {

    let params = Builder::build(|params| {
        params.req("a", |a| {
            a.coerce(Builder::u64());

            fn validate(val: &Json) -> Result<(), String> {
                if *val == 2u.to_json() {
                    Ok(())
                } else {
                    Err("Value is not exactly 2".to_string())
                }
            }

            a.validate_with(validate);
        })
    });

    assert_str_eq(&params, r#"{"a":"2"}"#, r#"{"a":2}"#);
    assert_error_key(&params, r#"{"a":3}"#, ["a", "validation"]);
    assert_error_key(&params, r#"{"a":"3"}"#, ["a", "validation"]);

}

#[test]
fn is_validate_with_regex() {

    let params = Builder::build(|params| {
        params.req("a", |a| {
            a.coerce(Builder::string());
            a.regex(regex!("^test$"));
        })
    });

    assert_str_eq(&params, r#"{"a":"test"}"#, r#"{"a":"test"}"#);

    // error because "a" is not match regex
    assert_error_key(&params, r#"{"a":"2"}"#, ["a", "validation"]);
    assert_error_key(&params, r#"{"a":"test "}"#, ["a", "validation"]);

    let params = Builder::build(|params| {
        params.req("a", |a| {
            // regex can't be applied to list, so it will never be valid
            a.coerce(Builder::list());
            a.regex(regex!("^test$"));
        })
    });

    // "a" is valid list but it can't pass regex validation
    assert_error_key(&params, r#"{"a":[]}"#, ["a", "validation"]);

}

#[test]
fn is_validate_opt() {

    let params = Builder::build(|params| {
        params.req_defined("a");
        params.opt_typed("b", Builder::u64());
    });

    // ok because a is optional
    assert_str_eq(&params, r#"{"a":"test"}"#, r#"{"a":"test"}"#);
    assert_str_eq(&params, r#"{"a":"test","b":"1"}"#, r#"{"a":"test","b":1}"#);

}

#[test]
fn is_validate_opt_with_default() {

    let params = Builder::build(|params| {
        params.opt("a", |a| {
            a.default("default".to_string());
        });
    });

    assert_str_eq(&params, r#"{"a":"test"}"#, r#"{"a":"test"}"#);
    assert_str_eq(&params, r#"{}"#, r#"{"a":"default"}"#);

}

#[test]
fn is_validate_mutually_exclusive() {

    let params = Builder::build(|params| {
        params.opt_defined("a");
        params.opt_defined("b");

        params.mutually_exclusive(["a", "b"])
    });

    assert_str_eq(&params, r#"{"a":1}"#, r#"{"a":1}"#);
    assert_str_eq(&params, r#"{"b":1}"#, r#"{"b":1}"#);
    assert_str_eq(&params, r#"{}"#, r#"{}"#);

    assert_error_key(&params, r#"{"a":1,"b":1}"#, ["$$0", "validation"]);

}

#[test]
fn is_validate_exactly_one_of() {

    let params = Builder::build(|params| {
        params.opt_defined("a");
        params.opt_defined("b");

        params.exactly_one_of(["a", "b"])
    });

    assert_str_eq(&params, r#"{"a":1}"#, r#"{"a":1}"#);
    assert_str_eq(&params, r#"{"b":1}"#, r#"{"b":1}"#);

    assert_error_key(&params, r#"{}"#, ["$$0", "validation"]);
    assert_error_key(&params, r#"{"a":1,"b":1}"#, ["$$0", "validation"]);

}

#[test]
fn is_validate_at_least_one_of() {

    let params = Builder::build(|params| {
        params.opt_defined("a");
        params.opt_defined("b");

        params.at_least_one_of(["a", "b"])
    });

    assert_str_eq(&params, r#"{"a":1}"#, r#"{"a":1}"#);
    assert_str_eq(&params, r#"{"b":1}"#, r#"{"b":1}"#);
    assert_str_eq(&params, r#"{"a":1,"b":1}"#, r#"{"a":1,"b":1}"#);

    assert_error_key(&params, r#"{}"#, ["$$0", "validation"]);

}

#[test]
fn is_validate_with_function() {

    let params = Builder::build(|params| {
        params.opt_defined("a");
        params.opt_defined("b");

        fn validate_params(_: &JsonObject) -> Result<(),String> {
            Err("YOU SHALL NOT PASS".to_string())
        }

        params.validate_with(validate_params);
    });

    assert_error_key(&params, r#"{}"#, ["$$0", "validation"]);

}

<|endoftext|>


<commit_before>#![allow(
    clippy::boxed_local,
    clippy::ptr_arg,
    clippy::trivially_copy_pass_by_ref
)]

use cxx::{CxxString, UniquePtr};
use std::fmt::{self, Display};

#[cxx::bridge(namespace = tests)]
pub mod ffi {
    struct Shared {
        z: usize,
    }

    enum Enum {
        AVal,
        BVal = 2020,
        CVal,
    }

    extern "C" {
        include!("tests/ffi/tests.h");

        type C;

        fn c_return_primitive() -> usize;
        fn c_return_shared() -> Shared;
        fn c_return_box() -> Box<R>;
        fn c_return_unique_ptr() -> UniquePtr<C>;
        fn c_return_ref(shared: &Shared) -> &usize;
        fn c_return_str(shared: &Shared) -> &str;
        fn c_return_sliceu8(shared: &Shared) -> &[u8];
        fn c_return_rust_string() -> String;
        fn c_return_unique_ptr_string() -> UniquePtr<CxxString>;
        fn c_return_unique_ptr_vector_u8() -> UniquePtr<CxxVector<u8>>;
        fn c_return_unique_ptr_vector_f64() -> UniquePtr<CxxVector<f64>>;
        fn c_return_unique_ptr_vector_shared() -> UniquePtr<CxxVector<Shared>>;
        fn c_return_unique_ptr_vector_opaque() -> UniquePtr<CxxVector<C>>;
        fn c_return_ref_vector(c: &C) -> &CxxVector<u8>;
        fn c_return_rust_vec() -> Vec<u8>;
        fn c_return_ref_rust_vec(c: &C) -> &Vec<u8>;
        fn c_return_identity(_: usize) -> usize;
        fn c_return_sum(_: usize, _: usize) -> usize;
        fn c_return_enum(n: u32) -> Enum;

        fn c_take_primitive(n: usize);
        fn c_take_shared(shared: Shared);
        fn c_take_box(r: Box<R>);
        fn c_take_unique_ptr(c: UniquePtr<C>);
        fn c_take_ref_r(r: &R);
        fn c_take_ref_c(c: &C);
        fn c_take_str(s: &str);
        fn c_take_sliceu8(s: &[u8]);
        fn c_take_rust_string(s: String);
        fn c_take_unique_ptr_string(s: UniquePtr<CxxString>);
        fn c_take_unique_ptr_vector_u8(v: UniquePtr<CxxVector<u8>>);
        fn c_take_unique_ptr_vector_f64(v: UniquePtr<CxxVector<f64>>);
        fn c_take_unique_ptr_vector_shared(v: UniquePtr<CxxVector<Shared>>);
        fn c_take_ref_vector(v: &CxxVector<u8>);
        fn c_take_rust_vec(v: Vec<u8>);
        fn c_take_rust_vec_shared(v: Vec<Shared>);
        fn c_take_rust_vec_shared_forward_iterator(v: Vec<Shared>);
        fn c_take_ref_rust_vec(v: &Vec<u8>);
        fn c_take_ref_rust_vec_copy(v: &Vec<u8>);
        fn c_take_callback(callback: fn(String) -> usize);
        fn c_take_enum(e: Enum);

        fn c_try_return_void() -> Result<()>;
        fn c_try_return_primitive() -> Result<usize>;
        fn c_fail_return_primitive() -> Result<usize>;
        fn c_try_return_box() -> Result<Box<R>>;
        fn c_try_return_ref(s: &String) -> Result<&String>;
        fn c_try_return_str(s: &str) -> Result<&str>;
        fn c_try_return_sliceu8(s: &[u8]) -> Result<&[u8]>;
        fn c_try_return_rust_string() -> Result<String>;
        fn c_try_return_unique_ptr_string() -> Result<UniquePtr<CxxString>>;
        fn c_try_return_rust_vec() -> Result<Vec<u8>>;
        fn c_try_return_ref_rust_vec(c: &C) -> Result<&Vec<u8>>;

        fn get(self: &C) -> usize;
        fn set(self: &mut C, n: usize) -> usize;
        fn get2(&self) -> usize;
        fn set2(&mut self, n: usize) -> usize;
    }

    extern "C" {
        type COwnedEnum;
    }

    enum COwnedEnum {
        CVal1,
        CVal2,
    }

    extern "Rust" {
        type R;
        type R2;

        fn r_return_primitive() -> usize;
        fn r_return_shared() -> Shared;
        fn r_return_box() -> Box<R>;
        fn r_return_unique_ptr() -> UniquePtr<C>;
        fn r_return_ref(shared: &Shared) -> &usize;
        fn r_return_str(shared: &Shared) -> &str;
        fn r_return_rust_string() -> String;
        fn r_return_unique_ptr_string() -> UniquePtr<CxxString>;
        fn r_return_rust_vec() -> Vec<u8>;
        fn r_return_ref_rust_vec(shared: &Shared) -> &Vec<u8>;
        fn r_return_identity(_: usize) -> usize;
        fn r_return_sum(_: usize, _: usize) -> usize;
        fn r_return_enum(n: u32) -> Enum;

        fn r_take_primitive(n: usize);
        fn r_take_shared(shared: Shared);
        fn r_take_box(r: Box<R>);
        fn r_take_unique_ptr(c: UniquePtr<C>);
        fn r_take_ref_r(r: &R);
        fn r_take_ref_c(c: &C);
        fn r_take_str(s: &str);
        fn r_take_sliceu8(s: &[u8]);
        fn r_take_rust_string(s: String);
        fn r_take_unique_ptr_string(s: UniquePtr<CxxString>);
        fn r_take_rust_vec(v: Vec<u8>);
        fn r_take_ref_rust_vec(v: &Vec<u8>);
        fn r_take_enum(e: Enum);

        fn r_try_return_void() -> Result<()>;
        fn r_try_return_primitive() -> Result<usize>;
        fn r_fail_return_primitive() -> Result<usize>;

        fn r_return_r2(n: usize) -> Box<R2>;
        fn get(self: &R2) -> usize;
        fn set(self: &mut R2, n: usize) -> usize;
    }
}

pub type R = usize;

pub struct R2(usize);

impl R2 {
    fn get(&self) -> usize {
        self.0
    }

    fn set(&mut self, n: usize) -> usize {
        self.0 = n;
        n
    }
}

#[derive(Debug)]
struct Error;

impl std::error::Error for Error {}

impl Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str("rust error")
    }
}

fn r_return_primitive() -> usize {
    2020
}

fn r_return_shared() -> ffi::Shared {
    ffi::Shared { z: 2020 }
}

fn r_return_box() -> Box<R> {
    Box::new(2020)
}

fn r_return_unique_ptr() -> UniquePtr<ffi::C> {
    extern "C" {
        fn cxx_test_suite_get_unique_ptr() -> *mut ffi::C;
    }
    unsafe { UniquePtr::from_raw(cxx_test_suite_get_unique_ptr()) }
}

fn r_return_ref(shared: &ffi::Shared) -> &usize {
    &shared.z
}

fn r_return_str(shared: &ffi::Shared) -> &str {
    let _ = shared;
    "2020"
}

fn r_return_rust_string() -> String {
    "2020".to_owned()
}

fn r_return_unique_ptr_string() -> UniquePtr<CxxString> {
    extern "C" {
        fn cxx_test_suite_get_unique_ptr_string() -> *mut CxxString;
    }
    unsafe { UniquePtr::from_raw(cxx_test_suite_get_unique_ptr_string()) }
}

fn r_return_rust_vec() -> Vec<u8> {
    Vec::new()
}

fn r_return_ref_rust_vec(shared: &ffi::Shared) -> &Vec<u8> {
    let _ = shared;
    unimplemented!()
}

fn r_return_identity(n: usize) -> usize {
    n
}

fn r_return_sum(n1: usize, n2: usize) -> usize {
    n1 + n2
}

fn r_return_enum(n: u32) -> ffi::Enum {
    if n <= 0 {
        ffi::Enum::AVal
    } else if n <= 2020 {
        ffi::Enum::BVal
    } else {
        ffi::Enum::CVal
    }
}

fn r_take_primitive(n: usize) {
    assert_eq!(n, 2020);
}

fn r_take_shared(shared: ffi::Shared) {
    assert_eq!(shared.z, 2020);
}

fn r_take_box(r: Box<R>) {
    let _ = r;
}

fn r_take_unique_ptr(c: UniquePtr<ffi::C>) {
    let _ = c;
}

fn r_take_ref_r(r: &R) {
    let _ = r;
}

fn r_take_ref_c(c: &ffi::C) {
    let _ = c;
}

fn r_take_str(s: &str) {
    assert_eq!(s, "2020");
}

fn r_take_rust_string(s: String) {
    assert_eq!(s, "2020");
}

fn r_take_sliceu8(s: &[u8]) {
    assert_eq!(s.len(), 5);
    assert_eq!(std::str::from_utf8(s).unwrap(), "2020\0");
}

fn r_take_unique_ptr_string(s: UniquePtr<CxxString>) {
    assert_eq!(s.as_ref().unwrap().to_str().unwrap(), "2020");
}

fn r_take_rust_vec(v: Vec<u8>) {
    let _ = v;
}

fn r_take_ref_rust_vec(v: &Vec<u8>) {
    let _ = v;
}

fn r_take_enum(e: ffi::Enum) {
    let _ = e;
}

fn r_try_return_void() -> Result<(), Error> {
    Ok(())
}

fn r_try_return_primitive() -> Result<usize, Error> {
    Ok(2020)
}

fn r_fail_return_primitive() -> Result<usize, Error> {
    Err(Error)
}

fn r_return_r2(n: usize) -> Box<R2> {
    Box::new(R2(n))
}
<commit_msg>Resolve absurd_extreme_comparisons lint<commit_after>#![allow(
    clippy::boxed_local,
    clippy::ptr_arg,
    clippy::trivially_copy_pass_by_ref
)]

use cxx::{CxxString, UniquePtr};
use std::fmt::{self, Display};

#[cxx::bridge(namespace = tests)]
pub mod ffi {
    struct Shared {
        z: usize,
    }

    enum Enum {
        AVal,
        BVal = 2020,
        CVal,
    }

    extern "C" {
        include!("tests/ffi/tests.h");

        type C;

        fn c_return_primitive() -> usize;
        fn c_return_shared() -> Shared;
        fn c_return_box() -> Box<R>;
        fn c_return_unique_ptr() -> UniquePtr<C>;
        fn c_return_ref(shared: &Shared) -> &usize;
        fn c_return_str(shared: &Shared) -> &str;
        fn c_return_sliceu8(shared: &Shared) -> &[u8];
        fn c_return_rust_string() -> String;
        fn c_return_unique_ptr_string() -> UniquePtr<CxxString>;
        fn c_return_unique_ptr_vector_u8() -> UniquePtr<CxxVector<u8>>;
        fn c_return_unique_ptr_vector_f64() -> UniquePtr<CxxVector<f64>>;
        fn c_return_unique_ptr_vector_shared() -> UniquePtr<CxxVector<Shared>>;
        fn c_return_unique_ptr_vector_opaque() -> UniquePtr<CxxVector<C>>;
        fn c_return_ref_vector(c: &C) -> &CxxVector<u8>;
        fn c_return_rust_vec() -> Vec<u8>;
        fn c_return_ref_rust_vec(c: &C) -> &Vec<u8>;
        fn c_return_identity(_: usize) -> usize;
        fn c_return_sum(_: usize, _: usize) -> usize;
        fn c_return_enum(n: u32) -> Enum;

        fn c_take_primitive(n: usize);
        fn c_take_shared(shared: Shared);
        fn c_take_box(r: Box<R>);
        fn c_take_unique_ptr(c: UniquePtr<C>);
        fn c_take_ref_r(r: &R);
        fn c_take_ref_c(c: &C);
        fn c_take_str(s: &str);
        fn c_take_sliceu8(s: &[u8]);
        fn c_take_rust_string(s: String);
        fn c_take_unique_ptr_string(s: UniquePtr<CxxString>);
        fn c_take_unique_ptr_vector_u8(v: UniquePtr<CxxVector<u8>>);
        fn c_take_unique_ptr_vector_f64(v: UniquePtr<CxxVector<f64>>);
        fn c_take_unique_ptr_vector_shared(v: UniquePtr<CxxVector<Shared>>);
        fn c_take_ref_vector(v: &CxxVector<u8>);
        fn c_take_rust_vec(v: Vec<u8>);
        fn c_take_rust_vec_shared(v: Vec<Shared>);
        fn c_take_rust_vec_shared_forward_iterator(v: Vec<Shared>);
        fn c_take_ref_rust_vec(v: &Vec<u8>);
        fn c_take_ref_rust_vec_copy(v: &Vec<u8>);
        fn c_take_callback(callback: fn(String) -> usize);
        fn c_take_enum(e: Enum);

        fn c_try_return_void() -> Result<()>;
        fn c_try_return_primitive() -> Result<usize>;
        fn c_fail_return_primitive() -> Result<usize>;
        fn c_try_return_box() -> Result<Box<R>>;
        fn c_try_return_ref(s: &String) -> Result<&String>;
        fn c_try_return_str(s: &str) -> Result<&str>;
        fn c_try_return_sliceu8(s: &[u8]) -> Result<&[u8]>;
        fn c_try_return_rust_string() -> Result<String>;
        fn c_try_return_unique_ptr_string() -> Result<UniquePtr<CxxString>>;
        fn c_try_return_rust_vec() -> Result<Vec<u8>>;
        fn c_try_return_ref_rust_vec(c: &C) -> Result<&Vec<u8>>;

        fn get(self: &C) -> usize;
        fn set(self: &mut C, n: usize) -> usize;
        fn get2(&self) -> usize;
        fn set2(&mut self, n: usize) -> usize;
    }

    extern "C" {
        type COwnedEnum;
    }

    enum COwnedEnum {
        CVal1,
        CVal2,
    }

    extern "Rust" {
        type R;
        type R2;

        fn r_return_primitive() -> usize;
        fn r_return_shared() -> Shared;
        fn r_return_box() -> Box<R>;
        fn r_return_unique_ptr() -> UniquePtr<C>;
        fn r_return_ref(shared: &Shared) -> &usize;
        fn r_return_str(shared: &Shared) -> &str;
        fn r_return_rust_string() -> String;
        fn r_return_unique_ptr_string() -> UniquePtr<CxxString>;
        fn r_return_rust_vec() -> Vec<u8>;
        fn r_return_ref_rust_vec(shared: &Shared) -> &Vec<u8>;
        fn r_return_identity(_: usize) -> usize;
        fn r_return_sum(_: usize, _: usize) -> usize;
        fn r_return_enum(n: u32) -> Enum;

        fn r_take_primitive(n: usize);
        fn r_take_shared(shared: Shared);
        fn r_take_box(r: Box<R>);
        fn r_take_unique_ptr(c: UniquePtr<C>);
        fn r_take_ref_r(r: &R);
        fn r_take_ref_c(c: &C);
        fn r_take_str(s: &str);
        fn r_take_sliceu8(s: &[u8]);
        fn r_take_rust_string(s: String);
        fn r_take_unique_ptr_string(s: UniquePtr<CxxString>);
        fn r_take_rust_vec(v: Vec<u8>);
        fn r_take_ref_rust_vec(v: &Vec<u8>);
        fn r_take_enum(e: Enum);

        fn r_try_return_void() -> Result<()>;
        fn r_try_return_primitive() -> Result<usize>;
        fn r_fail_return_primitive() -> Result<usize>;

        fn r_return_r2(n: usize) -> Box<R2>;
        fn get(self: &R2) -> usize;
        fn set(self: &mut R2, n: usize) -> usize;
    }
}

pub type R = usize;

pub struct R2(usize);

impl R2 {
    fn get(&self) -> usize {
        self.0
    }

    fn set(&mut self, n: usize) -> usize {
        self.0 = n;
        n
    }
}

#[derive(Debug)]
struct Error;

impl std::error::Error for Error {}

impl Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str("rust error")
    }
}

fn r_return_primitive() -> usize {
    2020
}

fn r_return_shared() -> ffi::Shared {
    ffi::Shared { z: 2020 }
}

fn r_return_box() -> Box<R> {
    Box::new(2020)
}

fn r_return_unique_ptr() -> UniquePtr<ffi::C> {
    extern "C" {
        fn cxx_test_suite_get_unique_ptr() -> *mut ffi::C;
    }
    unsafe { UniquePtr::from_raw(cxx_test_suite_get_unique_ptr()) }
}

fn r_return_ref(shared: &ffi::Shared) -> &usize {
    &shared.z
}

fn r_return_str(shared: &ffi::Shared) -> &str {
    let _ = shared;
    "2020"
}

fn r_return_rust_string() -> String {
    "2020".to_owned()
}

fn r_return_unique_ptr_string() -> UniquePtr<CxxString> {
    extern "C" {
        fn cxx_test_suite_get_unique_ptr_string() -> *mut CxxString;
    }
    unsafe { UniquePtr::from_raw(cxx_test_suite_get_unique_ptr_string()) }
}

fn r_return_rust_vec() -> Vec<u8> {
    Vec::new()
}

fn r_return_ref_rust_vec(shared: &ffi::Shared) -> &Vec<u8> {
    let _ = shared;
    unimplemented!()
}

fn r_return_identity(n: usize) -> usize {
    n
}

fn r_return_sum(n1: usize, n2: usize) -> usize {
    n1 + n2
}

fn r_return_enum(n: u32) -> ffi::Enum {
    if n == 0 {
        ffi::Enum::AVal
    } else if n <= 2020 {
        ffi::Enum::BVal
    } else {
        ffi::Enum::CVal
    }
}

fn r_take_primitive(n: usize) {
    assert_eq!(n, 2020);
}

fn r_take_shared(shared: ffi::Shared) {
    assert_eq!(shared.z, 2020);
}

fn r_take_box(r: Box<R>) {
    let _ = r;
}

fn r_take_unique_ptr(c: UniquePtr<ffi::C>) {
    let _ = c;
}

fn r_take_ref_r(r: &R) {
    let _ = r;
}

fn r_take_ref_c(c: &ffi::C) {
    let _ = c;
}

fn r_take_str(s: &str) {
    assert_eq!(s, "2020");
}

fn r_take_rust_string(s: String) {
    assert_eq!(s, "2020");
}

fn r_take_sliceu8(s: &[u8]) {
    assert_eq!(s.len(), 5);
    assert_eq!(std::str::from_utf8(s).unwrap(), "2020\0");
}

fn r_take_unique_ptr_string(s: UniquePtr<CxxString>) {
    assert_eq!(s.as_ref().unwrap().to_str().unwrap(), "2020");
}

fn r_take_rust_vec(v: Vec<u8>) {
    let _ = v;
}

fn r_take_ref_rust_vec(v: &Vec<u8>) {
    let _ = v;
}

fn r_take_enum(e: ffi::Enum) {
    let _ = e;
}

fn r_try_return_void() -> Result<(), Error> {
    Ok(())
}

fn r_try_return_primitive() -> Result<usize, Error> {
    Ok(2020)
}

fn r_fail_return_primitive() -> Result<usize, Error> {
    Err(Error)
}

fn r_return_r2(n: usize) -> Box<R2> {
    Box::new(R2(n))
}
<|endoftext|>


<commit_before>use std::path::PathBuf;
use std::path::Path;
use std::borrow::Borrow;
use std::ops::Deref;

use glob::Paths;
use semver::Version;
use std::fmt::{Debug, Formatter};
use std::fmt::Error as FmtError;
use std::result::Result as RResult;

use error::{StoreError, StoreErrorKind};
use store::Result;
use store::Store;

/// The Index into the Store
#[derive(Debug, Clone, PartialEq, Hash, Eq, PartialOrd, Ord)]
pub struct StoreId(PathBuf);

impl Into<PathBuf> for StoreId {

    fn into(self) -> PathBuf {
        self.0
    }

}

impl Deref for StoreId {
    type Target = PathBuf;

    fn deref(&self) -> &PathBuf {
        &self.0
    }

}

impl From<PathBuf> for StoreId {

    fn from(pb: PathBuf) -> StoreId {
        StoreId(pb)
    }

}

impl From<String> for StoreId {

    fn from(string: String) -> StoreId {
        StoreId(string.into())
    }

}

impl AsRef<Path> for StoreId {

    fn as_ref(&self) -> &Path {
        self.0.as_ref()
    }

}

impl Borrow<Path> for StoreId {

    fn borrow(&self) -> &Path {
        self.0.borrow()
    }

}

/// This Trait allows you to convert various representations to a single one
/// suitable for usage in the Store
pub trait IntoStoreId {
    fn into_storeid(self) -> StoreId;
}

impl IntoStoreId for PathBuf {
    fn into_storeid(self) -> StoreId {
        StoreId(self)
    }
}

pub fn build_entry_path(store: &Store, path_elem: &str) -> Result<PathBuf> {
    debug!("Checking path element for version");
    if path_elem.split("~").last().map(|v| Version::parse(v).is_err()).unwrap_or(false) {
        debug!("Version cannot be parsed from {:?}", path_elem);
        debug!("Path does not contain version!");
        return Err(StoreError::new(StoreErrorKind::StorePathLacksVersion, None));
    }
    debug!("Version checking succeeded");

    debug!("Building path from {:?}", path_elem);
    let mut path = store.path().clone();

    if path_elem.chars().next() == Some('/') {
        path.push(&path_elem[1..path_elem.len()]);
    } else {
        path.push(path_elem);
    }

    Ok(path)
}

#[macro_export]
macro_rules! module_entry_path_mod {
    ($name:expr, $version:expr) => (
        #[deny(missing_docs,
                missing_copy_implementations,
                trivial_casts, trivial_numeric_casts,
                unsafe_code,
                unstable_features,
                unused_import_braces, unused_qualifications,
                unused_imports)]
        /// A helper module to create valid module entry paths
        pub mod module_path {
            use semver::Version;
            use std::convert::AsRef;
            use std::path::Path;
            use std::path::PathBuf;

            /// A Struct giving you the ability to choose store entries assigned
            /// to it.
            ///
            /// It is created through a call to `new`.
            pub struct ModuleEntryPath(PathBuf);

            impl ModuleEntryPath {
                /// Path has to be a valid UTF-8 string or this will panic!
                pub fn new<P: AsRef<Path>>(pa: P) -> ModuleEntryPath {
                    let mut path = PathBuf::new();
                    path.push(format!("{}", $name));
                    path.push(pa.as_ref().clone());
                    let version = Version::parse($version).unwrap();
                    let name = pa.as_ref().file_name().unwrap()
                        .to_str().unwrap();
                    path.set_file_name(format!("{}~{}",
                                               name,
                                               version));
                    ModuleEntryPath(path)
                }
            }

            impl $crate::storeid::IntoStoreId for ModuleEntryPath {
                fn into_storeid(self) -> $crate::storeid::StoreId {
                    self.0
                }
            }
        }
    )
}

pub struct StoreIdIterator {
    paths: Paths,
}

impl Debug for StoreIdIterator {

    fn fmt(&self, fmt: &mut Formatter) -> RResult<(), FmtError> {
        write!(fmt, "StoreIdIterator")
    }

}

impl StoreIdIterator {

    pub fn new(paths: Paths) -> StoreIdIterator {
        StoreIdIterator {
            paths: paths,
        }
    }

}

impl Iterator for StoreIdIterator {
    type Item = StoreId;

    fn next(&mut self) -> Option<StoreId> {
        self.paths.next().and_then(|o| o.ok()).map(|p| StoreId::from(p))
    }

}

#[cfg(test)]
mod test {

    use storeid::IntoStoreId;

    module_entry_path_mod!("test", "0.2.0-alpha+leet1337");

    #[test]
    fn correct_path() {
        let p = module_path::ModuleEntryPath::new("test");

        assert_eq!(p.into_storeid().to_str().unwrap(), "test/test~0.2.0-alpha+leet1337");
    }

}
<commit_msg>Fixup ModuleEntryPath macro type<commit_after>use std::path::PathBuf;
use std::path::Path;
use std::borrow::Borrow;
use std::ops::Deref;

use glob::Paths;
use semver::Version;
use std::fmt::{Debug, Formatter};
use std::fmt::Error as FmtError;
use std::result::Result as RResult;

use error::{StoreError, StoreErrorKind};
use store::Result;
use store::Store;

/// The Index into the Store
#[derive(Debug, Clone, PartialEq, Hash, Eq, PartialOrd, Ord)]
pub struct StoreId(PathBuf);

impl Into<PathBuf> for StoreId {

    fn into(self) -> PathBuf {
        self.0
    }

}

impl Deref for StoreId {
    type Target = PathBuf;

    fn deref(&self) -> &PathBuf {
        &self.0
    }

}

impl From<PathBuf> for StoreId {

    fn from(pb: PathBuf) -> StoreId {
        StoreId(pb)
    }

}

impl From<String> for StoreId {

    fn from(string: String) -> StoreId {
        StoreId(string.into())
    }

}

impl AsRef<Path> for StoreId {

    fn as_ref(&self) -> &Path {
        self.0.as_ref()
    }

}

impl Borrow<Path> for StoreId {

    fn borrow(&self) -> &Path {
        self.0.borrow()
    }

}

/// This Trait allows you to convert various representations to a single one
/// suitable for usage in the Store
pub trait IntoStoreId {
    fn into_storeid(self) -> StoreId;
}

impl IntoStoreId for PathBuf {
    fn into_storeid(self) -> StoreId {
        StoreId(self)
    }
}

pub fn build_entry_path(store: &Store, path_elem: &str) -> Result<PathBuf> {
    debug!("Checking path element for version");
    if path_elem.split("~").last().map(|v| Version::parse(v).is_err()).unwrap_or(false) {
        debug!("Version cannot be parsed from {:?}", path_elem);
        debug!("Path does not contain version!");
        return Err(StoreError::new(StoreErrorKind::StorePathLacksVersion, None));
    }
    debug!("Version checking succeeded");

    debug!("Building path from {:?}", path_elem);
    let mut path = store.path().clone();

    if path_elem.chars().next() == Some('/') {
        path.push(&path_elem[1..path_elem.len()]);
    } else {
        path.push(path_elem);
    }

    Ok(path)
}

#[macro_export]
macro_rules! module_entry_path_mod {
    ($name:expr, $version:expr) => (
        #[deny(missing_docs,
                missing_copy_implementations,
                trivial_casts, trivial_numeric_casts,
                unsafe_code,
                unstable_features,
                unused_import_braces, unused_qualifications,
                unused_imports)]
        /// A helper module to create valid module entry paths
        pub mod module_path {
            use semver::Version;
            use std::convert::AsRef;
            use std::path::Path;
            use std::path::PathBuf;

            use $crate::storeid::StoreId;

            /// A Struct giving you the ability to choose store entries assigned
            /// to it.
            ///
            /// It is created through a call to `new`.
            pub struct ModuleEntryPath(PathBuf);

            impl ModuleEntryPath {
                /// Path has to be a valid UTF-8 string or this will panic!
                pub fn new<P: AsRef<Path>>(pa: P) -> ModuleEntryPath {
                    let mut path = PathBuf::new();
                    path.push(format!("{}", $name));
                    path.push(pa.as_ref().clone());
                    let version = Version::parse($version).unwrap();
                    let name = pa.as_ref().file_name().unwrap()
                        .to_str().unwrap();
                    path.set_file_name(format!("{}~{}",
                                               name,
                                               version));
                    ModuleEntryPath(path)
                }
            }

            impl $crate::storeid::IntoStoreId for ModuleEntryPath {
                fn into_storeid(self) -> $crate::storeid::StoreId {
                    StoreId::from(self.0)
                }
            }
        }
    )
}

pub struct StoreIdIterator {
    paths: Paths,
}

impl Debug for StoreIdIterator {

    fn fmt(&self, fmt: &mut Formatter) -> RResult<(), FmtError> {
        write!(fmt, "StoreIdIterator")
    }

}

impl StoreIdIterator {

    pub fn new(paths: Paths) -> StoreIdIterator {
        StoreIdIterator {
            paths: paths,
        }
    }

}

impl Iterator for StoreIdIterator {
    type Item = StoreId;

    fn next(&mut self) -> Option<StoreId> {
        self.paths.next().and_then(|o| o.ok()).map(|p| StoreId::from(p))
    }

}

#[cfg(test)]
mod test {

    use storeid::IntoStoreId;

    module_entry_path_mod!("test", "0.2.0-alpha+leet1337");

    #[test]
    fn correct_path() {
        let p = module_path::ModuleEntryPath::new("test");

        assert_eq!(p.into_storeid().to_str().unwrap(), "test/test~0.2.0-alpha+leet1337");
    }

}
<|endoftext|>


<commit_before>macro_rules! noparse(
    ($name:ident, $re:expr) => (
        #[test]
        fn $name() {
            let re = $re;
            match regex_new!(re) {
                Err(_) => {},
                Ok(_) => panic!("Regex '{}' should cause a parse error.", re),
            }
        }
    );
);

noparse!(fail_no_repeat_arg, "*");
noparse!(fail_incomplete_escape, "\\");
noparse!(fail_class_incomplete, "[A-");
noparse!(fail_class_not_closed, "[A");
noparse!(fail_class_no_begin, r"[\A]");
noparse!(fail_class_no_end, r"[\z]");
noparse!(fail_class_no_boundary, r"[\b]");
noparse!(fail_open_paren, "(");
noparse!(fail_close_paren, ")");
noparse!(fail_invalid_range, "[a-Z]");
noparse!(fail_empty_capture_name, "(?P<>a)");
noparse!(fail_bad_capture_name, "(?P<na-me>)");
noparse!(fail_bad_flag, "(?a)a");
noparse!(fail_too_big, "a{10000000}");
noparse!(fail_counted_no_close, "a{1001");
noparse!(fail_unfinished_cap, "(?");
noparse!(fail_unfinished_escape, "\\");
noparse!(fail_octal_digit, r"\8");
noparse!(fail_hex_digit, r"\xG0");
noparse!(fail_hex_short, r"\xF");
noparse!(fail_hex_long_digits, r"\x{fffg}");
noparse!(fail_flag_bad, "(?a)");
noparse!(fail_flag_empty, "(?)");
noparse!(fail_double_neg, "(?-i-i)");
noparse!(fail_neg_empty, "(?i-)");
noparse!(fail_dupe_named, "(?P<a>.)(?P<a>.)");
noparse!(fail_range_end_no_class, "[a-[:lower:]]");
noparse!(fail_range_end_no_begin, r"[a-\A]");
noparse!(fail_range_end_no_end, r"[a-\z]");
noparse!(fail_range_end_no_boundary, r"[a-\b]");
noparse!(fail_empty_alt1, r"|z");
noparse!(fail_empty_alt2, r"z|");
noparse!(fail_empty_alt3, r"|");
noparse!(fail_empty_alt4, r"||");
noparse!(fail_empty_alt5, r"()|z");
noparse!(fail_empty_alt6, r"z|()");
noparse!(fail_empty_alt7, r"(|)");
<commit_msg>tests: confirm that some counted repetitions are rejected<commit_after>macro_rules! noparse(
    ($name:ident, $re:expr) => (
        #[test]
        fn $name() {
            let re = $re;
            match regex_new!(re) {
                Err(_) => {},
                Ok(_) => panic!("Regex '{}' should cause a parse error.", re),
            }
        }
    );
);

noparse!(fail_no_repeat_arg, "*");
noparse!(fail_incomplete_escape, "\\");
noparse!(fail_class_incomplete, "[A-");
noparse!(fail_class_not_closed, "[A");
noparse!(fail_class_no_begin, r"[\A]");
noparse!(fail_class_no_end, r"[\z]");
noparse!(fail_class_no_boundary, r"[\b]");
noparse!(fail_open_paren, "(");
noparse!(fail_close_paren, ")");
noparse!(fail_invalid_range, "[a-Z]");
noparse!(fail_empty_capture_name, "(?P<>a)");
noparse!(fail_bad_capture_name, "(?P<na-me>)");
noparse!(fail_bad_flag, "(?a)a");
noparse!(fail_too_big, "a{10000000}");
noparse!(fail_counted_no_close, "a{1001");
noparse!(fail_counted_decreasing, "a{2,1}");
noparse!(fail_counted_nonnegative, "a{-1,1}");
noparse!(fail_unfinished_cap, "(?");
noparse!(fail_unfinished_escape, "\\");
noparse!(fail_octal_digit, r"\8");
noparse!(fail_hex_digit, r"\xG0");
noparse!(fail_hex_short, r"\xF");
noparse!(fail_hex_long_digits, r"\x{fffg}");
noparse!(fail_flag_bad, "(?a)");
noparse!(fail_flag_empty, "(?)");
noparse!(fail_double_neg, "(?-i-i)");
noparse!(fail_neg_empty, "(?i-)");
noparse!(fail_dupe_named, "(?P<a>.)(?P<a>.)");
noparse!(fail_range_end_no_class, "[a-[:lower:]]");
noparse!(fail_range_end_no_begin, r"[a-\A]");
noparse!(fail_range_end_no_end, r"[a-\z]");
noparse!(fail_range_end_no_boundary, r"[a-\b]");
noparse!(fail_empty_alt1, r"|z");
noparse!(fail_empty_alt2, r"z|");
noparse!(fail_empty_alt3, r"|");
noparse!(fail_empty_alt4, r"||");
noparse!(fail_empty_alt5, r"()|z");
noparse!(fail_empty_alt6, r"z|()");
noparse!(fail_empty_alt7, r"(|)");
<|endoftext|>


<commit_before>#![allow(dead_code, unused_variables)]
use alloc::heap::{Heap, Alloc, Layout};
use bytes::BytesMut;

use std::rc::Rc;

use audiosample::*;
use pixel::*;
use timeinfo::*;

error_chain! {
    errors {
        InvalidIndex
    }
}

#[derive(Clone, Debug, PartialEq)]
pub struct VideoInfo {
    pub width: usize,
    pub height: usize,
    pub format: Rc<Formaton>,
}

impl VideoInfo {
    fn size(&self, align: usize) -> usize {
        let mut size = 0;
        for &component in self.format.into_iter() {
            if let Some(c) = component {
                size += c.get_data_size(self.width, self.height, align);
            }
        }
        size
    }
}

#[derive(Clone, Debug, PartialEq)]
pub struct AudioInfo {
    pub samples: usize,
    pub rate: usize,
    pub map: ChannelMap,
    pub format: Rc<Soniton>,
}

impl AudioInfo {
    fn size(&self, align: usize) -> usize {
        unimplemented!()
    }
}

#[derive(Clone, Debug, PartialEq)]
pub enum MediaKind {
    Video(VideoInfo),
    Audio(AudioInfo),
}

use self::MediaKind::*;

pub trait FrameBuffer {
    fn as_slice<'a>(&'a self, idx: usize) -> Result<&'a [u8]>;
    fn as_mut_slice<'a>(&'a mut self, idx: usize) -> Result<&'a mut [u8]>;
    fn linesize(&self, idx: usize) -> Result<usize>;
    fn count(&self) -> usize;
}

pub struct Frame {
    pub kind: MediaKind,
    pub buf: Box<FrameBuffer>,
    pub t: Option<TimeInfo>,
}

const ALIGNMENT: usize = 32;

struct Plane {
    buf: BytesMut,
    linesize: usize,
}

struct DefaultFrameBuffer {
    buf: BytesMut,
    planes: Vec<Plane>,
}

impl FrameBuffer for DefaultFrameBuffer {
    fn as_slice<'a>(&'a self, idx: usize) -> Result<&'a [u8]> {
        match self.planes.get(idx) {
            None => Err(Error::from_kind(ErrorKind::InvalidIndex)),
            Some(plane) => Ok(&plane.buf),
        }
    }
    fn as_mut_slice<'a>(&'a mut self, idx: usize) -> Result<&'a mut [u8]> {
        match self.planes.get_mut(idx) {
            None => Err(Error::from_kind(ErrorKind::InvalidIndex)),
            Some(plane) => Ok(&mut plane.buf),
        }
    }
    fn linesize(&self, idx: usize) -> Result<usize> {
        match self.planes.get(idx) {
            None => Err(Error::from_kind(ErrorKind::InvalidIndex)),
            Some(plane) => Ok(plane.linesize),
        }
    }
    fn count(&self) -> usize {
        self.planes.len()
    }
}

impl DefaultFrameBuffer {
    pub fn new(kind: &MediaKind) -> DefaultFrameBuffer {
        match kind {
            &Video(ref video) => {
                let size = video.size(ALIGNMENT);
                let data = unsafe {
                    Heap.alloc(Layout::from_size_align(size, ALIGNMENT).unwrap())
                        .unwrap()
                };
                //let data = unsafe { Heap.alloc_zeroed(Layout::from_size_align(size, ALIGNMENT)) };
                let buf = BytesMut::from(unsafe { Vec::from_raw_parts(data, size, size) });
                let mut buffer = DefaultFrameBuffer {
                    buf: buf,
                    planes: Vec::new(),
                };
                for &component in video.format.iter() {
                    if let Some(c) = component {
                        let planesize = c.get_data_size(video.width, video.height, ALIGNMENT);
                        let linesize = c.get_linesize(video.width, ALIGNMENT);
                        buffer.planes.push(Plane {
                            buf: buffer.buf.split_to(planesize),
                            linesize: linesize,
                        });
                    }
                }
                buffer
            }
            _ => unimplemented!(),
        }
    }
}

pub fn new_default_frame(kind: &MediaKind, t: Option<TimeInfo>) -> Frame {
    let buf = DefaultFrameBuffer::new(kind);

    Frame {
        kind: kind.clone(),
        buf: box buf,
        t: t,
    }
}

use std::ptr::copy_nonoverlapping;

fn copy_plane(
    dst: &mut [u8],
    dst_linesize: usize,
    src: &[u8],
    src_linesize: usize,
    w: usize,
    h: usize,
) {
    let dst_chunks = dst.chunks_mut(dst_linesize);
    let src_chunks = src.chunks(src_linesize);

    for (d, s) in dst_chunks.zip(src_chunks).take(h) {
        unsafe {
            copy_nonoverlapping(s.as_ptr(), d.as_mut_ptr(), w);
        }
    }
}

fn copy_image<'a, IM, IU, I>(
    dst: IM,
    dst_linesizes: IU,
    src: I,
    src_linesizes: IU,
    w: usize,
    h: usize,
    fmt: &Formaton,
) where
    IM: Iterator<Item = &'a mut [u8]>,
    I: Iterator<Item = &'a [u8]>,
    IU: Iterator<Item = usize>,
{
    let dst_iter = dst.zip(dst_linesizes);
    let src_iter = src.zip(src_linesizes);
    let iter = dst_iter.zip(src_iter).zip(fmt.iter());

    for (((d, d_linesize), (s, s_linesize)), c) in iter {
        copy_plane(
            d,
            d_linesize,
            s,
            s_linesize,
            c.unwrap().get_width(w),
            c.unwrap().get_height(h),
        );
    }
}

// TODO: Add proper tests
fn copy_to_frame<'a, I, IU>(dst: &mut Frame, mut src: I, mut src_linesize: IU, w: usize, h: usize)
where
    I: Iterator<Item = &'a [u8]>,
    IU: Iterator<Item = usize>,
{
    if let MediaKind::Video(ref fmt) = dst.kind {
        let mut f_iter = fmt.format.iter();
        let w = fmt.width;
        let h = fmt.height;
        for i in 0..dst.buf.count() {
            let d_linesize = dst.buf.linesize(i).unwrap();
            let s_linesize = src_linesize.next().unwrap();
            let d = dst.buf.as_mut_slice(i).unwrap();
            let s = src.next().unwrap();
            let c = f_iter.next().unwrap();
            copy_plane(
                d,
                d_linesize,
                s,
                s_linesize,
                c.unwrap().get_width(w),
                c.unwrap().get_height(h),
            );
        }
    } else {
        unimplemented!();
    }
}

use std::mem;
use std::slice;

// TODO make it a separate trait
impl Frame {
    fn copy_from_slice<'a, I, IU>(&mut self, mut src: I, mut src_linesize: IU)
    where
        I: Iterator<Item = &'a [u8]>,
        IU: Iterator<Item = usize>,
    {
        if let MediaKind::Video(ref fmt) = self.kind {
            let mut f_iter = fmt.format.iter();
            let w = fmt.width;
            let h = fmt.height;
            for i in 0..self.buf.count() {
                let d_linesize = self.buf.linesize(i).unwrap();
                let s_linesize = src_linesize.next().unwrap();
                let d = self.buf.as_mut_slice(i).unwrap();
                let s = src.next().unwrap();
                let c = f_iter.next().unwrap();
                copy_plane(
                    d,
                    d_linesize,
                    s,
                    s_linesize,
                    c.unwrap().get_width(w),
                    c.unwrap().get_height(h),
                );
            }
        } else {
            unimplemented!();
        }
    }

    fn copy_from_raw_parts<'a, I, IU>(&mut self, mut src: I, mut src_linesize: IU)
    where
        I: Iterator<Item = *const u8>,
        IU: Iterator<Item = usize>,
    {
        if let MediaKind::Video(ref fmt) = self.kind {
            let mut f_iter = fmt.format.iter();
            let w = fmt.width;
            let h = fmt.height;
            for i in 0..self.buf.count() {
                let d_linesize = self.buf.linesize(i).unwrap();
                let s_linesize = src_linesize.next().unwrap();
                let d = self.buf.as_mut_slice(i).unwrap();
                let c = f_iter.next().unwrap();
                let r = src.next().unwrap();
                let hb = c.unwrap().get_height(h);
                let s = unsafe { slice::from_raw_parts(r, hb * s_linesize) };
                copy_plane(d, d_linesize, s, s_linesize, c.unwrap().get_width(w), hb);
                mem::forget(s);
            }
        } else {
            unimplemented!();
        }
    }
}
<commit_msg>frame: Make the copy functions public<commit_after>#![allow(dead_code, unused_variables)]
use alloc::heap::{Heap, Alloc, Layout};
use bytes::BytesMut;

use std::rc::Rc;

use audiosample::*;
use pixel::*;
use timeinfo::*;

error_chain! {
    errors {
        InvalidIndex
    }
}

#[derive(Clone, Debug, PartialEq)]
pub struct VideoInfo {
    pub width: usize,
    pub height: usize,
    pub format: Rc<Formaton>,
}

impl VideoInfo {
    fn size(&self, align: usize) -> usize {
        let mut size = 0;
        for &component in self.format.into_iter() {
            if let Some(c) = component {
                size += c.get_data_size(self.width, self.height, align);
            }
        }
        size
    }
}

#[derive(Clone, Debug, PartialEq)]
pub struct AudioInfo {
    pub samples: usize,
    pub rate: usize,
    pub map: ChannelMap,
    pub format: Rc<Soniton>,
}

impl AudioInfo {
    fn size(&self, align: usize) -> usize {
        unimplemented!()
    }
}

#[derive(Clone, Debug, PartialEq)]
pub enum MediaKind {
    Video(VideoInfo),
    Audio(AudioInfo),
}

use self::MediaKind::*;

pub trait FrameBuffer {
    fn as_slice<'a>(&'a self, idx: usize) -> Result<&'a [u8]>;
    fn as_mut_slice<'a>(&'a mut self, idx: usize) -> Result<&'a mut [u8]>;
    fn linesize(&self, idx: usize) -> Result<usize>;
    fn count(&self) -> usize;
}

pub struct Frame {
    pub kind: MediaKind,
    pub buf: Box<FrameBuffer>,
    pub t: Option<TimeInfo>,
}

const ALIGNMENT: usize = 32;

struct Plane {
    buf: BytesMut,
    linesize: usize,
}

struct DefaultFrameBuffer {
    buf: BytesMut,
    planes: Vec<Plane>,
}

impl FrameBuffer for DefaultFrameBuffer {
    fn as_slice<'a>(&'a self, idx: usize) -> Result<&'a [u8]> {
        match self.planes.get(idx) {
            None => Err(Error::from_kind(ErrorKind::InvalidIndex)),
            Some(plane) => Ok(&plane.buf),
        }
    }
    fn as_mut_slice<'a>(&'a mut self, idx: usize) -> Result<&'a mut [u8]> {
        match self.planes.get_mut(idx) {
            None => Err(Error::from_kind(ErrorKind::InvalidIndex)),
            Some(plane) => Ok(&mut plane.buf),
        }
    }
    fn linesize(&self, idx: usize) -> Result<usize> {
        match self.planes.get(idx) {
            None => Err(Error::from_kind(ErrorKind::InvalidIndex)),
            Some(plane) => Ok(plane.linesize),
        }
    }
    fn count(&self) -> usize {
        self.planes.len()
    }
}

impl DefaultFrameBuffer {
    pub fn new(kind: &MediaKind) -> DefaultFrameBuffer {
        match kind {
            &Video(ref video) => {
                let size = video.size(ALIGNMENT);
                let data = unsafe {
                    Heap.alloc(Layout::from_size_align(size, ALIGNMENT).unwrap())
                        .unwrap()
                };
                //let data = unsafe { Heap.alloc_zeroed(Layout::from_size_align(size, ALIGNMENT)) };
                let buf = BytesMut::from(unsafe { Vec::from_raw_parts(data, size, size) });
                let mut buffer = DefaultFrameBuffer {
                    buf: buf,
                    planes: Vec::new(),
                };
                for &component in video.format.iter() {
                    if let Some(c) = component {
                        let planesize = c.get_data_size(video.width, video.height, ALIGNMENT);
                        let linesize = c.get_linesize(video.width, ALIGNMENT);
                        buffer.planes.push(Plane {
                            buf: buffer.buf.split_to(planesize),
                            linesize: linesize,
                        });
                    }
                }
                buffer
            }
            _ => unimplemented!(),
        }
    }
}

pub fn new_default_frame(kind: &MediaKind, t: Option<TimeInfo>) -> Frame {
    let buf = DefaultFrameBuffer::new(kind);

    Frame {
        kind: kind.clone(),
        buf: box buf,
        t: t,
    }
}

use std::ptr::copy_nonoverlapping;

fn copy_plane(
    dst: &mut [u8],
    dst_linesize: usize,
    src: &[u8],
    src_linesize: usize,
    w: usize,
    h: usize,
) {
    let dst_chunks = dst.chunks_mut(dst_linesize);
    let src_chunks = src.chunks(src_linesize);

    for (d, s) in dst_chunks.zip(src_chunks).take(h) {
        unsafe {
            copy_nonoverlapping(s.as_ptr(), d.as_mut_ptr(), w);
        }
    }
}

fn copy_image<'a, IM, IU, I>(
    dst: IM,
    dst_linesizes: IU,
    src: I,
    src_linesizes: IU,
    w: usize,
    h: usize,
    fmt: &Formaton,
) where
    IM: Iterator<Item = &'a mut [u8]>,
    I: Iterator<Item = &'a [u8]>,
    IU: Iterator<Item = usize>,
{
    let dst_iter = dst.zip(dst_linesizes);
    let src_iter = src.zip(src_linesizes);
    let iter = dst_iter.zip(src_iter).zip(fmt.iter());

    for (((d, d_linesize), (s, s_linesize)), c) in iter {
        copy_plane(
            d,
            d_linesize,
            s,
            s_linesize,
            c.unwrap().get_width(w),
            c.unwrap().get_height(h),
        );
    }
}

// TODO: Add proper tests
fn copy_to_frame<'a, I, IU>(dst: &mut Frame, mut src: I, mut src_linesize: IU, w: usize, h: usize)
where
    I: Iterator<Item = &'a [u8]>,
    IU: Iterator<Item = usize>,
{
    if let MediaKind::Video(ref fmt) = dst.kind {
        let mut f_iter = fmt.format.iter();
        let w = fmt.width;
        let h = fmt.height;
        for i in 0..dst.buf.count() {
            let d_linesize = dst.buf.linesize(i).unwrap();
            let s_linesize = src_linesize.next().unwrap();
            let d = dst.buf.as_mut_slice(i).unwrap();
            let s = src.next().unwrap();
            let c = f_iter.next().unwrap();
            copy_plane(
                d,
                d_linesize,
                s,
                s_linesize,
                c.unwrap().get_width(w),
                c.unwrap().get_height(h),
            );
        }
    } else {
        unimplemented!();
    }
}

use std::mem;
use std::slice;

// TODO make it a separate trait
impl Frame {
    pub fn copy_from_slice<'a, I, IU>(&mut self, mut src: I, mut src_linesize: IU)
    where
        I: Iterator<Item = &'a [u8]>,
        IU: Iterator<Item = usize>,
    {
        if let MediaKind::Video(ref fmt) = self.kind {
            let mut f_iter = fmt.format.iter();
            let w = fmt.width;
            let h = fmt.height;
            for i in 0..self.buf.count() {
                let d_linesize = self.buf.linesize(i).unwrap();
                let s_linesize = src_linesize.next().unwrap();
                let d = self.buf.as_mut_slice(i).unwrap();
                let s = src.next().unwrap();
                let c = f_iter.next().unwrap();
                copy_plane(
                    d,
                    d_linesize,
                    s,
                    s_linesize,
                    c.unwrap().get_width(w),
                    c.unwrap().get_height(h),
                );
            }
        } else {
            unimplemented!();
        }
    }

    pub fn copy_from_raw_parts<'a, I, IU>(&mut self, mut src: I, mut src_linesize: IU)
    where
        I: Iterator<Item = *const u8>,
        IU: Iterator<Item = usize>,
    {
        if let MediaKind::Video(ref fmt) = self.kind {
            let mut f_iter = fmt.format.iter();
            let w = fmt.width;
            let h = fmt.height;
            for i in 0..self.buf.count() {
                let d_linesize = self.buf.linesize(i).unwrap();
                let s_linesize = src_linesize.next().unwrap();
                let d = self.buf.as_mut_slice(i).unwrap();
                let c = f_iter.next().unwrap();
                let r = src.next().unwrap();
                let hb = c.unwrap().get_height(h);
                let s = unsafe { slice::from_raw_parts(r, hb * s_linesize) };
                copy_plane(d, d_linesize, s, s_linesize, c.unwrap().get_width(w), hb);
                mem::forget(s);
            }
        } else {
            unimplemented!();
        }
    }
}
<|endoftext|>