<commit_before>#[allow(dead_code)]
pub mod command_grammar {
    include!(concat!(env!("OUT_DIR"), "/command_grammar.rs"));
}

#[derive(Debug, PartialEq, Eq)]
pub enum DslToken {
    BlockOpen,
    BlockClose,
    With(String),
    Command(String),
    Comment(String),
}

pub type DslTokens = Vec<DslToken>;

#[derive(Debug)]
pub enum DslTree {
    Scope(String, Vec<DslTree>),
    Command(String),
    Comment(String),
}

#[derive(Debug, Eq, PartialEq)]
pub enum XFlowCommand {
    AddNode(String),
}

#[derive(Debug, Eq, PartialEq)]
pub enum ConfigCommand {
    SetDefaultLocale(String),
}

pub trait GearsDsl {
    fn generate_dsl(&self) -> DslTokens;
    fn consume_dsl_tree(&mut self, tree: &Vec<DslTree>) -> Result<(), String>;
    fn consume_command(&mut self, cmd: &str) -> Result<(), String>;

    fn consume_dsl(&mut self, items: &[DslToken]) -> Result<(), String> {
        match tokens_as_tree(&items) {
            Ok(tree) => self.consume_dsl_tree(&tree),
            Err(err) => Err(err),
        }
    }

    fn generate_dsl_tree(&self) -> Result<Vec<DslTree>, String> {
        tokens_as_tree(&self.generate_dsl())
    }

    fn interpret_dsl(&mut self, txt: &str) -> Result<(), String> {
        debug!("interpret_dsl : {}", txt);
        match command_grammar::expression(&txt) {
            Ok(dsl_items) => {
                match self.consume_dsl(&dsl_items) {
                    Ok(_) => Ok(()),
                    Err(err) => {
                        error!("interpret_dsl : error with commands : {:?}", err);
                        Err(format!("interpret_dsl : error with commands : {:?}", err))
                    }
                }
            }
            Err(err) => {
                error!("interpret_dsl : error : {:?}", err);
                Err(format!("interpret_dsl : error : {:?}", err))
            }
        }
    }
    fn interpret_dsl_via_tree(&mut self, txt: &str) -> Result<(), String> {
        match command_grammar::expression(&txt) {
            Ok(dsl_items) => {
                match tokens_as_tree(&dsl_items) {
                    Ok(tree) => self.consume_dsl_tree(&tree),
                    Err(err) => {
                        error!("interpret_dsl : error with commands : {:?}", err);
                        Err(format!("interpret_dsl : error with commands : {:?}", err))
                    }
                }
            }
            Err(err) => {
                error!("interpret_dsl : error : {:?}", err);
                Err(format!("interpret_dsl : error : {:?}", err))
            }
        }
    }

    fn to_text_dsl(&self) -> String {
        let items = &self.generate_dsl();
        let indent_size = 4;
        let mut indent: usize = 0;
        let mut res = Vec::<String>::new();
        for item in items.iter() {
            match *item {
                DslToken::BlockOpen => {
                    res.push(format!("{{"));
                    indent += indent_size;
                }
                DslToken::BlockClose => {
                    res.push(format!("}};"));
                    indent -= indent_size;
                }
                DslToken::With(ref s) => {
                    res.push(format!(" with {label}", label = s));
                }
                DslToken::Command(ref s) => {
                    res.push(format!(" {cmd};", cmd = s));
                }
                DslToken::Comment(_) => {}
            }
        }

        res.join("\n")
    }
}

pub fn tokens_as_tree(tokens: &[DslToken]) -> Result<Vec<DslTree>, String> {

    fn to_tree(tokens: &[DslToken], offset: &mut usize) -> Result<Vec<DslTree>, String> {
        let mut res = Vec::<DslTree>::new();

        let mut subject = "".to_owned();

        debug!("tokens_as_tree entry {} : {}", offset, *offset);

        while *offset < tokens.len() {
            debug!("tokens_as_tree loop {} : {}", offset, *offset);
            match tokens[*offset] {
                DslToken::BlockOpen => {
                    *offset += 1;
                    match to_tree(&tokens, offset) {
                        Ok(out) => {
                            res.push(DslTree::Scope(subject.clone(), out));
                        }
                        Err(err) => {
                            return Err(err);
                        }
                    }
                }
                DslToken::BlockClose => {
                    return Ok(res);
                }
                DslToken::With(ref s) => {
                    subject = s.clone();

                    // Lookahead
                    if *offset >= (tokens.len() - 1) {
                        return Err(
                            "tokens_as_tree : With statement ends prematurely".to_owned(),
                        );
                    } else if (tokens[*offset + 1]).ne(&DslToken::BlockOpen) {
                        return Err(
                            "tokens_as_tree : Encountered With statement not followed by BlockOpen"
                                .to_owned(),
                        );
                    }
                }
                DslToken::Comment(ref c) => {
                    res.push(DslTree::Comment((*c).clone()));
                }
                DslToken::Command(ref c) => {
                    res.push(DslTree::Command((*c).clone()));
                }
            }
            *offset += 1;
        }

        Ok(res)
    }

    let mut offset: usize = 0;
    to_tree(&tokens, &mut offset)
}
<commit_msg>feat: Add structure DSL command<commit_after>#[allow(dead_code)]
pub mod command_grammar {
    include!(concat!(env!("OUT_DIR"), "/command_grammar.rs"));
}

#[derive(Debug, PartialEq, Eq)]
pub enum DslToken {
    BlockOpen,
    BlockClose,
    With(String),
    Command(String),
    Comment(String),
}

pub type DslTokens = Vec<DslToken>;

#[derive(Debug)]
pub enum DslTree {
    Scope(String, Vec<DslTree>),
    Command(String),
    Comment(String),
}

#[derive(Debug, Eq, PartialEq)]
pub enum XFlowCommand {
    AddNode(String),
}

#[derive(Debug, Eq, PartialEq)]
pub enum ConfigCommand {
    SetDefaultLocale(String),
}

pub trait GearsDsl {
    fn generate_dsl(&self) -> DslTokens;
    fn consume_dsl_tree(&mut self, tree: &Vec<DslTree>) -> Result<(), String>;
    fn consume_command(&mut self, cmd: &str) -> Result<(), String>;

    fn consume_dsl(&mut self, items: &[DslToken]) -> Result<(), String> {
        match tokens_as_tree(&items) {
            Ok(tree) => self.consume_dsl_tree(&tree),
            Err(err) => Err(err),
        }
    }

    fn generate_dsl_tree(&self) -> Result<Vec<DslTree>, String> {
        tokens_as_tree(&self.generate_dsl())
    }

    fn interpret_dsl(&mut self, txt: &str) -> Result<(), String> {
        debug!("interpret_dsl : {}", txt);
        match command_grammar::expression(&txt) {
            Ok(dsl_items) => {
                match self.consume_dsl(&dsl_items) {
                    Ok(_) => Ok(()),
                    Err(err) => {
                        error!("interpret_dsl : error with commands : {:?}", err);
                        Err(format!("interpret_dsl : error with commands : {:?}", err))
                    }
                }
            }
            Err(err) => {
                error!("interpret_dsl : error : {:?}", err);
                Err(format!("interpret_dsl : error : {:?}", err))
            }
        }
    }
    fn interpret_dsl_via_tree(&mut self, txt: &str) -> Result<(), String> {
        match command_grammar::expression(&txt) {
            Ok(dsl_items) => {
                match tokens_as_tree(&dsl_items) {
                    Ok(tree) => self.consume_dsl_tree(&tree),
                    Err(err) => {
                        error!("interpret_dsl : error with commands : {:?}", err);
                        Err(format!("interpret_dsl : error with commands : {:?}", err))
                    }
                }
            }
            Err(err) => {
                error!("interpret_dsl : error : {:?}", err);
                Err(format!("interpret_dsl : error : {:?}", err))
            }
        }
    }

    fn to_text_dsl(&self) -> String {
        let items = &self.generate_dsl();
        let indent_size = 4;
        let mut indent: usize = 0;
        let mut res = Vec::<String>::new();
        for item in items.iter() {
            match *item {
                DslToken::BlockOpen => {
                    res.push(format!("{{\n"));
                    indent += indent_size;
                }
                DslToken::BlockClose => {
                    res.push(format!("}};\n"));
                    indent -= indent_size;
                }
                DslToken::With(ref s) => {
                    res.push(format!(" with {label} ", label = s));
                }
                DslToken::Command(ref s) => {
                    res.push(format!(" {cmd};\n", cmd = s));
                }
                DslToken::Comment(_) => {}
            }
        }

        res.join("")
    }
}

pub fn tokens_as_tree(tokens: &[DslToken]) -> Result<Vec<DslTree>, String> {

    fn to_tree(tokens: &[DslToken], offset: &mut usize) -> Result<Vec<DslTree>, String> {
        let mut res = Vec::<DslTree>::new();

        let mut subject = "".to_owned();

        debug!("tokens_as_tree entry {} : {}", offset, *offset);

        while *offset < tokens.len() {
            debug!("tokens_as_tree loop {} : {}", offset, *offset);
            match tokens[*offset] {
                DslToken::BlockOpen => {
                    *offset += 1;
                    match to_tree(&tokens, offset) {
                        Ok(out) => {
                            res.push(DslTree::Scope(subject.clone(), out));
                        }
                        Err(err) => {
                            return Err(err);
                        }
                    }
                }
                DslToken::BlockClose => {
                    return Ok(res);
                }
                DslToken::With(ref s) => {
                    subject = s.clone();

                    // Lookahead
                    if *offset >= (tokens.len() - 1) {
                        return Err(
                            "tokens_as_tree : With statement ends prematurely".to_owned(),
                        );
                    } else if (tokens[*offset + 1]).ne(&DslToken::BlockOpen) {
                        return Err(
                            "tokens_as_tree : Encountered With statement not followed by BlockOpen"
                                .to_owned(),
                        );
                    }
                }
                DslToken::Comment(ref c) => {
                    res.push(DslTree::Comment((*c).clone()));
                }
                DslToken::Command(ref c) => {
                    res.push(DslTree::Command((*c).clone()));
                }
            }
            *offset += 1;
        }

        Ok(res)
    }

    let mut offset: usize = 0;
    to_tree(&tokens, &mut offset)
}
<|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 https://mozilla.org/MPL/2.0/. */

//! Misc information about a given computed style.

bitflags! {
    /// Misc information about a given computed style.
    ///
    /// All flags are currently inherited for text, pseudo elements, and
    /// anonymous boxes, see StyleBuilder::for_inheritance and its callsites.
    /// If we ever want to add some flags that shouldn't inherit for them,
    /// we might want to add a function to handle this.
    pub struct ComputedValueFlags: u16 {
        /// Whether the style or any of the ancestors has a text-decoration-line
        /// property that should get propagated to descendants.
        ///
        /// text-decoration-line is a reset property, but gets propagated in the
        /// frame/box tree.
        const HAS_TEXT_DECORATION_LINES = 1 << 0;

        /// Whether line break inside should be suppressed.
        ///
        /// If this flag is set, the line should not be broken inside,
        /// which means inlines act as if nowrap is set,
 element is
        /// suppressed, and blocks are inlinized.
        ///
        /// This bit is propagated to all children of line participants.
        /// It is currently used by ruby to make its content unbreakable.
        const SHOULD_SUPPRESS_LINEBREAK = 1 << 1;

        /// A flag used to mark text that that has text-combine-upright.
        ///
        /// This is used from Gecko's layout engine.
        const IS_TEXT_COMBINED = 1 << 2;

        /// A flag used to mark styles under a relevant link that is also
        /// visited.
        const IS_RELEVANT_LINK_VISITED = 1 << 3;

        /// A flag used to mark styles which are a pseudo-element or under one.
        const IS_IN_PSEUDO_ELEMENT_SUBTREE = 1 << 4;

        /// Whether this style inherits the `display` property.
        ///
        /// This is important because it may affect our optimizations to avoid
        /// computing the style of pseudo-elements, given whether the
        /// pseudo-element is generated depends on the `display` value.
        const INHERITS_DISPLAY = 1 << 6;

        /// Whether this style inherits the `content` property.
        ///
        /// Important because of the same reason.
        const INHERITS_CONTENT = 1 << 7;

        /// Whether the child explicitly inherits any reset property.
        const INHERITS_RESET_STYLE = 1 << 8;

        /// Whether any value on our style is font-metric-dependent.
        const DEPENDS_ON_FONT_METRICS = 1 << 9;

        /// Whether the style or any of the ancestors has a multicol style.
        ///
        /// Only used in Servo.
        const CAN_BE_FRAGMENTED = 1 << 10;
    }
}

impl ComputedValueFlags {
    /// Flags that are unconditionally propagated to descendants.
    #[inline]
    fn inherited_flags() -> Self {
        ComputedValueFlags::IS_RELEVANT_LINK_VISITED |
        ComputedValueFlags::CAN_BE_FRAGMENTED |
        ComputedValueFlags::IS_IN_PSEUDO_ELEMENT_SUBTREE |
        ComputedValueFlags::HAS_TEXT_DECORATION_LINES
    }

    /// Flags that may be propagated to descendants.
    #[inline]
    fn maybe_inherited_flags() -> Self {
        Self::inherited_flags() | ComputedValueFlags::SHOULD_SUPPRESS_LINEBREAK
    }

    /// Returns the flags that are always propagated to descendants.
    ///
    /// See StyleAdjuster::set_bits and StyleBuilder.
    #[inline]
    pub fn inherited(self) -> Self {
        self & Self::inherited_flags()
    }

    /// Flags that are conditionally propagated to descendants, just to handle
    /// properly style invalidation.
    #[inline]
    pub fn maybe_inherited(self) -> Self {
        self & Self::maybe_inherited_flags()
    }
}
<commit_msg>style: Remove ComputedStyle::mBits.<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 https://mozilla.org/MPL/2.0/. */

//! Misc information about a given computed style.

bitflags! {
    /// Misc information about a given computed style.
    ///
    /// All flags are currently inherited for text, pseudo elements, and
    /// anonymous boxes, see StyleBuilder::for_inheritance and its callsites.
    /// If we ever want to add some flags that shouldn't inherit for them,
    /// we might want to add a function to handle this.
    pub struct ComputedValueFlags: u16 {
        /// Whether the style or any of the ancestors has a text-decoration-line
        /// property that should get propagated to descendants.
        ///
        /// text-decoration-line is a reset property, but gets propagated in the
        /// frame/box tree.
        const HAS_TEXT_DECORATION_LINES = 1 << 0;

        /// Whether line break inside should be suppressed.
        ///
        /// If this flag is set, the line should not be broken inside,
        /// which means inlines act as if nowrap is set,
 element is
        /// suppressed, and blocks are inlinized.
        ///
        /// This bit is propagated to all children of line participants.
        /// It is currently used by ruby to make its content unbreakable.
        const SHOULD_SUPPRESS_LINEBREAK = 1 << 1;

        /// A flag used to mark text that that has text-combine-upright.
        ///
        /// This is used from Gecko's layout engine.
        const IS_TEXT_COMBINED = 1 << 2;

        /// A flag used to mark styles under a relevant link that is also
        /// visited.
        const IS_RELEVANT_LINK_VISITED = 1 << 3;

        /// A flag used to mark styles which are a pseudo-element or under one.
        const IS_IN_PSEUDO_ELEMENT_SUBTREE = 1 << 4;

        /// Whether this style inherits the `display` property.
        ///
        /// This is important because it may affect our optimizations to avoid
        /// computing the style of pseudo-elements, given whether the
        /// pseudo-element is generated depends on the `display` value.
        const INHERITS_DISPLAY = 1 << 6;

        /// Whether this style inherits the `content` property.
        ///
        /// Important because of the same reason.
        const INHERITS_CONTENT = 1 << 7;

        /// Whether the child explicitly inherits any reset property.
        const INHERITS_RESET_STYLE = 1 << 8;

        /// Whether any value on our style is font-metric-dependent.
        const DEPENDS_ON_FONT_METRICS = 1 << 9;

        /// Whether the style or any of the ancestors has a multicol style.
        ///
        /// Only used in Servo.
        const CAN_BE_FRAGMENTED = 1 << 10;
    }
}

impl ComputedValueFlags {
    /// Flags that are unconditionally propagated to descendants.
    #[inline]
    fn inherited_flags() -> Self {
        ComputedValueFlags::IS_RELEVANT_LINK_VISITED |
        ComputedValueFlags::CAN_BE_FRAGMENTED |
        ComputedValueFlags::IS_IN_PSEUDO_ELEMENT_SUBTREE |
        ComputedValueFlags::HAS_TEXT_DECORATION_LINES
    }

    /// Flags that may be propagated to descendants.
    #[inline]
    fn maybe_inherited_flags() -> Self {
        Self::inherited_flags() | ComputedValueFlags::SHOULD_SUPPRESS_LINEBREAK
    }

    /// Returns the flags that are always propagated to descendants.
    ///
    /// See StyleAdjuster::set_bits and StyleBuilder.
    #[inline]
    pub fn inherited(self) -> Self {
        self & Self::inherited_flags()
    }

    /// Flags that are conditionally propagated to descendants, just to handle
    /// properly style invalidation.
    #[inline]
    pub fn maybe_inherited(self) -> Self {
        self & Self::maybe_inherited_flags()
    }
}

/// Asserts that the relevant servo and Gecko representations match.
#[cfg(feature = "gecko")]
#[inline]
pub fn assert_match() {
    use crate::gecko_bindings::structs;
    macro_rules! assert_bit {
        ($rust:ident, $cpp:ident) => {
            debug_assert_eq!(ComputedValueFlags::$rust.bits, structs::$cpp);
        }
    }

    assert_bit!(HAS_TEXT_DECORATION_LINES, ComputedStyleBit_HasTextDecorationLines);
    assert_bit!(IS_IN_PSEUDO_ELEMENT_SUBTREE, ComputedStyleBit_HasPseudoElementData);
    assert_bit!(SHOULD_SUPPRESS_LINEBREAK, ComputedStyleBit_SuppressLineBreak);
    assert_bit!(IS_TEXT_COMBINED, ComputedStyleBit_IsTextCombined);
    assert_bit!(IS_RELEVANT_LINK_VISITED, ComputedStyleBit_RelevantLinkVisited);
    assert_bit!(DEPENDS_ON_FONT_METRICS, ComputedStyleBit_DependsOnFontMetrics);
}
<|endoftext|>


<commit_before><commit_msg>Log ping/pong only in case of a discrepancy (#893)<commit_after><|endoftext|>


<commit_before>use std::os::raw::{c_int, c_double, c_void, c_char};
use std::mem;
use std::clone::Clone;
use std::default::Default;

use ffi::enums::*;

#[repr(C)]
#[derive(Copy)]
pub struct SoundIoChannelLayout {
    pub name: *const c_char,
    pub channel_count: c_int,
    pub channels: [SioChannelId; 24usize],
}
impl Clone for SoundIoChannelLayout {
    fn clone(&self) -> Self {
        *self
    }
}
impl Default for SoundIoChannelLayout {
    fn default() -> Self {
        unsafe { mem::zeroed() }
    }
}

#[repr(C)]
#[derive(Copy)]
pub struct SoundIoSampleRateRange {
    pub min: c_int,
    pub max: c_int,
}
impl Clone for SoundIoSampleRateRange {
    fn clone(&self) -> Self {
        *self
    }
}
impl Default for SoundIoSampleRateRange {
    fn default() -> Self {
        unsafe { mem::zeroed() }
    }
}
#[repr(C)]
#[derive(Copy)]
pub struct SoundIoChannelArea {
    pub ptr: *mut c_char,
    pub step: c_int,
}
impl Clone for SoundIoChannelArea {
    fn clone(&self) -> Self {
        *self
    }
}
impl Default for SoundIoChannelArea {
    fn default() -> Self {
        unsafe { mem::zeroed() }
    }
}

#[repr(C)]
#[derive(Copy)]
pub struct SoundIo {
    pub userdata: *mut c_void,
    pub on_devices_change: Option<unsafe extern "C" fn(arg1: *mut SoundIo)>,
    pub on_backend_disconnect: Option<unsafe extern "C" fn(arg1: *mut SoundIo, err: c_int)>,
    pub on_events_signal: Option<unsafe extern "C" fn(arg1: *mut SoundIo)>,
    pub current_backend: SioBackend,
    pub app_name: *const c_char,
    pub emit_rtprio_warning: Option<extern "C" fn()>,
    pub jack_info_callback: Option<unsafe extern "C" fn(msg: *const c_char)>,
    pub jack_error_callback: Option<unsafe extern "C" fn(msg: *const c_char)>,
}
impl Clone for SoundIo {
    fn clone(&self) -> Self {
        *self
    }
}
impl Default for SoundIo {
    fn default() -> Self {
        unsafe { mem::zeroed() }
    }
}

#[repr(C)]
#[derive(Copy)]
pub struct SoundIoDevice {
    pub soundio: *mut SoundIo,
    pub id: *mut c_char,
    pub name: *mut c_char,
    pub aim: SioDeviceAim,
    pub layouts: *mut SoundIoChannelLayout,
    pub layout_count: c_int,
    pub current_layout: SoundIoChannelLayout,
    pub formats: *mut SioFormat,
    pub format_count: c_int,
    pub current_format: SioFormat,
    pub sample_rates: *mut SoundIoSampleRateRange,
    pub sample_rate_count: c_int,
    pub sample_rate_current: c_int,
    pub software_latency_min: c_double,
    pub software_latency_max: c_double,
    pub software_latency_current: c_double,
    pub is_raw: u8,
    pub ref_count: c_int,
    pub probe_error: c_int,
}
impl Clone for SoundIoDevice {
    fn clone(&self) -> Self {
        *self
    }
}
impl Default for SoundIoDevice {
    fn default() -> Self {
        unsafe { mem::zeroed() }
    }
}

#[repr(C)]
#[derive(Copy)]
pub struct SoundIoOutStream {
    pub device: *mut SoundIoDevice,
    pub format: SioFormat,
    pub sample_rate: c_int,
    pub layout: SoundIoChannelLayout,
    pub software_latency: c_double,
    pub userdata: *mut c_void,
    pub write_callback: Option<unsafe extern "C" fn(arg1: *mut SoundIoOutStream,
                                                    frame_count_min: c_int,
                                                    frame_count_max: c_int)
                                                   >,
    pub underflow_callback: Option<unsafe extern "C" fn(arg1: *mut SoundIoOutStream)>,
    pub error_callback: Option<unsafe extern "C" fn(arg1: *mut SoundIoOutStream, err: c_int)>,
    pub name: *const c_char,
    pub non_terminal_hint: u8,
    pub bytes_per_frame: c_int,
    pub bytes_per_sample: c_int,
    pub layout_error: c_int,
}
impl Clone for SoundIoOutStream {
    fn clone(&self) -> Self {
        *self
    }
}
impl Default for SoundIoOutStream {
    fn default() -> Self {
        unsafe { mem::zeroed() }
    }
}
#[repr(C)]
#[derive(Copy)]
pub struct SoundIoInStream {
    pub device: *mut SoundIoDevice,
    pub format: SioFormat,
    pub sample_rate: c_int,
    pub layout: SoundIoChannelLayout,
    pub software_latency: c_double,
    pub userdata: *mut c_void,
    pub read_callback: Option<unsafe extern "C" fn(arg1: *mut SoundIoInStream,
                                                   frame_count_min: c_int,
                                                   frame_count_max: c_int)
                                                  >,
    pub overflow_callback: Option<unsafe extern "C" fn(arg1: *mut SoundIoInStream)>,
    pub error_callback: Option<unsafe extern "C" fn(arg1: *mut SoundIoInStream, err: c_int)>,
    pub name: *const c_char,
    pub non_terminal_hint: u8,
    pub bytes_per_frame: c_int,
    pub bytes_per_sample: c_int,
    pub layout_error: c_int,
}
impl Clone for SoundIoInStream {
    fn clone(&self) -> Self {
        *self
    }
}
impl Default for SoundIoInStream {
    fn default() -> Self {
        unsafe { mem::zeroed() }
    }
}
<commit_msg>Remove `unsafe` from function declaration<commit_after>use std::os::raw::{c_int, c_double, c_void, c_char};
use std::mem;
use std::clone::Clone;
use std::default::Default;

use ffi::enums::*;

#[repr(C)]
#[derive(Copy)]
pub struct SoundIoChannelLayout {
    pub name: *const c_char,
    pub channel_count: c_int,
    pub channels: [SioChannelId; 24usize],
}
impl Clone for SoundIoChannelLayout {
    fn clone(&self) -> Self {
        *self
    }
}
impl Default for SoundIoChannelLayout {
    fn default() -> Self {
        unsafe { mem::zeroed() }
    }
}

#[repr(C)]
#[derive(Copy)]
pub struct SoundIoSampleRateRange {
    pub min: c_int,
    pub max: c_int,
}
impl Clone for SoundIoSampleRateRange {
    fn clone(&self) -> Self {
        *self
    }
}
impl Default for SoundIoSampleRateRange {
    fn default() -> Self {
        unsafe { mem::zeroed() }
    }
}
#[repr(C)]
#[derive(Copy)]
pub struct SoundIoChannelArea {
    pub ptr: *mut c_char,
    pub step: c_int,
}
impl Clone for SoundIoChannelArea {
    fn clone(&self) -> Self {
        *self
    }
}
impl Default for SoundIoChannelArea {
    fn default() -> Self {
        unsafe { mem::zeroed() }
    }
}

#[repr(C)]
#[derive(Copy)]
pub struct SoundIo {
    pub userdata: *mut c_void,
    pub on_devices_change: Option<unsafe extern "C" fn(arg1: *mut SoundIo)>,
    pub on_backend_disconnect: Option<unsafe extern "C" fn(arg1: *mut SoundIo, err: c_int)>,
    pub on_events_signal: Option<unsafe extern "C" fn(arg1: *mut SoundIo)>,
    pub current_backend: SioBackend,
    pub app_name: *const c_char,
    pub emit_rtprio_warning: Option<extern "C" fn()>,
    pub jack_info_callback: Option<unsafe extern "C" fn(msg: *const c_char)>,
    pub jack_error_callback: Option<unsafe extern "C" fn(msg: *const c_char)>,
}
impl Clone for SoundIo {
    fn clone(&self) -> Self {
        *self
    }
}
impl Default for SoundIo {
    fn default() -> Self {
        unsafe { mem::zeroed() }
    }
}

#[repr(C)]
#[derive(Copy)]
pub struct SoundIoDevice {
    pub soundio: *mut SoundIo,
    pub id: *mut c_char,
    pub name: *mut c_char,
    pub aim: SioDeviceAim,
    pub layouts: *mut SoundIoChannelLayout,
    pub layout_count: c_int,
    pub current_layout: SoundIoChannelLayout,
    pub formats: *mut SioFormat,
    pub format_count: c_int,
    pub current_format: SioFormat,
    pub sample_rates: *mut SoundIoSampleRateRange,
    pub sample_rate_count: c_int,
    pub sample_rate_current: c_int,
    pub software_latency_min: c_double,
    pub software_latency_max: c_double,
    pub software_latency_current: c_double,
    pub is_raw: u8,
    pub ref_count: c_int,
    pub probe_error: c_int,
}
impl Clone for SoundIoDevice {
    fn clone(&self) -> Self {
        *self
    }
}
impl Default for SoundIoDevice {
    fn default() -> Self {
        unsafe { mem::zeroed() }
    }
}

#[repr(C)]
#[derive(Copy)]
pub struct SoundIoOutStream {
    pub device: *mut SoundIoDevice,
    pub format: SioFormat,
    pub sample_rate: c_int,
    pub layout: SoundIoChannelLayout,
    pub software_latency: c_double,
    pub userdata: *mut c_void,
    pub write_callback: Option<extern "C" fn(arg1: *mut SoundIoOutStream,
                                                    frame_count_min: c_int,
                                                    frame_count_max: c_int)
                                                   >,
    pub underflow_callback: Option<unsafe extern "C" fn(arg1: *mut SoundIoOutStream)>,
    pub error_callback: Option<unsafe extern "C" fn(arg1: *mut SoundIoOutStream, err: c_int)>,
    pub name: *const c_char,
    pub non_terminal_hint: u8,
    pub bytes_per_frame: c_int,
    pub bytes_per_sample: c_int,
    pub layout_error: c_int,
}
impl Clone for SoundIoOutStream {
    fn clone(&self) -> Self {
        *self
    }
}
impl Default for SoundIoOutStream {
    fn default() -> Self {
        unsafe { mem::zeroed() }
    }
}
#[repr(C)]
#[derive(Copy)]
pub struct SoundIoInStream {
    pub device: *mut SoundIoDevice,
    pub format: SioFormat,
    pub sample_rate: c_int,
    pub layout: SoundIoChannelLayout,
    pub software_latency: c_double,
    pub userdata: *mut c_void,
    pub read_callback: Option<unsafe extern "C" fn(arg1: *mut SoundIoInStream,
                                                   frame_count_min: c_int,
                                                   frame_count_max: c_int)
                                                  >,
    pub overflow_callback: Option<unsafe extern "C" fn(arg1: *mut SoundIoInStream)>,
    pub error_callback: Option<unsafe extern "C" fn(arg1: *mut SoundIoInStream, err: c_int)>,
    pub name: *const c_char,
    pub non_terminal_hint: u8,
    pub bytes_per_frame: c_int,
    pub bytes_per_sample: c_int,
    pub layout_error: c_int,
}
impl Clone for SoundIoInStream {
    fn clone(&self) -> Self {
        *self
    }
}
impl Default for SoundIoInStream {
    fn default() -> Self {
        unsafe { mem::zeroed() }
    }
}
<|endoftext|>


<commit_before>use cssparser::Parser;

use crate::coord_units::CoordUnits;
use crate::drawing_ctx::{DrawingCtx, ViewParams};
use crate::error::*;
use crate::filter::Filter;
use crate::filters::{
    gaussian_blur::GaussianBlur, FilterResolveError, FilterSpec, Primitive, PrimitiveParams,
    ResolvedPrimitive,
};
use crate::length::*;
use crate::parsers::Parse;
use crate::properties::ComputedValues;

/// CSS Filter functions from the Filter Effects Module Level 1
///
/// https://www.w3.org/TR/filter-effects/#filter-functions
#[derive(Debug, Clone, PartialEq)]
pub enum FilterFunction {
    Blur(Blur),
}

/// Parameters for the `blur()` filter function
///
/// https://www.w3.org/TR/filter-effects/#funcdef-filter-blur
#[derive(Debug, Clone, PartialEq)]
pub struct Blur {
    std_deviation: Option<Length<Both>>,
}

fn parse_function<'i, F>(
    parser: &mut Parser<'i, '_>,
    name: &str,
    f: F,
) -> Result<FilterFunction, ParseError<'i>>
where
    F: for<'tt> FnOnce(&mut Parser<'i, 'tt>) -> Result<FilterFunction, ParseError<'i>>,
{
    parser.expect_function_matching(name)?;
    parser.parse_nested_block(f)
}

// This function doesn't fail, but returns a Result like the other parsers, so tell Clippy
// about that.
#[allow(clippy::unnecessary_wraps)]
fn parse_blur<'i>(parser: &mut Parser<'i, '_>) -> Result<FilterFunction, ParseError<'i>> {
    let length = if let Ok(length) = parser.try_parse(|p| Length::parse(p)) {
        Some(length)
    } else {
        None
    };

    Ok(FilterFunction::Blur(Blur {
        std_deviation: length,
    }))
}

impl Blur {
    fn to_filter_spec(
        &self,
        values: &ComputedValues,
        params: &ViewParams,
    ) -> FilterSpec {
        // The 0.0 default is from the spec
        let std_dev = self
            .std_deviation
            .map(|l| l.normalize(values, params))
            .unwrap_or(0.0);

        let user_space_filter = Filter::default().to_user_space(values, params);

        let gaussian_blur = ResolvedPrimitive {
            primitive: Primitive::default(),
            params: PrimitiveParams::GaussianBlur(GaussianBlur {
                std_deviation: (std_dev, std_dev),
                ..GaussianBlur::default()
            }),
        }
        .into_user_space(values, params);

        FilterSpec {
            user_space_filter,
            primitives: vec![gaussian_blur],
        }
    }
}

impl Parse for FilterFunction {
    fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<Self, crate::error::ParseError<'i>> {
        let loc = parser.current_source_location();

        parser
            .try_parse(|p| parse_function(p, "blur", parse_blur))
            .or_else(|_| {
                Err(loc.new_custom_error(ValueErrorKind::parse_error("expected filter function")))
            })
    }
}

impl FilterFunction {
    // If this function starts actually returning an Err, remove this Clippy exception:
    #[allow(clippy::unnecessary_wraps)]
    pub fn to_filter_spec(
        &self,
        values: &ComputedValues,
        draw_ctx: &DrawingCtx,
    ) -> Result<FilterSpec, FilterResolveError> {
        // This is the default for primitive_units
        let params = draw_ctx.push_coord_units(CoordUnits::UserSpaceOnUse);

        match self {
            FilterFunction::Blur(v) => Ok(v.to_filter_spec(values, &params)),
        }
    }
}

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

    #[test]
    fn parses_blur() {
        assert_eq!(
            FilterFunction::parse_str("blur()").unwrap(),
            FilterFunction::Blur(Blur {
                std_deviation: None
            })
        );

        assert_eq!(
            FilterFunction::parse_str("blur(5px)").unwrap(),
            FilterFunction::Blur(Blur {
                std_deviation: Some(Length::new(5.0, LengthUnit::Px))
            })
        );
    }

    #[test]
    fn invalid_blur_yields_error() {
        assert!(FilterFunction::parse_str("blur(foo)").is_err());
        assert!(FilterFunction::parse_str("blur(42 43)").is_err());
    }
}
<commit_msg>FilterFunction::parse - make it more obvious how to add new filter functions there<commit_after>use cssparser::Parser;

use crate::coord_units::CoordUnits;
use crate::drawing_ctx::{DrawingCtx, ViewParams};
use crate::error::*;
use crate::filter::Filter;
use crate::filters::{
    gaussian_blur::GaussianBlur, FilterResolveError, FilterSpec, Primitive, PrimitiveParams,
    ResolvedPrimitive,
};
use crate::length::*;
use crate::parsers::Parse;
use crate::properties::ComputedValues;

/// CSS Filter functions from the Filter Effects Module Level 1
///
/// https://www.w3.org/TR/filter-effects/#filter-functions
#[derive(Debug, Clone, PartialEq)]
pub enum FilterFunction {
    Blur(Blur),
}

/// Parameters for the `blur()` filter function
///
/// https://www.w3.org/TR/filter-effects/#funcdef-filter-blur
#[derive(Debug, Clone, PartialEq)]
pub struct Blur {
    std_deviation: Option<Length<Both>>,
}

fn parse_function<'i, F>(
    parser: &mut Parser<'i, '_>,
    name: &str,
    f: F,
) -> Result<FilterFunction, ParseError<'i>>
where
    F: for<'tt> FnOnce(&mut Parser<'i, 'tt>) -> Result<FilterFunction, ParseError<'i>>,
{
    parser.expect_function_matching(name)?;
    parser.parse_nested_block(f)
}

// This function doesn't fail, but returns a Result like the other parsers, so tell Clippy
// about that.
#[allow(clippy::unnecessary_wraps)]
fn parse_blur<'i>(parser: &mut Parser<'i, '_>) -> Result<FilterFunction, ParseError<'i>> {
    let length = if let Ok(length) = parser.try_parse(|p| Length::parse(p)) {
        Some(length)
    } else {
        None
    };

    Ok(FilterFunction::Blur(Blur {
        std_deviation: length,
    }))
}

impl Blur {
    fn to_filter_spec(&self, values: &ComputedValues, params: &ViewParams) -> FilterSpec {
        // The 0.0 default is from the spec
        let std_dev = self
            .std_deviation
            .map(|l| l.normalize(values, params))
            .unwrap_or(0.0);

        let user_space_filter = Filter::default().to_user_space(values, params);

        let gaussian_blur = ResolvedPrimitive {
            primitive: Primitive::default(),
            params: PrimitiveParams::GaussianBlur(GaussianBlur {
                std_deviation: (std_dev, std_dev),
                ..GaussianBlur::default()
            }),
        }
        .into_user_space(values, params);

        FilterSpec {
            user_space_filter,
            primitives: vec![gaussian_blur],
        }
    }
}

impl Parse for FilterFunction {
    fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<Self, crate::error::ParseError<'i>> {
        let loc = parser.current_source_location();

        if let Ok(func) = parser.try_parse(|p| parse_function(p, "blur", parse_blur)) {
            return Ok(func);
        }

        return Err(loc.new_custom_error(ValueErrorKind::parse_error("expected filter function")));
    }
}

impl FilterFunction {
    // If this function starts actually returning an Err, remove this Clippy exception:
    #[allow(clippy::unnecessary_wraps)]
    pub fn to_filter_spec(
        &self,
        values: &ComputedValues,
        draw_ctx: &DrawingCtx,
    ) -> Result<FilterSpec, FilterResolveError> {
        // This is the default for primitive_units
        let params = draw_ctx.push_coord_units(CoordUnits::UserSpaceOnUse);

        match self {
            FilterFunction::Blur(v) => Ok(v.to_filter_spec(values, &params)),
        }
    }
}

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

    #[test]
    fn parses_blur() {
        assert_eq!(
            FilterFunction::parse_str("blur()").unwrap(),
            FilterFunction::Blur(Blur {
                std_deviation: None
            })
        );

        assert_eq!(
            FilterFunction::parse_str("blur(5px)").unwrap(),
            FilterFunction::Blur(Blur {
                std_deviation: Some(Length::new(5.0, LengthUnit::Px))
            })
        );
    }

    #[test]
    fn invalid_blur_yields_error() {
        assert!(FilterFunction::parse_str("blur(foo)").is_err());
        assert!(FilterFunction::parse_str("blur(42 43)").is_err());
    }
}
<|endoftext|>


<commit_before>//! Entry point for the CSS filters infrastructure.

use cssparser::{BasicParseError, Parser};
use markup5ever::{expanded_name, local_name, namespace_url, ns};
use std::ops::Deref;
use std::time::Instant;

use crate::bbox::BoundingBox;
use crate::coord_units::CoordUnits;
use crate::document::AcquiredNodes;
use crate::drawing_ctx::DrawingCtx;
use crate::element::{Draw, Element, ElementResult, SetAttributes};
use crate::error::{ParseError, RenderingError};
use crate::length::*;
use crate::node::{CascadedValues, Node, NodeBorrow};
use crate::parsers::{CustomIdent, Parse, ParseValue};
use crate::properties::ComputedValues;
use crate::property_defs::ColorInterpolationFilters;
use crate::surface_utils::shared_surface::{SharedImageSurface, SurfaceType};
use crate::transform::Transform;
use crate::xml::Attributes;

mod bounds;
use self::bounds::BoundsBuilder;

pub mod context;
use self::context::{FilterContext, FilterInput, FilterResult};

mod error;
use self::error::FilterError;

/// Trait to render filter effect primitives.
pub trait FilterRender {
    /// Renders this filter primitive.
    ///
    /// If this filter primitive can't be rendered for whatever reason (for instance, a required
    /// property hasn't been provided), an error is returned.
    fn render(
        &self,
        node: &Node,
        ctx: &FilterContext,
        acquired_nodes: &mut AcquiredNodes<'_>,
        draw_ctx: &mut DrawingCtx,
    ) -> Result<FilterResult, FilterError>;
}

/// A filter primitive interface.
pub trait FilterEffect: SetAttributes + Draw + FilterRender {
    /// Returns `true` if this filter primitive is affected by the `color-interpolation-filters`
    /// property.
    ///
    /// Primitives that do color blending (like `feComposite` or `feBlend`) should return `true`
    /// here, whereas primitives that don't (like `feOffset`) should return `false`.
    fn is_affected_by_color_interpolation_filters(&self) -> bool;
}

// Filter Effects do not need to draw themselves
impl<T: FilterEffect> Draw for T {}

pub mod blend;
pub mod color_matrix;
pub mod component_transfer;
pub mod composite;
pub mod convolve_matrix;
pub mod displacement_map;
pub mod flood;
pub mod gaussian_blur;
pub mod image;
pub mod lighting;
pub mod merge;
pub mod morphology;
pub mod offset;
pub mod tile;
pub mod turbulence;

/// The base filter primitive node containing common properties.
struct Primitive {
    x: Option<Length<Horizontal>>,
    y: Option<Length<Vertical>>,
    width: Option<ULength<Horizontal>>,
    height: Option<ULength<Vertical>>,
    result: Option<CustomIdent>,
}

/// An enumeration of possible inputs for a filter primitive.
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub enum Input {
    SourceGraphic,
    SourceAlpha,
    BackgroundImage,
    BackgroundAlpha,
    FillPaint,
    StrokePaint,
    FilterOutput(CustomIdent),
}

impl Parse for Input {
    fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<Self, ParseError<'i>> {
        parser
            .try_parse(|p| {
                Ok(parse_identifiers!(
                    p,
                    "SourceGraphic" => Input::SourceGraphic,
                    "SourceAlpha" => Input::SourceAlpha,
                    "BackgroundImage" => Input::BackgroundImage,
                    "BackgroundAlpha" => Input::BackgroundAlpha,
                    "FillPaint" => Input::FillPaint,
                    "StrokePaint" => Input::StrokePaint,
                )?)
            })
            .or_else(|_: BasicParseError<'_>| {
                let ident = CustomIdent::parse(parser)?;
                Ok(Input::FilterOutput(ident))
            })
    }
}

/// The base node for filter primitives which accept input.
struct PrimitiveWithInput {
    base: Primitive,
    in_: Option<Input>,
}

impl Primitive {
    /// Constructs a new `Primitive` with empty properties.
    #[inline]
    fn new<T: FilterEffect>() -> Primitive {
        Primitive {
            x: None,
            y: None,
            width: None,
            height: None,
            result: None,
        }
    }

    /// Validates attributes and returns the `BoundsBuilder` for bounds computation.
    #[inline]
    fn get_bounds<'a>(
        &self,
        ctx: &'a FilterContext,
        parent: Option<&Node>,
    ) -> Result<BoundsBuilder, FilterError> {
        let primitive_units = parent
            .and_then(|parent| {
                assert!(parent.is_element());
                match *parent.borrow_element() {
                    Element::Filter(ref f) => Some(f.get_primitive_units()),
                    _ => None,
                }
            })
            .unwrap_or(CoordUnits::UserSpaceOnUse);

        // With ObjectBoundingBox, only fractions and percents are allowed.
        if primitive_units == CoordUnits::ObjectBoundingBox {
            check_units(self.x)?;
            check_units(self.y)?;
            check_units(self.width)?;
            check_units(self.height)?;
        }

        let transform = ctx.paffine();
        if !transform.is_invertible() {
            return Err(FilterError::InvalidParameter(
                "transform is not invertible".to_string(),
            ));
        }

        Ok(BoundsBuilder::new(
            self.x,
            self.y,
            self.width,
            self.height,
            transform,
        ))
    }
}

fn check_units<N: Normalize, V: Validate>(
    length: Option<CssLength<N, V>>,
) -> Result<(), FilterError> {
    match length {
        Some(l) if l.unit == LengthUnit::Px || l.unit == LengthUnit::Percent => Ok(()),
        Some(_) => Err(FilterError::InvalidUnits),
        None => Ok(()),
    }
}

impl SetAttributes for Primitive {
    fn set_attributes(&mut self, attrs: &Attributes) -> ElementResult {
        for (attr, value) in attrs.iter() {
            match attr.expanded() {
                expanded_name!("", "x") => self.x = attr.parse(value)?,
                expanded_name!("", "y") => self.y = attr.parse(value)?,
                expanded_name!("", "width") => self.width = attr.parse(value)?,
                expanded_name!("", "height") => self.height = attr.parse(value)?,
                expanded_name!("", "result") => self.result = attr.parse(value)?,
                _ => (),
            }
        }

        Ok(())
    }
}

impl PrimitiveWithInput {
    /// Constructs a new `PrimitiveWithInput` with empty properties.
    #[inline]
    fn new<T: FilterEffect>() -> PrimitiveWithInput {
        PrimitiveWithInput {
            base: Primitive::new::<T>(),
            in_: None,
        }
    }

    /// Returns the input Cairo surface for this filter primitive.
    #[inline]
    fn get_input(
        &self,
        ctx: &FilterContext,
        acquired_nodes: &mut AcquiredNodes<'_>,
        draw_ctx: &mut DrawingCtx,
    ) -> Result<FilterInput, FilterError> {
        ctx.get_input(acquired_nodes, draw_ctx, self.in_.as_ref())
    }
}

impl SetAttributes for PrimitiveWithInput {
    fn set_attributes(&mut self, attrs: &Attributes) -> ElementResult {
        self.base.set_attributes(attrs)?;

        self.in_ = attrs
            .iter()
            .find(|(attr, _)| attr.expanded() == expanded_name!("", "in"))
            .and_then(|(attr, value)| attr.parse(value).ok());

        Ok(())
    }
}

impl Deref for PrimitiveWithInput {
    type Target = Primitive;

    #[inline]
    fn deref(&self) -> &Self::Target {
        &self.base
    }
}

/// Applies a filter and returns the resulting surface.
pub fn render(
    filter_node: &Node,
    computed_from_node_being_filtered: &ComputedValues,
    source_surface: SharedImageSurface,
    acquired_nodes: &mut AcquiredNodes<'_>,
    draw_ctx: &mut DrawingCtx,
    transform: Transform,
    node_bbox: BoundingBox,
) -> Result<SharedImageSurface, RenderingError> {
    let filter_node = &*filter_node;
    assert!(is_element_of_type!(filter_node, Filter));

    if filter_node.borrow_element().is_in_error() {
        return Ok(source_surface);
    }

    let filter = borrow_element_as!(filter_node, Filter);
    let mut filter_ctx = FilterContext::new(
        &filter,
        computed_from_node_being_filtered,
        source_surface,
        draw_ctx,
        transform,
        node_bbox,
    );

    // If paffine is non-invertible, we won't draw anything. Also bbox combining in bounds
    // computations will panic due to non-invertible martrix.
    if !filter_ctx.paffine().is_invertible() {
        return Ok(filter_ctx.into_output()?);
    }

    let primitives = filter_node
        .children()
        .filter(|c| c.is_element())
        // Skip nodes in error.
        .filter(|c| {
            let in_error = c.borrow_element().is_in_error();

            if in_error {
                rsvg_log!("(ignoring filter primitive {} because it is in error)", c);
            }

            !in_error
        })
        // Keep only filter primitives (those that implement the Filter trait)
        .filter(|c| c.borrow_element().as_filter_effect().is_some())
        // Check if the node wants linear RGB.
        .map(|c| {
            let linear_rgb = {
                let cascaded = CascadedValues::new_from_node(&c);
                let values = cascaded.get();

                values.color_interpolation_filters() == ColorInterpolationFilters::LinearRgb
            };

            (c, linear_rgb)
        });

    for (c, linear_rgb) in primitives {
        let elt = c.borrow_element();
        let filter = elt.as_filter_effect().unwrap();

        let mut render = |filter_ctx: &mut FilterContext| {
            if let Err(err) = filter
                .render(&c, filter_ctx, acquired_nodes, draw_ctx)
                .and_then(|result| filter_ctx.store_result(result))
            {
                rsvg_log!("(filter primitive {} returned an error: {})", c, err);

                // Exit early on Cairo errors. Continue rendering otherwise.
                if let FilterError::CairoError(status) = err {
                    return Err(status);
                }
            }

            Ok(())
        };

        let start = Instant::now();

        if filter.is_affected_by_color_interpolation_filters() && linear_rgb {
            filter_ctx.with_linear_rgb(render)?;
        } else {
            render(&mut filter_ctx)?;
        }

        let elapsed = start.elapsed();
        rsvg_log!(
            "(rendered filter primitive {} in\n    {} seconds)",
            c,
            elapsed.as_secs() as f64 + f64::from(elapsed.subsec_nanos()) / 1e9
        );
    }

    Ok(filter_ctx.into_output()?)
}

impl From<ColorInterpolationFilters> for SurfaceType {
    fn from(c: ColorInterpolationFilters) -> Self {
        match c {
            ColorInterpolationFilters::LinearRgb => SurfaceType::LinearRgb,
            _ => SurfaceType::SRgb,
        }
    }
}
<commit_msg>Use the primitive_units from the FilterContext; no need to get them from a parent node<commit_after>//! Entry point for the CSS filters infrastructure.

use cssparser::{BasicParseError, Parser};
use markup5ever::{expanded_name, local_name, namespace_url, ns};
use std::ops::Deref;
use std::time::Instant;

use crate::bbox::BoundingBox;
use crate::coord_units::CoordUnits;
use crate::document::AcquiredNodes;
use crate::drawing_ctx::DrawingCtx;
use crate::element::{Draw, Element, ElementResult, SetAttributes};
use crate::error::{ParseError, RenderingError};
use crate::length::*;
use crate::node::{CascadedValues, Node, NodeBorrow};
use crate::parsers::{CustomIdent, Parse, ParseValue};
use crate::properties::ComputedValues;
use crate::property_defs::ColorInterpolationFilters;
use crate::surface_utils::shared_surface::{SharedImageSurface, SurfaceType};
use crate::transform::Transform;
use crate::xml::Attributes;

mod bounds;
use self::bounds::BoundsBuilder;

pub mod context;
use self::context::{FilterContext, FilterInput, FilterResult};

mod error;
use self::error::FilterError;

/// Trait to render filter effect primitives.
pub trait FilterRender {
    /// Renders this filter primitive.
    ///
    /// If this filter primitive can't be rendered for whatever reason (for instance, a required
    /// property hasn't been provided), an error is returned.
    fn render(
        &self,
        node: &Node,
        ctx: &FilterContext,
        acquired_nodes: &mut AcquiredNodes<'_>,
        draw_ctx: &mut DrawingCtx,
    ) -> Result<FilterResult, FilterError>;
}

/// A filter primitive interface.
pub trait FilterEffect: SetAttributes + Draw + FilterRender {
    /// Returns `true` if this filter primitive is affected by the `color-interpolation-filters`
    /// property.
    ///
    /// Primitives that do color blending (like `feComposite` or `feBlend`) should return `true`
    /// here, whereas primitives that don't (like `feOffset`) should return `false`.
    fn is_affected_by_color_interpolation_filters(&self) -> bool;
}

// Filter Effects do not need to draw themselves
impl<T: FilterEffect> Draw for T {}

pub mod blend;
pub mod color_matrix;
pub mod component_transfer;
pub mod composite;
pub mod convolve_matrix;
pub mod displacement_map;
pub mod flood;
pub mod gaussian_blur;
pub mod image;
pub mod lighting;
pub mod merge;
pub mod morphology;
pub mod offset;
pub mod tile;
pub mod turbulence;

/// The base filter primitive node containing common properties.
struct Primitive {
    x: Option<Length<Horizontal>>,
    y: Option<Length<Vertical>>,
    width: Option<ULength<Horizontal>>,
    height: Option<ULength<Vertical>>,
    result: Option<CustomIdent>,
}

/// An enumeration of possible inputs for a filter primitive.
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub enum Input {
    SourceGraphic,
    SourceAlpha,
    BackgroundImage,
    BackgroundAlpha,
    FillPaint,
    StrokePaint,
    FilterOutput(CustomIdent),
}

impl Parse for Input {
    fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<Self, ParseError<'i>> {
        parser
            .try_parse(|p| {
                Ok(parse_identifiers!(
                    p,
                    "SourceGraphic" => Input::SourceGraphic,
                    "SourceAlpha" => Input::SourceAlpha,
                    "BackgroundImage" => Input::BackgroundImage,
                    "BackgroundAlpha" => Input::BackgroundAlpha,
                    "FillPaint" => Input::FillPaint,
                    "StrokePaint" => Input::StrokePaint,
                )?)
            })
            .or_else(|_: BasicParseError<'_>| {
                let ident = CustomIdent::parse(parser)?;
                Ok(Input::FilterOutput(ident))
            })
    }
}

/// The base node for filter primitives which accept input.
struct PrimitiveWithInput {
    base: Primitive,
    in_: Option<Input>,
}

impl Primitive {
    /// Constructs a new `Primitive` with empty properties.
    #[inline]
    fn new<T: FilterEffect>() -> Primitive {
        Primitive {
            x: None,
            y: None,
            width: None,
            height: None,
            result: None,
        }
    }

    /// Validates attributes and returns the `BoundsBuilder` for bounds computation.
    #[inline]
    fn get_bounds<'a>(
        &self,
        ctx: &'a FilterContext,
        parent: Option<&Node>,
    ) -> Result<BoundsBuilder, FilterError> {
        // With ObjectBoundingBox, only fractions and percents are allowed.
        if ctx.primitive_units() == CoordUnits::ObjectBoundingBox {
            check_units(self.x)?;
            check_units(self.y)?;
            check_units(self.width)?;
            check_units(self.height)?;
        }

        let transform = ctx.paffine();
        if !transform.is_invertible() {
            return Err(FilterError::InvalidParameter(
                "transform is not invertible".to_string(),
            ));
        }

        Ok(BoundsBuilder::new(
            self.x,
            self.y,
            self.width,
            self.height,
            transform,
        ))
    }
}

fn check_units<N: Normalize, V: Validate>(
    length: Option<CssLength<N, V>>,
) -> Result<(), FilterError> {
    match length {
        Some(l) if l.unit == LengthUnit::Px || l.unit == LengthUnit::Percent => Ok(()),
        Some(_) => Err(FilterError::InvalidUnits),
        None => Ok(()),
    }
}

impl SetAttributes for Primitive {
    fn set_attributes(&mut self, attrs: &Attributes) -> ElementResult {
        for (attr, value) in attrs.iter() {
            match attr.expanded() {
                expanded_name!("", "x") => self.x = attr.parse(value)?,
                expanded_name!("", "y") => self.y = attr.parse(value)?,
                expanded_name!("", "width") => self.width = attr.parse(value)?,
                expanded_name!("", "height") => self.height = attr.parse(value)?,
                expanded_name!("", "result") => self.result = attr.parse(value)?,
                _ => (),
            }
        }

        Ok(())
    }
}

impl PrimitiveWithInput {
    /// Constructs a new `PrimitiveWithInput` with empty properties.
    #[inline]
    fn new<T: FilterEffect>() -> PrimitiveWithInput {
        PrimitiveWithInput {
            base: Primitive::new::<T>(),
            in_: None,
        }
    }

    /// Returns the input Cairo surface for this filter primitive.
    #[inline]
    fn get_input(
        &self,
        ctx: &FilterContext,
        acquired_nodes: &mut AcquiredNodes<'_>,
        draw_ctx: &mut DrawingCtx,
    ) -> Result<FilterInput, FilterError> {
        ctx.get_input(acquired_nodes, draw_ctx, self.in_.as_ref())
    }
}

impl SetAttributes for PrimitiveWithInput {
    fn set_attributes(&mut self, attrs: &Attributes) -> ElementResult {
        self.base.set_attributes(attrs)?;

        self.in_ = attrs
            .iter()
            .find(|(attr, _)| attr.expanded() == expanded_name!("", "in"))
            .and_then(|(attr, value)| attr.parse(value).ok());

        Ok(())
    }
}

impl Deref for PrimitiveWithInput {
    type Target = Primitive;

    #[inline]
    fn deref(&self) -> &Self::Target {
        &self.base
    }
}

/// Applies a filter and returns the resulting surface.
pub fn render(
    filter_node: &Node,
    computed_from_node_being_filtered: &ComputedValues,
    source_surface: SharedImageSurface,
    acquired_nodes: &mut AcquiredNodes<'_>,
    draw_ctx: &mut DrawingCtx,
    transform: Transform,
    node_bbox: BoundingBox,
) -> Result<SharedImageSurface, RenderingError> {
    let filter_node = &*filter_node;
    assert!(is_element_of_type!(filter_node, Filter));

    if filter_node.borrow_element().is_in_error() {
        return Ok(source_surface);
    }

    let filter = borrow_element_as!(filter_node, Filter);
    let mut filter_ctx = FilterContext::new(
        &filter,
        computed_from_node_being_filtered,
        source_surface,
        draw_ctx,
        transform,
        node_bbox,
    );

    // If paffine is non-invertible, we won't draw anything. Also bbox combining in bounds
    // computations will panic due to non-invertible martrix.
    if !filter_ctx.paffine().is_invertible() {
        return Ok(filter_ctx.into_output()?);
    }

    let primitives = filter_node
        .children()
        .filter(|c| c.is_element())
        // Skip nodes in error.
        .filter(|c| {
            let in_error = c.borrow_element().is_in_error();

            if in_error {
                rsvg_log!("(ignoring filter primitive {} because it is in error)", c);
            }

            !in_error
        })
        // Keep only filter primitives (those that implement the Filter trait)
        .filter(|c| c.borrow_element().as_filter_effect().is_some())
        // Check if the node wants linear RGB.
        .map(|c| {
            let linear_rgb = {
                let cascaded = CascadedValues::new_from_node(&c);
                let values = cascaded.get();

                values.color_interpolation_filters() == ColorInterpolationFilters::LinearRgb
            };

            (c, linear_rgb)
        });

    for (c, linear_rgb) in primitives {
        let elt = c.borrow_element();
        let filter = elt.as_filter_effect().unwrap();

        let mut render = |filter_ctx: &mut FilterContext| {
            if let Err(err) = filter
                .render(&c, filter_ctx, acquired_nodes, draw_ctx)
                .and_then(|result| filter_ctx.store_result(result))
            {
                rsvg_log!("(filter primitive {} returned an error: {})", c, err);

                // Exit early on Cairo errors. Continue rendering otherwise.
                if let FilterError::CairoError(status) = err {
                    return Err(status);
                }
            }

            Ok(())
        };

        let start = Instant::now();

        if filter.is_affected_by_color_interpolation_filters() && linear_rgb {
            filter_ctx.with_linear_rgb(render)?;
        } else {
            render(&mut filter_ctx)?;
        }

        let elapsed = start.elapsed();
        rsvg_log!(
            "(rendered filter primitive {} in\n    {} seconds)",
            c,
            elapsed.as_secs() as f64 + f64::from(elapsed.subsec_nanos()) / 1e9
        );
    }

    Ok(filter_ctx.into_output()?)
}

impl From<ColorInterpolationFilters> for SurfaceType {
    fn from(c: ColorInterpolationFilters) -> Self {
        match c {
            ColorInterpolationFilters::LinearRgb => SurfaceType::LinearRgb,
            _ => SurfaceType::SRgb,
        }
    }
}
<|endoftext|>


<commit_before>extern crate zip;

use std::fs;
use zip_info::WriteZipInfo;

pub struct ZipInfoFlatWriter {
    archive: zip::ZipArchive<fs::File>,
}

impl ZipInfoFlatWriter {
    /// Open a zip archive for this writer:
    pub fn new(file_path: String) -> ZipInfoFlatWriter {
        let file = fs::File::open(&file_path).unwrap();

        let whole_archive = zip::ZipArchive::new(file).unwrap();

        ZipInfoFlatWriter { archive: whole_archive }
    }
}

impl WriteZipInfo for ZipInfoFlatWriter {
    fn write_zip_info(&mut self) -> String {
        let mut info = String::new();

        for i in 0..self.archive.len() {
            let archive_item = self.archive.by_index(i).unwrap();
            info = format!("{}{}", info, info_for_archive_item(archive_item));
        }

        info
    }
}

fn info_for_archive_item(archive_item: zip::read::ZipFile) -> String {
    let mut info = String::new();
    let item_path = archive_item.name();
    info = format!("{}\n\t{}", info, item_path);

    let compression_type = archive_item.compression();
    info = format!("{}\n\t\tCompression type: {}", info, compression_type);

    let original_size = archive_item.size();
    info = format!("{}\n\t\tOriginal size: {}", info, original_size);

    let compressed_size = archive_item.compressed_size();
    info = format!("{}\n\t\tCompressed size: {}", info, compressed_size);

    if original_size > 0 {
        let compression_rate =
            (original_size as f64 - compressed_size as f64)
            / original_size as f64;
        let compression_perc = format!("{:.*}%", 2, compression_rate * 100.0);
        info = format!("{}\n\t\tCompression_rate: {}", info, compression_perc);
    }

    let comment = archive_item.comment();
    if comment.len() > 0 {
        info = format!("{}\n\t\tComment: {}", info, comment);
    }

    info
}
<commit_msg>Add zip file names back to flat output<commit_after>extern crate zip;

use std::fs;
use zip_info::WriteZipInfo;

pub struct ZipInfoFlatWriter {
    archive: zip::ZipArchive<fs::File>,
    path_name: String,
}

impl ZipInfoFlatWriter {
    /// Open a zip archive for this writer:
    pub fn new(file_path: String) -> ZipInfoFlatWriter {
        let file = fs::File::open(&file_path).unwrap();

        let whole_archive = zip::ZipArchive::new(file).unwrap();

        ZipInfoFlatWriter { archive: whole_archive, path_name: file_path }
    }
}

impl WriteZipInfo for ZipInfoFlatWriter {
    /// Concatenate Zip file name with indented stats:
    fn write_zip_info(&mut self) -> String {
        let mut info = format!("{}", self.path_name);

        for i in 0..self.archive.len() {
            let archive_item = self.archive.by_index(i).unwrap();
            info = format!("{}{}", info, info_for_archive_item(archive_item));
        }

        info
    }
}

fn info_for_archive_item(archive_item: zip::read::ZipFile) -> String {
    let mut info = String::new();
    let item_path = archive_item.name();
    info = format!("{}\n\t{}", info, item_path);

    let compression_type = archive_item.compression();
    info = format!("{}\n\t\tCompression type: {}", info, compression_type);

    let original_size = archive_item.size();
    info = format!("{}\n\t\tOriginal size: {}", info, original_size);

    let compressed_size = archive_item.compressed_size();
    info = format!("{}\n\t\tCompressed size: {}", info, compressed_size);

    if original_size > 0 {
        let compression_rate =
            (original_size as f64 - compressed_size as f64)
            / original_size as f64;
        let compression_perc = format!("{:.*}%", 2, compression_rate * 100.0);
        info = format!("{}\n\t\tCompression_rate: {}", info, compression_perc);
    }

    let comment = archive_item.comment();
    if comment.len() > 0 {
        info = format!("{}\n\t\tComment: {}", info, comment);
    }

    info
}
<|endoftext|>


<commit_before>use super::response::Response;
use super::request::Request;
use super::alloy::Alloy;
use super::ingot::Ingot;

pub mod ironfurnace;

/// `Furnaces` are the backbone of `Iron`. They coordinate `Ingots`
/// and ensure they are resolved and called in the right order,
/// create and distribute `Alloys`, and handle incoming requests.
///
/// `Furnaces` are internal tools. Unless you want additional
/// or unusual behavior such as enhanced debug logging you
/// probably don't need to mess with `Furnace` internals.
///
/// That being said, custom `Furnaces` are extremely powerful as
/// they allow you to completely control the resolution of `Ingots`.
///
/// The default `Furnace` used in `Iron` is the `IronFurnace`.
pub trait Furnace<Rq: Request, Rs: Response>: Send + Clone {
    /// A `Furnace's` forge method will get called once per Request, and may be
    /// called either with or without an existing `Alloy`. `Forge` is responsible
    /// for delegating the request to the correct `Ingots` and in the correct
    /// order. Effectively, 99% of the work done by a `Furnace` is done here.
    fn forge(&mut self, _request: &mut Rq, _response: &mut Rs, Option<&mut Alloy>);

    /// `Smelt` is responsible for adding an `Ingot` to the `Furnace's` internal
    /// storage of `Ingots`. Different `Furnaces` may implement different behavior
    /// for smelt, but ideally an `Ingot` added here will be delegated to during
    /// Requests.
    fn smelt<I: Ingot<Rq, Rs>>(&mut self, _ingot: I);

    /// Create a new instance of `Furnace`.
    /// If you are making your own furnace, you'll need to
    /// pass a new instance of it to `Iron`, otherwise,
    /// this function will only be used internally.
    fn new() -> Self;
}

<commit_msg>(doc) Added rustdoc for furnace module.<commit_after>//! Exposes the `Furnace` trait and `IronFurnace` type.

use super::response::Response;
use super::request::Request;
use super::alloy::Alloy;
use super::ingot::Ingot;

pub mod ironfurnace;

/// `Furnaces` are the backbone of `Iron`. They coordinate `Ingots`
/// and ensure they are resolved and called in the right order,
/// create and distribute `Alloys`, and handle incoming requests.
///
/// `Furnaces` are internal tools. Unless you want additional
/// or unusual behavior such as enhanced debug logging you
/// probably don't need to mess with `Furnace` internals.
///
/// That being said, custom `Furnaces` are extremely powerful as
/// they allow you to completely control the resolution of `Ingots`.
///
/// The default `Furnace` used in `Iron` is the `IronFurnace`.
pub trait Furnace<Rq: Request, Rs: Response>: Send + Clone {
    /// A `Furnace's` forge method will get called once per Request, and may be
    /// called either with or without an existing `Alloy`. `Forge` is responsible
    /// for delegating the request to the correct `Ingots` and in the correct
    /// order. Effectively, 99% of the work done by a `Furnace` is done here.
    fn forge(&mut self, _request: &mut Rq, _response: &mut Rs, Option<&mut Alloy>);

    /// `Smelt` is responsible for adding an `Ingot` to the `Furnace's` internal
    /// storage of `Ingots`. Different `Furnaces` may implement different behavior
    /// for smelt, but ideally an `Ingot` added here will be delegated to during
    /// Requests.
    fn smelt<I: Ingot<Rq, Rs>>(&mut self, _ingot: I);

    /// Create a new instance of `Furnace`.
    /// If you are making your own furnace, you'll need to
    /// pass a new instance of it to `Iron`, otherwise,
    /// this function will only be used internally.
    fn new() -> Self;
}

<|endoftext|>


<commit_before>use clap::{Arg, App, SubCommand};

pub fn build_ui<'a>(app: App<'a, 'a, 'a, 'a, 'a, 'a>) -> App<'a, 'a, 'a, 'a, 'a, 'a> {
    app.subcommand(SubCommand::with_name("create")
                   .about("Create an entry from the store")
                   .version("0.1")
                   .arg(Arg::with_name("path")
                        .long("path")
                        .short("p")
                        .takes_value(true)
                        .required(true)
                        .help("Create at this store path"))
                   .arg(Arg::with_name("from-raw")
                        .long("from-raw")
                        .takes_value(true)
                        .help("Create a new entry by reading this file ('-' for stdin)"))
                   .subcommand(SubCommand::with_name("entry")
                               .about("Create an entry via commandline")
                               .version("0.1")
                               .arg(Arg::with_name("content")
                                    .long("content")
                                    .short("c")
                                    .takes_value(true)
                                    .help("Content for the Entry from this file ('-' for stdin)"))
                               .arg(Arg::with_name("header")
                                    .long("header")
                                    .short("h")
                                    .takes_value(true)
                                    .multiple(true)
                                    .help("Set a header field. Specify as 'header.field.value=value', multiple allowed"))
                               )
                   )

       .subcommand(SubCommand::with_name("retrieve")
                   .about("Get an entry from the store")
                   .version("0.1")
                   .arg(Arg::with_name("id")
                        .long("id")
                        .short("i")
                        .takes_value(true)
                        .required(true)
                        .help("Retreive by Store Path, where root (/) is the store itself"))
                   .arg(Arg::with_name("content")
                        .long("content")
                        .short("c")
                        .help("Print content"))
                   .arg(Arg::with_name("header")
                        .long("header")
                        .short("h")
                        .help("Print header"))
                   .arg(Arg::with_name("header-json")
                        .long("header-json")
                        .short("j")
                        .help("Print header as json"))
                   .arg(Arg::with_name("raw")
                        .long("raw")
                        .short("r")
                        .help("Print Entries as they are in the store"))
                   .subcommand(SubCommand::with_name("filter-header")
                               .about("Retrieve Entries by filtering")
                               .version("0.1")
                               .arg(Arg::with_name("header-field-where")
                                    .long("where")
                                    .short("w")
                                    .takes_value(true)
                                    .help("Filter with 'header.field=foo' where the header field 'header.field' equals 'foo'")
                               )
                               .arg(Arg::with_name("header-field-grep")
                                    .long("grep")
                                    .short("g")
                                    .takes_value(true)
                                    .help("Filter with 'header.field=[a-zA-Z0-9]*' where the header field 'header.field' matches '[a-zA-Z0-9]*'"))
                               )
                   )

       .subcommand(SubCommand::with_name("update")
                   .about("Get an entry from the store")
                   .version("0.1")
                   .arg(Arg::with_name("id")
                        .long("id")
                        .short("i")
                        .takes_value(true)
                        .required(true)
                        .help("Update Store Entry with this path. Root (/) is the store itself"))
                   .arg(Arg::with_name("content")
                        .long("content")
                        .short("c")
                        .takes_value(true)
                        .help("Take the content for the new Entry from this file ('-' for stdin)"))
                   .arg(Arg::with_name("header")
                        .long("header")
                        .short("h")
                        .takes_value(true)
                        .multiple(true)
                        .help("Set a header field. Specify as 'header.field.value=value', multiple allowed"))
                   )

       .subcommand(SubCommand::with_name("delete")
                   .about("Delete an entry from the store")
                   .version("0.1")
                   .arg(Arg::with_name("id")
                        .long("id")
                        .short("i")
                        .takes_value(true)
                        .required(true)
                        .help("Remove Store Entry with this path. Root (/) is the store itself"))
                   )
}

<commit_msg>UI: Add content-from argument, to get content from CLI _or_ from File/stdin<commit_after>use clap::{Arg, App, SubCommand};

pub fn build_ui<'a>(app: App<'a, 'a, 'a, 'a, 'a, 'a>) -> App<'a, 'a, 'a, 'a, 'a, 'a> {
    app.subcommand(SubCommand::with_name("create")
                   .about("Create an entry from the store")
                   .version("0.1")
                   .arg(Arg::with_name("path")
                        .long("path")
                        .short("p")
                        .takes_value(true)
                        .required(true)
                        .help("Create at this store path"))
                   .arg(Arg::with_name("from-raw")
                        .long("from-raw")
                        .takes_value(true)
                        .help("Create a new entry by reading this file ('-' for stdin)"))
                   .subcommand(SubCommand::with_name("entry")
                               .about("Create an entry via commandline")
                               .version("0.1")
                               .arg(Arg::with_name("content")
                                    .long("content")
                                    .short("c")
                                    .takes_value(true)
                                    .help("Content for the Entry from commandline"))
                               .arg(Arg::with_name("content-from")
                                    .long("content-from")
                                    .short("f")
                                    .takes_value(true)
                                    .help("Content for the Entry from this file ('-' for stdin)"))
                               .arg(Arg::with_name("header")
                                    .long("header")
                                    .short("h")
                                    .takes_value(true)
                                    .multiple(true)
                                    .help("Set a header field. Specify as 'header.field.value=value', multiple allowed"))
                               )
                   )

       .subcommand(SubCommand::with_name("retrieve")
                   .about("Get an entry from the store")
                   .version("0.1")
                   .arg(Arg::with_name("id")
                        .long("id")
                        .short("i")
                        .takes_value(true)
                        .required(true)
                        .help("Retreive by Store Path, where root (/) is the store itself"))
                   .arg(Arg::with_name("content")
                        .long("content")
                        .short("c")
                        .help("Print content"))
                   .arg(Arg::with_name("header")
                        .long("header")
                        .short("h")
                        .help("Print header"))
                   .arg(Arg::with_name("header-json")
                        .long("header-json")
                        .short("j")
                        .help("Print header as json"))
                   .arg(Arg::with_name("raw")
                        .long("raw")
                        .short("r")
                        .help("Print Entries as they are in the store"))
                   .subcommand(SubCommand::with_name("filter-header")
                               .about("Retrieve Entries by filtering")
                               .version("0.1")
                               .arg(Arg::with_name("header-field-where")
                                    .long("where")
                                    .short("w")
                                    .takes_value(true)
                                    .help("Filter with 'header.field=foo' where the header field 'header.field' equals 'foo'")
                               )
                               .arg(Arg::with_name("header-field-grep")
                                    .long("grep")
                                    .short("g")
                                    .takes_value(true)
                                    .help("Filter with 'header.field=[a-zA-Z0-9]*' where the header field 'header.field' matches '[a-zA-Z0-9]*'"))
                               )
                   )

       .subcommand(SubCommand::with_name("update")
                   .about("Get an entry from the store")
                   .version("0.1")
                   .arg(Arg::with_name("id")
                        .long("id")
                        .short("i")
                        .takes_value(true)
                        .required(true)
                        .help("Update Store Entry with this path. Root (/) is the store itself"))
                   .arg(Arg::with_name("content")
                        .long("content")
                        .short("c")
                        .takes_value(true)
                        .help("Take the content for the new Entry from this file ('-' for stdin)"))
                   .arg(Arg::with_name("header")
                        .long("header")
                        .short("h")
                        .takes_value(true)
                        .multiple(true)
                        .help("Set a header field. Specify as 'header.field.value=value', multiple allowed"))
                   )

       .subcommand(SubCommand::with_name("delete")
                   .about("Delete an entry from the store")
                   .version("0.1")
                   .arg(Arg::with_name("id")
                        .long("id")
                        .short("i")
                        .takes_value(true)
                        .required(true)
                        .help("Remove Store Entry with this path. Root (/) is the store itself"))
                   )
}

<|endoftext|>


<commit_before>//! rustdoc handler

use super::pool::Pool;
use super::file::File;
use super::MetaData;
use iron::prelude::*;
use iron::{status, Url};
use iron::modifiers::Redirect;
use router::Router;
use super::match_version;
use super::error::Nope;
use super::page::Page;
use rustc_serialize::json::{Json, ToJson};
use std::collections::BTreeMap;

#[derive(Debug)]
struct RustdocPage {
    pub head: String,
    pub body: String,
    pub name: String,
    pub version: String,
    pub description: Option<String>,
    pub metadata: Option<MetaData>,
    pub platforms: Option<Json>
}

impl Default for RustdocPage {
    fn default() -> RustdocPage {
        RustdocPage {
            head: String::new(),
            body: String::new(),
            name: String::new(),
            version: String::new(),
            description: None,
            metadata: None,
            platforms: None,
        }
    }
}

impl ToJson for RustdocPage {
    fn to_json(&self) -> Json {
        let mut m: BTreeMap<String, Json> = BTreeMap::new();
        m.insert("rustdoc_head".to_string(), self.head.to_json());
        m.insert("rustdoc_body".to_string(), self.body.to_json());
        m.insert("rustdoc_status".to_string(), true.to_json());
        m.insert("name".to_string(), self.name.to_json());
        m.insert("version".to_string(), self.version.to_json());
        m.insert("description".to_string(), self.description.to_json());
        m.insert("metadata".to_string(), self.metadata.to_json());
        m.insert("platforms".to_string(), self.platforms.to_json());
        m.to_json()
    }
}

pub fn rustdoc_redirector_handler(req: &mut Request) -> IronResult<Response> {

    fn redirect_to_doc(req: &Request,
                       name: &str,
                       vers: &str,
                       target_name: &str)
                       -> IronResult<Response> {
        let url = Url::parse(&format!("{}://{}:{}/{}/{}/{}/",
                                      req.url.scheme,
                                      req.url.host,
                                      req.url.port,
                                      name,
                                      vers,
                                      target_name)[..])
            .unwrap();
        let mut resp = Response::with((status::Found, Redirect(url)));

        use iron::headers::{Expires, HttpDate};
        use time;
        resp.headers.set(Expires(HttpDate(time::now())));

        Ok(resp)
    }

    // this handler should never called without crate pattern
    let crate_name = req.extensions.get::<Router>().unwrap().find("crate").unwrap();
    let req_version = req.extensions.get::<Router>().unwrap().find("version");

    let conn = req.extensions.get::<Pool>().unwrap();

    let version = match match_version(&conn, &crate_name, req_version) {
        Some(v) => v,
        None => return Err(IronError::new(Nope::CrateNotFound, status::NotFound)),
    };

    // get target name
    // FIXME: This is a bit inefficient but allowing us to use less code in general
    let target_name: String = conn.query("SELECT target_name FROM releases INNER JOIN crates ON \
                                          crates.id = releases.crate_id WHERE crates.name = $1 \
                                          AND releases.version = $2",
               &[&crate_name, &version])
        .unwrap()
        .get(0)
        .get(0);

    redirect_to_doc(req, &crate_name, &version, &target_name)
}

pub fn rustdoc_html_server_handler(req: &mut Request) -> IronResult<Response> {

    let name = req.extensions.get::<Router>().unwrap().find("crate").unwrap_or("").to_string();
    let version = req.extensions
        .get::<Router>()
        .unwrap()
        .find("version");
    let conn = req.extensions.get::<Pool>().unwrap();
    let version = try!(match_version(&conn, &name, version)
        .ok_or(IronError::new(Nope::ResourceNotFound, status::NotFound)));

    // remove name and version from path
    for _ in 0..2 {
        req.url.path.remove(0);
    }

    // docs have "rustdoc" prefix in database
    req.url.path.insert(0, "rustdoc".to_owned());

    // add crate name and version
    req.url.path.insert(1, name.clone());
    req.url.path.insert(2, version.clone());

    let path = {
        let mut path = req.url.path.join("/");
        if path.ends_with("/") {
            path.push_str("index.html");
            req.url.path.push("index.html".to_owned());
        }
        path
    };

    // don't touch anything other than html files
    if !path.ends_with(".html") {
        return Err(IronError::new(Nope::ResourceNotFound, status::NotFound));
    }

    let file = match File::from_path(&conn, &path) {
        Some(f) => f,
        None => return Err(IronError::new(Nope::ResourceNotFound, status::NotFound)),
    };

    let (mut in_head, mut in_body) = (false, false);

    let mut content = RustdocPage::default();

    let file_content = String::from_utf8(file.content).unwrap();

    for line in file_content.lines() {

        if line.starts_with("<head") {
            in_head = true;
            continue;
        } else if line.starts_with("</head") {
            in_head = false;
        } else if line.starts_with("<body") {
            in_body = true;
            continue;
        } else if line.starts_with("</body") {
            in_body = false;
        }

        if in_head {
            content.head.push_str(&line[..]);
            content.head.push('\n');
        } else if in_body {
            content.body.push_str(&line[..]);
            content.body.push('\n');
        }
    }

    // content.metadata = MetaData::from_crate(&conn, &name, &version);
    let (metadata, platforms) = {
        let rows = conn.query("SELECT crates.name,
                                      releases.version,
                                      releases.description,
                                      releases.target_name,
                                      releases.rustdoc_status,
                                      doc_targets
                               FROM releases
                               INNER JOIN crates ON crates.id = releases.crate_id
                               WHERE crates.name = $1 AND releases.version = $2",
                              &[&name, &version]).unwrap();

        let metadata = MetaData {
            name: rows.get(0).get(0),
            version: rows.get(0).get(1),
            description: rows.get(0).get(2),
            target_name: rows.get(0).get(3),
            rustdoc_status: rows.get(0).get(4),
        };
        let platforms: Json = rows.get(0).get(5);
        (Some(metadata), platforms)
    };

    content.metadata = metadata;
    content.platforms = Some(platforms);

    Page::new(content)
        .set_true("show_package_navigation")
        .set_true("package_navigation_documentation_tab")
        .set_true("package_navigation_show_platforms_tab")
        .to_resp("rustdoc")
}

pub fn badge_handler(req: &mut Request) -> IronResult<Response> {
    use iron::headers::ContentType;
    use params::{Params, Value};
    use badge::{Badge, BadgeOptions};

    let version = {
        let params = req.get_ref::<Params>().unwrap();
        match params.find(&["version"]) {
            Some(&Value::String(ref version)) => version.clone(),
            _ => "*".to_owned(),
        }
    };

    let name = req.extensions.get::<Router>().unwrap().find("crate").unwrap();
    let conn = req.extensions.get::<Pool>().unwrap();

    let options = match match_version(&conn, &name, Some(&version)) {
        Some(version) => {
            let rows = conn.query("SELECT rustdoc_status
                                   FROM releases
                                   INNER JOIN crates ON crates.id = releases.crate_id
                                   WHERE crates.name = $1 AND releases.version = $2",
                                  &[&name, &version]).unwrap();
            if rows.len() > 0 && rows.get(0).get(0) {
                BadgeOptions {
                    subject: "docs".to_owned(),
                    status: version,
                    color: "#4d76ae".to_owned(),
                }
            } else {
                BadgeOptions {
                    subject: "docs".to_owned(),
                    status: version,
                    color: "#e05d44".to_owned(),
                }
            }
        },
        None => {
            BadgeOptions {
                subject: "docs".to_owned(),
                status: "no builds".to_owned(),
                color: "#e05d44".to_owned(),
            }
        },
    };

    let mut resp = Response::with((status::Ok, Badge::new(options).unwrap().to_svg()));
    resp.headers.set(ContentType("image/svg+xml".parse().unwrap()));
    Ok(resp)
}
<commit_msg>Add Expires header to badge handler<commit_after>//! rustdoc handler

use super::pool::Pool;
use super::file::File;
use super::MetaData;
use iron::prelude::*;
use iron::{status, Url};
use iron::modifiers::Redirect;
use router::Router;
use super::match_version;
use super::error::Nope;
use super::page::Page;
use rustc_serialize::json::{Json, ToJson};
use std::collections::BTreeMap;
use iron::headers::{Expires, HttpDate};
use time;

#[derive(Debug)]
struct RustdocPage {
    pub head: String,
    pub body: String,
    pub name: String,
    pub version: String,
    pub description: Option<String>,
    pub metadata: Option<MetaData>,
    pub platforms: Option<Json>
}

impl Default for RustdocPage {
    fn default() -> RustdocPage {
        RustdocPage {
            head: String::new(),
            body: String::new(),
            name: String::new(),
            version: String::new(),
            description: None,
            metadata: None,
            platforms: None,
        }
    }
}

impl ToJson for RustdocPage {
    fn to_json(&self) -> Json {
        let mut m: BTreeMap<String, Json> = BTreeMap::new();
        m.insert("rustdoc_head".to_string(), self.head.to_json());
        m.insert("rustdoc_body".to_string(), self.body.to_json());
        m.insert("rustdoc_status".to_string(), true.to_json());
        m.insert("name".to_string(), self.name.to_json());
        m.insert("version".to_string(), self.version.to_json());
        m.insert("description".to_string(), self.description.to_json());
        m.insert("metadata".to_string(), self.metadata.to_json());
        m.insert("platforms".to_string(), self.platforms.to_json());
        m.to_json()
    }
}

pub fn rustdoc_redirector_handler(req: &mut Request) -> IronResult<Response> {

    fn redirect_to_doc(req: &Request,
                       name: &str,
                       vers: &str,
                       target_name: &str)
                       -> IronResult<Response> {
        let url = Url::parse(&format!("{}://{}:{}/{}/{}/{}/",
                                      req.url.scheme,
                                      req.url.host,
                                      req.url.port,
                                      name,
                                      vers,
                                      target_name)[..])
            .unwrap();
        let mut resp = Response::with((status::Found, Redirect(url)));
        resp.headers.set(Expires(HttpDate(time::now())));

        Ok(resp)
    }

    // this handler should never called without crate pattern
    let crate_name = req.extensions.get::<Router>().unwrap().find("crate").unwrap();
    let req_version = req.extensions.get::<Router>().unwrap().find("version");

    let conn = req.extensions.get::<Pool>().unwrap();

    let version = match match_version(&conn, &crate_name, req_version) {
        Some(v) => v,
        None => return Err(IronError::new(Nope::CrateNotFound, status::NotFound)),
    };

    // get target name
    // FIXME: This is a bit inefficient but allowing us to use less code in general
    let target_name: String = conn.query("SELECT target_name FROM releases INNER JOIN crates ON \
                                          crates.id = releases.crate_id WHERE crates.name = $1 \
                                          AND releases.version = $2",
               &[&crate_name, &version])
        .unwrap()
        .get(0)
        .get(0);

    redirect_to_doc(req, &crate_name, &version, &target_name)
}

pub fn rustdoc_html_server_handler(req: &mut Request) -> IronResult<Response> {

    let name = req.extensions.get::<Router>().unwrap().find("crate").unwrap_or("").to_string();
    let version = req.extensions
        .get::<Router>()
        .unwrap()
        .find("version");
    let conn = req.extensions.get::<Pool>().unwrap();
    let version = try!(match_version(&conn, &name, version)
        .ok_or(IronError::new(Nope::ResourceNotFound, status::NotFound)));

    // remove name and version from path
    for _ in 0..2 {
        req.url.path.remove(0);
    }

    // docs have "rustdoc" prefix in database
    req.url.path.insert(0, "rustdoc".to_owned());

    // add crate name and version
    req.url.path.insert(1, name.clone());
    req.url.path.insert(2, version.clone());

    let path = {
        let mut path = req.url.path.join("/");
        if path.ends_with("/") {
            path.push_str("index.html");
            req.url.path.push("index.html".to_owned());
        }
        path
    };

    // don't touch anything other than html files
    if !path.ends_with(".html") {
        return Err(IronError::new(Nope::ResourceNotFound, status::NotFound));
    }

    let file = match File::from_path(&conn, &path) {
        Some(f) => f,
        None => return Err(IronError::new(Nope::ResourceNotFound, status::NotFound)),
    };

    let (mut in_head, mut in_body) = (false, false);

    let mut content = RustdocPage::default();

    let file_content = String::from_utf8(file.content).unwrap();

    for line in file_content.lines() {

        if line.starts_with("<head") {
            in_head = true;
            continue;
        } else if line.starts_with("</head") {
            in_head = false;
        } else if line.starts_with("<body") {
            in_body = true;
            continue;
        } else if line.starts_with("</body") {
            in_body = false;
        }

        if in_head {
            content.head.push_str(&line[..]);
            content.head.push('\n');
        } else if in_body {
            content.body.push_str(&line[..]);
            content.body.push('\n');
        }
    }

    // content.metadata = MetaData::from_crate(&conn, &name, &version);
    let (metadata, platforms) = {
        let rows = conn.query("SELECT crates.name,
                                      releases.version,
                                      releases.description,
                                      releases.target_name,
                                      releases.rustdoc_status,
                                      doc_targets
                               FROM releases
                               INNER JOIN crates ON crates.id = releases.crate_id
                               WHERE crates.name = $1 AND releases.version = $2",
                              &[&name, &version]).unwrap();

        let metadata = MetaData {
            name: rows.get(0).get(0),
            version: rows.get(0).get(1),
            description: rows.get(0).get(2),
            target_name: rows.get(0).get(3),
            rustdoc_status: rows.get(0).get(4),
        };
        let platforms: Json = rows.get(0).get(5);
        (Some(metadata), platforms)
    };

    content.metadata = metadata;
    content.platforms = Some(platforms);

    Page::new(content)
        .set_true("show_package_navigation")
        .set_true("package_navigation_documentation_tab")
        .set_true("package_navigation_show_platforms_tab")
        .to_resp("rustdoc")
}

pub fn badge_handler(req: &mut Request) -> IronResult<Response> {
    use iron::headers::ContentType;
    use params::{Params, Value};
    use badge::{Badge, BadgeOptions};

    let version = {
        let params = req.get_ref::<Params>().unwrap();
        match params.find(&["version"]) {
            Some(&Value::String(ref version)) => version.clone(),
            _ => "*".to_owned(),
        }
    };

    let name = req.extensions.get::<Router>().unwrap().find("crate").unwrap();
    let conn = req.extensions.get::<Pool>().unwrap();

    let options = match match_version(&conn, &name, Some(&version)) {
        Some(version) => {
            let rows = conn.query("SELECT rustdoc_status
                                   FROM releases
                                   INNER JOIN crates ON crates.id = releases.crate_id
                                   WHERE crates.name = $1 AND releases.version = $2",
                                  &[&name, &version]).unwrap();
            if rows.len() > 0 && rows.get(0).get(0) {
                BadgeOptions {
                    subject: "docs".to_owned(),
                    status: version,
                    color: "#4d76ae".to_owned(),
                }
            } else {
                BadgeOptions {
                    subject: "docs".to_owned(),
                    status: version,
                    color: "#e05d44".to_owned(),
                }
            }
        },
        None => {
            BadgeOptions {
                subject: "docs".to_owned(),
                status: "no builds".to_owned(),
                color: "#e05d44".to_owned(),
            }
        },
    };

    let mut resp = Response::with((status::Ok, Badge::new(options).unwrap().to_svg()));
    resp.headers.set(ContentType("image/svg+xml".parse().unwrap()));
    resp.headers.set(Expires(HttpDate(time::now())));
    Ok(resp)
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate nom;

use nom::{IResult,digit, multispace};

use std::str;
use std::str::FromStr;

named!(parens<i64>, delimited!(
    delimited!(opt!(multispace), tag!("("), opt!(multispace)),
    expr,
    delimited!(opt!(multispace), tag!(")"), opt!(multispace))
  )
);

named!(factor<i64>, alt!(
    map_res!(
      map_res!(
        delimited!(opt!(multispace), digit, opt!(multispace)),
        str::from_utf8
      ),
      FromStr::from_str
    )
  | parens
  )
);

named!(term <i64>, chain!(
    mut acc: factor  ~
             many0!(
               alt!(
                 tap!(mul: preceded!(tag!("*"), factor) => acc = acc * mul) |
                 tap!(div: preceded!(tag!("/"), factor) => acc = acc / div)
               )
             ),
    || { return acc }
  )
);

named!(expr <i64>, chain!(
    mut acc: term  ~
             many0!(
               alt!(
                 tap!(add: preceded!(tag!("+"), term) => acc = acc + add) |
                 tap!(sub: preceded!(tag!("-"), term) => acc = acc - sub)
               )
             ),
    || { return acc }
  )
);

#[test]
fn factor_test() {
  assert_eq!(factor(&b"3"[..]), IResult::Done(&b""[..], 3));
  assert_eq!(factor(&b" 12"[..]), IResult::Done(&b""[..], 12));
  assert_eq!(factor(&b"537  "[..]), IResult::Done(&b""[..], 537));
  assert_eq!(factor(&b"  24   "[..]), IResult::Done(&b""[..], 24));
}

#[test]
fn term_test() {
  assert_eq!(term(&b" 12 *2 /  3"[..]), IResult::Done(&b""[..], 8));
  assert_eq!(term(&b" 2* 3  *2 *2 /  3"[..]), IResult::Done(&b""[..], 8));
  assert_eq!(term(&b" 48 /  3/2"[..]), IResult::Done(&b""[..], 8));
}

#[test]
fn expr_test() {
  assert_eq!(expr(&b" 1 +  2 "[..]), IResult::Done(&b""[..], 3));
  assert_eq!(expr(&b" 12 + 6 - 4+  3"[..]), IResult::Done(&b""[..], 17));
  assert_eq!(expr(&b" 1 + 2*3 + 4"[..]), IResult::Done(&b""[..], 11));
}

#[test]
fn parens_test() {
  assert_eq!(expr(&b" (  2 )"[..]), IResult::Done(&b""[..], 2));
  assert_eq!(expr(&b" 2* (  3 + 4 ) "[..]), IResult::Done(&b""[..], 14));
  assert_eq!(expr(&b"  2*2 / ( 5 - 1) + 3"[..]), IResult::Done(&b""[..], 4));
}
<commit_msg>use ws! in one of the arithmetic examples<commit_after>#[macro_use]
extern crate nom;

use nom::{IResult,digit};

use std::str;
use std::str::FromStr;

named!(parens<i64>, ws!(delimited!( tag!("("), expr, tag!(")") )) );

named!(factor<i64>, alt!(
    map_res!(
      map_res!(
        ws!(digit),
        str::from_utf8
      ),
      FromStr::from_str
    )
  | parens
  )
);

named!(term <i64>, chain!(
    mut acc: factor  ~
             many0!(
               alt!(
                 tap!(mul: preceded!(tag!("*"), factor) => acc = acc * mul) |
                 tap!(div: preceded!(tag!("/"), factor) => acc = acc / div)
               )
             ),
    || { return acc }
  )
);

named!(expr <i64>, chain!(
    mut acc: term  ~
             many0!(
               alt!(
                 tap!(add: preceded!(tag!("+"), term) => acc = acc + add) |
                 tap!(sub: preceded!(tag!("-"), term) => acc = acc - sub)
               )
             ),
    || { return acc }
  )
);

#[test]
fn factor_test() {
  assert_eq!(factor(&b"3"[..]), IResult::Done(&b""[..], 3));
  assert_eq!(factor(&b" 12"[..]), IResult::Done(&b""[..], 12));
  assert_eq!(factor(&b"537  "[..]), IResult::Done(&b""[..], 537));
  assert_eq!(factor(&b"  24   "[..]), IResult::Done(&b""[..], 24));
}

#[test]
fn term_test() {
  assert_eq!(term(&b" 12 *2 /  3"[..]), IResult::Done(&b""[..], 8));
  assert_eq!(term(&b" 2* 3  *2 *2 /  3"[..]), IResult::Done(&b""[..], 8));
  assert_eq!(term(&b" 48 /  3/2"[..]), IResult::Done(&b""[..], 8));
}

#[test]
fn expr_test() {
  assert_eq!(expr(&b" 1 +  2 "[..]), IResult::Done(&b""[..], 3));
  assert_eq!(expr(&b" 12 + 6 - 4+  3"[..]), IResult::Done(&b""[..], 17));
  assert_eq!(expr(&b" 1 + 2*3 + 4"[..]), IResult::Done(&b""[..], 11));
}

#[test]
fn parens_test() {
  assert_eq!(expr(&b" (  2 )"[..]), IResult::Done(&b""[..], 2));
  assert_eq!(expr(&b" 2* (  3 + 4 ) "[..]), IResult::Done(&b""[..], 14));
  assert_eq!(expr(&b"  2*2 / ( 5 - 1) + 3"[..]), IResult::Done(&b""[..], 4));
}
<|endoftext|>