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

<%namespace name="helpers" file="/helpers.mako.rs" />
<% from data import to_rust_ident, ALL_SIDES, PHYSICAL_SIDES, maybe_moz_logical_alias %>

${helpers.four_sides_shorthand("border-color", "border-%s-color", "specified::CSSColor::parse",
                               spec="https://drafts.csswg.org/css-backgrounds/#border-color")}

${helpers.four_sides_shorthand("border-style", "border-%s-style",
                               "specified::BorderStyle::parse",
                               spec="https://drafts.csswg.org/css-backgrounds/#border-style")}

<%helpers:shorthand name="border-width" sub_properties="${
        ' '.join('border-%s-width' % side
                 for side in PHYSICAL_SIDES)}"
    spec="https://drafts.csswg.org/css-backgrounds/#border-width">
    use super::parse_four_sides;
    use values::specified::{AllowQuirks, BorderWidth};

    pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
        let (top, right, bottom, left) = try!(parse_four_sides(input, |i| {
            BorderWidth::parse_quirky(context, i, AllowQuirks::Yes)
        }));
        Ok(Longhands {
            % for side in PHYSICAL_SIDES:
                ${to_rust_ident('border-%s-width' % side)}: ${side},
            % endfor
        })
    }

    impl<'a> ToCss for LonghandsToSerialize<'a>  {
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            % for side in PHYSICAL_SIDES:
                let ${side} = self.border_${side}_width.clone();
            % endfor

            super::serialize_four_sides(dest, &top, &right, &bottom, &left)
        }
    }
</%helpers:shorthand>

pub fn parse_border(context: &ParserContext, input: &mut Parser)
                 -> Result<(specified::CSSColor,
                            specified::BorderStyle,
                            specified::BorderWidth), ()> {
    use values::specified::{CSSColor, BorderStyle, BorderWidth};
    let _unused = context;
    let mut color = None;
    let mut style = None;
    let mut width = None;
    let mut any = false;
    loop {
        if color.is_none() {
            if let Ok(value) = input.try(|i| CSSColor::parse(context, i)) {
                color = Some(value);
                any = true;
                continue
            }
        }
        if style.is_none() {
            if let Ok(value) = input.try(|i| BorderStyle::parse(context, i)) {
                style = Some(value);
                any = true;
                continue
            }
        }
        if width.is_none() {
            if let Ok(value) = input.try(|i| BorderWidth::parse(context, i)) {
                width = Some(value);
                any = true;
                continue
            }
        }
        break
    }
    if any {
        Ok((color.unwrap_or_else(|| CSSColor::currentcolor()),
            style.unwrap_or(BorderStyle::none),
            width.unwrap_or(BorderWidth::Medium)))
    } else {
        Err(())
    }
}

% for side, logical in ALL_SIDES:
    <%
        spec = "https://drafts.csswg.org/css-backgrounds/#border-%s" % side
        if logical:
            spec = "https://drafts.csswg.org/css-logical-props/#propdef-border-%s" % side
    %>
    <%helpers:shorthand
        name="border-${side}"
        sub_properties="${' '.join(
            'border-%s-%s' % (side, prop)
            for prop in ['color', 'style', 'width']
        )}"
        alias="${maybe_moz_logical_alias(product, (side, logical), '-moz-border-%s')}"
        spec="${spec}">

    pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
        let (color, style, width) = try!(super::parse_border(context, input));
        Ok(Longhands {
            border_${to_rust_ident(side)}_color: color,
            border_${to_rust_ident(side)}_style: style,
            border_${to_rust_ident(side)}_width: width
        })
    }

    impl<'a> ToCss for LonghandsToSerialize<'a>  {
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            super::serialize_directional_border(
                dest,
                self.border_${to_rust_ident(side)}_width,
                self.border_${to_rust_ident(side)}_style,
                self.border_${to_rust_ident(side)}_color
            )
        }
    }

    </%helpers:shorthand>
% endfor

<%helpers:shorthand name="border"
    sub_properties="${' '.join('border-%s-%s' % (side, prop)
        for side in PHYSICAL_SIDES
        for prop in ['color', 'style', 'width'])}
        ${' '.join('border-image-%s' % name
        for name in ['outset', 'repeat', 'slice', 'source', 'width'])}
        ${' '.join('-moz-border-%s-colors' % side
        for side in PHYSICAL_SIDES) if product == 'gecko' else ''}"
    spec="https://drafts.csswg.org/css-backgrounds/#border">

    % if product == "gecko":
        use properties::longhands::{_moz_border_top_colors, _moz_border_right_colors,
                                    _moz_border_bottom_colors, _moz_border_left_colors};
    % endif

    pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
        use properties::longhands::{border_image_outset, border_image_repeat, border_image_slice};
        use properties::longhands::{border_image_source, border_image_width};

        let (color, style, width) = try!(super::parse_border(context, input));
        Ok(Longhands {
            % for side in PHYSICAL_SIDES:
                border_${side}_color: color.clone(),
                border_${side}_style: style,
                border_${side}_width: width.clone(),
                % if product == "gecko":
                    _moz_border_${side}_colors: _moz_border_${side}_colors::get_initial_specified_value(),
                % endif
            % endfor

            // The ‘border’ shorthand resets ‘border-image’ to its initial value.
            // See https://drafts.csswg.org/css-backgrounds-3/#the-border-shorthands
            % for name in "outset repeat slice source width".split():
                border_image_${name}: border_image_${name}::get_initial_specified_value(),
            % endfor
        })
    }

    impl<'a> ToCss for LonghandsToSerialize<'a>  {
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            let all_equal = {
                % for side in PHYSICAL_SIDES:
                  let border_${side}_width = self.border_${side}_width;
                  let border_${side}_style = self.border_${side}_style;
                  let border_${side}_color = self.border_${side}_color;
                % endfor

                border_top_width == border_right_width &&
                border_right_width == border_bottom_width &&
                border_bottom_width == border_left_width &&

                border_top_style == border_right_style &&
                border_right_style == border_bottom_style &&
                border_bottom_style == border_left_style &&

                border_top_color == border_right_color &&
                border_right_color == border_bottom_color &&
                border_bottom_color == border_left_color
            };

            // If all longhands are all present, then all sides should be the same,
            // so we can just one set of color/style/width
            if all_equal {
                super::serialize_directional_border(
                    dest,
                    self.border_${side}_width,
                    self.border_${side}_style,
                    self.border_${side}_color
                )
            } else {
                Ok(())
            }
        }
    }

</%helpers:shorthand>

<%helpers:shorthand name="border-radius" sub_properties="${' '.join(
    'border-%s-radius' % (corner)
     for corner in ['top-left', 'top-right', 'bottom-right', 'bottom-left']
)}" extra_prefixes="webkit" spec="https://drafts.csswg.org/css-backgrounds/#border-radius">
    use values::generics::serialize_radius_values;
    use values::specified::basic_shape::BorderRadius;
    use parser::Parse;

    pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
        let radii = try!(BorderRadius::parse(context, input));
        Ok(Longhands {
            border_top_left_radius: radii.top_left,
            border_top_right_radius: radii.top_right,
            border_bottom_right_radius: radii.bottom_right,
            border_bottom_left_radius: radii.bottom_left,
        })
    }

    impl<'a> ToCss for LonghandsToSerialize<'a>  {
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            serialize_radius_values(dest,
                                    &self.border_top_left_radius.0,
                                    &self.border_top_right_radius.0,
                                    &self.border_bottom_right_radius.0,
                                    &self.border_bottom_left_radius.0)
        }
    }
</%helpers:shorthand>

<%helpers:shorthand name="border-image" sub_properties="border-image-outset
    border-image-repeat border-image-slice border-image-source border-image-width"
    extra_prefixes="moz webkit" spec="https://drafts.csswg.org/css-backgrounds-3/#border-image">
    use properties::longhands::{border_image_outset, border_image_repeat, border_image_slice};
    use properties::longhands::{border_image_source, border_image_width};

    pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
        % for name in "outset repeat slice source width".split():
            let mut border_image_${name} = border_image_${name}::get_initial_specified_value();
        % endfor

        try!(input.try(|input| {
            % for name in "outset repeat slice source width".split():
                let mut ${name} = None;
            % endfor
            loop {
                if slice.is_none() {
                    if let Ok(value) = input.try(|input| border_image_slice::parse(context, input)) {
                        slice = Some(value);
                        // Parse border image width and outset, if applicable.
                        let maybe_width_outset: Result<_, ()> = input.try(|input| {
                            try!(input.expect_delim('/'));

                            // Parse border image width, if applicable.
                            let w = input.try(|input|
                                border_image_width::parse(context, input)).ok();

                            // Parse border image outset if applicable.
                            let o = input.try(|input| {
                                try!(input.expect_delim('/'));
                                border_image_outset::parse(context, input)
                            }).ok();
                            if w.is_none() && o.is_none() {
                               Err(())
                            }
                            else {
                               Ok((w, o))
                            }
                        });
                        if let Ok((w, o)) = maybe_width_outset {
                            width = w;
                            outset = o;
                        }

                        continue
                    }
                }
                % for name in "source repeat".split():
                    if ${name}.is_none() {
                        if let Ok(value) = input.try(|input| border_image_${name}::parse(context, input)) {
                            ${name} = Some(value);
                            continue
                        }
                    }
                % endfor
                break
            }
            let mut any = false;
            % for name in "outset repeat slice source width".split():
                any = any || ${name}.is_some();
            % endfor
            if any {
                % for name in "outset repeat slice source width".split():
                    if let Some(b_${name}) = ${name} {
                        border_image_${name} = b_${name};
                    }
                % endfor
                Ok(())
            } else {
                Err(())
            }
        }));

        Ok(Longhands {
            % for name in "outset repeat slice source width".split():
                border_image_${name}: border_image_${name},
            % endfor
         })
    }

    impl<'a> ToCss for LonghandsToSerialize<'a>  {
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            self.border_image_source.to_css(dest)?;
            dest.write_str(" ")?;
            self.border_image_slice.to_css(dest)?;
            dest.write_str(" / ")?;
            self.border_image_width.to_css(dest)?;
            dest.write_str(" / ")?;
            self.border_image_outset.to_css(dest)?;
            dest.write_str(" ")?;
            self.border_image_repeat.to_css(dest)
        }
    }
</%helpers:shorthand>
<commit_msg>Support hashless color quirk in border-color<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

<%namespace name="helpers" file="/helpers.mako.rs" />
<% from data import to_rust_ident, ALL_SIDES, PHYSICAL_SIDES, maybe_moz_logical_alias %>

${helpers.four_sides_shorthand("border-color", "border-%s-color", "specified::CSSColor::parse",
                               spec="https://drafts.csswg.org/css-backgrounds/#border-color",
                               allow_quirks=True)}

${helpers.four_sides_shorthand("border-style", "border-%s-style",
                               "specified::BorderStyle::parse",
                               spec="https://drafts.csswg.org/css-backgrounds/#border-style")}

<%helpers:shorthand name="border-width" sub_properties="${
        ' '.join('border-%s-width' % side
                 for side in PHYSICAL_SIDES)}"
    spec="https://drafts.csswg.org/css-backgrounds/#border-width">
    use super::parse_four_sides;
    use values::specified::{AllowQuirks, BorderWidth};

    pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
        let (top, right, bottom, left) = try!(parse_four_sides(input, |i| {
            BorderWidth::parse_quirky(context, i, AllowQuirks::Yes)
        }));
        Ok(Longhands {
            % for side in PHYSICAL_SIDES:
                ${to_rust_ident('border-%s-width' % side)}: ${side},
            % endfor
        })
    }

    impl<'a> ToCss for LonghandsToSerialize<'a>  {
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            % for side in PHYSICAL_SIDES:
                let ${side} = self.border_${side}_width.clone();
            % endfor

            super::serialize_four_sides(dest, &top, &right, &bottom, &left)
        }
    }
</%helpers:shorthand>

pub fn parse_border(context: &ParserContext, input: &mut Parser)
                 -> Result<(specified::CSSColor,
                            specified::BorderStyle,
                            specified::BorderWidth), ()> {
    use values::specified::{CSSColor, BorderStyle, BorderWidth};
    let _unused = context;
    let mut color = None;
    let mut style = None;
    let mut width = None;
    let mut any = false;
    loop {
        if color.is_none() {
            if let Ok(value) = input.try(|i| CSSColor::parse(context, i)) {
                color = Some(value);
                any = true;
                continue
            }
        }
        if style.is_none() {
            if let Ok(value) = input.try(|i| BorderStyle::parse(context, i)) {
                style = Some(value);
                any = true;
                continue
            }
        }
        if width.is_none() {
            if let Ok(value) = input.try(|i| BorderWidth::parse(context, i)) {
                width = Some(value);
                any = true;
                continue
            }
        }
        break
    }
    if any {
        Ok((color.unwrap_or_else(|| CSSColor::currentcolor()),
            style.unwrap_or(BorderStyle::none),
            width.unwrap_or(BorderWidth::Medium)))
    } else {
        Err(())
    }
}

% for side, logical in ALL_SIDES:
    <%
        spec = "https://drafts.csswg.org/css-backgrounds/#border-%s" % side
        if logical:
            spec = "https://drafts.csswg.org/css-logical-props/#propdef-border-%s" % side
    %>
    <%helpers:shorthand
        name="border-${side}"
        sub_properties="${' '.join(
            'border-%s-%s' % (side, prop)
            for prop in ['color', 'style', 'width']
        )}"
        alias="${maybe_moz_logical_alias(product, (side, logical), '-moz-border-%s')}"
        spec="${spec}">

    pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
        let (color, style, width) = try!(super::parse_border(context, input));
        Ok(Longhands {
            border_${to_rust_ident(side)}_color: color,
            border_${to_rust_ident(side)}_style: style,
            border_${to_rust_ident(side)}_width: width
        })
    }

    impl<'a> ToCss for LonghandsToSerialize<'a>  {
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            super::serialize_directional_border(
                dest,
                self.border_${to_rust_ident(side)}_width,
                self.border_${to_rust_ident(side)}_style,
                self.border_${to_rust_ident(side)}_color
            )
        }
    }

    </%helpers:shorthand>
% endfor

<%helpers:shorthand name="border"
    sub_properties="${' '.join('border-%s-%s' % (side, prop)
        for side in PHYSICAL_SIDES
        for prop in ['color', 'style', 'width'])}
        ${' '.join('border-image-%s' % name
        for name in ['outset', 'repeat', 'slice', 'source', 'width'])}
        ${' '.join('-moz-border-%s-colors' % side
        for side in PHYSICAL_SIDES) if product == 'gecko' else ''}"
    spec="https://drafts.csswg.org/css-backgrounds/#border">

    % if product == "gecko":
        use properties::longhands::{_moz_border_top_colors, _moz_border_right_colors,
                                    _moz_border_bottom_colors, _moz_border_left_colors};
    % endif

    pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
        use properties::longhands::{border_image_outset, border_image_repeat, border_image_slice};
        use properties::longhands::{border_image_source, border_image_width};

        let (color, style, width) = try!(super::parse_border(context, input));
        Ok(Longhands {
            % for side in PHYSICAL_SIDES:
                border_${side}_color: color.clone(),
                border_${side}_style: style,
                border_${side}_width: width.clone(),
                % if product == "gecko":
                    _moz_border_${side}_colors: _moz_border_${side}_colors::get_initial_specified_value(),
                % endif
            % endfor

            // The ‘border’ shorthand resets ‘border-image’ to its initial value.
            // See https://drafts.csswg.org/css-backgrounds-3/#the-border-shorthands
            % for name in "outset repeat slice source width".split():
                border_image_${name}: border_image_${name}::get_initial_specified_value(),
            % endfor
        })
    }

    impl<'a> ToCss for LonghandsToSerialize<'a>  {
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            let all_equal = {
                % for side in PHYSICAL_SIDES:
                  let border_${side}_width = self.border_${side}_width;
                  let border_${side}_style = self.border_${side}_style;
                  let border_${side}_color = self.border_${side}_color;
                % endfor

                border_top_width == border_right_width &&
                border_right_width == border_bottom_width &&
                border_bottom_width == border_left_width &&

                border_top_style == border_right_style &&
                border_right_style == border_bottom_style &&
                border_bottom_style == border_left_style &&

                border_top_color == border_right_color &&
                border_right_color == border_bottom_color &&
                border_bottom_color == border_left_color
            };

            // If all longhands are all present, then all sides should be the same,
            // so we can just one set of color/style/width
            if all_equal {
                super::serialize_directional_border(
                    dest,
                    self.border_${side}_width,
                    self.border_${side}_style,
                    self.border_${side}_color
                )
            } else {
                Ok(())
            }
        }
    }

</%helpers:shorthand>

<%helpers:shorthand name="border-radius" sub_properties="${' '.join(
    'border-%s-radius' % (corner)
     for corner in ['top-left', 'top-right', 'bottom-right', 'bottom-left']
)}" extra_prefixes="webkit" spec="https://drafts.csswg.org/css-backgrounds/#border-radius">
    use values::generics::serialize_radius_values;
    use values::specified::basic_shape::BorderRadius;
    use parser::Parse;

    pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
        let radii = try!(BorderRadius::parse(context, input));
        Ok(Longhands {
            border_top_left_radius: radii.top_left,
            border_top_right_radius: radii.top_right,
            border_bottom_right_radius: radii.bottom_right,
            border_bottom_left_radius: radii.bottom_left,
        })
    }

    impl<'a> ToCss for LonghandsToSerialize<'a>  {
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            serialize_radius_values(dest,
                                    &self.border_top_left_radius.0,
                                    &self.border_top_right_radius.0,
                                    &self.border_bottom_right_radius.0,
                                    &self.border_bottom_left_radius.0)
        }
    }
</%helpers:shorthand>

<%helpers:shorthand name="border-image" sub_properties="border-image-outset
    border-image-repeat border-image-slice border-image-source border-image-width"
    extra_prefixes="moz webkit" spec="https://drafts.csswg.org/css-backgrounds-3/#border-image">
    use properties::longhands::{border_image_outset, border_image_repeat, border_image_slice};
    use properties::longhands::{border_image_source, border_image_width};

    pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
        % for name in "outset repeat slice source width".split():
            let mut border_image_${name} = border_image_${name}::get_initial_specified_value();
        % endfor

        try!(input.try(|input| {
            % for name in "outset repeat slice source width".split():
                let mut ${name} = None;
            % endfor
            loop {
                if slice.is_none() {
                    if let Ok(value) = input.try(|input| border_image_slice::parse(context, input)) {
                        slice = Some(value);
                        // Parse border image width and outset, if applicable.
                        let maybe_width_outset: Result<_, ()> = input.try(|input| {
                            try!(input.expect_delim('/'));

                            // Parse border image width, if applicable.
                            let w = input.try(|input|
                                border_image_width::parse(context, input)).ok();

                            // Parse border image outset if applicable.
                            let o = input.try(|input| {
                                try!(input.expect_delim('/'));
                                border_image_outset::parse(context, input)
                            }).ok();
                            if w.is_none() && o.is_none() {
                               Err(())
                            }
                            else {
                               Ok((w, o))
                            }
                        });
                        if let Ok((w, o)) = maybe_width_outset {
                            width = w;
                            outset = o;
                        }

                        continue
                    }
                }
                % for name in "source repeat".split():
                    if ${name}.is_none() {
                        if let Ok(value) = input.try(|input| border_image_${name}::parse(context, input)) {
                            ${name} = Some(value);
                            continue
                        }
                    }
                % endfor
                break
            }
            let mut any = false;
            % for name in "outset repeat slice source width".split():
                any = any || ${name}.is_some();
            % endfor
            if any {
                % for name in "outset repeat slice source width".split():
                    if let Some(b_${name}) = ${name} {
                        border_image_${name} = b_${name};
                    }
                % endfor
                Ok(())
            } else {
                Err(())
            }
        }));

        Ok(Longhands {
            % for name in "outset repeat slice source width".split():
                border_image_${name}: border_image_${name},
            % endfor
         })
    }

    impl<'a> ToCss for LonghandsToSerialize<'a>  {
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            self.border_image_source.to_css(dest)?;
            dest.write_str(" ")?;
            self.border_image_slice.to_css(dest)?;
            dest.write_str(" / ")?;
            self.border_image_width.to_css(dest)?;
            dest.write_str(" / ")?;
            self.border_image_outset.to_css(dest)?;
            dest.write_str(" ")?;
            self.border_image_repeat.to_css(dest)
        }
    }
</%helpers:shorthand>
<|endoftext|>


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

use dom::audionode::MAX_CHANNEL_COUNT;
use dom::bindings::cell::DomRefCell;
use dom::bindings::codegen::Bindings::AudioBufferBinding::{self, AudioBufferMethods, AudioBufferOptions};
use dom::bindings::error::{Error, Fallible};
use dom::bindings::num::Finite;
use dom::bindings::reflector::{DomObject, Reflector, reflect_dom_object};
use dom::bindings::root::DomRoot;
use dom::window::Window;
use dom_struct::dom_struct;
use js::jsapi::{Heap, JSContext, JSObject, JS_StealArrayBufferContents};
use js::rust::CustomAutoRooterGuard;
use js::typedarray::{CreateWith, Float32Array};
use servo_media::audio::buffer_source_node::AudioBuffer as ServoMediaAudioBuffer;
use std::cmp::min;
use std::ptr::{self, NonNull};

type JSAudioChannel = Heap<*mut JSObject>;

#[dom_struct]
pub struct AudioBuffer {
    reflector_: Reflector,
    js_channels: DomRefCell<Vec<JSAudioChannel>>,
    #[ignore_malloc_size_of = "servo_media"]
    shared_channels: DomRefCell<ServoMediaAudioBuffer>,
    sample_rate: f32,
    length: u32,
    duration: f64,
    number_of_channels: u32,
}

impl AudioBuffer {
    #[allow(unrooted_must_root)]
    #[allow(unsafe_code)]
    pub fn new_inherited(global: &Window,
                         number_of_channels: u32,
                         length: u32,
                         sample_rate: f32,
                         initial_data: Option<&[f32]>) -> AudioBuffer {
        let initial_data = match initial_data {
            Some(initial_data) => {
                let mut data = vec![];
                data.extend_from_slice(initial_data);
                data
            },
            None => vec![0.; (length * number_of_channels) as usize]
        };
        let cx = global.get_cx();
        let mut js_channels: Vec<JSAudioChannel> = Vec::with_capacity(number_of_channels as usize);
        for channel in 0..number_of_channels {
            rooted!(in (cx) let mut array = ptr::null_mut::<JSObject>());
            let offset = (channel * length) as usize;
            let _ = unsafe {
                Float32Array::create(
                    cx,
                    CreateWith::Slice(&initial_data.as_slice()[offset..offset + (length as usize)]),
                    array.handle_mut())
            };
            let js_channel = Heap::default();
            js_channel.set(array.get());
            js_channels.push(js_channel);
        }
        AudioBuffer {
            reflector_: Reflector::new(),
            js_channels: DomRefCell::new(js_channels),
            shared_channels: DomRefCell::new(ServoMediaAudioBuffer::new(number_of_channels as u8, length as usize)),
            sample_rate,
            length,
            duration: length as f64 / sample_rate as f64,
            number_of_channels,
        }
    }

    #[allow(unrooted_must_root)]
    pub fn new(global: &Window,
               number_of_channels: u32,
               length: u32,
               sample_rate: f32,
               initial_data: Option<&[f32]>) -> DomRoot<AudioBuffer> {
        let buffer = AudioBuffer::new_inherited(global, number_of_channels, length, sample_rate, initial_data);
        reflect_dom_object(Box::new(buffer), global, AudioBufferBinding::Wrap)
    }

    pub fn Constructor(window: &Window,
                       options: &AudioBufferOptions) -> Fallible<DomRoot<AudioBuffer>> {
        if options.numberOfChannels > MAX_CHANNEL_COUNT {
            return Err(Error::NotSupported);
        }
        Ok(AudioBuffer::new(window, options.numberOfChannels, options.length, *options.sampleRate, None))
    }

    #[allow(unsafe_code)]
    fn restore_js_channel_data(&self, cx: *mut JSContext) -> bool {
        for (i, channel) in self.js_channels.borrow_mut().iter().enumerate() {
            if !channel.get().is_null() {
                // Already have data in JS array.
                continue;
            }

            // Move the channel data from shared_channels to js_channels.
            rooted!(in (cx) let mut array = ptr::null_mut::<JSObject>());
            let shared_channel = (*self.shared_channels.borrow_mut()).buffers.remove(i);
            if unsafe {
                Float32Array::create(cx, CreateWith::Slice(&shared_channel), array.handle_mut())
            }.is_err() {
                return false;
            }
            channel.set(array.get());
        }

        true
    }

    // https://webaudio.github.io/web-audio-api/#acquire-the-content
    #[allow(unsafe_code)]
    pub fn acquire_contents(&self) -> Option<ServoMediaAudioBuffer> {
        let cx = self.global().get_cx();
        for (i, channel) in self.js_channels.borrow_mut().iter().enumerate() {
            // Step 1.
            if channel.get().is_null() {
                return None;
            }

            // Step 2.
            let channel_data = unsafe {
                typedarray!(in(cx) let array: Float32Array = channel.get());
                if let Ok(array) = array {
                    // XXX TypedArrays API does not expose a way to steal the buffer's
                    //     content.
                    let data = array.to_vec();
                    let _ = JS_StealArrayBufferContents(cx, channel.handle());
                    data
                } else {
                    return None;
                }
            };

            channel.set(ptr::null_mut());

            // Step 3.
            (*self.shared_channels.borrow_mut()).buffers[i] = channel_data;

            // Step 4 will complete turning shared_channels
            // data into js_channels ArrayBuffers in restore_js_channel_data.
        }

        self.js_channels.borrow_mut().clear();

        Some((*self.shared_channels.borrow()).clone())
    }
}

impl AudioBufferMethods for AudioBuffer {
    // https://webaudio.github.io/web-audio-api/#dom-audiobuffer-samplerate
    fn SampleRate(&self) -> Finite<f32> {
        Finite::wrap(self.sample_rate)
    }

    // https://webaudio.github.io/web-audio-api/#dom-audiobuffer-length
    fn Length(&self) -> u32 {
        self.length
    }

    // https://webaudio.github.io/web-audio-api/#dom-audiobuffer-duration
    fn Duration(&self) -> Finite<f64> {
        Finite::wrap(self.duration)
    }

    // https://webaudio.github.io/web-audio-api/#dom-audiobuffer-numberofchannels
    fn NumberOfChannels(&self) -> u32 {
        self.number_of_channels
    }

    // https://webaudio.github.io/web-audio-api/#dom-audiobuffer-getchanneldata
    #[allow(unsafe_code)]
    unsafe fn GetChannelData(&self, cx: *mut JSContext, channel: u32) -> Fallible<NonNull<JSObject>> {
        if channel >= self.number_of_channels {
            return Err(Error::IndexSize);
        }

        if !self.restore_js_channel_data(cx) {
            return Err(Error::JSFailed);
        }

        Ok(NonNull::new_unchecked(self.js_channels.borrow()[channel as usize].get()))
    }

    // https://webaudio.github.io/web-audio-api/#dom-audiobuffer-copyfromchannel
    #[allow(unsafe_code)]
    fn CopyFromChannel(&self,
                       mut destination: CustomAutoRooterGuard<Float32Array>,
                       channel_number: u32,
                       start_in_channel: u32) -> Fallible<()> {
        if channel_number >= self.number_of_channels || start_in_channel > self.length {
            return Err(Error::IndexSize);
        }

        let bytes_to_copy = min(self.length - start_in_channel, destination.len() as u32) as usize;
        let cx = self.global().get_cx();
        let channel_number = channel_number as usize;
        let offset = start_in_channel as usize;
        let mut dest = Vec::with_capacity(destination.len());
        // let destination = unsafe { destination.as_mut_slice() };

        // We either copy form js_channels or shared_channels.

        let js_channel = self.js_channels.borrow()[channel_number].get();
        if !js_channel.is_null() {
            typedarray!(in(cx) let array: Float32Array = js_channel);
            if let Ok(array) = array {
                let data = unsafe { array.as_slice() };
                dest.extend_from_slice(&data[offset..offset + bytes_to_copy]);
                return Ok(());
            }
        }

        if let Some(shared_channel) = self.shared_channels.borrow().buffers.get(channel_number) {
            dest.extend_from_slice(&shared_channel.as_slice()[offset..offset + bytes_to_copy]);
        }

        unsafe { destination.update(&dest); }

        Ok(())
    }

    // https://webaudio.github.io/web-audio-api/#dom-audiobuffer-copytochannel
    #[allow(unsafe_code)]
    fn CopyToChannel(&self,
                     source: CustomAutoRooterGuard<Float32Array>,
                     channel_number: u32,
                     start_in_channel: u32) -> Fallible<()> {
        if channel_number >= self.number_of_channels || start_in_channel > (source.len() as u32) {
            return Err(Error::IndexSize);
        }

        let cx = self.global().get_cx();
        if !self.restore_js_channel_data(cx) {
            return Err(Error::JSFailed);
        }

        let js_channel = self.js_channels.borrow()[channel_number as usize].get();
        if js_channel.is_null() {
            // The array buffer was detached.
            return Err(Error::IndexSize);
        }

        typedarray!(in(cx) let array: Float32Array = js_channel);
        if let Ok(mut array) = array {
            let bytes_to_copy = min(self.length - start_in_channel, source.len() as u32) as usize;
            let offset = start_in_channel as usize;
            unsafe { array.update(&source.as_slice()[offset..offset + bytes_to_copy]); }
        } else {
            return Err(Error::IndexSize);
        }

        Ok(())
    }
}
<commit_msg>Add spec link to AudioBuffer::Constructor<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use dom::audionode::MAX_CHANNEL_COUNT;
use dom::bindings::cell::DomRefCell;
use dom::bindings::codegen::Bindings::AudioBufferBinding::{self, AudioBufferMethods, AudioBufferOptions};
use dom::bindings::error::{Error, Fallible};
use dom::bindings::num::Finite;
use dom::bindings::reflector::{DomObject, Reflector, reflect_dom_object};
use dom::bindings::root::DomRoot;
use dom::window::Window;
use dom_struct::dom_struct;
use js::jsapi::{Heap, JSContext, JSObject, JS_StealArrayBufferContents};
use js::rust::CustomAutoRooterGuard;
use js::typedarray::{CreateWith, Float32Array};
use servo_media::audio::buffer_source_node::AudioBuffer as ServoMediaAudioBuffer;
use std::cmp::min;
use std::ptr::{self, NonNull};

type JSAudioChannel = Heap<*mut JSObject>;

#[dom_struct]
pub struct AudioBuffer {
    reflector_: Reflector,
    js_channels: DomRefCell<Vec<JSAudioChannel>>,
    #[ignore_malloc_size_of = "servo_media"]
    shared_channels: DomRefCell<ServoMediaAudioBuffer>,
    sample_rate: f32,
    length: u32,
    duration: f64,
    number_of_channels: u32,
}

impl AudioBuffer {
    #[allow(unrooted_must_root)]
    #[allow(unsafe_code)]
    pub fn new_inherited(global: &Window,
                         number_of_channels: u32,
                         length: u32,
                         sample_rate: f32,
                         initial_data: Option<&[f32]>) -> AudioBuffer {
        let initial_data = match initial_data {
            Some(initial_data) => {
                let mut data = vec![];
                data.extend_from_slice(initial_data);
                data
            },
            None => vec![0.; (length * number_of_channels) as usize]
        };
        let cx = global.get_cx();
        let mut js_channels: Vec<JSAudioChannel> = Vec::with_capacity(number_of_channels as usize);
        for channel in 0..number_of_channels {
            rooted!(in (cx) let mut array = ptr::null_mut::<JSObject>());
            let offset = (channel * length) as usize;
            let _ = unsafe {
                Float32Array::create(
                    cx,
                    CreateWith::Slice(&initial_data.as_slice()[offset..offset + (length as usize)]),
                    array.handle_mut())
            };
            let js_channel = Heap::default();
            js_channel.set(array.get());
            js_channels.push(js_channel);
        }
        AudioBuffer {
            reflector_: Reflector::new(),
            js_channels: DomRefCell::new(js_channels),
            shared_channels: DomRefCell::new(ServoMediaAudioBuffer::new(number_of_channels as u8, length as usize)),
            sample_rate,
            length,
            duration: length as f64 / sample_rate as f64,
            number_of_channels,
        }
    }

    #[allow(unrooted_must_root)]
    pub fn new(global: &Window,
               number_of_channels: u32,
               length: u32,
               sample_rate: f32,
               initial_data: Option<&[f32]>) -> DomRoot<AudioBuffer> {
        let buffer = AudioBuffer::new_inherited(global, number_of_channels, length, sample_rate, initial_data);
        reflect_dom_object(Box::new(buffer), global, AudioBufferBinding::Wrap)
    }

    /// https://webaudio.github.io/web-audio-api/#dom-audiobuffer-audiobuffer
    pub fn Constructor(window: &Window,
                       options: &AudioBufferOptions) -> Fallible<DomRoot<AudioBuffer>> {
        if options.numberOfChannels > MAX_CHANNEL_COUNT {
            return Err(Error::NotSupported);
        }
        Ok(AudioBuffer::new(window, options.numberOfChannels, options.length, *options.sampleRate, None))
    }

    #[allow(unsafe_code)]
    fn restore_js_channel_data(&self, cx: *mut JSContext) -> bool {
        for (i, channel) in self.js_channels.borrow_mut().iter().enumerate() {
            if !channel.get().is_null() {
                // Already have data in JS array.
                continue;
            }

            // Move the channel data from shared_channels to js_channels.
            rooted!(in (cx) let mut array = ptr::null_mut::<JSObject>());
            let shared_channel = (*self.shared_channels.borrow_mut()).buffers.remove(i);
            if unsafe {
                Float32Array::create(cx, CreateWith::Slice(&shared_channel), array.handle_mut())
            }.is_err() {
                return false;
            }
            channel.set(array.get());
        }

        true
    }

    // https://webaudio.github.io/web-audio-api/#acquire-the-content
    #[allow(unsafe_code)]
    pub fn acquire_contents(&self) -> Option<ServoMediaAudioBuffer> {
        let cx = self.global().get_cx();
        for (i, channel) in self.js_channels.borrow_mut().iter().enumerate() {
            // Step 1.
            if channel.get().is_null() {
                return None;
            }

            // Step 2.
            let channel_data = unsafe {
                typedarray!(in(cx) let array: Float32Array = channel.get());
                if let Ok(array) = array {
                    // XXX TypedArrays API does not expose a way to steal the buffer's
                    //     content.
                    let data = array.to_vec();
                    let _ = JS_StealArrayBufferContents(cx, channel.handle());
                    data
                } else {
                    return None;
                }
            };

            channel.set(ptr::null_mut());

            // Step 3.
            (*self.shared_channels.borrow_mut()).buffers[i] = channel_data;

            // Step 4 will complete turning shared_channels
            // data into js_channels ArrayBuffers in restore_js_channel_data.
        }

        self.js_channels.borrow_mut().clear();

        Some((*self.shared_channels.borrow()).clone())
    }
}

impl AudioBufferMethods for AudioBuffer {
    // https://webaudio.github.io/web-audio-api/#dom-audiobuffer-samplerate
    fn SampleRate(&self) -> Finite<f32> {
        Finite::wrap(self.sample_rate)
    }

    // https://webaudio.github.io/web-audio-api/#dom-audiobuffer-length
    fn Length(&self) -> u32 {
        self.length
    }

    // https://webaudio.github.io/web-audio-api/#dom-audiobuffer-duration
    fn Duration(&self) -> Finite<f64> {
        Finite::wrap(self.duration)
    }

    // https://webaudio.github.io/web-audio-api/#dom-audiobuffer-numberofchannels
    fn NumberOfChannels(&self) -> u32 {
        self.number_of_channels
    }

    // https://webaudio.github.io/web-audio-api/#dom-audiobuffer-getchanneldata
    #[allow(unsafe_code)]
    unsafe fn GetChannelData(&self, cx: *mut JSContext, channel: u32) -> Fallible<NonNull<JSObject>> {
        if channel >= self.number_of_channels {
            return Err(Error::IndexSize);
        }

        if !self.restore_js_channel_data(cx) {
            return Err(Error::JSFailed);
        }

        Ok(NonNull::new_unchecked(self.js_channels.borrow()[channel as usize].get()))
    }

    // https://webaudio.github.io/web-audio-api/#dom-audiobuffer-copyfromchannel
    #[allow(unsafe_code)]
    fn CopyFromChannel(&self,
                       mut destination: CustomAutoRooterGuard<Float32Array>,
                       channel_number: u32,
                       start_in_channel: u32) -> Fallible<()> {
        if channel_number >= self.number_of_channels || start_in_channel > self.length {
            return Err(Error::IndexSize);
        }

        let bytes_to_copy = min(self.length - start_in_channel, destination.len() as u32) as usize;
        let cx = self.global().get_cx();
        let channel_number = channel_number as usize;
        let offset = start_in_channel as usize;
        let mut dest = Vec::with_capacity(destination.len());
        // let destination = unsafe { destination.as_mut_slice() };

        // We either copy form js_channels or shared_channels.

        let js_channel = self.js_channels.borrow()[channel_number].get();
        if !js_channel.is_null() {
            typedarray!(in(cx) let array: Float32Array = js_channel);
            if let Ok(array) = array {
                let data = unsafe { array.as_slice() };
                dest.extend_from_slice(&data[offset..offset + bytes_to_copy]);
                return Ok(());
            }
        }

        if let Some(shared_channel) = self.shared_channels.borrow().buffers.get(channel_number) {
            dest.extend_from_slice(&shared_channel.as_slice()[offset..offset + bytes_to_copy]);
        }

        unsafe { destination.update(&dest); }

        Ok(())
    }

    // https://webaudio.github.io/web-audio-api/#dom-audiobuffer-copytochannel
    #[allow(unsafe_code)]
    fn CopyToChannel(&self,
                     source: CustomAutoRooterGuard<Float32Array>,
                     channel_number: u32,
                     start_in_channel: u32) -> Fallible<()> {
        if channel_number >= self.number_of_channels || start_in_channel > (source.len() as u32) {
            return Err(Error::IndexSize);
        }

        let cx = self.global().get_cx();
        if !self.restore_js_channel_data(cx) {
            return Err(Error::JSFailed);
        }

        let js_channel = self.js_channels.borrow()[channel_number as usize].get();
        if js_channel.is_null() {
            // The array buffer was detached.
            return Err(Error::IndexSize);
        }

        typedarray!(in(cx) let array: Float32Array = js_channel);
        if let Ok(mut array) = array {
            let bytes_to_copy = min(self.length - start_in_channel, source.len() as u32) as usize;
            let offset = start_in_channel as usize;
            unsafe { array.update(&source.as_slice()[offset..offset + bytes_to_copy]); }
        } else {
            return Err(Error::IndexSize);
        }

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


<commit_before>//! Provides types and functions related to working with PostgreSQL
//!
//! Much of this module is re-exported from database agnostic locations.
//! However, if you are writing code specifically to extend Diesel on
//! PostgreSQL, you may need to work with this module directly.

pub mod expression;
pub mod types;

mod backend;
mod connection;
mod metadata_lookup;
mod query_builder;
pub(crate) mod serialize;
mod transaction;
mod value;

pub use self::backend::{Pg, PgTypeMetadata};
pub use self::connection::PgConnection;
pub use self::metadata_lookup::PgMetadataLookup;
pub use self::query_builder::DistinctOnClause;
pub use self::query_builder::PgQueryBuilder;
pub use self::transaction::TransactionBuilder;
pub use self::value::PgValue;

/// Data structures for PG types which have no corresponding Rust type
///
/// Most of these types are used to implement `ToSql` and `FromSql` for higher
/// level types.
pub mod data_types {
    #[doc(inline)]
    pub use super::types::date_and_time::{PgDate, PgInterval, PgTime, PgTimestamp};
    #[doc(inline)]
    pub use super::types::floats::PgNumeric;
    #[doc(inline)]
    pub use super::types::money::PgMoney;
    pub use super::types::money::PgMoney as Cents;
}
<commit_msg>Add deprecated attribute to pg::upsert<commit_after>//! Provides types and functions related to working with PostgreSQL
//!
//! Much of this module is re-exported from database agnostic locations.
//! However, if you are writing code specifically to extend Diesel on
//! PostgreSQL, you may need to work with this module directly.

pub mod expression;
pub mod types;
#[deprecated(since = "1.4.0", note = "Use `diesel::upsert` instead")]
pub use upsert;

mod backend;
mod connection;
mod metadata_lookup;
mod query_builder;
pub(crate) mod serialize;
mod transaction;
mod value;

pub use self::backend::{Pg, PgTypeMetadata};
pub use self::connection::PgConnection;
pub use self::metadata_lookup::PgMetadataLookup;
pub use self::query_builder::DistinctOnClause;
pub use self::query_builder::PgQueryBuilder;
pub use self::transaction::TransactionBuilder;
pub use self::value::PgValue;

/// Data structures for PG types which have no corresponding Rust type
///
/// Most of these types are used to implement `ToSql` and `FromSql` for higher
/// level types.
pub mod data_types {
    #[doc(inline)]
    pub use super::types::date_and_time::{PgDate, PgInterval, PgTime, PgTimestamp};
    #[doc(inline)]
    pub use super::types::floats::PgNumeric;
    #[doc(inline)]
    pub use super::types::money::PgMoney;
    pub use super::types::money::PgMoney as Cents;
}
<|endoftext|>


<commit_before>
// Copyright (c) 2017 Chef Software Inc. and/or applicable contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#[macro_use]
extern crate log;
extern crate env_logger;
extern crate time;
extern crate petgraph;
extern crate walkdir;
extern crate habitat_core as hab_core;
extern crate builder_core as bldr_core;
extern crate habitat_builder_protocol as protocol;
extern crate habitat_builder_db as db;
extern crate habitat_net as hab_net;
extern crate clap;
extern crate postgres;
extern crate protobuf;
extern crate r2d2;
extern crate serde;
#[macro_use]
extern crate serde_derive;

pub mod config;
pub mod data_store;
pub mod error;

use std::io::{self, Write};
use clap::{Arg, App};
use time::PreciseTime;
use bldr_core::package_graph::PackageGraph;
use hab_core::config::ConfigFile;
use config::Config;
use data_store::DataStore;
use std::collections::HashMap;

const VERSION: &'static str = include_str!(concat!(env!("OUT_DIR"), "/VERSION"));

fn main() {
    env_logger::init().unwrap();

    let matches = App::new("bldr-graph")
        .version(VERSION)
        .about("Habitat Graph Dev Tool")
        .arg(Arg::with_name("config")
                 .help("Filepath to configuration file")
                 .required(false)
                 .index(1))
        .get_matches();

    let config = match matches.value_of("config") {
        Some(cfg_path) => Config::from_file(cfg_path).unwrap(),
        None => Config::default(),
    };

    println!("Connecting to {}", config.datastore.database);

    let datastore = DataStore::new(&config).unwrap();
    datastore.setup().unwrap();

    println!("Building graph... please wait.");

    let mut graph = PackageGraph::new();
    let packages = datastore.get_packages().unwrap();
    let start_time = PreciseTime::now();
    let (ncount, ecount) = graph.build(packages.into_iter());
    let end_time = PreciseTime::now();

    println!("OK: {} nodes, {} edges ({} sec)",
             ncount,
             ecount,
             start_time.to(end_time));

    println!("\nAvailable commands: help, stats, top, find, resolve, filter, rdeps, deps, check, exit\n",);

    let mut filter = String::from("");
    let mut done = false;
    while !done {
        print!("command> ");
        io::stdout().flush().ok().expect("Could not flush stdout");

        let mut cmd = String::new();
        io::stdin().read_line(&mut cmd).unwrap();

        let v: Vec<&str> = cmd.trim_right().split_whitespace().collect();

        if v.len() > 0 {
            match v[0].to_lowercase().as_str() {
                "help" => do_help(),
                "stats" => do_stats(&graph),
                "top" => {
                    let count = if v.len() < 2 {
                        10
                    } else {
                        v[1].parse::<usize>().unwrap()
                    };
                    do_top(&graph, count);
                }
                "filter" => {
                    if v.len() < 2 {
                        filter = String::from("");
                        println!("Removed filter\n");
                    } else {
                        filter = String::from(v[1]);
                        println!("New filter: {}\n", filter);
                    }
                }
                "find" => {
                    if v.len() < 2 {
                        println!("Missing search term\n")
                    } else {
                        let max = if v.len() > 2 {
                            v[2].parse::<usize>().unwrap()
                        } else {
                            10
                        };
                        do_find(&graph, v[1].to_lowercase().as_str(), max)
                    }
                }
                "resolve" => {
                    if v.len() < 2 {
                        println!("Missing package name\n")
                    } else {
                        do_resolve(&graph, v[1].to_lowercase().as_str())
                    }
                }
                "rdeps" => {
                    if v.len() < 2 {
                        println!("Missing package name\n")
                    } else {
                        let max = if v.len() > 2 {
                            v[2].parse::<usize>().unwrap()
                        } else {
                            10
                        };
                        do_rdeps(&graph, v[1].to_lowercase().as_str(), &filter, max)
                    }
                }
                "deps" => {
                    if v.len() < 2 {
                        println!("Missing package name\n")
                    } else {
                        do_deps(&datastore, &graph, v[1].to_lowercase().as_str(), &filter)
                    }
                }
                "check" => {
                    if v.len() < 2 {
                        println!("Missing package name\n")
                    } else {
                        do_check(&datastore, &graph, v[1].to_lowercase().as_str(), &filter)
                    }
                }
                "exit" => done = true,
                _ => println!("Unknown command\n"),
            }
        }
    }
}

fn do_help() {
    println!("Commands:");
    println!("  help                    Print this message");
    println!("  stats                   Print graph statistics");
    println!("  top     [<count>]       Print nodes with the most reverse dependencies");
    println!("  filter  [<origin>]      Filter outputs to the specified origin");
    println!("  resolve <name>          Find the most recent version of the package 'origin/name'");
    println!("  find    <term> [<max>]  Find packages that match the search term, up to max items");
    println!("  rdeps   <name> [<max>]  Print the reverse dependencies for the package, up to max");
    println!("  deps    <name>|<ident>  Print the forward dependencies for the package");
    println!("  check   <name>|<ident>  Validate the latest dependencies for the package");
    println!("  exit                    Exit the application\n");
}

fn do_stats(graph: &PackageGraph) {
    let stats = graph.stats();

    println!("Node count: {}", stats.node_count);
    println!("Edge count: {}", stats.edge_count);
    println!("Connected components: {}", stats.connected_comp);
    println!("Is cyclic: {}", stats.is_cyclic);
}

fn do_top(graph: &PackageGraph, count: usize) {
    let start_time = PreciseTime::now();
    let top = graph.top(count);
    let end_time = PreciseTime::now();

    println!("OK: {} items ({} sec)\n",
             top.len(),
             start_time.to(end_time));

    for (name, count) in top {
        println!("{}: {}", name, count);
    }
    println!("");
}

fn do_find(graph: &PackageGraph, phrase: &str, max: usize) {
    let start_time = PreciseTime::now();
    let mut v = graph.search(phrase);
    let end_time = PreciseTime::now();

    println!("OK: {} items ({} sec)\n", v.len(), start_time.to(end_time));

    if v.is_empty() {
        println!("No matching packages found")
    } else {
        if v.len() > max {
            v.drain(max..);
        }
        for s in v {
            println!("{}", s);
        }
    }

    println!("");
}

fn do_resolve(graph: &PackageGraph, name: &str) {
    let start_time = PreciseTime::now();
    let result = graph.resolve(name);
    let end_time = PreciseTime::now();

    println!("OK: ({} sec)\n", start_time.to(end_time));

    match result {
        Some(s) => println!("{}", s),
        None => println!("No matching packages found"),
    }

    println!("");
}

fn do_rdeps(graph: &PackageGraph, name: &str, filter: &str, max: usize) {
    let start_time = PreciseTime::now();

    match graph.rdeps(name) {
        Some(mut rdeps) => {
            let end_time = PreciseTime::now();
            println!("OK: {} items ({} sec)\n",
                     rdeps.len(),
                     start_time.to(end_time));

            if rdeps.len() > max {
                rdeps.drain(max..);
            }

            if filter.len() > 0 {
                println!("Results filtered by: {}", filter);
            }

            for (s1, s2) in rdeps {
                if s1.starts_with(filter) {
                    println!("{} ({})", s1, s2);
                }
            }
        }
        None => println!("No entries found"),
    }

    println!("");
}

fn resolve_name(graph: &PackageGraph, name: &str) -> String {
    let parts: Vec<&str> = name.split("/").collect();
    if parts.len() == 2 {
        match graph.resolve(name) {
            Some(s) => s,
            None => String::from(name),
        }
    } else {
        String::from(name)
    }
}

fn do_deps(datastore: &DataStore, graph: &PackageGraph, name: &str, filter: &str) {
    let start_time = PreciseTime::now();
    let ident = resolve_name(graph, name);

    println!("Dependencies for: {}", ident);

    match datastore.get_package(&ident) {
        Ok(package) => {
            let end_time = PreciseTime::now();
            println!("OK: {} items ({} sec)\n",
                     package.get_deps().len(),
                     start_time.to(end_time));

            if filter.len() > 0 {
                println!("Results filtered by: {}\n", filter);
            }

            for dep in package.get_deps() {
                if dep.starts_with(filter) {
                    println!("{}", dep)
                }
            }
        }
        Err(_) => println!("No matching package found"),
    }

    println!("");
}

fn short_name(ident: &str) -> String {
    let parts: Vec<&str> = ident.split("/").collect();
    assert!(parts.len() >= 2);
    format!("{}/{}", parts[0], parts[1])
}

fn do_check(datastore: &DataStore, graph: &PackageGraph, name: &str, filter: &str) {
    let start_time = PreciseTime::now();
    let mut deps_map = HashMap::new();
    let mut new_deps = Vec::new();
    let ident = resolve_name(graph, name);

    match datastore.get_package(&ident) {
        Ok(package) => {
            if filter.len() > 0 {
                println!("Checks filtered by: {}\n", filter);
            }

            println!("Dependecy version updates:");
            for dep in package.get_deps() {
                if dep.starts_with(filter) {
                    let dep_name = short_name(dep);
                    let dep_latest = resolve_name(graph, &dep_name);
                    deps_map.insert(dep_name, dep_latest.clone());
                    new_deps.push(dep_latest.clone());
                    println!("{} -> {}", dep, dep_latest);
                }
            }

            println!("");

            for new_dep in new_deps {
                check_package(datastore, &mut deps_map, &new_dep, filter);
            }
        }
        Err(_) => println!("No matching package found"),
    }

    let end_time = PreciseTime::now();
    println!("\nTime: {} sec\n", start_time.to(end_time));
}

fn check_package(datastore: &DataStore,
                 mut deps_map: &mut HashMap<String, String>,
                 ident: &str,
                 filter: &str) {
    match datastore.get_package(ident) {
        Ok(package) => {
            for dep in package.get_deps() {
                if dep.starts_with(filter) {
                    let name = short_name(dep);
                    {
                        let entry = deps_map.entry(name).or_insert(dep.clone());
                        if *entry != *dep {
                            println!("Conflict: {}", ident);
                            println!("  {}", *entry);
                            println!("  {}", dep);
                        }
                    }
                    check_package(datastore, deps_map, dep, filter);
                }
            }
        }
        Err(_) => println!("No matching package found for {}", ident),
    };
}
<commit_msg>Better filtering for rdeps<commit_after>
// Copyright (c) 2017 Chef Software Inc. and/or applicable contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#[macro_use]
extern crate log;
extern crate env_logger;
extern crate time;
extern crate petgraph;
extern crate walkdir;
extern crate habitat_core as hab_core;
extern crate builder_core as bldr_core;
extern crate habitat_builder_protocol as protocol;
extern crate habitat_builder_db as db;
extern crate habitat_net as hab_net;
extern crate clap;
extern crate postgres;
extern crate protobuf;
extern crate r2d2;
extern crate serde;
#[macro_use]
extern crate serde_derive;

pub mod config;
pub mod data_store;
pub mod error;

use std::io::{self, Write};
use clap::{Arg, App};
use time::PreciseTime;
use bldr_core::package_graph::PackageGraph;
use hab_core::config::ConfigFile;
use config::Config;
use data_store::DataStore;
use std::collections::HashMap;

const VERSION: &'static str = include_str!(concat!(env!("OUT_DIR"), "/VERSION"));

fn main() {
    env_logger::init().unwrap();

    let matches = App::new("bldr-graph")
        .version(VERSION)
        .about("Habitat Graph Dev Tool")
        .arg(Arg::with_name("config")
                 .help("Filepath to configuration file")
                 .required(false)
                 .index(1))
        .get_matches();

    let config = match matches.value_of("config") {
        Some(cfg_path) => Config::from_file(cfg_path).unwrap(),
        None => Config::default(),
    };

    println!("Connecting to {}", config.datastore.database);

    let datastore = DataStore::new(&config).unwrap();
    datastore.setup().unwrap();

    println!("Building graph... please wait.");

    let mut graph = PackageGraph::new();
    let packages = datastore.get_packages().unwrap();
    let start_time = PreciseTime::now();
    let (ncount, ecount) = graph.build(packages.into_iter());
    let end_time = PreciseTime::now();

    println!("OK: {} nodes, {} edges ({} sec)",
             ncount,
             ecount,
             start_time.to(end_time));

    println!("\nAvailable commands: help, stats, top, find, resolve, filter, rdeps, deps, check, exit\n",);

    let mut filter = String::from("");
    let mut done = false;
    while !done {
        print!("command> ");
        io::stdout().flush().ok().expect("Could not flush stdout");

        let mut cmd = String::new();
        io::stdin().read_line(&mut cmd).unwrap();

        let v: Vec<&str> = cmd.trim_right().split_whitespace().collect();

        if v.len() > 0 {
            match v[0].to_lowercase().as_str() {
                "help" => do_help(),
                "stats" => do_stats(&graph),
                "top" => {
                    let count = if v.len() < 2 {
                        10
                    } else {
                        v[1].parse::<usize>().unwrap()
                    };
                    do_top(&graph, count);
                }
                "filter" => {
                    if v.len() < 2 {
                        filter = String::from("");
                        println!("Removed filter\n");
                    } else {
                        filter = String::from(v[1]);
                        println!("New filter: {}\n", filter);
                    }
                }
                "find" => {
                    if v.len() < 2 {
                        println!("Missing search term\n")
                    } else {
                        let max = if v.len() > 2 {
                            v[2].parse::<usize>().unwrap()
                        } else {
                            10
                        };
                        do_find(&graph, v[1].to_lowercase().as_str(), max)
                    }
                }
                "resolve" => {
                    if v.len() < 2 {
                        println!("Missing package name\n")
                    } else {
                        do_resolve(&graph, v[1].to_lowercase().as_str())
                    }
                }
                "rdeps" => {
                    if v.len() < 2 {
                        println!("Missing package name\n")
                    } else {
                        let max = if v.len() > 2 {
                            v[2].parse::<usize>().unwrap()
                        } else {
                            10
                        };
                        do_rdeps(&graph, v[1].to_lowercase().as_str(), &filter, max)
                    }
                }
                "deps" => {
                    if v.len() < 2 {
                        println!("Missing package name\n")
                    } else {
                        do_deps(&datastore, &graph, v[1].to_lowercase().as_str(), &filter)
                    }
                }
                "check" => {
                    if v.len() < 2 {
                        println!("Missing package name\n")
                    } else {
                        do_check(&datastore, &graph, v[1].to_lowercase().as_str(), &filter)
                    }
                }
                "exit" => done = true,
                _ => println!("Unknown command\n"),
            }
        }
    }
}

fn do_help() {
    println!("Commands:");
    println!("  help                    Print this message");
    println!("  stats                   Print graph statistics");
    println!("  top     [<count>]       Print nodes with the most reverse dependencies");
    println!("  filter  [<origin>]      Filter outputs to the specified origin");
    println!("  resolve <name>          Find the most recent version of the package 'origin/name'");
    println!("  find    <term> [<max>]  Find packages that match the search term, up to max items");
    println!("  rdeps   <name> [<max>]  Print the reverse dependencies for the package, up to max");
    println!("  deps    <name>|<ident>  Print the forward dependencies for the package");
    println!("  check   <name>|<ident>  Validate the latest dependencies for the package");
    println!("  exit                    Exit the application\n");
}

fn do_stats(graph: &PackageGraph) {
    let stats = graph.stats();

    println!("Node count: {}", stats.node_count);
    println!("Edge count: {}", stats.edge_count);
    println!("Connected components: {}", stats.connected_comp);
    println!("Is cyclic: {}", stats.is_cyclic);
}

fn do_top(graph: &PackageGraph, count: usize) {
    let start_time = PreciseTime::now();
    let top = graph.top(count);
    let end_time = PreciseTime::now();

    println!("OK: {} items ({} sec)\n",
             top.len(),
             start_time.to(end_time));

    for (name, count) in top {
        println!("{}: {}", name, count);
    }
    println!("");
}

fn do_find(graph: &PackageGraph, phrase: &str, max: usize) {
    let start_time = PreciseTime::now();
    let mut v = graph.search(phrase);
    let end_time = PreciseTime::now();

    println!("OK: {} items ({} sec)\n", v.len(), start_time.to(end_time));

    if v.is_empty() {
        println!("No matching packages found")
    } else {
        if v.len() > max {
            v.drain(max..);
        }
        for s in v {
            println!("{}", s);
        }
    }

    println!("");
}

fn do_resolve(graph: &PackageGraph, name: &str) {
    let start_time = PreciseTime::now();
    let result = graph.resolve(name);
    let end_time = PreciseTime::now();

    println!("OK: ({} sec)\n", start_time.to(end_time));

    match result {
        Some(s) => println!("{}", s),
        None => println!("No matching packages found"),
    }

    println!("");
}

fn do_rdeps(graph: &PackageGraph, name: &str, filter: &str, max: usize) {
    let start_time = PreciseTime::now();

    match graph.rdeps(name) {
        Some(rdeps) => {
            let end_time = PreciseTime::now();
            let mut filtered: Vec<(String, String)> = rdeps
                .into_iter()
                .filter(|&(ref x, _)| x.starts_with(filter))
                .collect();

            println!("OK: {} items ({} sec)\n",
                     filtered.len(),
                     start_time.to(end_time));

            if filtered.len() > max {
                filtered.drain(max..);
            }

            if filter.len() > 0 {
                println!("Results filtered by: {}", filter);
            }

            for (s1, s2) in filtered {
                println!("{} ({})", s1, s2);
            }
        }
        None => println!("No entries found"),
    }

    println!("");
}

fn resolve_name(graph: &PackageGraph, name: &str) -> String {
    let parts: Vec<&str> = name.split("/").collect();
    if parts.len() == 2 {
        match graph.resolve(name) {
            Some(s) => s,
            None => String::from(name),
        }
    } else {
        String::from(name)
    }
}

fn do_deps(datastore: &DataStore, graph: &PackageGraph, name: &str, filter: &str) {
    let start_time = PreciseTime::now();
    let ident = resolve_name(graph, name);

    println!("Dependencies for: {}", ident);

    match datastore.get_package(&ident) {
        Ok(package) => {
            let end_time = PreciseTime::now();
            println!("OK: {} items ({} sec)\n",
                     package.get_deps().len(),
                     start_time.to(end_time));

            if filter.len() > 0 {
                println!("Results filtered by: {}\n", filter);
            }

            for dep in package.get_deps() {
                if dep.starts_with(filter) {
                    println!("{}", dep)
                }
            }
        }
        Err(_) => println!("No matching package found"),
    }

    println!("");
}

fn short_name(ident: &str) -> String {
    let parts: Vec<&str> = ident.split("/").collect();
    assert!(parts.len() >= 2);
    format!("{}/{}", parts[0], parts[1])
}

fn do_check(datastore: &DataStore, graph: &PackageGraph, name: &str, filter: &str) {
    let start_time = PreciseTime::now();
    let mut deps_map = HashMap::new();
    let mut new_deps = Vec::new();
    let ident = resolve_name(graph, name);

    match datastore.get_package(&ident) {
        Ok(package) => {
            if filter.len() > 0 {
                println!("Checks filtered by: {}\n", filter);
            }

            println!("Dependecy version updates:");
            for dep in package.get_deps() {
                if dep.starts_with(filter) {
                    let dep_name = short_name(dep);
                    let dep_latest = resolve_name(graph, &dep_name);
                    deps_map.insert(dep_name, dep_latest.clone());
                    new_deps.push(dep_latest.clone());
                    println!("{} -> {}", dep, dep_latest);
                }
            }

            println!("");

            for new_dep in new_deps {
                check_package(datastore, &mut deps_map, &new_dep, filter);
            }
        }
        Err(_) => println!("No matching package found"),
    }

    let end_time = PreciseTime::now();
    println!("\nTime: {} sec\n", start_time.to(end_time));
}

fn check_package(datastore: &DataStore,
                 mut deps_map: &mut HashMap<String, String>,
                 ident: &str,
                 filter: &str) {
    match datastore.get_package(ident) {
        Ok(package) => {
            for dep in package.get_deps() {
                if dep.starts_with(filter) {
                    let name = short_name(dep);
                    {
                        let entry = deps_map.entry(name).or_insert(dep.clone());
                        if *entry != *dep {
                            println!("Conflict: {}", ident);
                            println!("  {}", *entry);
                            println!("  {}", dep);
                        }
                    }
                    check_package(datastore, deps_map, dep, filter);
                }
            }
        }
        Err(_) => println!("No matching package found for {}", ident),
    };
}
<|endoftext|>


<commit_before>// Copyright 2020 The Exonum Team
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use exonum_merkledb::{
    access::{AsReadonly, FromAccess, Prefixed, RawAccess},
    Snapshot, SystemSchema,
};

use super::{
    versioning::{ArtifactReqError, RequireArtifact},
    DispatcherSchema, InstanceQuery, InstanceSpec, InstanceStatus,
};
use crate::blockchain::{IndexProof, Schema as CoreSchema};

/// Provides access to blockchain data for the executing service.
#[derive(Debug, Clone)]
pub struct BlockchainData<T> {
    access: T,
    instance_name: String,
}

impl<T: RawAccess + AsReadonly> BlockchainData<T> {
    /// Creates structured access to blockchain data based on the unstructured access
    /// (e.g., a `Snapshot` or a `Fork`) and the descriptor of the executing service.
    pub fn new(access: T, instance_name: impl Into<String>) -> Self {
        Self {
            access,
            instance_name: instance_name.into(),
        }
    }

    /// Returns the instance name of the executing service.
    pub fn instance_name(&self) -> &str {
        &self.instance_name
    }

    /// Returns unstructured readonly access to blockchain data.
    ///
    /// # Safety
    ///
    /// Trying to access data of the executing service through the returned access
    /// can lead to a panic because of borrowing checks performed by the database.
    /// Check that your wrapper prevent such attempts to access.
    #[doc(hidden)]
    pub fn unstructured_access(&self) -> T::Readonly {
        self.access.as_readonly()
    }

    /// Returns core schema.
    pub fn for_core(&self) -> CoreSchema<T::Readonly> {
        CoreSchema::new(self.unstructured_access())
    }

    /// Returns dispatcher schema.
    pub fn for_dispatcher(&self) -> DispatcherSchema<T::Readonly> {
        DispatcherSchema::new(self.unstructured_access())
    }

    /// Returns a mount point for another service. If the service with `id` does not exist,
    /// returns `None`.
    ///
    /// # Safety
    ///
    /// This method does not check the service type; the caller is responsible
    /// for constructing a schema of a correct type around the returned access. Constructing
    /// an incorrect schema can lead to a panic or unexpected behavior. Use [`service_schema`]
    /// as a safer alternative, which performs all necessary checks.
    ///
    /// [`service_schema`]: #method.service_schema
    pub fn for_service<'q>(
        &self,
        id: impl Into<InstanceQuery<'q>>,
    ) -> Option<Prefixed<T::Readonly>> {
        mount_point_for_service(self.access.as_readonly(), id).map(|(access, _)| access)
    }

    /// Retrieves schema for a service.
    ///
    /// # Errors
    ///
    /// Returns an error in the following situations (see [`ArtifactReqError`] for more details):
    ///
    /// - Service with the given ID does not exist
    /// - Service has an unexpected artifact name
    /// - Service has an incompatible artifact version
    ///
    /// [`ArtifactReqError`]: versioning/enum.ArtifactReqError.html
    pub fn service_schema<'q, S, I>(&self, service_id: I) -> Result<S, ArtifactReqError>
    where
        S: RequireArtifact + FromAccess<Prefixed<T::Readonly>>,
        I: Into<InstanceQuery<'q>>,
    {
        schema_for_service(self.access.as_readonly(), service_id)
    }

    /// Returns a mount point for the data of the executing service instance.
    /// Unlike other data, this one may be writeable provided that this `BlockchainData`
    /// wraps a `Fork`.
    pub fn for_executing_service(&self) -> Prefixed<T> {
        Prefixed::new(&self.instance_name, self.access.clone())
    }
}

impl BlockchainData<&dyn Snapshot> {
    /// Returns a proof for a Merkelized index with the specified name
    /// in the currently executing service.
    ///
    /// # Return value
    ///
    /// If the index does not exist or is not Merkelized, returns `None`. Note that this may
    /// occur before the index is accessed the first time, which, depending on the service logic,
    /// may happen indefinitely after the service has been initialized. Thus, `unwrap`ping the
    /// returned value may unexpectedly lead to a panic unless the index is initialized early
    /// (e.g., during service initialization).
    pub fn proof_for_service_index(&self, index_name: &str) -> Option<IndexProof> {
        let full_index_name = [&self.instance_name, ".", index_name].concat();
        self.access.proof_for_index(&full_index_name)
    }
}

fn mount_point_for_service<'q, T: RawAccess>(
    access: T,
    id: impl Into<InstanceQuery<'q>>,
) -> Option<(Prefixed<T>, InstanceSpec)> {
    let state = DispatcherSchema::new(access.clone())
        .get_instance(id)
        .filter(|state| match (&state.status, &state.pending_status) {
            (Some(InstanceStatus::Active), _) | (None, Some(InstanceStatus::Active)) => true,
            _ => false,
        })?;
    Some((Prefixed::new(state.spec.name.clone(), access), state.spec))
}

fn schema_for_service<'q, T, S>(
    access: T,
    service_id: impl Into<InstanceQuery<'q>>,
) -> Result<S, ArtifactReqError>
where
    T: RawAccess,
    S: RequireArtifact + FromAccess<Prefixed<T>>,
{
    let (access, spec) =
        mount_point_for_service(access, service_id).ok_or(ArtifactReqError::NoService)?;

    let artifact_req = S::required_artifact();
    artifact_req.try_match(&spec.artifact)?;
    Ok(S::from_root(access).unwrap())
}

/// Extension trait for `Snapshot` allowing to access blockchain data in a more structured way.
pub trait SnapshotExt {
    /// Returns core schema.
    fn for_core(&self) -> CoreSchema<&'_ dyn Snapshot>;
    /// Returns dispatcher schema.
    fn for_dispatcher(&self) -> DispatcherSchema<&'_ dyn Snapshot>;

    /// Returns a mount point for a service. If the service does not exist, returns `None`.
    ///
    /// # Safety
    ///
    /// This method does not check the service type; the caller is responsible
    /// for constructing a schema of a correct type around the returned access. Constructing
    /// an incorrect schema can lead to a panic or unexpected behavior. Use [`service_schema`]
    /// as a safer alternative, which performs all necessary checks.
    ///
    /// [`service_schema`]: #tymethod.service_schema
    fn for_service<'q>(&self, id: impl Into<InstanceQuery<'q>>) -> Option<Prefixed<&dyn Snapshot>>;

    /// Returns a proof for a Merkelized index with the specified name.
    ///
    /// # Return value
    ///
    /// If the index does not exist or is not Merkelized, returns `None`. Note that this may
    /// occur before the index is accessed the first time, which, depending on the service logic,
    /// may happen indefinitely after the service has been initialized. Thus, `unwrap`ping the
    /// returned value may unexpectedly lead to a panic unless the index is initialized early
    /// (e.g., during service initialization).
    #[doc(hidden)]
    fn proof_for_index(&self, index_name: &str) -> Option<IndexProof>;

    /// Retrieves schema for a service.
    ///
    /// # Errors
    ///
    /// Returns an error in the following situations (see [`ArtifactReqError`] for more details):
    ///
    /// - Service with the given ID does not exist
    /// - Service has an unexpected artifact name
    /// - Service has an incompatible artifact version
    ///
    /// [`ArtifactReqError`]: versioning/enum.ArtifactReqError.html
    fn service_schema<'s, 'q, S, I>(&'s self, service_id: I) -> Result<S, ArtifactReqError>
    where
        S: RequireArtifact + FromAccess<Prefixed<&'s dyn Snapshot>>,
        I: Into<InstanceQuery<'q>>;
}

impl SnapshotExt for dyn Snapshot {
    fn for_core(&self) -> CoreSchema<&'_ dyn Snapshot> {
        CoreSchema::new(self)
    }

    fn for_dispatcher(&self) -> DispatcherSchema<&'_ dyn Snapshot> {
        DispatcherSchema::new(self)
    }

    fn for_service<'q>(&self, id: impl Into<InstanceQuery<'q>>) -> Option<Prefixed<&dyn Snapshot>> {
        mount_point_for_service(self, id).map(|(access, _)| access)
    }

    fn proof_for_index(&self, index_name: &str) -> Option<IndexProof> {
        let core_schema = self.for_core();
        let height = core_schema.height();
        let block_proof = core_schema.block_and_precommits(height).unwrap();

        let aggregator = SystemSchema::new(self).state_aggregator();
        aggregator.get(index_name)?;
        let index_proof = aggregator.get_proof(index_name.to_string());
        Some(IndexProof::new(block_proof, index_proof))
    }

    fn service_schema<'s, 'q, S, I>(&'s self, service_id: I) -> Result<S, ArtifactReqError>
    where
        S: RequireArtifact + FromAccess<Prefixed<&'s dyn Snapshot>>,
        I: Into<InstanceQuery<'q>>,
    {
        schema_for_service(self, service_id)
    }
}
<commit_msg>Add BlockchainData::erase_access method (#1767)<commit_after>// Copyright 2020 The Exonum Team
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use exonum_merkledb::{
    access::{AsReadonly, FromAccess, Prefixed, RawAccess},
    generic::GenericRawAccess,
    Snapshot, SystemSchema,
};

use super::{
    versioning::{ArtifactReqError, RequireArtifact},
    DispatcherSchema, InstanceQuery, InstanceSpec, InstanceStatus,
};
use crate::blockchain::{IndexProof, Schema as CoreSchema};

/// Provides access to blockchain data for the executing service.
#[derive(Debug, Clone)]
pub struct BlockchainData<T> {
    access: T,
    instance_name: String,
}

impl<T: RawAccess + AsReadonly> BlockchainData<T> {
    /// Creates structured access to blockchain data based on the unstructured access
    /// (e.g., a `Snapshot` or a `Fork`) and the descriptor of the executing service.
    pub fn new(access: T, instance_name: impl Into<String>) -> Self {
        Self {
            access,
            instance_name: instance_name.into(),
        }
    }

    /// Returns the instance name of the executing service.
    pub fn instance_name(&self) -> &str {
        &self.instance_name
    }

    /// Returns unstructured readonly access to blockchain data.
    ///
    /// # Safety
    ///
    /// Trying to access data of the executing service through the returned access
    /// can lead to a panic because of borrowing checks performed by the database.
    /// Check that your wrapper prevent such attempts to access.
    #[doc(hidden)]
    pub fn unstructured_access(&self) -> T::Readonly {
        self.access.as_readonly()
    }

    /// Returns core schema.
    pub fn for_core(&self) -> CoreSchema<T::Readonly> {
        CoreSchema::new(self.unstructured_access())
    }

    /// Returns dispatcher schema.
    pub fn for_dispatcher(&self) -> DispatcherSchema<T::Readonly> {
        DispatcherSchema::new(self.unstructured_access())
    }

    /// Returns a mount point for another service. If the service with `id` does not exist,
    /// returns `None`.
    ///
    /// # Safety
    ///
    /// This method does not check the service type; the caller is responsible
    /// for constructing a schema of a correct type around the returned access. Constructing
    /// an incorrect schema can lead to a panic or unexpected behavior. Use [`service_schema`]
    /// as a safer alternative, which performs all necessary checks.
    ///
    /// [`service_schema`]: #method.service_schema
    pub fn for_service<'q>(
        &self,
        id: impl Into<InstanceQuery<'q>>,
    ) -> Option<Prefixed<T::Readonly>> {
        mount_point_for_service(self.access.as_readonly(), id).map(|(access, _)| access)
    }

    /// Retrieves schema for a service.
    ///
    /// # Errors
    ///
    /// Returns an error in the following situations (see [`ArtifactReqError`] for more details):
    ///
    /// - Service with the given ID does not exist
    /// - Service has an unexpected artifact name
    /// - Service has an incompatible artifact version
    ///
    /// [`ArtifactReqError`]: versioning/enum.ArtifactReqError.html
    pub fn service_schema<'q, S, I>(&self, service_id: I) -> Result<S, ArtifactReqError>
    where
        S: RequireArtifact + FromAccess<Prefixed<T::Readonly>>,
        I: Into<InstanceQuery<'q>>,
    {
        schema_for_service(self.access.as_readonly(), service_id)
    }

    /// Returns a mount point for the data of the executing service instance.
    /// Unlike other data, this one may be writeable provided that this `BlockchainData`
    /// wraps a `Fork`.
    pub fn for_executing_service(&self) -> Prefixed<T> {
        Prefixed::new(&self.instance_name, self.access.clone())
    }
}

impl BlockchainData<&dyn Snapshot> {
    /// Returns a proof for a Merkelized index with the specified name
    /// in the currently executing service.
    ///
    /// # Return value
    ///
    /// If the index does not exist or is not Merkelized, returns `None`. Note that this may
    /// occur before the index is accessed the first time, which, depending on the service logic,
    /// may happen indefinitely after the service has been initialized. Thus, `unwrap`ping the
    /// returned value may unexpectedly lead to a panic unless the index is initialized early
    /// (e.g., during service initialization).
    pub fn proof_for_service_index(&self, index_name: &str) -> Option<IndexProof> {
        let full_index_name = [&self.instance_name, ".", index_name].concat();
        self.access.proof_for_index(&full_index_name)
    }
}

impl<'a, T> BlockchainData<T>
where
    T: Into<GenericRawAccess<'a>>,
{
    /// Erases the enclosed access, converting it to the generic form.
    pub fn erase_access(self) -> BlockchainData<GenericRawAccess<'a>> {
        BlockchainData::new(self.access.into(), self.instance_name)
    }
}

fn mount_point_for_service<'q, T: RawAccess>(
    access: T,
    id: impl Into<InstanceQuery<'q>>,
) -> Option<(Prefixed<T>, InstanceSpec)> {
    let state = DispatcherSchema::new(access.clone())
        .get_instance(id)
        .filter(|state| match (&state.status, &state.pending_status) {
            (Some(InstanceStatus::Active), _) | (None, Some(InstanceStatus::Active)) => true,
            _ => false,
        })?;
    Some((Prefixed::new(state.spec.name.clone(), access), state.spec))
}

fn schema_for_service<'q, T, S>(
    access: T,
    service_id: impl Into<InstanceQuery<'q>>,
) -> Result<S, ArtifactReqError>
where
    T: RawAccess,
    S: RequireArtifact + FromAccess<Prefixed<T>>,
{
    let (access, spec) =
        mount_point_for_service(access, service_id).ok_or(ArtifactReqError::NoService)?;

    let artifact_req = S::required_artifact();
    artifact_req.try_match(&spec.artifact)?;
    Ok(S::from_root(access).unwrap())
}

/// Extension trait for `Snapshot` allowing to access blockchain data in a more structured way.
pub trait SnapshotExt {
    /// Returns core schema.
    fn for_core(&self) -> CoreSchema<&'_ dyn Snapshot>;
    /// Returns dispatcher schema.
    fn for_dispatcher(&self) -> DispatcherSchema<&'_ dyn Snapshot>;

    /// Returns a mount point for a service. If the service does not exist, returns `None`.
    ///
    /// # Safety
    ///
    /// This method does not check the service type; the caller is responsible
    /// for constructing a schema of a correct type around the returned access. Constructing
    /// an incorrect schema can lead to a panic or unexpected behavior. Use [`service_schema`]
    /// as a safer alternative, which performs all necessary checks.
    ///
    /// [`service_schema`]: #tymethod.service_schema
    fn for_service<'q>(&self, id: impl Into<InstanceQuery<'q>>) -> Option<Prefixed<&dyn Snapshot>>;

    /// Returns a proof for a Merkelized index with the specified name.
    ///
    /// # Return value
    ///
    /// If the index does not exist or is not Merkelized, returns `None`. Note that this may
    /// occur before the index is accessed the first time, which, depending on the service logic,
    /// may happen indefinitely after the service has been initialized. Thus, `unwrap`ping the
    /// returned value may unexpectedly lead to a panic unless the index is initialized early
    /// (e.g., during service initialization).
    #[doc(hidden)]
    fn proof_for_index(&self, index_name: &str) -> Option<IndexProof>;

    /// Retrieves schema for a service.
    ///
    /// # Errors
    ///
    /// Returns an error in the following situations (see [`ArtifactReqError`] for more details):
    ///
    /// - Service with the given ID does not exist
    /// - Service has an unexpected artifact name
    /// - Service has an incompatible artifact version
    ///
    /// [`ArtifactReqError`]: versioning/enum.ArtifactReqError.html
    fn service_schema<'s, 'q, S, I>(&'s self, service_id: I) -> Result<S, ArtifactReqError>
    where
        S: RequireArtifact + FromAccess<Prefixed<&'s dyn Snapshot>>,
        I: Into<InstanceQuery<'q>>;
}

impl SnapshotExt for dyn Snapshot {
    fn for_core(&self) -> CoreSchema<&'_ dyn Snapshot> {
        CoreSchema::new(self)
    }

    fn for_dispatcher(&self) -> DispatcherSchema<&'_ dyn Snapshot> {
        DispatcherSchema::new(self)
    }

    fn for_service<'q>(&self, id: impl Into<InstanceQuery<'q>>) -> Option<Prefixed<&dyn Snapshot>> {
        mount_point_for_service(self, id).map(|(access, _)| access)
    }

    fn proof_for_index(&self, index_name: &str) -> Option<IndexProof> {
        let core_schema = self.for_core();
        let height = core_schema.height();
        let block_proof = core_schema.block_and_precommits(height).unwrap();

        let aggregator = SystemSchema::new(self).state_aggregator();
        aggregator.get(index_name)?;
        let index_proof = aggregator.get_proof(index_name.to_string());
        Some(IndexProof::new(block_proof, index_proof))
    }

    fn service_schema<'s, 'q, S, I>(&'s self, service_id: I) -> Result<S, ArtifactReqError>
    where
        S: RequireArtifact + FromAccess<Prefixed<&'s dyn Snapshot>>,
        I: Into<InstanceQuery<'q>>,
    {
        schema_for_service(self, service_id)
    }
}
<|endoftext|>