<commit_before>use std::cmp::min;

use cairo::{self, ImageSurface};
use markup5ever::{expanded_name, local_name, namespace_url, ns, QualName};

use crate::drawing_ctx::DrawingCtx;
use crate::error::*;
use crate::node::{NodeResult, NodeTrait, NodeType, RsvgNode};
use crate::number_list::{NumberList, NumberListError, NumberListLength};
use crate::parsers;
use crate::property_bag::PropertyBag;
use crate::surface_utils::{
    iterators::Pixels,
    shared_surface::SharedImageSurface,
    ImageSurfaceDataExt,
    Pixel,
};
use crate::util::clamp;

use super::context::{FilterContext, FilterOutput, FilterResult};
use super::{FilterEffect, FilterError, PrimitiveWithInput};

/// The `feComponentTransfer` filter primitive.
pub struct FeComponentTransfer {
    base: PrimitiveWithInput,
}

impl Default for FeComponentTransfer {
    /// Constructs a new `ComponentTransfer` with empty properties.
    #[inline]
    fn default() -> FeComponentTransfer {
        FeComponentTransfer {
            base: PrimitiveWithInput::new::<Self>(),
        }
    }
}

impl NodeTrait for FeComponentTransfer {
    impl_node_as_filter_effect!();

    #[inline]
    fn set_atts(&mut self, parent: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> NodeResult {
        self.base.set_atts(parent, pbag)
    }
}

/// Pixel components that can be influenced by `feComponentTransfer`.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum Channel {
    R,
    G,
    B,
    A,
}

/// Component transfer function types.
#[derive(Debug, Clone, Copy)]
enum FunctionType {
    Identity,
    Table,
    Discrete,
    Linear,
    Gamma,
}

impl FunctionType {
    fn parse(attr: QualName, s: &str) -> Result<Self, NodeError> {
        match s {
            "identity" => Ok(FunctionType::Identity),
            "table" => Ok(FunctionType::Table),
            "discrete" => Ok(FunctionType::Discrete),
            "linear" => Ok(FunctionType::Linear),
            "gamma" => Ok(FunctionType::Gamma),
            _ => Err(ValueErrorKind::parse_error("invalid value")).attribute(attr),
        }
    }
}

/// The compute function parameters.
struct FunctionParameters<'a> {
    table_values: &'a Vec<f64>,
    slope: f64,
    intercept: f64,
    amplitude: f64,
    exponent: f64,
    offset: f64,
}

/// The compute function type.
type Function = fn(&FunctionParameters<'_>, f64) -> f64;

/// The identity component transfer function.
fn identity(_: &FunctionParameters<'_>, value: f64) -> f64 {
    value
}

/// The table component transfer function.
fn table(params: &FunctionParameters<'_>, value: f64) -> f64 {
    let n = params.table_values.len() - 1;
    let k = (value * (n as f64)).floor() as usize;

    let k = min(k, n); // Just in case.

    if k == n {
        return params.table_values[k];
    }

    let vk = params.table_values[k];
    let vk1 = params.table_values[k + 1];
    let k = k as f64;
    let n = n as f64;

    vk + (value - k / n) * n * (vk1 - vk)
}

/// The discrete component transfer function.
fn discrete(params: &FunctionParameters<'_>, value: f64) -> f64 {
    let n = params.table_values.len();
    let k = (value * (n as f64)).floor() as usize;

    params.table_values[min(k, n - 1)]
}

/// The linear component transfer function.
fn linear(params: &FunctionParameters<'_>, value: f64) -> f64 {
    params.slope * value + params.intercept
}

/// The gamma component transfer function.
fn gamma(params: &FunctionParameters<'_>, value: f64) -> f64 {
    params.amplitude * value.powf(params.exponent) + params.offset
}

trait FeComponentTransferFunc {
    /// Returns the component transfer function.
    fn function(&self) -> Function;

    /// Returns the component transfer function parameters.
    fn function_parameters(&self) -> FunctionParameters<'_>;

    /// Returns the channel.
    fn channel(&self) -> Channel;
}

macro_rules! func_x {
    ($func_name:ident, $channel:expr) => {
        pub struct $func_name {
            channel: Channel,
            function_type: FunctionType,
            table_values: Vec<f64>,
            slope: f64,
            intercept: f64,
            amplitude: f64,
            exponent: f64,
            offset: f64,
        }

        impl Default for $func_name {
            #[inline]
            fn default() -> Self {
                Self {
                    channel: $channel,
                    function_type: FunctionType::Identity,
                    table_values: Vec::new(),
                    slope: 1.0,
                    intercept: 0.0,
                    amplitude: 1.0,
                    exponent: 1.0,
                    offset: 0.0,
                }
            }
        }

        impl FeComponentTransferFunc for $func_name {
            #[inline]
            fn function_parameters(&self) -> FunctionParameters<'_> {
                FunctionParameters {
                    table_values: &self.table_values,
                    slope: self.slope,
                    intercept: self.intercept,
                    amplitude: self.amplitude,
                    exponent: self.exponent,
                    offset: self.offset,
                }
            }

            #[inline]
            fn function(&self) -> Function {
                match self.function_type {
                    FunctionType::Identity => identity,
                    FunctionType::Table => table,
                    FunctionType::Discrete => discrete,
                    FunctionType::Linear => linear,
                    FunctionType::Gamma => gamma,
                }
            }

            #[inline]
            fn channel(&self) -> Channel {
                self.channel
            }
        }

        impl NodeTrait for $func_name {
            #[inline]
            fn set_atts(
                &mut self,
                _parent: Option<&RsvgNode>,
                pbag: &PropertyBag<'_>,
            ) -> NodeResult {
                for (attr, value) in pbag.iter() {
                    match attr.expanded() {
                        expanded_name!(svg "type") => {
                            self.function_type = FunctionType::parse(attr, value)?
                        }
                        expanded_name!(svg "tableValues") => {
                            let NumberList(v) =
                                NumberList::parse_str(value, NumberListLength::Unbounded).map_err(
                                    |err| {
                                        if let NumberListError::Parse(err) = err {
                                            ValueErrorKind::parse_error(&err)
                                        } else {
                                            panic!("unexpected number list error");
                                        }
                                    },
                                ).attribute(attr)?;
                            self.table_values = v;
                        }
                        expanded_name!(svg "slope") => {
                            self.slope = parsers::number(value).attribute(attr)?
                        }
                        expanded_name!(svg "intercept") => {
                            self.intercept = parsers::number(value).attribute(attr)?
                        }
                        expanded_name!(svg "amplitude") => {
                            self.amplitude = parsers::number(value).attribute(attr)?
                        }
                        expanded_name!(svg "exponent") => {
                            self.exponent = parsers::number(value).attribute(attr)?
                        }
                        expanded_name!(svg "offset") => {
                            self.offset = parsers::number(value).attribute(attr)?
                        }
                        _ => (),
                    }
                }

                // The table function type with empty table_values is considered
                // an identity function.
                match self.function_type {
                    FunctionType::Table | FunctionType::Discrete => {
                        if self.table_values.is_empty() {
                            self.function_type = FunctionType::Identity;
                        }
                    }
                    _ => (),
                }

                Ok(())
            }
        }
    };
}

// The `<feFuncR>` element
func_x!(FeFuncR, Channel::R);

// The `<feFuncG>` element
func_x!(FeFuncG, Channel::G);

// The `<feFuncB>` element
func_x!(FeFuncB, Channel::B);

// The `<feFuncA>` element
func_x!(FeFuncA, Channel::A);

macro_rules! func_or_default {
    ($func_node:ident, $func_type:ty, $func_data:ident, $func_default:ident) => {
        match $func_node {
            Some(ref f) => {
                $func_data = f.borrow();
                $func_data.get_impl::<$func_type>()
            }
            _ => &$func_default,
        };
    };
}

impl FilterEffect for FeComponentTransfer {
    fn render(
        &self,
        node: &RsvgNode,
        ctx: &FilterContext,
        draw_ctx: &mut DrawingCtx,
    ) -> Result<FilterResult, FilterError> {
        let input = self.base.get_input(ctx, draw_ctx)?;
        let bounds = self
            .base
            .get_bounds(ctx)
            .add_input(&input)
            .into_irect(draw_ctx);

        // Create the output surface.
        let mut output_surface = ImageSurface::create(
            cairo::Format::ARgb32,
            ctx.source_graphic().width(),
            ctx.source_graphic().height(),
        )?;

        // Get a node for every pixel component.
        fn get_node<F>(node: &RsvgNode, node_type: NodeType, channel: Channel) -> Option<RsvgNode>
        where
            F: FeComponentTransferFunc + NodeTrait,
        {
            node.children()
                .rev()
                .filter(|c| c.borrow().get_type() == node_type)
                .find(|c| c.borrow().get_impl::<F>().channel() == channel)
        };

        let func_r_node = get_node::<FeFuncR>(node, NodeType::FeFuncR, Channel::R);
        let func_g_node = get_node::<FeFuncG>(node, NodeType::FeFuncG, Channel::G);
        let func_b_node = get_node::<FeFuncB>(node, NodeType::FeFuncB, Channel::B);
        let func_a_node = get_node::<FeFuncA>(node, NodeType::FeFuncA, Channel::A);

        for node in [&func_r_node, &func_g_node, &func_b_node, &func_a_node]
            .iter()
            .filter_map(|x| x.as_ref())
        {
            if node.borrow().is_in_error() {
                return Err(FilterError::ChildNodeInError);
            }
        }

        // These are the default funcs that perform an identity transformation.
        let func_r_default = FeFuncR::default();
        let func_g_default = FeFuncG::default();
        let func_b_default = FeFuncB::default();
        let func_a_default = FeFuncA::default();

        // We need to tell the borrow checker that these live long enough
        let func_r_data;
        let func_g_data;
        let func_b_data;
        let func_a_data;

        let func_r = func_or_default!(func_r_node, FeFuncR, func_r_data, func_r_default);
        let func_g = func_or_default!(func_g_node, FeFuncG, func_g_data, func_g_default);
        let func_b = func_or_default!(func_b_node, FeFuncB, func_b_data, func_b_default);
        let func_a = func_or_default!(func_a_node, FeFuncA, func_a_data, func_a_default);

        #[inline]
        fn compute_func<'a, F>(func: &'a F) -> impl Fn(u8, f64, f64) -> u8 + 'a
        where
            F: FeComponentTransferFunc,
        {
            let compute = func.function();
            let params = func.function_parameters();

            move |value, alpha, new_alpha| {
                let value = f64::from(value) / 255f64;

                let unpremultiplied = if alpha == 0f64 { 0f64 } else { value / alpha };

                let new_value = compute(&params, unpremultiplied);
                let new_value = clamp(new_value, 0f64, 1f64);

                ((new_value * new_alpha * 255f64) + 0.5) as u8
            }
        }

        let compute_r = compute_func::<FeFuncR>(&func_r);
        let compute_g = compute_func::<FeFuncG>(&func_g);
        let compute_b = compute_func::<FeFuncB>(&func_b);

        // Alpha gets special handling since everything else depends on it.
        let compute_a = func_a.function();
        let params_a = func_a.function_parameters();
        let compute_a = |alpha| compute_a(&params_a, alpha);

        // Do the actual processing.
        let output_stride = output_surface.get_stride() as usize;
        {
            let mut output_data = output_surface.get_data().unwrap();

            for (x, y, pixel) in Pixels::new(input.surface(), bounds) {
                let alpha = f64::from(pixel.a) / 255f64;
                let new_alpha = compute_a(alpha);

                let output_pixel = Pixel {
                    r: compute_r(pixel.r, alpha, new_alpha),
                    g: compute_g(pixel.g, alpha, new_alpha),
                    b: compute_b(pixel.b, alpha, new_alpha),
                    a: ((new_alpha * 255f64) + 0.5) as u8,
                };

                output_data.set_pixel(output_stride, output_pixel, x, y);
            }
        }

        Ok(FilterResult {
            name: self.base.result.clone(),
            output: FilterOutput {
                surface: SharedImageSurface::new(output_surface, input.surface().surface_type())?,
                bounds,
            },
        })
    }

    fn is_affected_by_color_interpolation_filters(&self) -> bool {
        true
    }
}
<commit_msg>component_transfer.rs: impl Parse for FunctionType instead of a custom method<commit_after>use std::cmp::min;

use cairo::{self, ImageSurface};
use cssparser::Parser;
use markup5ever::{expanded_name, local_name, namespace_url, ns};

use crate::drawing_ctx::DrawingCtx;
use crate::error::*;
use crate::node::{NodeResult, NodeTrait, NodeType, RsvgNode};
use crate::number_list::{NumberList, NumberListError, NumberListLength};
use crate::parsers::{self, Parse, ParseValue};
use crate::property_bag::PropertyBag;
use crate::surface_utils::{
    iterators::Pixels, shared_surface::SharedImageSurface, ImageSurfaceDataExt, Pixel,
};
use crate::util::clamp;

use super::context::{FilterContext, FilterOutput, FilterResult};
use super::{FilterEffect, FilterError, PrimitiveWithInput};

/// The `feComponentTransfer` filter primitive.
pub struct FeComponentTransfer {
    base: PrimitiveWithInput,
}

impl Default for FeComponentTransfer {
    /// Constructs a new `ComponentTransfer` with empty properties.
    #[inline]
    fn default() -> FeComponentTransfer {
        FeComponentTransfer {
            base: PrimitiveWithInput::new::<Self>(),
        }
    }
}

impl NodeTrait for FeComponentTransfer {
    impl_node_as_filter_effect!();

    #[inline]
    fn set_atts(&mut self, parent: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> NodeResult {
        self.base.set_atts(parent, pbag)
    }
}

/// Pixel components that can be influenced by `feComponentTransfer`.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum Channel {
    R,
    G,
    B,
    A,
}

/// Component transfer function types.
#[derive(Debug, Clone, Copy)]
enum FunctionType {
    Identity,
    Table,
    Discrete,
    Linear,
    Gamma,
}

impl Parse for FunctionType {
    fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<FunctionType, ValueErrorKind> {
        parse_identifiers!(
            parser,
            "identity" => FunctionType::Identity,
            "table" => FunctionType::Table,
            "discrete" => FunctionType::Discrete,
            "linear" => FunctionType::Linear,
            "gamma" => FunctionType::Gamma,
        )
        .map_err(|_: ParseError| ValueErrorKind::parse_error("parse error"))
    }
}

/// The compute function parameters.
struct FunctionParameters<'a> {
    table_values: &'a Vec<f64>,
    slope: f64,
    intercept: f64,
    amplitude: f64,
    exponent: f64,
    offset: f64,
}

/// The compute function type.
type Function = fn(&FunctionParameters<'_>, f64) -> f64;

/// The identity component transfer function.
fn identity(_: &FunctionParameters<'_>, value: f64) -> f64 {
    value
}

/// The table component transfer function.
fn table(params: &FunctionParameters<'_>, value: f64) -> f64 {
    let n = params.table_values.len() - 1;
    let k = (value * (n as f64)).floor() as usize;

    let k = min(k, n); // Just in case.

    if k == n {
        return params.table_values[k];
    }

    let vk = params.table_values[k];
    let vk1 = params.table_values[k + 1];
    let k = k as f64;
    let n = n as f64;

    vk + (value - k / n) * n * (vk1 - vk)
}

/// The discrete component transfer function.
fn discrete(params: &FunctionParameters<'_>, value: f64) -> f64 {
    let n = params.table_values.len();
    let k = (value * (n as f64)).floor() as usize;

    params.table_values[min(k, n - 1)]
}

/// The linear component transfer function.
fn linear(params: &FunctionParameters<'_>, value: f64) -> f64 {
    params.slope * value + params.intercept
}

/// The gamma component transfer function.
fn gamma(params: &FunctionParameters<'_>, value: f64) -> f64 {
    params.amplitude * value.powf(params.exponent) + params.offset
}

trait FeComponentTransferFunc {
    /// Returns the component transfer function.
    fn function(&self) -> Function;

    /// Returns the component transfer function parameters.
    fn function_parameters(&self) -> FunctionParameters<'_>;

    /// Returns the channel.
    fn channel(&self) -> Channel;
}

macro_rules! func_x {
    ($func_name:ident, $channel:expr) => {
        pub struct $func_name {
            channel: Channel,
            function_type: FunctionType,
            table_values: Vec<f64>,
            slope: f64,
            intercept: f64,
            amplitude: f64,
            exponent: f64,
            offset: f64,
        }

        impl Default for $func_name {
            #[inline]
            fn default() -> Self {
                Self {
                    channel: $channel,
                    function_type: FunctionType::Identity,
                    table_values: Vec::new(),
                    slope: 1.0,
                    intercept: 0.0,
                    amplitude: 1.0,
                    exponent: 1.0,
                    offset: 0.0,
                }
            }
        }

        impl FeComponentTransferFunc for $func_name {
            #[inline]
            fn function_parameters(&self) -> FunctionParameters<'_> {
                FunctionParameters {
                    table_values: &self.table_values,
                    slope: self.slope,
                    intercept: self.intercept,
                    amplitude: self.amplitude,
                    exponent: self.exponent,
                    offset: self.offset,
                }
            }

            #[inline]
            fn function(&self) -> Function {
                match self.function_type {
                    FunctionType::Identity => identity,
                    FunctionType::Table => table,
                    FunctionType::Discrete => discrete,
                    FunctionType::Linear => linear,
                    FunctionType::Gamma => gamma,
                }
            }

            #[inline]
            fn channel(&self) -> Channel {
                self.channel
            }
        }

        impl NodeTrait for $func_name {
            #[inline]
            fn set_atts(
                &mut self,
                _parent: Option<&RsvgNode>,
                pbag: &PropertyBag<'_>,
            ) -> NodeResult {
                for (attr, value) in pbag.iter() {
                    match attr.expanded() {
                        expanded_name!(svg "type") => {
                            self.function_type = attr.parse(value)?
                        }
                        expanded_name!(svg "tableValues") => {
                            let NumberList(v) =
                                NumberList::parse_str(value, NumberListLength::Unbounded).map_err(
                                    |err| {
                                        if let NumberListError::Parse(err) = err {
                                            ValueErrorKind::parse_error(&err)
                                        } else {
                                            panic!("unexpected number list error");
                                        }
                                    },
                                ).attribute(attr)?;
                            self.table_values = v;
                        }
                        expanded_name!(svg "slope") => {
                            self.slope = parsers::number(value).attribute(attr)?
                        }
                        expanded_name!(svg "intercept") => {
                            self.intercept = parsers::number(value).attribute(attr)?
                        }
                        expanded_name!(svg "amplitude") => {
                            self.amplitude = parsers::number(value).attribute(attr)?
                        }
                        expanded_name!(svg "exponent") => {
                            self.exponent = parsers::number(value).attribute(attr)?
                        }
                        expanded_name!(svg "offset") => {
                            self.offset = parsers::number(value).attribute(attr)?
                        }
                        _ => (),
                    }
                }

                // The table function type with empty table_values is considered
                // an identity function.
                match self.function_type {
                    FunctionType::Table | FunctionType::Discrete => {
                        if self.table_values.is_empty() {
                            self.function_type = FunctionType::Identity;
                        }
                    }
                    _ => (),
                }

                Ok(())
            }
        }
    };
}

// The `<feFuncR>` element
func_x!(FeFuncR, Channel::R);

// The `<feFuncG>` element
func_x!(FeFuncG, Channel::G);

// The `<feFuncB>` element
func_x!(FeFuncB, Channel::B);

// The `<feFuncA>` element
func_x!(FeFuncA, Channel::A);

macro_rules! func_or_default {
    ($func_node:ident, $func_type:ty, $func_data:ident, $func_default:ident) => {
        match $func_node {
            Some(ref f) => {
                $func_data = f.borrow();
                $func_data.get_impl::<$func_type>()
            }
            _ => &$func_default,
        };
    };
}

impl FilterEffect for FeComponentTransfer {
    fn render(
        &self,
        node: &RsvgNode,
        ctx: &FilterContext,
        draw_ctx: &mut DrawingCtx,
    ) -> Result<FilterResult, FilterError> {
        let input = self.base.get_input(ctx, draw_ctx)?;
        let bounds = self
            .base
            .get_bounds(ctx)
            .add_input(&input)
            .into_irect(draw_ctx);

        // Create the output surface.
        let mut output_surface = ImageSurface::create(
            cairo::Format::ARgb32,
            ctx.source_graphic().width(),
            ctx.source_graphic().height(),
        )?;

        // Get a node for every pixel component.
        fn get_node<F>(node: &RsvgNode, node_type: NodeType, channel: Channel) -> Option<RsvgNode>
        where
            F: FeComponentTransferFunc + NodeTrait,
        {
            node.children()
                .rev()
                .filter(|c| c.borrow().get_type() == node_type)
                .find(|c| c.borrow().get_impl::<F>().channel() == channel)
        };

        let func_r_node = get_node::<FeFuncR>(node, NodeType::FeFuncR, Channel::R);
        let func_g_node = get_node::<FeFuncG>(node, NodeType::FeFuncG, Channel::G);
        let func_b_node = get_node::<FeFuncB>(node, NodeType::FeFuncB, Channel::B);
        let func_a_node = get_node::<FeFuncA>(node, NodeType::FeFuncA, Channel::A);

        for node in [&func_r_node, &func_g_node, &func_b_node, &func_a_node]
            .iter()
            .filter_map(|x| x.as_ref())
        {
            if node.borrow().is_in_error() {
                return Err(FilterError::ChildNodeInError);
            }
        }

        // These are the default funcs that perform an identity transformation.
        let func_r_default = FeFuncR::default();
        let func_g_default = FeFuncG::default();
        let func_b_default = FeFuncB::default();
        let func_a_default = FeFuncA::default();

        // We need to tell the borrow checker that these live long enough
        let func_r_data;
        let func_g_data;
        let func_b_data;
        let func_a_data;

        let func_r = func_or_default!(func_r_node, FeFuncR, func_r_data, func_r_default);
        let func_g = func_or_default!(func_g_node, FeFuncG, func_g_data, func_g_default);
        let func_b = func_or_default!(func_b_node, FeFuncB, func_b_data, func_b_default);
        let func_a = func_or_default!(func_a_node, FeFuncA, func_a_data, func_a_default);

        #[inline]
        fn compute_func<'a, F>(func: &'a F) -> impl Fn(u8, f64, f64) -> u8 + 'a
        where
            F: FeComponentTransferFunc,
        {
            let compute = func.function();
            let params = func.function_parameters();

            move |value, alpha, new_alpha| {
                let value = f64::from(value) / 255f64;

                let unpremultiplied = if alpha == 0f64 { 0f64 } else { value / alpha };

                let new_value = compute(&params, unpremultiplied);
                let new_value = clamp(new_value, 0f64, 1f64);

                ((new_value * new_alpha * 255f64) + 0.5) as u8
            }
        }

        let compute_r = compute_func::<FeFuncR>(&func_r);
        let compute_g = compute_func::<FeFuncG>(&func_g);
        let compute_b = compute_func::<FeFuncB>(&func_b);

        // Alpha gets special handling since everything else depends on it.
        let compute_a = func_a.function();
        let params_a = func_a.function_parameters();
        let compute_a = |alpha| compute_a(&params_a, alpha);

        // Do the actual processing.
        let output_stride = output_surface.get_stride() as usize;
        {
            let mut output_data = output_surface.get_data().unwrap();

            for (x, y, pixel) in Pixels::new(input.surface(), bounds) {
                let alpha = f64::from(pixel.a) / 255f64;
                let new_alpha = compute_a(alpha);

                let output_pixel = Pixel {
                    r: compute_r(pixel.r, alpha, new_alpha),
                    g: compute_g(pixel.g, alpha, new_alpha),
                    b: compute_b(pixel.b, alpha, new_alpha),
                    a: ((new_alpha * 255f64) + 0.5) as u8,
                };

                output_data.set_pixel(output_stride, output_pixel, x, y);
            }
        }

        Ok(FilterResult {
            name: self.base.result.clone(),
            output: FilterOutput {
                surface: SharedImageSurface::new(output_surface, input.surface().surface_type())?,
                bounds,
            },
        })
    }

    fn is_affected_by_color_interpolation_filters(&self) -> bool {
        true
    }
}
<|endoftext|>


<commit_before>use std::rc::Rc;
use std::path::Path;
use std::cell::Cell;
use std::collections::BTreeMap;
use url::Url;
use regex::Regex;
use html5ever::tree_builder::TreeSink;
use html5ever::rcdom::Node;
use html5ever::rcdom::NodeData::{Element, Text};
use html5ever::rcdom::Handle;
use html5ever::rcdom::NodeData::{
    Document,
    Doctype,
    Comment,
    ProcessingInstruction
};
use html5ever:: rcdom::RcDom;
use html5ever::{QualName, LocalName};
use html5ever::tree_builder::{NodeOrText, ElementFlags};
use dom;

pub static PUNCTUATIONS_REGEX: &'static str = r"([、。，．！？]|\.[^A-Za-z0-9]|,[^0-9]|!|\?)";
pub static UNLIKELY_CANDIDATES: &'static str =
    "combx|comment|community|disqus|extra|foot|header|menu\
     |remark|rss|shoutbox|sidebar|sponsor|ad-break|agegate\
     |pagination|pager|popup|tweet|twitter";
pub static LIKELY_CANDIDATES: &'static str = "and|article|body|column|main|shadow";
pub static POSITIVE_CANDIDATES: &'static str =
    "article|body|content|entry|hentry|main|page\
     |pagination|post|text|blog|story";
pub static NEGATIVE_CANDIDATES: &'static str =
    "combx|comment|com|contact|foot|footer|footnote\
     |masthead|media|meta|outbrain|promo|related\
     |scroll|shoutbox|sidebar|sponsor|shopping\
     |tags|tool|widget|form|textfield\
     |uiScale";// medium
static BLOCK_CHILD_TAGS: [&'static str; 10] = [
    "a", "blockquote", "dl", "div", "img", "ol", "p", "pre", "table", "ul",
];

lazy_static! {
    static ref PUNCTUATIONS: Regex = Regex::new(PUNCTUATIONS_REGEX).unwrap();
    static ref LIKELY:       Regex = Regex::new(LIKELY_CANDIDATES).unwrap();
    static ref UNLIKELY:     Regex = Regex::new(UNLIKELY_CANDIDATES).unwrap();
    static ref POSITIVE:     Regex = Regex::new(POSITIVE_CANDIDATES).unwrap();
    static ref NEGATIVE:     Regex = Regex::new(NEGATIVE_CANDIDATES).unwrap();
}

pub struct Candidate {
    pub node:  Rc<Node>,
    pub score: Cell<f32>,
}

pub fn fix_img_path(handle: Handle, url: &Url) -> bool {
    let src = dom::get_attr("src", handle.clone());
    if src.is_none() {
        return false
    }
    let s = src.unwrap();
    if !s.starts_with("//") && !s.starts_with("http://") && s.starts_with("https://") {
        match url.join(&s) {
            Ok(new_url) => dom::set_attr("src", new_url.as_str(), handle),
            Err(_)      => (),
        }
    }
    true
}

pub fn get_link_density(handle: Handle) -> f32 {
    let text_length = dom::text_len(handle.clone()) as f32;
    if text_length == 0.0 {
        return 0.0;
    }
    let mut link_length = 0.0;
    let mut links: Vec<Rc<Node>> = vec![];
    dom::find_node(handle.clone(), "a", &mut links);
    for link in links.iter() {
        link_length += dom::text_len(link.clone()) as f32;
    }
    link_length / text_length
}

pub fn is_candidate(handle: Handle) -> bool {
    let text_len = dom::text_len(handle.clone());
    if text_len < 20 {
        return false
    }
    let n: &str = &dom::get_tag_name(handle. clone()).unwrap_or("".to_string());
    match n {
        "p" => true,
        "div" | "article" | "center" | "section" => {
            if !dom::has_nodes(handle.clone(),
                               &BLOCK_CHILD_TAGS.iter().map(|t| *t).collect()) {
                true
            } else {
                dom::text_children_count(handle.clone()) > 5
            }
        },
        _ => false
    }
}

pub fn init_content_score(handle: Handle) -> f32 {
    let tag_name = dom::get_tag_name(handle.clone()).unwrap_or("".to_string());
    let score = match tag_name.as_ref() {
        "article"    => 10.0,
        "div"        => 5.0,
        "blockquote" => 3.0,
        "form"       => -3.0,
        "th"         => 5.0,
        _            => 0.0,
    };
    score + get_class_weight(handle.clone())
}

pub fn calc_content_score(handle: Handle) -> f32 {
    let mut score: f32 = 1.0;
    let mut text = String::new();
    dom::extract_text(handle.clone(), &mut text, true);
    let mat = PUNCTUATIONS.find_iter(&text);
    score += mat.count() as f32;
    score += f32::min(f32::floor(text.chars().count() as f32 / 100.0), 3.0);
    return score
}

pub fn get_class_weight(handle: Handle) -> f32 {
    let mut weight: f32 = 0.0;
    match handle.data {
        Element { name: _, ref attrs, .. } => {
            for name in ["id", "class"].iter() {
                if let Some(val) = dom::attr(name, &attrs.borrow()) {
                    if POSITIVE.is_match(&val) {
                        weight += 25.0
                    };
                    if NEGATIVE.is_match(&val) {
                        weight -= 25.0
                    }
                }
            }
        },
        _ => (),
    };
    weight
}

pub fn preprocess(mut dom: &mut RcDom,  handle: Handle) -> bool {
    match handle.clone().data {
        Element { ref name, ref attrs, .. } => {
            let tag_name = name.local.as_ref();
            match tag_name.to_lowercase().as_ref() {
                "script" | "link" | "style"  => {
                    return true
                },
                _     => (),
            }
            for name in ["id", "class"].iter() {
                if let Some(val) = dom::attr(name, &attrs.borrow()) {
                    if tag_name != "body" && UNLIKELY.is_match(&val) {
                        if !LIKELY.is_match(&val) {
                            return true
                        }
                    }
                }
            }
        },
        _ => (),
    }
    let mut useless_nodes = vec![];
    let mut paragraph_nodes = vec![];
    let mut br_count = 0;
    for child in handle.children.borrow().iter() {
        if preprocess(&mut dom, child.clone()) {
            useless_nodes.push(child.clone());
        }
        let c = child.clone();
        match c.data {
            Element { ref name, .. } => {
                let tag_name = name.local.as_ref();
                if "br" == tag_name.to_lowercase() {
                    br_count += 1
                } else {
                    br_count = 0
                }
            },
            Text { ref contents } => {
                let s = contents.borrow();
                if br_count >= 2 && s.trim().len() > 0 {
                    paragraph_nodes.push(child.clone());
                    br_count = 0
                }
            },
            _ => ()
        }
    }
    for node in useless_nodes.iter() {
        dom.remove_from_parent(node);
    }
    for node in paragraph_nodes.iter() {
        let name = QualName::new(None, ns!(), LocalName::from("p"));
        let p = dom.create_element(name, vec![], ElementFlags::default());
        dom.append_before_sibling(node, NodeOrText::AppendNode(p.clone()));
        dom.remove_from_parent(node);
        match node.clone().data {
            Text { ref contents } => {
                let text = contents.clone().into_inner().clone();
                dom.append(&p, NodeOrText::AppendText(text))
            },
            _ => (),
        }
    }
    false
}

pub fn clean(mut dom: &mut RcDom, id: &Path, handle: Handle, url: &Url, candidates: &BTreeMap<String, Candidate>) -> bool {
    let mut useless = false;
    match handle.data {
        Document       => (),
        Doctype { .. } => (),
        Text { ref contents } => {
            let s = contents.borrow();
            if s.trim().len() == 0 {
                useless = true
            }
        },
        Comment { .. } => useless = true,
        Element { ref name, ref attrs, .. } => {
            let tag_name = name.local.as_ref();
            match tag_name.to_lowercase().as_ref() {
                "script" | "link" | "style" | "noscript" | "meta"
                    | "h1" | "object" | "header" | "footer" | "aside" => {
                    useless = true
                },
                "form" | "table" | "ul" | "div" => {
                    useless = is_useless(id, handle.clone(), candidates)
                },
                "img" => useless = fix_img_path(handle.clone(), url),
                _     => (),
            }
            dom::clean_attr("id"   , &mut *attrs.borrow_mut());
            dom::clean_attr("class", &mut *attrs.borrow_mut());
            dom::clean_attr("style", &mut *attrs.borrow_mut());
        },
        ProcessingInstruction { .. } => unreachable!()
    }
    let mut useless_nodes = vec![];
    for (i, child) in handle.children.borrow().iter().enumerate() {
        let pid = id.join(i.to_string());
        if clean(&mut dom, pid.as_path(), child.clone(), url, candidates) {
            useless_nodes.push(child.clone());
        }
    }
    for node in useless_nodes.iter() {
        dom.remove_from_parent(node);
    }
    if dom::is_empty(handle) {
        useless = true
    }
    useless
}

pub fn is_useless(id: &Path, handle: Handle, candidates: &BTreeMap<String, Candidate>) -> bool {
    let tag_name = &dom::get_tag_name(handle.clone()).unwrap_or("".to_string());
    let weight = get_class_weight(handle.clone());
    let score = id.to_str()
        .and_then(|id| candidates.get(id))
        .map(|c| c.score.get()).unwrap_or(0.0);
    if weight + score < 0.0 {
        return true
    }
    let text_nodes_len = dom::text_children_count(handle.clone());
    let mut p_nodes:     Vec<Rc<Node>> = vec![];
    let mut img_nodes:   Vec<Rc<Node>> = vec![];
    let mut li_nodes:    Vec<Rc<Node>> = vec![];
    let mut input_nodes: Vec<Rc<Node>> = vec![];
    let mut embed_nodes: Vec<Rc<Node>> = vec![];
    dom::find_node(handle.clone(), "p"     , &mut p_nodes);
    dom::find_node(handle.clone(), "img"   , &mut img_nodes);
    dom::find_node(handle.clone(), "li"    , &mut li_nodes);
    dom::find_node(handle.clone(), "input" , &mut input_nodes);
    dom::find_node(handle.clone(), "embed" , &mut embed_nodes);
    let p_count        = p_nodes.len();
    let img_count      = img_nodes.len();
    let li_count       = li_nodes.len() as i32 - 100;
    let input_count    = input_nodes.len();
    let embed_count    = embed_nodes.len();
    let link_density   = get_link_density(handle.clone());
    let content_length = dom::text_len(handle.clone());
    let para_count = text_nodes_len + p_count;

    if img_count > para_count + text_nodes_len {
        return true
    }
    if li_count > para_count as i32 && tag_name != "ul" && tag_name != "ol" {
        return true
    }
    if input_count as f32 > f32::floor(para_count as f32 / 3.0) {
        return true
    }
    if content_length < 25 && (img_count == 0 || img_count > 2) {
        return true
    }
    if weight < 25.0 && link_density > 0.2 {
        return true
    }
    if (embed_count == 1 && content_length < 35) || embed_count > 1 {
        return true
    }
    return false
}

pub fn is_link_list(handle: Handle) -> bool {
    let rate = evaluate_list(handle);
    rate > 7.5
}

fn evaluate_list(handle: Handle) -> f32 {
    let mut hit: i32 = 0;
    let mut len: i32 = 0;
    for child in handle.children.borrow().iter() {
        let c = child.clone();
        match c.data {
            Element { ref name, .. } => {
                let tag_name = name.local.as_ref();
                match tag_name.to_lowercase().as_ref() {
                    "li" | "dt" | "dd" => {
                        len += 1;
                        if dom::has_link(child.clone()) {
                            hit += 1
                        }
                    }
                    _ => (),
                }
            },
            _ => ()
        }
    }
    if len == 0 {
        0.0
    } else {
        9.0 * ((hit / len) as f32).powi(2) + 1.0
    }
}
<commit_msg>Add additional phrases to regex<commit_after>use std::rc::Rc;
use std::path::Path;
use std::cell::Cell;
use std::collections::BTreeMap;
use url::Url;
use regex::Regex;
use html5ever::tree_builder::TreeSink;
use html5ever::rcdom::Node;
use html5ever::rcdom::NodeData::{Element, Text};
use html5ever::rcdom::Handle;
use html5ever::rcdom::NodeData::{
    Document,
    Doctype,
    Comment,
    ProcessingInstruction
};
use html5ever:: rcdom::RcDom;
use html5ever::{QualName, LocalName};
use html5ever::tree_builder::{NodeOrText, ElementFlags};
use dom;

pub static PUNCTUATIONS_REGEX: &'static str = r"([、。，．！？]|\.[^A-Za-z0-9]|,[^0-9]|!|\?)";
pub static UNLIKELY_CANDIDATES: &'static str =
    "combx|comment|community|disqus|extra|foot|header|menu\
     |remark|rss|shoutbox|sidebar|sponsor|ad-break|agegate\
     |pagination|pager|popup|tweet|twitter\
     |ssba";
pub static LIKELY_CANDIDATES: &'static str = "and|article|body|column|main|shadow\
                                              |content|hentry";
pub static POSITIVE_CANDIDATES: &'static str =
    "article|body|content|entry|hentry|main|page\
     |pagination|post|text|blog|story";
pub static NEGATIVE_CANDIDATES: &'static str =
    "combx|comment|com|contact|foot|footer|footnote\
     |masthead|media|meta|outbrain|promo|related\
     |scroll|shoutbox|sidebar|sponsor|shopping\
     |tags|tool|widget|form|textfield\
     |uiScale|hidden";
static BLOCK_CHILD_TAGS: [&'static str; 10] = [
    "a", "blockquote", "dl", "div", "img", "ol", "p", "pre", "table", "ul",
];

lazy_static! {
    static ref PUNCTUATIONS: Regex = Regex::new(PUNCTUATIONS_REGEX).unwrap();
    static ref LIKELY:       Regex = Regex::new(LIKELY_CANDIDATES).unwrap();
    static ref UNLIKELY:     Regex = Regex::new(UNLIKELY_CANDIDATES).unwrap();
    static ref POSITIVE:     Regex = Regex::new(POSITIVE_CANDIDATES).unwrap();
    static ref NEGATIVE:     Regex = Regex::new(NEGATIVE_CANDIDATES).unwrap();
}

pub struct Candidate {
    pub node:  Rc<Node>,
    pub score: Cell<f32>,
}

pub fn fix_img_path(handle: Handle, url: &Url) -> bool {
    let src = dom::get_attr("src", handle.clone());
    if src.is_none() {
        return false
    }
    let s = src.unwrap();
    if !s.starts_with("//") && !s.starts_with("http://") && s.starts_with("https://") {
        match url.join(&s) {
            Ok(new_url) => dom::set_attr("src", new_url.as_str(), handle),
            Err(_)      => (),
        }
    }
    true
}

pub fn get_link_density(handle: Handle) -> f32 {
    let text_length = dom::text_len(handle.clone()) as f32;
    if text_length == 0.0 {
        return 0.0;
    }
    let mut link_length = 0.0;
    let mut links: Vec<Rc<Node>> = vec![];
    dom::find_node(handle.clone(), "a", &mut links);
    for link in links.iter() {
        link_length += dom::text_len(link.clone()) as f32;
    }
    link_length / text_length
}

pub fn is_candidate(handle: Handle) -> bool {
    let text_len = dom::text_len(handle.clone());
    if text_len < 20 {
        return false
    }
    let n: &str = &dom::get_tag_name(handle. clone()).unwrap_or("".to_string());
    match n {
        "p" => true,
        "div" | "article" | "center" | "section" => {
            if !dom::has_nodes(handle.clone(),
                               &BLOCK_CHILD_TAGS.iter().map(|t| *t).collect()) {
                true
            } else {
                dom::text_children_count(handle.clone()) > 5
            }
        },
        _ => false
    }
}

pub fn init_content_score(handle: Handle) -> f32 {
    let tag_name = dom::get_tag_name(handle.clone()).unwrap_or("".to_string());
    let score = match tag_name.as_ref() {
        "article"    => 10.0,
        "div"        => 5.0,
        "blockquote" => 3.0,
        "form"       => -3.0,
        "th"         => 5.0,
        _            => 0.0,
    };
    score + get_class_weight(handle.clone())
}

pub fn calc_content_score(handle: Handle) -> f32 {
    let mut score: f32 = 1.0;
    let mut text = String::new();
    dom::extract_text(handle.clone(), &mut text, true);
    let mat = PUNCTUATIONS.find_iter(&text);
    score += mat.count() as f32;
    score += f32::min(f32::floor(text.chars().count() as f32 / 100.0), 3.0);
    return score
}

pub fn get_class_weight(handle: Handle) -> f32 {
    let mut weight: f32 = 0.0;
    match handle.data {
        Element { name: _, ref attrs, .. } => {
            for name in ["id", "class"].iter() {
                if let Some(val) = dom::attr(name, &attrs.borrow()) {
                    if POSITIVE.is_match(&val) {
                        weight += 25.0
                    };
                    if NEGATIVE.is_match(&val) {
                        weight -= 25.0
                    }
                }
            }
        },
        _ => (),
    };
    weight
}

pub fn preprocess(mut dom: &mut RcDom,  handle: Handle) -> bool {
    match handle.clone().data {
        Element { ref name, ref attrs, .. } => {
            let tag_name = name.local.as_ref();
            match tag_name.to_lowercase().as_ref() {
                "script" | "link" | "style"  => {
                    return true
                },
                _     => (),
            }
            for name in ["id", "class"].iter() {
                if let Some(val) = dom::attr(name, &attrs.borrow()) {
                    if tag_name != "body" && UNLIKELY.is_match(&val) {
                        if !LIKELY.is_match(&val) {
                            return true
                        }
                    }
                }
            }
        },
        _ => (),
    }
    let mut useless_nodes = vec![];
    let mut paragraph_nodes = vec![];
    let mut br_count = 0;
    for child in handle.children.borrow().iter() {
        if preprocess(&mut dom, child.clone()) {
            useless_nodes.push(child.clone());
        }
        let c = child.clone();
        match c.data {
            Element { ref name, .. } => {
                let tag_name = name.local.as_ref();
                if "br" == tag_name.to_lowercase() {
                    br_count += 1
                } else {
                    br_count = 0
                }
            },
            Text { ref contents } => {
                let s = contents.borrow();
                if br_count >= 2 && s.trim().len() > 0 {
                    paragraph_nodes.push(child.clone());
                    br_count = 0
                }
            },
            _ => ()
        }
    }
    for node in useless_nodes.iter() {
        dom.remove_from_parent(node);
    }
    for node in paragraph_nodes.iter() {
        let name = QualName::new(None, ns!(), LocalName::from("p"));
        let p = dom.create_element(name, vec![], ElementFlags::default());
        dom.append_before_sibling(node, NodeOrText::AppendNode(p.clone()));
        dom.remove_from_parent(node);
        match node.clone().data {
            Text { ref contents } => {
                let text = contents.clone().into_inner().clone();
                dom.append(&p, NodeOrText::AppendText(text))
            },
            _ => (),
        }
    }
    false
}

pub fn clean(mut dom: &mut RcDom, id: &Path, handle: Handle, url: &Url, candidates: &BTreeMap<String, Candidate>) -> bool {
    let mut useless = false;
    match handle.data {
        Document       => (),
        Doctype { .. } => (),
        Text { ref contents } => {
            let s = contents.borrow();
            if s.trim().len() == 0 {
                useless = true
            }
        },
        Comment { .. } => useless = true,
        Element { ref name, ref attrs, .. } => {
            let tag_name = name.local.as_ref();
            match tag_name.to_lowercase().as_ref() {
                "script" | "link" | "style" | "noscript" | "meta"
                    | "h1" | "object" | "header" | "footer" | "aside" => {
                    useless = true
                },
                "form" | "table" | "ul" | "div" => {
                    useless = is_useless(id, handle.clone(), candidates)
                },
                "img" => useless = fix_img_path(handle.clone(), url),
                _     => (),
            }
            dom::clean_attr("id"   , &mut *attrs.borrow_mut());
            dom::clean_attr("class", &mut *attrs.borrow_mut());
            dom::clean_attr("style", &mut *attrs.borrow_mut());
        },
        ProcessingInstruction { .. } => unreachable!()
    }
    let mut useless_nodes = vec![];
    for (i, child) in handle.children.borrow().iter().enumerate() {
        let pid = id.join(i.to_string());
        if clean(&mut dom, pid.as_path(), child.clone(), url, candidates) {
            useless_nodes.push(child.clone());
        }
    }
    for node in useless_nodes.iter() {
        dom.remove_from_parent(node);
    }
    if dom::is_empty(handle) {
        useless = true
    }
    useless
}

pub fn is_useless(id: &Path, handle: Handle, candidates: &BTreeMap<String, Candidate>) -> bool {
    let tag_name = &dom::get_tag_name(handle.clone()).unwrap_or("".to_string());
    let weight = get_class_weight(handle.clone());
    let score = id.to_str()
        .and_then(|id| candidates.get(id))
        .map(|c| c.score.get()).unwrap_or(0.0);
    if weight + score < 0.0 {
        return true
    }
    let text_nodes_len = dom::text_children_count(handle.clone());
    let mut p_nodes:     Vec<Rc<Node>> = vec![];
    let mut img_nodes:   Vec<Rc<Node>> = vec![];
    let mut li_nodes:    Vec<Rc<Node>> = vec![];
    let mut input_nodes: Vec<Rc<Node>> = vec![];
    let mut embed_nodes: Vec<Rc<Node>> = vec![];
    dom::find_node(handle.clone(), "p"     , &mut p_nodes);
    dom::find_node(handle.clone(), "img"   , &mut img_nodes);
    dom::find_node(handle.clone(), "li"    , &mut li_nodes);
    dom::find_node(handle.clone(), "input" , &mut input_nodes);
    dom::find_node(handle.clone(), "embed" , &mut embed_nodes);
    let p_count        = p_nodes.len();
    let img_count      = img_nodes.len();
    let li_count       = li_nodes.len() as i32 - 100;
    let input_count    = input_nodes.len();
    let embed_count    = embed_nodes.len();
    let link_density   = get_link_density(handle.clone());
    let content_length = dom::text_len(handle.clone());
    let para_count = text_nodes_len + p_count;

    if img_count > para_count + text_nodes_len {
        return true
    }
    if li_count > para_count as i32 && tag_name != "ul" && tag_name != "ol" {
        return true
    }
    if input_count as f32 > f32::floor(para_count as f32 / 3.0) {
        return true
    }
    if content_length < 25 && (img_count == 0 || img_count > 2) {
        return true
    }
    if weight < 25.0 && link_density > 0.2 {
        return true
    }
    if (embed_count == 1 && content_length < 35) || embed_count > 1 {
        return true
    }
    return false
}

pub fn is_link_list(handle: Handle) -> bool {
    let rate = evaluate_list(handle);
    rate > 7.5
}

fn evaluate_list(handle: Handle) -> f32 {
    let mut hit: i32 = 0;
    let mut len: i32 = 0;
    for child in handle.children.borrow().iter() {
        let c = child.clone();
        match c.data {
            Element { ref name, .. } => {
                let tag_name = name.local.as_ref();
                match tag_name.to_lowercase().as_ref() {
                    "li" | "dt" | "dd" => {
                        len += 1;
                        if dom::has_link(child.clone()) {
                            hit += 1
                        }
                    }
                    _ => (),
                }
            },
            _ => ()
        }
    }
    if len == 0 {
        0.0
    } else {
        9.0 * ((hit / len) as f32).powi(2) + 1.0
    }
}
<|endoftext|>


<commit_before>//! `GET /_matrix/client/*/rooms/{roomId}/members`

pub mod v3 {
    //! `/v3/` ([spec])
    //!
    //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3roomsroomidmembers

    use ruma_common::{
        api::ruma_api,
        events::room::member::OriginalRoomMemberEvent,
        serde::{Raw, StringEnum},
        RoomId,
    };

    use crate::PrivOwnedStr;

    ruma_api! {
        metadata: {
            description: "Get membership events for a room.",
            method: GET,
            name: "get_member_events",
            r0_path: "/_matrix/client/r0/rooms/:room_id/members",
            stable_path: "/_matrix/client/v3/rooms/:room_id/members",
            rate_limited: false,
            authentication: AccessToken,
            added: 1.0,
        }

        request: {
            /// The room to get the member events for.
            #[ruma_api(path)]
            pub room_id: &'a RoomId,

            /// The point in time (pagination token) to return members for in the room.
            ///
            /// This token can be obtained from a prev_batch token returned for each room by the sync
            /// API.
            #[serde(skip_serializing_if = "Option::is_none")]
            #[ruma_api(query)]
            pub at: Option<&'a str>,

            /// The kind of memberships to filter for.
            ///
            /// Defaults to no filtering if unspecified. When specified alongside not_membership, the
            /// two parameters create an 'or' condition: either the membership is the same as membership
            /// or is not the same as not_membership.
            #[serde(skip_serializing_if = "Option::is_none")]
            #[ruma_api(query)]
            pub membership: Option<MembershipEventFilter>,

            /// The kind of memberships to *exclude* from the results.
            ///
            /// Defaults to no filtering if unspecified.
            #[serde(skip_serializing_if = "Option::is_none")]
            #[ruma_api(query)]
            pub not_membership: Option<MembershipEventFilter>,
        }

        response: {
            /// A list of member events.
            pub chunk: Vec<Raw<OriginalRoomMemberEvent>>,
        }

        error: crate::Error
    }

    impl<'a> Request<'a> {
        /// Creates a new `Request` with the given room ID.
        pub fn new(room_id: &'a RoomId) -> Self {
            Self { room_id, at: None, membership: None, not_membership: None }
        }
    }

    impl Response {
        /// Creates a new `Response` with the given member event chunk.
        pub fn new(chunk: Vec<Raw<OriginalRoomMemberEvent>>) -> Self {
            Self { chunk }
        }
    }

    /// The kind of membership events to filter for.
    #[doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/doc/string_enum.md"))]
    #[derive(Clone, Debug, PartialEq, Eq, StringEnum)]
    #[ruma_enum(rename_all = "lowercase")]
    #[non_exhaustive]
    pub enum MembershipEventFilter {
        /// The user has joined.
        Join,

        /// The user has been invited.
        Invite,

        /// The user has left.
        Leave,

        /// The user has been banned.
        Ban,

        #[doc(hidden)]
        _Custom(PrivOwnedStr),
    }

    impl MembershipEventFilter {
        /// Creates a string slice from this `MembershipEventFilter`.
        pub fn as_str(&self) -> &str {
            self.as_ref()
        }
    }

    #[cfg(all(test, feature = "server"))]
    mod tests {
        use matches::assert_matches;
        use ruma_common::api::IncomingRequest as _;

        use super::{IncomingRequest, MembershipEventFilter};

        #[test]
        fn deserialization() {
            let uri = http::Uri::builder()
                .scheme("https")
                .authority("example.org")
                .path_and_query(
                    "/_matrix/client/r0/rooms/!dummy%3Aexample.org/members\
                 ?not_membership=leave\
                 &at=1026",
                )
                .build()
                .unwrap();

            let req = IncomingRequest::try_from_http_request(
                http::Request::builder().uri(uri).body(&[] as &[u8]).unwrap(),
                &["!dummy:example.org"],
            );

            assert_matches!(
                req,
                Ok(IncomingRequest {
                    room_id,
                    at: Some(at),
                    membership: None,
                    not_membership: Some(MembershipEventFilter::Leave),
                }) if room_id == "!dummy:example.org" && at == "1026"
            );
        }
    }
}
<commit_msg>client-api: Update type of get_member_events::Response::chunk<commit_after>//! `GET /_matrix/client/*/rooms/{roomId}/members`

pub mod v3 {
    //! `/v3/` ([spec])
    //!
    //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3roomsroomidmembers

    use ruma_common::{
        api::ruma_api,
        events::room::member::RoomMemberEvent,
        serde::{Raw, StringEnum},
        RoomId,
    };

    use crate::PrivOwnedStr;

    ruma_api! {
        metadata: {
            description: "Get membership events for a room.",
            method: GET,
            name: "get_member_events",
            r0_path: "/_matrix/client/r0/rooms/:room_id/members",
            stable_path: "/_matrix/client/v3/rooms/:room_id/members",
            rate_limited: false,
            authentication: AccessToken,
            added: 1.0,
        }

        request: {
            /// The room to get the member events for.
            #[ruma_api(path)]
            pub room_id: &'a RoomId,

            /// The point in time (pagination token) to return members for in the room.
            ///
            /// This token can be obtained from a prev_batch token returned for each room by the sync
            /// API.
            #[serde(skip_serializing_if = "Option::is_none")]
            #[ruma_api(query)]
            pub at: Option<&'a str>,

            /// The kind of memberships to filter for.
            ///
            /// Defaults to no filtering if unspecified. When specified alongside not_membership, the
            /// two parameters create an 'or' condition: either the membership is the same as membership
            /// or is not the same as not_membership.
            #[serde(skip_serializing_if = "Option::is_none")]
            #[ruma_api(query)]
            pub membership: Option<MembershipEventFilter>,

            /// The kind of memberships to *exclude* from the results.
            ///
            /// Defaults to no filtering if unspecified.
            #[serde(skip_serializing_if = "Option::is_none")]
            #[ruma_api(query)]
            pub not_membership: Option<MembershipEventFilter>,
        }

        response: {
            /// A list of member events.
            pub chunk: Vec<Raw<RoomMemberEvent>>,
        }

        error: crate::Error
    }

    impl<'a> Request<'a> {
        /// Creates a new `Request` with the given room ID.
        pub fn new(room_id: &'a RoomId) -> Self {
            Self { room_id, at: None, membership: None, not_membership: None }
        }
    }

    impl Response {
        /// Creates a new `Response` with the given member event chunk.
        pub fn new(chunk: Vec<Raw<RoomMemberEvent>>) -> Self {
            Self { chunk }
        }
    }

    /// The kind of membership events to filter for.
    #[doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/doc/string_enum.md"))]
    #[derive(Clone, Debug, PartialEq, Eq, StringEnum)]
    #[ruma_enum(rename_all = "lowercase")]
    #[non_exhaustive]
    pub enum MembershipEventFilter {
        /// The user has joined.
        Join,

        /// The user has been invited.
        Invite,

        /// The user has left.
        Leave,

        /// The user has been banned.
        Ban,

        #[doc(hidden)]
        _Custom(PrivOwnedStr),
    }

    impl MembershipEventFilter {
        /// Creates a string slice from this `MembershipEventFilter`.
        pub fn as_str(&self) -> &str {
            self.as_ref()
        }
    }

    #[cfg(all(test, feature = "server"))]
    mod tests {
        use matches::assert_matches;
        use ruma_common::api::IncomingRequest as _;

        use super::{IncomingRequest, MembershipEventFilter};

        #[test]
        fn deserialization() {
            let uri = http::Uri::builder()
                .scheme("https")
                .authority("example.org")
                .path_and_query(
                    "/_matrix/client/r0/rooms/!dummy%3Aexample.org/members\
                 ?not_membership=leave\
                 &at=1026",
                )
                .build()
                .unwrap();

            let req = IncomingRequest::try_from_http_request(
                http::Request::builder().uri(uri).body(&[] as &[u8]).unwrap(),
                &["!dummy:example.org"],
            );

            assert_matches!(
                req,
                Ok(IncomingRequest {
                    room_id,
                    at: Some(at),
                    membership: None,
                    not_membership: Some(MembershipEventFilter::Leave),
                }) if room_id == "!dummy:example.org" && at == "1026"
            );
        }
    }
}
<|endoftext|>


<commit_before>// Copyright (C) 2016  ParadoxSpiral
//
// This file is part of mpv-rs.
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

use mpv;
use mpv::{MpvInstance, MpvError, Parent, PlaylistOp};
use mpv::utils;
use mpv::protocol::*;

use std::fs::File;
use std::io::{Read, Seek, SeekFrom};
use std::mem;
use std::path::Path;
use std::slice;
use std::time::Duration;
use std::thread;

fn open(cookie: *mut File, _: &mut (), uri: &str) -> Result<(), MpvError> {
    unsafe {
        // Strip the `filereader://` part
        *cookie = File::open(&uri[13..]).unwrap()
    };
    println!("Opened file, ready for orders o7");
    Ok(())
}

fn close(_: Box<File>) {
    println!("Closing file, bye bye~~");
}

fn read(cookie: &mut File, buf: *mut i8, nbytes: u64) -> i64 {
    unsafe {
        let slice = slice::from_raw_parts_mut(buf, nbytes as _);
        let forbidden_magic = mem::transmute::<&mut [i8], &mut [u8]>(slice);

        cookie.read(forbidden_magic).unwrap() as _
    }
}

fn seek(cookie: &mut File, offset: i64) -> i64 {
    cookie.seek(SeekFrom::Start(offset as u64)).unwrap() as _
}

fn size(cookie: &mut File) -> i64 {
    cookie.metadata().unwrap().len() as _
}

pub fn exec() {
    let path = format!("filereader://{}", ::std::env::args().nth(1).unwrap());

    let protocol = unsafe {
        Protocol::new("filereader".into(),
                      (),
                      box open,
                      box close,
                      box read,
                      Some(box seek),
                      Some(box size))
    };

    let mpv = Parent::new(false).unwrap();
    mpv.register_protocol(protocol).unwrap();

    mpv.set_property("volume", 30).unwrap();

    mpv.playlist(&PlaylistOp::Loadfiles(&[utils::File::new(Path::new(&path),
                                                           utils::FileState::AppendPlay,
                                                           None)]))
       .unwrap();

    thread::sleep(Duration::from_secs(10));

    mpv.seek(&mpv::Seek::RelativeForward(Duration::from_secs(15))).unwrap();

    thread::sleep(Duration::from_secs(5));
}
<commit_msg>Add error message if no file was passed to protocol example<commit_after>// Copyright (C) 2016  ParadoxSpiral
//
// This file is part of mpv-rs.
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

use mpv;
use mpv::{MpvInstance, MpvError, Parent, PlaylistOp};
use mpv::utils;
use mpv::protocol::*;

use std::env;
use std::fs::File;
use std::io::{Read, Seek, SeekFrom};
use std::mem;
use std::path::Path;
use std::slice;
use std::time::Duration;
use std::thread;

fn open(cookie: *mut File, _: &mut (), uri: &str) -> Result<(), MpvError> {
    unsafe {
        // Strip the `filereader://` part
        *cookie = File::open(&uri[13..]).unwrap()
    };
    println!("Opened file, ready for orders o7");
    Ok(())
}

fn close(_: Box<File>) {
    println!("Closing file, bye bye~~");
}

fn read(cookie: &mut File, buf: *mut i8, nbytes: u64) -> i64 {
    unsafe {
        let slice = slice::from_raw_parts_mut(buf, nbytes as _);
        let forbidden_magic = mem::transmute::<&mut [i8], &mut [u8]>(slice);

        cookie.read(forbidden_magic).unwrap() as _
    }
}

fn seek(cookie: &mut File, offset: i64) -> i64 {
    cookie.seek(SeekFrom::Start(offset as u64)).unwrap() as _
}

fn size(cookie: &mut File) -> i64 {
    cookie.metadata().unwrap().len() as _
}

pub fn exec() {
    let path = format!("filereader://{}", env::args()
                                            .nth(1).expect("Expected local path, found nil."));

    let protocol = unsafe {
        Protocol::new("filereader".into(),
                      (),
                      box open,
                      box close,
                      box read,
                      Some(box seek),
                      Some(box size))
    };

    let mpv = Parent::new(false).unwrap();
    mpv.register_protocol(protocol).unwrap();

    mpv.set_property("volume", 30).unwrap();

    mpv.playlist(&PlaylistOp::Loadfiles(&[utils::File::new(Path::new(&path),
                                                           utils::FileState::AppendPlay,
                                                           None)]))
       .unwrap();

    thread::sleep(Duration::from_secs(10));

    mpv.seek(&mpv::Seek::RelativeForward(Duration::from_secs(15))).unwrap();

    thread::sleep(Duration::from_secs(5));
}
<|endoftext|>


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

//! Mesh loading.
//!
//! A `Mesh` describes the geometry of an object. All or part of a `Mesh` can be drawn with a
//! single draw call. A `Mesh` consists of a series of vertices, each with a certain amount of
//! user-specified attributes. These attributes are fed into shader programs. The easiest way to
//! create a mesh is to use the `#[vertex_format]` attribute on a struct, upload them into a
//! `Buffer`, and then use `Mesh::from`.

use device;
use device::{PrimitiveType, Resources, VertexCount};
use device::attrib;
use device::handle::Buffer as BufferHandle;
use device::handle::IndexBuffer as IndexBufferHandle;

/// Describes a single attribute of a vertex buffer, including its type, name, etc.
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct Attribute<R: Resources> {
    /// A name to match the shader input
    pub name: String,
    /// Vertex buffer to contain the data
    pub buffer: device::handle::RawBuffer<R>,
    /// Format of the attribute
    pub format: attrib::Format,
}

/// A trait implemented automatically for user vertex structure by
/// `#[vertex_format] attribute
#[allow(missing_docs)]
pub trait VertexFormat {
    /// Create the attributes for this type, using the given buffer.
    fn generate<R: Resources>(buffer: device::handle::RawBuffer<R>) -> Vec<Attribute<R>>;
}

/// Describes geometry to render.
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct Mesh<R: Resources> {
    /// Number of vertices in the mesh.
    pub num_vertices: device::VertexCount,
    /// Vertex attributes to use.
    pub attributes: Vec<Attribute<R>>,
}

impl<R: Resources> Mesh<R> {
    /// Create a new mesh, which is a `TriangleList` with no attributes and `nv` vertices.
    pub fn new(nv: device::VertexCount) -> Mesh<R> {
        Mesh {
            num_vertices: nv,
            attributes: Vec::new(),
        }
    }

    /// Create a new `Mesh` from a struct that implements `VertexFormat` and a buffer.
    pub fn from_format<V: VertexFormat>(buf: BufferHandle<R, V>, nv: device::VertexCount)
                       -> Mesh<R> {
        Mesh {
            num_vertices: nv,
            attributes: <V as VertexFormat>::generate(buf.raw().clone()),
        }
    }

    /// Create a new intanced `Mesh` given a vertex buffer and an instance buffer.
    pub fn from_format_instanced<V: VertexFormat, U: VertexFormat>(
                                 buf: BufferHandle<R, V>,
                                 nv: device::VertexCount,
                                 inst: BufferHandle<R, U>) -> Mesh<R> {
        let per_vertex   = <V as VertexFormat>::generate(buf.raw().clone());
        let per_instance = <U as VertexFormat>::generate(inst.raw().clone());

        let mut attributes = per_vertex;
        for mut at in per_instance.into_iter() {
            at.format.instance_rate = 1;
            attributes.push(at);
        }

        Mesh {
            num_vertices: nv,
            attributes: attributes,
        }
    }
}

/// Description of a subset of `Mesh` data to render.
///
/// Only vertices between `start` and `end` are rendered. The
/// source of the vertex data is dependent on the `kind` value.
///
/// The `prim_type` defines how the mesh contents are interpreted.
/// For example,  `Point` typed vertex slice can be used to do shape
/// blending, while still rendereing it as an indexed `TriangleList`.
#[derive(Clone, Debug, PartialEq)]
pub struct Slice<R: Resources> {
    /// Start index of vertices to draw.
    pub start: VertexCount,
    /// End index of vertices to draw.
    pub end: VertexCount,
    /// Primitive type to render collections of vertices as.
    pub prim_type: PrimitiveType,
    /// Source of the vertex ordering when drawing.
    pub kind: SliceKind<R>,
}

/// Source of vertex ordering for a slice
#[derive(Clone, Debug, PartialEq)]
pub enum SliceKind<R: Resources> {
    /// Render vertex data directly from the `Mesh`'s buffer.
    Vertex,
    /// The `Index*` buffer contains a list of indices into the `Mesh`
    /// data, so every vertex attribute does not need to be duplicated, only
    /// its position in the `Mesh`. The base index is added to this index
    /// before fetching the vertex from the buffer.  For example, when drawing
    /// a square, two triangles are needed.  Using only `Vertex`, one
    /// would need 6 separate vertices, 3 for each triangle. However, two of
    /// the vertices will be identical, wasting space for the duplicated
    /// attributes.  Instead, the `Mesh` can store 4 vertices and an
    /// `Index8` can be used instead.
    Index8(IndexBufferHandle<R, u8>, VertexCount),
    /// As `Index8` but with `u16` indices
    Index16(IndexBufferHandle<R, u16>, VertexCount),
    /// As `Index8` but with `u32` indices
    Index32(IndexBufferHandle<R, u32>, VertexCount),
}

/// Helper methods for cleanly getting the slice of a type.
pub trait ToSlice<R: Resources> {
    /// Get the slice of a type.
    fn to_slice(&self, pt: PrimitiveType) -> Slice<R>;
}

impl<R: Resources> ToSlice<R> for Mesh<R> {
    /// Return a vertex slice of the whole mesh.
    fn to_slice(&self, ty: PrimitiveType) -> Slice<R> {
        Slice {
            start: 0,
            end: self.num_vertices,
            prim_type: ty,
            kind: SliceKind::Vertex
        }
    }
}

impl<R: Resources> ToSlice<R> for IndexBufferHandle<R, u8> {
    /// Return an index slice of the whole buffer.
    fn to_slice(&self, ty: PrimitiveType) -> Slice<R> {
        Slice {
            start: 0,
            end: self.len() as VertexCount,
            prim_type: ty,
            kind: SliceKind::Index8(self.clone(), 0)
        }
    }
}

impl<R: Resources> ToSlice<R> for IndexBufferHandle<R, u16> {
    /// Return an index slice of the whole buffer.
    fn to_slice(&self, ty: PrimitiveType) -> Slice<R> {
        Slice {
            start: 0,
            end: self.len() as VertexCount,
            prim_type: ty,
            kind: SliceKind::Index16(self.clone(), 0)
        }
    }
}

impl<R: Resources> ToSlice<R> for IndexBufferHandle<R, u32> {
    /// Return an index slice of the whole buffer.
    fn to_slice(&self, ty: PrimitiveType) -> Slice<R> {
        Slice {
            start: 0,
            end: self.len() as VertexCount,
            prim_type: ty,
            kind: SliceKind::Index32(self.clone(), 0)
        }
    }
}

/// Index of a vertex attribute inside the mesh
pub type AttributeIndex = usize;

/// Describes kinds of errors that may occur in the mesh linking
#[derive(Clone, Debug, PartialEq)]
pub enum Error {
    /// A required attribute was missing.
    AttributeMissing(String),
    /// An attribute's type from the vertex format differed from the type used in the shader.
    AttributeType(String, device::shade::BaseType),
    /// An attribute index is out of supported bounds
    AttributeIndex(AttributeIndex),
    /// An input index is out of supported bounds
    ShaderInputIndex(usize),
}

const BITS_PER_ATTRIBUTE: AttributeIndex = 4;
const MESH_ATTRIBUTE_MASK: AttributeIndex = (1 << BITS_PER_ATTRIBUTE) - 1;
const MAX_SHADER_INPUTS: usize = 64 / BITS_PER_ATTRIBUTE;

/// An iterator over mesh attributes.
#[derive(Clone, Copy)]
pub struct AttributeIter {
    value: u64,
}

impl Iterator for AttributeIter {
    type Item = AttributeIndex;

    fn next(&mut self) -> Option<AttributeIndex> {
        let id = (self.value as AttributeIndex) & MESH_ATTRIBUTE_MASK;
        self.value >>= BITS_PER_ATTRIBUTE;
        Some(id)
    }
}

/// Holds a remapping table from shader inputs to mesh attributes.
#[derive(Clone, Copy)]
pub struct Link {
    table: u64,
}

impl Link {
    /// Match mesh attributes against shader inputs, produce a mesh link.
    /// Exposed to public to allow external `Batch` implementations to use it.
    pub fn new<R: Resources>(mesh: &Mesh<R>, pinfo: &device::shade::ProgramInfo)
                             -> Result<Link, Error> {
        let mut indices = Vec::new();
        for sat in pinfo.attributes.iter() {
            match mesh.attributes.iter().enumerate()
                      .find(|&(_, a)| a.name == sat.name) {
                Some((attrib_id, vat)) => match vat.format.elem_type.is_compatible(sat.base_type) {
                    Ok(_) => indices.push(attrib_id),
                    Err(_) => return Err(Error::AttributeType(sat.name.clone(), sat.base_type)),
                },
                None => return Err(Error::AttributeMissing(sat.name.clone())),
            }
        }
        Link::from_iter(indices.into_iter())
    }

    /// Construct a new link from an iterator over attribute indices.
    pub fn from_iter<I: Iterator<Item = AttributeIndex>>(iter: I)
                     -> Result<Link, Error> {
        let mut table = 0u64;
        for (input, attrib) in iter.enumerate() {
            if input >= MAX_SHADER_INPUTS {
                return Err(Error::ShaderInputIndex(input))
            } else if attrib > MESH_ATTRIBUTE_MASK {
                return Err(Error::AttributeIndex(attrib))
            } else {
                table |= (attrib as u64) << (input * BITS_PER_ATTRIBUTE);
            }
        }
        Ok(Link {
            table: table,
        })
    }

    /// Convert to an iterator returning attribute indices
    pub fn to_iter(&self) -> AttributeIter {
        AttributeIter {
            value: self.table,
        }
    }
}
<commit_msg>Implemented Slice::get_prim_count<commit_after>// Copyright 2014 The Gfx-rs Developers.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Mesh loading.
//!
//! A `Mesh` describes the geometry of an object. All or part of a `Mesh` can be drawn with a
//! single draw call. A `Mesh` consists of a series of vertices, each with a certain amount of
//! user-specified attributes. These attributes are fed into shader programs. The easiest way to
//! create a mesh is to use the `#[vertex_format]` attribute on a struct, upload them into a
//! `Buffer`, and then use `Mesh::from`.

use device;
use device::{PrimitiveType, Resources, VertexCount};
use device::attrib;
use device::handle::Buffer as BufferHandle;
use device::handle::IndexBuffer as IndexBufferHandle;

/// Describes a single attribute of a vertex buffer, including its type, name, etc.
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct Attribute<R: Resources> {
    /// A name to match the shader input
    pub name: String,
    /// Vertex buffer to contain the data
    pub buffer: device::handle::RawBuffer<R>,
    /// Format of the attribute
    pub format: attrib::Format,
}

/// A trait implemented automatically for user vertex structure by
/// `#[vertex_format] attribute
#[allow(missing_docs)]
pub trait VertexFormat {
    /// Create the attributes for this type, using the given buffer.
    fn generate<R: Resources>(buffer: device::handle::RawBuffer<R>) -> Vec<Attribute<R>>;
}

/// Describes geometry to render.
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct Mesh<R: Resources> {
    /// Number of vertices in the mesh.
    pub num_vertices: device::VertexCount,
    /// Vertex attributes to use.
    pub attributes: Vec<Attribute<R>>,
}

impl<R: Resources> Mesh<R> {
    /// Create a new mesh, which is a `TriangleList` with no attributes and `nv` vertices.
    pub fn new(nv: device::VertexCount) -> Mesh<R> {
        Mesh {
            num_vertices: nv,
            attributes: Vec::new(),
        }
    }

    /// Create a new `Mesh` from a struct that implements `VertexFormat` and a buffer.
    pub fn from_format<V: VertexFormat>(buf: BufferHandle<R, V>, nv: device::VertexCount)
                       -> Mesh<R> {
        Mesh {
            num_vertices: nv,
            attributes: <V as VertexFormat>::generate(buf.raw().clone()),
        }
    }

    /// Create a new intanced `Mesh` given a vertex buffer and an instance buffer.
    pub fn from_format_instanced<V: VertexFormat, U: VertexFormat>(
                                 buf: BufferHandle<R, V>,
                                 nv: device::VertexCount,
                                 inst: BufferHandle<R, U>) -> Mesh<R> {
        let per_vertex   = <V as VertexFormat>::generate(buf.raw().clone());
        let per_instance = <U as VertexFormat>::generate(inst.raw().clone());

        let mut attributes = per_vertex;
        for mut at in per_instance.into_iter() {
            at.format.instance_rate = 1;
            attributes.push(at);
        }

        Mesh {
            num_vertices: nv,
            attributes: attributes,
        }
    }
}

/// Description of a subset of `Mesh` data to render.
///
/// Only vertices between `start` and `end` are rendered. The
/// source of the vertex data is dependent on the `kind` value.
///
/// The `prim_type` defines how the mesh contents are interpreted.
/// For example,  `Point` typed vertex slice can be used to do shape
/// blending, while still rendereing it as an indexed `TriangleList`.
#[derive(Clone, Debug, PartialEq)]
pub struct Slice<R: Resources> {
    /// Start index of vertices to draw.
    pub start: VertexCount,
    /// End index of vertices to draw.
    pub end: VertexCount,
    /// Primitive type to render collections of vertices as.
    pub prim_type: PrimitiveType,
    /// Source of the vertex ordering when drawing.
    pub kind: SliceKind<R>,
}

impl<R: Resources> Slice<R> {
    /// Get the number of primitives in this slice.
    pub fn get_prim_count(&self) -> u32 {
        use device::PrimitiveType::*;
        let nv = (self.end - self.start) as u32;
        match self.prim_type {
            Point => nv,
            Line => nv / 2,
            LineStrip => (nv-1),
            TriangleList => nv / 3,
            TriangleStrip | TriangleFan => (nv-2) / 3,
        }
    }
}

/// Source of vertex ordering for a slice
#[derive(Clone, Debug, PartialEq)]
pub enum SliceKind<R: Resources> {
    /// Render vertex data directly from the `Mesh`'s buffer.
    Vertex,
    /// The `Index*` buffer contains a list of indices into the `Mesh`
    /// data, so every vertex attribute does not need to be duplicated, only
    /// its position in the `Mesh`. The base index is added to this index
    /// before fetching the vertex from the buffer.  For example, when drawing
    /// a square, two triangles are needed.  Using only `Vertex`, one
    /// would need 6 separate vertices, 3 for each triangle. However, two of
    /// the vertices will be identical, wasting space for the duplicated
    /// attributes.  Instead, the `Mesh` can store 4 vertices and an
    /// `Index8` can be used instead.
    Index8(IndexBufferHandle<R, u8>, VertexCount),
    /// As `Index8` but with `u16` indices
    Index16(IndexBufferHandle<R, u16>, VertexCount),
    /// As `Index8` but with `u32` indices
    Index32(IndexBufferHandle<R, u32>, VertexCount),
}

/// Helper methods for cleanly getting the slice of a type.
pub trait ToSlice<R: Resources> {
    /// Get the slice of a type.
    fn to_slice(&self, pt: PrimitiveType) -> Slice<R>;
}

impl<R: Resources> ToSlice<R> for Mesh<R> {
    /// Return a vertex slice of the whole mesh.
    fn to_slice(&self, ty: PrimitiveType) -> Slice<R> {
        Slice {
            start: 0,
            end: self.num_vertices,
            prim_type: ty,
            kind: SliceKind::Vertex
        }
    }
}

impl<R: Resources> ToSlice<R> for IndexBufferHandle<R, u8> {
    /// Return an index slice of the whole buffer.
    fn to_slice(&self, ty: PrimitiveType) -> Slice<R> {
        Slice {
            start: 0,
            end: self.len() as VertexCount,
            prim_type: ty,
            kind: SliceKind::Index8(self.clone(), 0)
        }
    }
}

impl<R: Resources> ToSlice<R> for IndexBufferHandle<R, u16> {
    /// Return an index slice of the whole buffer.
    fn to_slice(&self, ty: PrimitiveType) -> Slice<R> {
        Slice {
            start: 0,
            end: self.len() as VertexCount,
            prim_type: ty,
            kind: SliceKind::Index16(self.clone(), 0)
        }
    }
}

impl<R: Resources> ToSlice<R> for IndexBufferHandle<R, u32> {
    /// Return an index slice of the whole buffer.
    fn to_slice(&self, ty: PrimitiveType) -> Slice<R> {
        Slice {
            start: 0,
            end: self.len() as VertexCount,
            prim_type: ty,
            kind: SliceKind::Index32(self.clone(), 0)
        }
    }
}

/// Index of a vertex attribute inside the mesh
pub type AttributeIndex = usize;

/// Describes kinds of errors that may occur in the mesh linking
#[derive(Clone, Debug, PartialEq)]
pub enum Error {
    /// A required attribute was missing.
    AttributeMissing(String),
    /// An attribute's type from the vertex format differed from the type used in the shader.
    AttributeType(String, device::shade::BaseType),
    /// An attribute index is out of supported bounds
    AttributeIndex(AttributeIndex),
    /// An input index is out of supported bounds
    ShaderInputIndex(usize),
}

const BITS_PER_ATTRIBUTE: AttributeIndex = 4;
const MESH_ATTRIBUTE_MASK: AttributeIndex = (1 << BITS_PER_ATTRIBUTE) - 1;
const MAX_SHADER_INPUTS: usize = 64 / BITS_PER_ATTRIBUTE;

/// An iterator over mesh attributes.
#[derive(Clone, Copy)]
pub struct AttributeIter {
    value: u64,
}

impl Iterator for AttributeIter {
    type Item = AttributeIndex;

    fn next(&mut self) -> Option<AttributeIndex> {
        let id = (self.value as AttributeIndex) & MESH_ATTRIBUTE_MASK;
        self.value >>= BITS_PER_ATTRIBUTE;
        Some(id)
    }
}

/// Holds a remapping table from shader inputs to mesh attributes.
#[derive(Clone, Copy)]
pub struct Link {
    table: u64,
}

impl Link {
    /// Match mesh attributes against shader inputs, produce a mesh link.
    /// Exposed to public to allow external `Batch` implementations to use it.
    pub fn new<R: Resources>(mesh: &Mesh<R>, pinfo: &device::shade::ProgramInfo)
                             -> Result<Link, Error> {
        let mut indices = Vec::new();
        for sat in pinfo.attributes.iter() {
            match mesh.attributes.iter().enumerate()
                      .find(|&(_, a)| a.name == sat.name) {
                Some((attrib_id, vat)) => match vat.format.elem_type.is_compatible(sat.base_type) {
                    Ok(_) => indices.push(attrib_id),
                    Err(_) => return Err(Error::AttributeType(sat.name.clone(), sat.base_type)),
                },
                None => return Err(Error::AttributeMissing(sat.name.clone())),
            }
        }
        Link::from_iter(indices.into_iter())
    }

    /// Construct a new link from an iterator over attribute indices.
    pub fn from_iter<I: Iterator<Item = AttributeIndex>>(iter: I)
                     -> Result<Link, Error> {
        let mut table = 0u64;
        for (input, attrib) in iter.enumerate() {
            if input >= MAX_SHADER_INPUTS {
                return Err(Error::ShaderInputIndex(input))
            } else if attrib > MESH_ATTRIBUTE_MASK {
                return Err(Error::AttributeIndex(attrib))
            } else {
                table |= (attrib as u64) << (input * BITS_PER_ATTRIBUTE);
            }
        }
        Ok(Link {
            table: table,
        })
    }

    /// Convert to an iterator returning attribute indices
    pub fn to_iter(&self) -> AttributeIter {
        AttributeIter {
            value: self.table,
        }
    }
}
<|endoftext|>