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

use cssparser::{BasicParseError, Parser};
use markup5ever::{expanded_name, local_name, namespace_url, ns};
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::{ElementError, ParseError, RenderingError};
use crate::length::*;
use crate::node::{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, FilterResult};

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

/// A filter primitive interface.
pub trait FilterEffect: SetAttributes + Draw {
    fn resolve(&self, node: &Node) -> Result<PrimitiveParams, FilterError>;
}

// 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;

/// Resolved parameters for each filter primitive.
///
/// These gather all the data that a primitive may need during rendering:
/// the `feFoo` element's attributes, any computed values from its properties,
/// and parameters extracted from the element's children (for example,
/// `feMerge` gathers info from its `feMergNode` children).
pub enum PrimitiveParams {
    Blend(blend::Blend),
    ColorMatrix(color_matrix::ColorMatrix),
    ComponentTransfer(component_transfer::ComponentTransfer),
    Composite(composite::Composite),
    ConvolveMatrix(convolve_matrix::ConvolveMatrix),
    DiffuseLighting(lighting::DiffuseLighting),
    DisplacementMap(displacement_map::DisplacementMap),
    Flood(flood::Flood),
    GaussianBlur(gaussian_blur::GaussianBlur),
    Image(image::Image),
    Merge(merge::Merge),
    Morphology(morphology::Morphology),
    Offset(offset::Offset),
    SpecularLighting(lighting::SpecularLighting),
    Tile(tile::Tile),
    Turbulence(turbulence::Turbulence),
}

/// The base filter primitive node containing common properties.
#[derive(Clone)]
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 {
    Unspecified,
    SourceGraphic,
    SourceAlpha,
    BackgroundImage,
    BackgroundAlpha,
    FillPaint,
    StrokePaint,
    FilterOutput(CustomIdent),
}

impl Default for Input {
    fn default() -> Self {
        Input::Unspecified
    }
}

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))
            })
    }
}

impl Primitive {
    /// Constructs a new `Primitive` with empty properties.
    #[inline]
    fn new() -> 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(&self, ctx: &FilterContext) -> 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 Primitive {
    fn parse_standard_attributes(
        &mut self,
        attrs: &Attributes,
    ) -> Result<(Input, Input), ElementError> {
        let mut input_1 = Input::Unspecified;
        let mut input_2 = Input::Unspecified;

        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)?,
                expanded_name!("", "in") => input_1 = attr.parse(value)?,
                expanded_name!("", "in2") => input_2 = attr.parse(value)?,
                _ => (),
            }
        }

        Ok((input_1, input_2))
    }

    pub fn parse_no_inputs(&mut self, attrs: &Attributes) -> ElementResult {
        let (_, _) = self.parse_standard_attributes(attrs)?;
        Ok(())
    }

    pub fn parse_one_input(&mut self, attrs: &Attributes) -> Result<Input, ElementError> {
        let (input_1, _) = self.parse_standard_attributes(attrs)?;
        Ok(input_1)
    }

    pub fn parse_two_inputs(&mut self, attrs: &Attributes) -> Result<(Input, Input), ElementError> {
        self.parse_standard_attributes(attrs)
    }
}

/// 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());

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

        let start = Instant::now();

        if let Err(err) = filter
            .resolve(&c)
            .and_then(|params| render_primitive(&c, params, &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(RenderingError::from(status));
            }
        }

        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()?)
}

#[rustfmt::skip]
fn render_primitive(
    node: &Node,
    params: PrimitiveParams,
    ctx: &FilterContext,
    acquired_nodes: &mut AcquiredNodes<'_>,
    draw_ctx: &mut DrawingCtx,
) -> Result<FilterResult, FilterError> {
    use PrimitiveParams::*;

    let elt = node.borrow_element();
    let elt = &*elt;

    match (elt, params) {
        (Element::FeBlend(_), Blend(p))                         => p.render(ctx, acquired_nodes, draw_ctx),
        (Element::FeColorMatrix(_), ColorMatrix(p))             => p.render(ctx, acquired_nodes, draw_ctx),
        (Element::FeComponentTransfer(_), ComponentTransfer(p)) => p.render(ctx, acquired_nodes, draw_ctx),
        (Element::FeComposite(_), Composite(p))                 => p.render(ctx, acquired_nodes, draw_ctx),
        (Element::FeConvolveMatrix(_), ConvolveMatrix(p))       => p.render(ctx, acquired_nodes, draw_ctx),
        (Element::FeDiffuseLighting(_), DiffuseLighting(p))     => p.render(ctx, acquired_nodes, draw_ctx),
        (Element::FeDisplacementMap(_), DisplacementMap(p))     => p.render(ctx, acquired_nodes, draw_ctx),
        (Element::FeFlood(_), Flood(p))                         => p.render(ctx, acquired_nodes, draw_ctx),
        (Element::FeGaussianBlur(_), GaussianBlur(p))           => p.render(ctx, acquired_nodes, draw_ctx),
        (Element::FeImage(_), Image(p))                         => p.render(ctx, acquired_nodes, draw_ctx),
        (Element::FeMerge(_), Merge(p))                         => p.render(ctx, acquired_nodes, draw_ctx),
        (Element::FeMorphology(_), Morphology(p))               => p.render(ctx, acquired_nodes, draw_ctx),
        (Element::FeOffset(_), Offset(p))                       => p.render(ctx, acquired_nodes, draw_ctx),
        (Element::FeSpecularLighting(_), SpecularLighting(p))   => p.render(ctx, acquired_nodes, draw_ctx),
        (Element::FeTile(_), Tile(p))                           => p.render(ctx, acquired_nodes, draw_ctx),
        (Element::FeTurbulence(_), Turbulence(p))               => p.render(ctx, acquired_nodes, draw_ctx),
        (_, _) => unreachable!(),
    }
}

impl From<ColorInterpolationFilters> for SurfaceType {
    fn from(c: ColorInterpolationFilters) -> Self {
        match c {
            ColorInterpolationFilters::LinearRgb => SurfaceType::LinearRgb,
            _ => SurfaceType::SRgb,
        }
    }
}
<commit_msg>Make render_primitive() pretty again<commit_after>//! Entry point for the CSS filters infrastructure.

use cssparser::{BasicParseError, Parser};
use markup5ever::{expanded_name, local_name, namespace_url, ns};
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, ElementResult, SetAttributes};
use crate::error::{ElementError, ParseError, RenderingError};
use crate::length::*;
use crate::node::{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, FilterResult};

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

/// A filter primitive interface.
pub trait FilterEffect: SetAttributes + Draw {
    fn resolve(&self, node: &Node) -> Result<PrimitiveParams, FilterError>;
}

// 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;

/// Resolved parameters for each filter primitive.
///
/// These gather all the data that a primitive may need during rendering:
/// the `feFoo` element's attributes, any computed values from its properties,
/// and parameters extracted from the element's children (for example,
/// `feMerge` gathers info from its `feMergNode` children).
pub enum PrimitiveParams {
    Blend(blend::Blend),
    ColorMatrix(color_matrix::ColorMatrix),
    ComponentTransfer(component_transfer::ComponentTransfer),
    Composite(composite::Composite),
    ConvolveMatrix(convolve_matrix::ConvolveMatrix),
    DiffuseLighting(lighting::DiffuseLighting),
    DisplacementMap(displacement_map::DisplacementMap),
    Flood(flood::Flood),
    GaussianBlur(gaussian_blur::GaussianBlur),
    Image(image::Image),
    Merge(merge::Merge),
    Morphology(morphology::Morphology),
    Offset(offset::Offset),
    SpecularLighting(lighting::SpecularLighting),
    Tile(tile::Tile),
    Turbulence(turbulence::Turbulence),
}

/// The base filter primitive node containing common properties.
#[derive(Clone)]
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 {
    Unspecified,
    SourceGraphic,
    SourceAlpha,
    BackgroundImage,
    BackgroundAlpha,
    FillPaint,
    StrokePaint,
    FilterOutput(CustomIdent),
}

impl Default for Input {
    fn default() -> Self {
        Input::Unspecified
    }
}

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))
            })
    }
}

impl Primitive {
    /// Constructs a new `Primitive` with empty properties.
    #[inline]
    fn new() -> 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(&self, ctx: &FilterContext) -> 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 Primitive {
    fn parse_standard_attributes(
        &mut self,
        attrs: &Attributes,
    ) -> Result<(Input, Input), ElementError> {
        let mut input_1 = Input::Unspecified;
        let mut input_2 = Input::Unspecified;

        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)?,
                expanded_name!("", "in") => input_1 = attr.parse(value)?,
                expanded_name!("", "in2") => input_2 = attr.parse(value)?,
                _ => (),
            }
        }

        Ok((input_1, input_2))
    }

    pub fn parse_no_inputs(&mut self, attrs: &Attributes) -> ElementResult {
        let (_, _) = self.parse_standard_attributes(attrs)?;
        Ok(())
    }

    pub fn parse_one_input(&mut self, attrs: &Attributes) -> Result<Input, ElementError> {
        let (input_1, _) = self.parse_standard_attributes(attrs)?;
        Ok(input_1)
    }

    pub fn parse_two_inputs(&mut self, attrs: &Attributes) -> Result<(Input, Input), ElementError> {
        self.parse_standard_attributes(attrs)
    }
}

/// 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());

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

        let start = Instant::now();

        if let Err(err) = filter
            .resolve(&c)
            .and_then(|params| render_primitive(&params, &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(RenderingError::from(status));
            }
        }

        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()?)
}

#[rustfmt::skip]
fn render_primitive(
    params: &PrimitiveParams,
    ctx: &FilterContext,
    acquired_nodes: &mut AcquiredNodes<'_>,
    draw_ctx: &mut DrawingCtx,
) -> Result<FilterResult, FilterError> {
    use PrimitiveParams::*;

    match params {
        Blend(p)             => p.render(ctx, acquired_nodes, draw_ctx),
        ColorMatrix(p)       => p.render(ctx, acquired_nodes, draw_ctx),
        ComponentTransfer(p) => p.render(ctx, acquired_nodes, draw_ctx),
        Composite(p)         => p.render(ctx, acquired_nodes, draw_ctx),
        ConvolveMatrix(p)    => p.render(ctx, acquired_nodes, draw_ctx),
        DiffuseLighting(p)   => p.render(ctx, acquired_nodes, draw_ctx),
        DisplacementMap(p)   => p.render(ctx, acquired_nodes, draw_ctx),
        Flood(p)             => p.render(ctx, acquired_nodes, draw_ctx),
        GaussianBlur(p)      => p.render(ctx, acquired_nodes, draw_ctx),
        Image(p)             => p.render(ctx, acquired_nodes, draw_ctx),
        Merge(p)             => p.render(ctx, acquired_nodes, draw_ctx),
        Morphology(p)        => p.render(ctx, acquired_nodes, draw_ctx),
        Offset(p)            => p.render(ctx, acquired_nodes, draw_ctx),
        SpecularLighting(p)  => p.render(ctx, acquired_nodes, draw_ctx),
        Tile(p)              => p.render(ctx, acquired_nodes, draw_ctx),
        Turbulence(p)        => p.render(ctx, acquired_nodes, draw_ctx),
    }
}

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


<commit_before>//! Geosciences related constants

// taken from http://hpiers.obspm.fr/eop-pc/models/constants.html

// Earth's rotation constants
pub const mean_angular_velocity_earth: f64 = 7.29211501; // [10^(-5)rad/s]
pub const nominal_angular_velocity_earth: f64 = 7.2921151467064	; // [10^(-5)rad/s]
pub const conventional_duration_mean_solar_day: f64 = 86400.0; // [s]
<commit_msg>New constants<commit_after>//! Geosciences related constants

// taken from http://hpiers.obspm.fr/eop-pc/models/constants.html

// Earth's rotation constants
pub const mean_angular_velocity_earth: f64 = 7.29211501; // [10^(-5)rad/s]
pub const nominal_angular_velocity_earth: f64 = 7.2921151467064	; // [10^(-5)rad/s]
pub const conventional_duration_mean_solar_day: f64 = 86400.0; // [s]
pub const ratio_mean_solar_day_sidereal_day: f64 = 1.002737909350795; // []
pub const conventional_duration_sidereal_day: f64 = 86164.09053083288; // [s]
pub const ratio_mean_solar_day_stellar_day: f64 = 1.00273781191135448; // []
pub const stellar_day: f64 = 86164.098903691; // [s]
pub const general_precession_in_longitude: f64 = 5028.7922; // [" per century]
pub const chandler_frequency_terrestrial_frame: f64 = 0.843330; // [cycle per tropical year]
pub const chandler_period_terrestrial_frame: f64 = 433.1; // [mean solar day D]
pub const quality_factor_chandler_peak: f64 = 170; // []
pub const free_core_nutation_celestial_frame: f64 = 430.23; // [mean solar day D]
pub const quality_factor_free_core_nutation: f64 = 20000; // []
pub const sidereal_year: f64 = 365.256363004; // [mean solar day D=86400s]
pub const tropical_year: f64 = 365.242190402; // [mean solar day D=86400s]
pub const mean_motion_moon: f64 = 0.2299708345219431; // [rad/mean solar day D=86400s]

// Geodetic constants
pub const earth_equatorial_radius: f64 = 6378136.61; // [m]
pub const first_equatorial_unit_inertia: f64 = 8.01012; // [10^(37) kg m²]
pub const second_equatorial_unit_inertia: f64 = 8.01032; // [10^(37 kg m²]
pub const mean_equatorial_unit_inertia: f64 = 8.010171184; // []
pub const axial_moment_inertia: f64 = 8.03652; // [10^(37 kg m²]
pub const Longitude_principal_inertia_axis_a: f64 = -14.929110; // [°]
pub const Colatitude_principal_inertia_axis_a: f64 = 0.0000378848; // [°]
pub const first_equatorial_moment_inertia_core: f64 = 9.1152379; // [10^(36 kg m²]
pub const second_equatorial_moment_inertia_core: f64 = 9.1153997; // [10^(36 kg m²]
pub const axial_moment_inertia_core: f64 = 9.1393530; // [10^(36 kg m²]
pub const first_equatorial_moment_inertia_mantle: f64 = 7.0165; // [10^(37 kg m²]
pub const second_equatorial_moment_inertia_core: f64 = 7.09872; // []
pub const dynamical_ellipticity: f64 = 3.28454791e-3; // []
pub const dynamical_ellipticity_core: f64 = 2.6462e-3; // []
pub const second_degree_term_earth_gravity_potential: f64 = 1.08263591e-3; // []
pub const love_number: f64 = 0.3; // []
pub const secular_love_number: f64 = 0.9383; // []

// Gravitational constants
pub const mean_equatorial_gravity: f64 = 9.780327810; // [m s^(-2)]
pub const gravitational_constant: f64 = 6.6738480; // [10^(-11) m³ kg^(-1) s^(-2)]
pub const geocentric_constant_gravitation: f64 = 3.9860044188; // [10^(14) m³ s^(-2)]
pub const heliocentric_constant_gravitation: f64 = 1.3271244207650; // [10^(14) m³ s^(-2)]

// Cosmologial constants
pub const hubble_constant: f64 = 733; // [km s^(-1) Mpc^(-1)]
pub const hubble_length: f64 = 1.275e26; // [m]
pub const age_universe: f64 = 13.7315; // [Giga year]
<|endoftext|>


<commit_before>//! OpenGL back-end for Rust-Graphics.

// External crates.
use graphics::BackEnd;
use gl;
use gl::types::{
    GLint,
    GLsizei,
    GLuint,
};
use shader_utils::{
    compile_shader,
    DynamicAttribute,
};

// Local crate.
use Texture;

static VERTEX_SHADER_XY_RGBA: &'static str = "
#version 330
in vec4 pos;
in vec4 color;

out vec4 v_color;

void main()
{
    v_color = color;
    gl_Position = pos;
}
";

static FRAGMENT_SHADER_XY_RGBA: &'static str = "
#version 330
out vec4 out_color;
in vec4 v_color;

void main()
{
    out_color = v_color;
}
";

static VERTEX_SHADER_XY_RGBA_UV: &'static str = "
#version 330
in vec4 pos;
in vec4 color;
in vec2 uv;

uniform sampler2D s_texture;

out vec2 v_uv;
out vec4 v_color;

void main()
{
    v_uv = uv;
    v_color = color;
    gl_Position = pos;
}
";

static FRAGMENT_SHADER_XY_RGBA_UV: &'static str = "
#version 330
out vec4 out_color;

uniform sampler2D s_texture;

in vec2 v_uv;
in vec4 v_color;

void main()
{
    out_color = texture(s_texture, v_uv) * v_color;
}
";

struct XYRGBA {
    vao: GLuint,
    vertex_shader: GLuint,
    fragment_shader: GLuint,
    program: GLuint,
    pos: DynamicAttribute,
    color: DynamicAttribute,
}

impl Drop for XYRGBA {
    fn drop(&mut self) {
        unsafe {
            gl::DeleteVertexArrays(1, &self.vao);
        }
        gl::DeleteProgram(self.program);
        gl::DeleteShader(self.vertex_shader);
        gl::DeleteShader(self.fragment_shader);
    }
}

impl XYRGBA {
    fn new() -> XYRGBA {
        let vertex_shader = match compile_shader(
            gl::VERTEX_SHADER,                  // shader type
            VERTEX_SHADER_XY_RGBA      // shader source
        ) {
            Ok(id) => id,
            Err(s) => fail!("compile_shader: {}", s)
        };
        let fragment_shader = match compile_shader(
            gl::FRAGMENT_SHADER,                // shader type
            FRAGMENT_SHADER_XY_RGBA    // shader source
        ) {
            Ok(id) => id,
            Err(s) => fail!("compile_shader: {}", s)
        };
        let program = gl::CreateProgram();

        gl::AttachShader(program, vertex_shader);
        gl::AttachShader(program, fragment_shader);

        unsafe {
            "out_color".with_c_str(
                |ptr| gl::BindFragDataLocation(program, 0, ptr)
            );
        }

        let mut vao = 0;
        unsafe {
            gl::GenVertexArrays(1, &mut vao);
        }
        gl::LinkProgram(program);
        let pos = DynamicAttribute::xy(
                program,
                "pos",
                vao
            ).unwrap();
        let color = DynamicAttribute::rgba(
                program,
                "color",
                vao
            ).unwrap();
        XYRGBA {
            vao: vao,
            vertex_shader: vertex_shader,
            fragment_shader: fragment_shader,
            program: program,
            pos: pos,
            color: color,
        }
    }
}

struct XYRGBAUV {
    vertex_shader: GLuint,
    fragment_shader: GLuint,
    program: GLuint,
    vao: GLuint,
    pos: DynamicAttribute,
    color: DynamicAttribute,
    uv: DynamicAttribute,
}

impl Drop for XYRGBAUV {
    fn drop(&mut self) {
        unsafe {
            gl::DeleteVertexArrays(1, &self.vao);
        }
        gl::DeleteProgram(self.program);
        gl::DeleteShader(self.vertex_shader);
        gl::DeleteShader(self.fragment_shader);
    }
}

impl XYRGBAUV {
    fn new() -> XYRGBAUV {
        let vertex_shader = match compile_shader(
            gl::VERTEX_SHADER,                  // shader type
            VERTEX_SHADER_XY_RGBA_UV   // shader type
        ) {
            Ok(id) => id,
            Err(s) => fail!("compile_shader: {}", s)
        };
        let fragment_shader = match compile_shader(
            gl::FRAGMENT_SHADER,                // shader type
            FRAGMENT_SHADER_XY_RGBA_UV // shader source
        ) {
            Ok(id) => id,
            Err(s) => fail!("compile_shader: {}", s)
        };

        let program = gl::CreateProgram();
        gl::AttachShader(program, vertex_shader);
        gl::AttachShader(program, fragment_shader);

        unsafe {
            "out_color".with_c_str(
                |ptr| gl::BindFragDataLocation(program, 0, ptr)
            );
        }

        let mut vao = 0;
        unsafe {
            gl::GenVertexArrays(1, &mut vao);
        }
        gl::LinkProgram(program);
        let pos = DynamicAttribute::xy(
                program,
                "pos",
                vao
            ).unwrap();
        let color = DynamicAttribute::rgba(
                program,
                "color",
                vao
            ).unwrap();
        let uv = DynamicAttribute::uv(
                program,
                "uv",
                vao
            ).unwrap();
        XYRGBAUV {
            vao: vao,
            vertex_shader: vertex_shader,
            fragment_shader: fragment_shader,
            program: program,
            pos: pos,
            color: color,
            uv: uv,
        }
    }
}

/// Contains OpenGL data.
pub struct Gl {
    tri_list_xy_rgba: XYRGBA,
    tri_list_xy_rgba_uv: XYRGBAUV,
    // Keeps track of the current shader program.
    current_program: Option<GLuint>,
}

impl<'a> Gl {
    /// Creates a new OpenGl back-end.
    pub fn new() -> Gl {
        // Load the vertices, color and texture coord buffers.
        Gl {
            tri_list_xy_rgba: XYRGBA::new(),
            tri_list_xy_rgba_uv: XYRGBAUV::new(),
            current_program: None,
       }
    }

    /// Sets viewport with normalized coordinates and center as origin.
    pub fn viewport(
        &mut self,
        x: i32,
        y: i32,
        w: i32,
        h: i32
    ) {
        gl::Viewport(x as GLint, y as GLint, w as GLsizei, h as GLsizei);
    }

    /// Sets the current program only if the program is not in use.
    pub fn use_program(&mut self, program: GLuint) {
        match self.current_program {
            None => {},
            Some(current_program) => {
                if program == current_program { return; }
            },
        }

        gl::UseProgram(program);
        self.current_program = Some(program);
    }

    /// Unset the current program.
    ///
    /// This forces the current program to be set on next drawing call.
    pub fn clear_program(&mut self) {
        self.current_program = None
    }
}

impl BackEnd<Texture> for Gl {
    fn supports_clear_rgba(&self) -> bool { true }

    fn clear_rgba(&mut self, r: f32, g: f32, b: f32, a: f32) {
        gl::ClearColor(r, g, b, a);
        gl::Clear(gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT);
    }

    fn enable_alpha_blend(&mut self) {
        gl::Enable(gl::BLEND);
        gl::BlendFunc(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA);
    }

    fn disable_alpha_blend(&mut self) {
        gl::Disable(gl::BLEND);
    }

    fn supports_single_texture(&self) -> bool { true }

    fn enable_single_texture(&mut self, texture: &Texture) {
        let texture = texture.get_id();
        gl::BindTexture(gl::TEXTURE_2D, texture);
    }

    fn disable_single_texture(&mut self) {}

    // Assume all textures has alpha channel for now.
    fn has_texture_alpha(&self, _texture: &Texture) -> bool { true }

    fn supports_tri_list_xy_f32_rgba_f32(&self) -> bool { true }

    fn tri_list_xy_f32_rgba_f32(
        &mut self,
        vertices: &[f32],
        colors: &[f32]
    ) {
        {
            // Set shader program.
            let shader_program = self.tri_list_xy_rgba.program;
            self.use_program(shader_program);
        }
        let ref mut shader = self.tri_list_xy_rgba;
        gl::BindVertexArray(shader.vao);

        // xy makes two floats.
        let size_vertices: i32 = 2;

        unsafe {
            shader.pos.set(vertices);
            shader.color.set(colors);
        }

        // Render triangles whether they are facing
        // clockwise or counter clockwise.
        gl::CullFace(gl::FRONT_AND_BACK);

        let items: i32 = vertices.len() as i32 / size_vertices;
        gl::DrawArrays(gl::TRIANGLES, 0, items);

        gl::BindVertexArray(0);
    }

    fn supports_tri_list_xy_f32_rgba_f32_uv_f32(&self) -> bool { true }

    fn tri_list_xy_f32_rgba_f32_uv_f32(
        &mut self,
        vertices: &[f32],
        colors: &[f32],
        texture_coords: &[f32]
    ) {
        {
            // Set shader program.
            let shader_program = self.tri_list_xy_rgba_uv.program;
            self.use_program(shader_program);
        }
        let ref mut shader = self.tri_list_xy_rgba_uv;
        gl::BindVertexArray(shader.vao);

        let size_vertices: i32 = 2;

        unsafe {
            shader.pos.set(vertices);
            shader.color.set(colors);
            shader.uv.set(texture_coords);
        }

        // Render triangles whether they are facing
        // clockwise or counter clockwise.
        gl::CullFace(gl::FRONT_AND_BACK);

        let items: i32 = vertices.len() as i32 / size_vertices;
        gl::DrawArrays(gl::TRIANGLES, 0, items);

        gl::BindVertexArray(0);
    }
}

<commit_msg>Removed "tri_list" prefix<commit_after>//! OpenGL back-end for Rust-Graphics.

// External crates.
use graphics::BackEnd;
use gl;
use gl::types::{
    GLint,
    GLsizei,
    GLuint,
};
use shader_utils::{
    compile_shader,
    DynamicAttribute,
};

// Local crate.
use Texture;

static VERTEX_SHADER_XY_RGBA: &'static str = "
#version 330
in vec4 pos;
in vec4 color;

out vec4 v_color;

void main()
{
    v_color = color;
    gl_Position = pos;
}
";

static FRAGMENT_SHADER_XY_RGBA: &'static str = "
#version 330
out vec4 out_color;
in vec4 v_color;

void main()
{
    out_color = v_color;
}
";

static VERTEX_SHADER_XY_RGBA_UV: &'static str = "
#version 330
in vec4 pos;
in vec4 color;
in vec2 uv;

uniform sampler2D s_texture;

out vec2 v_uv;
out vec4 v_color;

void main()
{
    v_uv = uv;
    v_color = color;
    gl_Position = pos;
}
";

static FRAGMENT_SHADER_XY_RGBA_UV: &'static str = "
#version 330
out vec4 out_color;

uniform sampler2D s_texture;

in vec2 v_uv;
in vec4 v_color;

void main()
{
    out_color = texture(s_texture, v_uv) * v_color;
}
";

struct XYRGBA {
    vao: GLuint,
    vertex_shader: GLuint,
    fragment_shader: GLuint,
    program: GLuint,
    pos: DynamicAttribute,
    color: DynamicAttribute,
}

impl Drop for XYRGBA {
    fn drop(&mut self) {
        unsafe {
            gl::DeleteVertexArrays(1, &self.vao);
        }
        gl::DeleteProgram(self.program);
        gl::DeleteShader(self.vertex_shader);
        gl::DeleteShader(self.fragment_shader);
    }
}

impl XYRGBA {
    fn new() -> XYRGBA {
        let vertex_shader = match compile_shader(
            gl::VERTEX_SHADER,                  // shader type
            VERTEX_SHADER_XY_RGBA      // shader source
        ) {
            Ok(id) => id,
            Err(s) => fail!("compile_shader: {}", s)
        };
        let fragment_shader = match compile_shader(
            gl::FRAGMENT_SHADER,                // shader type
            FRAGMENT_SHADER_XY_RGBA    // shader source
        ) {
            Ok(id) => id,
            Err(s) => fail!("compile_shader: {}", s)
        };
        let program = gl::CreateProgram();

        gl::AttachShader(program, vertex_shader);
        gl::AttachShader(program, fragment_shader);

        unsafe {
            "out_color".with_c_str(
                |ptr| gl::BindFragDataLocation(program, 0, ptr)
            );
        }

        let mut vao = 0;
        unsafe {
            gl::GenVertexArrays(1, &mut vao);
        }
        gl::LinkProgram(program);
        let pos = DynamicAttribute::xy(
                program,
                "pos",
                vao
            ).unwrap();
        let color = DynamicAttribute::rgba(
                program,
                "color",
                vao
            ).unwrap();
        XYRGBA {
            vao: vao,
            vertex_shader: vertex_shader,
            fragment_shader: fragment_shader,
            program: program,
            pos: pos,
            color: color,
        }
    }
}

struct XYRGBAUV {
    vertex_shader: GLuint,
    fragment_shader: GLuint,
    program: GLuint,
    vao: GLuint,
    pos: DynamicAttribute,
    color: DynamicAttribute,
    uv: DynamicAttribute,
}

impl Drop for XYRGBAUV {
    fn drop(&mut self) {
        unsafe {
            gl::DeleteVertexArrays(1, &self.vao);
        }
        gl::DeleteProgram(self.program);
        gl::DeleteShader(self.vertex_shader);
        gl::DeleteShader(self.fragment_shader);
    }
}

impl XYRGBAUV {
    fn new() -> XYRGBAUV {
        let vertex_shader = match compile_shader(
            gl::VERTEX_SHADER,                  // shader type
            VERTEX_SHADER_XY_RGBA_UV   // shader type
        ) {
            Ok(id) => id,
            Err(s) => fail!("compile_shader: {}", s)
        };
        let fragment_shader = match compile_shader(
            gl::FRAGMENT_SHADER,                // shader type
            FRAGMENT_SHADER_XY_RGBA_UV // shader source
        ) {
            Ok(id) => id,
            Err(s) => fail!("compile_shader: {}", s)
        };

        let program = gl::CreateProgram();
        gl::AttachShader(program, vertex_shader);
        gl::AttachShader(program, fragment_shader);

        unsafe {
            "out_color".with_c_str(
                |ptr| gl::BindFragDataLocation(program, 0, ptr)
            );
        }

        let mut vao = 0;
        unsafe {
            gl::GenVertexArrays(1, &mut vao);
        }
        gl::LinkProgram(program);
        let pos = DynamicAttribute::xy(
                program,
                "pos",
                vao
            ).unwrap();
        let color = DynamicAttribute::rgba(
                program,
                "color",
                vao
            ).unwrap();
        let uv = DynamicAttribute::uv(
                program,
                "uv",
                vao
            ).unwrap();
        XYRGBAUV {
            vao: vao,
            vertex_shader: vertex_shader,
            fragment_shader: fragment_shader,
            program: program,
            pos: pos,
            color: color,
            uv: uv,
        }
    }
}

/// Contains OpenGL data.
pub struct Gl {
    xy_rgba: XYRGBA,
    xy_rgba_uv: XYRGBAUV,
    // Keeps track of the current shader program.
    current_program: Option<GLuint>,
}

impl<'a> Gl {
    /// Creates a new OpenGl back-end.
    pub fn new() -> Gl {
        // Load the vertices, color and texture coord buffers.
        Gl {
            xy_rgba: XYRGBA::new(),
            xy_rgba_uv: XYRGBAUV::new(),
            current_program: None,
       }
    }

    /// Sets viewport with normalized coordinates and center as origin.
    pub fn viewport(
        &mut self,
        x: i32,
        y: i32,
        w: i32,
        h: i32
    ) {
        gl::Viewport(x as GLint, y as GLint, w as GLsizei, h as GLsizei);
    }

    /// Sets the current program only if the program is not in use.
    pub fn use_program(&mut self, program: GLuint) {
        match self.current_program {
            None => {},
            Some(current_program) => {
                if program == current_program { return; }
            },
        }

        gl::UseProgram(program);
        self.current_program = Some(program);
    }

    /// Unset the current program.
    ///
    /// This forces the current program to be set on next drawing call.
    pub fn clear_program(&mut self) {
        self.current_program = None
    }
}

impl BackEnd<Texture> for Gl {
    fn supports_clear_rgba(&self) -> bool { true }

    fn clear_rgba(&mut self, r: f32, g: f32, b: f32, a: f32) {
        gl::ClearColor(r, g, b, a);
        gl::Clear(gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT);
    }

    fn enable_alpha_blend(&mut self) {
        gl::Enable(gl::BLEND);
        gl::BlendFunc(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA);
    }

    fn disable_alpha_blend(&mut self) {
        gl::Disable(gl::BLEND);
    }

    fn supports_single_texture(&self) -> bool { true }

    fn enable_single_texture(&mut self, texture: &Texture) {
        let texture = texture.get_id();
        gl::BindTexture(gl::TEXTURE_2D, texture);
    }

    fn disable_single_texture(&mut self) {}

    // Assume all textures has alpha channel for now.
    fn has_texture_alpha(&self, _texture: &Texture) -> bool { true }

    fn supports_tri_list_xy_f32_rgba_f32(&self) -> bool { true }

    fn tri_list_xy_f32_rgba_f32(
        &mut self,
        vertices: &[f32],
        colors: &[f32]
    ) {
        {
            // Set shader program.
            let shader_program = self.xy_rgba.program;
            self.use_program(shader_program);
        }
        let ref mut shader = self.xy_rgba;
        gl::BindVertexArray(shader.vao);

        // xy makes two floats.
        let size_vertices: i32 = 2;

        unsafe {
            shader.pos.set(vertices);
            shader.color.set(colors);
        }

        // Render triangles whether they are facing
        // clockwise or counter clockwise.
        gl::CullFace(gl::FRONT_AND_BACK);

        let items: i32 = vertices.len() as i32 / size_vertices;
        gl::DrawArrays(gl::TRIANGLES, 0, items);

        gl::BindVertexArray(0);
    }

    fn supports_tri_list_xy_f32_rgba_f32_uv_f32(&self) -> bool { true }

    fn tri_list_xy_f32_rgba_f32_uv_f32(
        &mut self,
        vertices: &[f32],
        colors: &[f32],
        texture_coords: &[f32]
    ) {
        {
            // Set shader program.
            let shader_program = self.xy_rgba_uv.program;
            self.use_program(shader_program);
        }
        let ref mut shader = self.xy_rgba_uv;
        gl::BindVertexArray(shader.vao);

        let size_vertices: i32 = 2;

        unsafe {
            shader.pos.set(vertices);
            shader.color.set(colors);
            shader.uv.set(texture_coords);
        }

        // Render triangles whether they are facing
        // clockwise or counter clockwise.
        gl::CullFace(gl::FRONT_AND_BACK);

        let items: i32 = vertices.len() as i32 / size_vertices;
        gl::DrawArrays(gl::TRIANGLES, 0, items);

        gl::BindVertexArray(0);
    }
}

<|endoftext|>


<commit_before>//! OpenGL back-end for Rust-Graphics.

// External crates.
use shader_version::{opengl, glsl};
use graphics::BackEnd;
use gl;
use gl::types::{
    GLint,
    GLsizei,
    GLuint,
};
use shader_utils::{
    compile_shader,
    DynamicAttribute,
};

// Local crate.
use Texture;

static VERTEX_SHADER_XY_RGBA_120: &'static str = "
#version 120
attribute vec4 pos;
attribute vec4 color;

varying vec4 v_color;

void main()
{
    v_color = color;
    gl_Position = pos;
}
";

static VERTEX_SHADER_XY_RGBA_150_CORE: &'static str = "
#version 150 core
in vec4 pos;
in vec4 color;

out vec4 v_color;

void main()
{
    v_color = color;
    gl_Position = pos;
}
";

static FRAGMENT_SHADER_XY_RGBA_120: &'static str = "
#version 120
varying vec4 v_color;

void main()
{
    gl_FragColor = v_color;
}
";

static FRAGMENT_SHADER_XY_RGBA_150_CORE: &'static str = "
#version 150 core
out vec4 out_color;
in vec4 v_color;

void main()
{
    out_color = v_color;
}
";

static VERTEX_SHADER_XY_RGBA_UV_120: &'static str = "
#version 120
attribute vec4 pos;
attribute vec4 color;
attribute vec2 uv;

uniform sampler2D s_texture;

varying vec2 v_uv;
varying vec4 v_color;

void main()
{
    v_uv = uv;
    v_color = color;
    gl_Position = pos;
}
";

static VERTEX_SHADER_XY_RGBA_UV_150_CORE: &'static str = "
#version 150 core
in vec4 pos;
in vec4 color;
in vec2 uv;

uniform sampler2D s_texture;

out vec2 v_uv;
out vec4 v_color;

void main()
{
    v_uv = uv;
    v_color = color;
    gl_Position = pos;
}
";

static FRAGMENT_SHADER_XY_RGBA_UV_120: &'static str = "
#version 120
uniform sampler2D s_texture;

varying vec2 v_uv;
varying vec4 v_color;

void main()
{
    gl_FragColor = texture2D(s_texture, v_uv) * v_color;
}
";

static FRAGMENT_SHADER_XY_RGBA_UV_150_CORE: &'static str = "
#version 150 core
out vec4 out_color;

uniform sampler2D s_texture;

in vec2 v_uv;
in vec4 v_color;

void main()
{
    out_color = texture(s_texture, v_uv) * v_color;
}
";

fn pick_120_150<T>(glsl: glsl::GLSL, for_120: T, for_150: T) -> T {
    match glsl {
        glsl::GLSL_1_10 => fail!("GLSL 1.10 not supported"),
        glsl::GLSL_1_20
      | glsl::GLSL_1_30
      | glsl::GLSL_1_40 => for_120,
        _ => for_150,
    }
}

struct XYRGBA {
    vao: GLuint,
    vertex_shader: GLuint,
    fragment_shader: GLuint,
    program: GLuint,
    pos: DynamicAttribute,
    color: DynamicAttribute,
}

impl Drop for XYRGBA {
    fn drop(&mut self) {
        unsafe {
            gl::DeleteVertexArrays(1, &self.vao);
        }
        gl::DeleteProgram(self.program);
        gl::DeleteShader(self.vertex_shader);
        gl::DeleteShader(self.fragment_shader);
    }
}

impl XYRGBA {
    fn new(glsl: glsl::GLSL) -> XYRGBA {
        let vertex_shader = match compile_shader(
            gl::VERTEX_SHADER,                  // shader type
            pick_120_150(glsl, VERTEX_SHADER_XY_RGBA_120, VERTEX_SHADER_XY_RGBA_150_CORE)
        ) {
            Ok(id) => id,
            Err(s) => fail!("compile_shader: {}", s)
        };
        let fragment_shader = match compile_shader(
            gl::FRAGMENT_SHADER,                // shader type
            pick_120_150(glsl, FRAGMENT_SHADER_XY_RGBA_120, FRAGMENT_SHADER_XY_RGBA_150_CORE)
        ) {
            Ok(id) => id,
            Err(s) => fail!("compile_shader: {}", s)
        };
        let program = gl::CreateProgram();

        gl::AttachShader(program, vertex_shader);
        gl::AttachShader(program, fragment_shader);

        unsafe {
            "out_color".with_c_str(
                |ptr| gl::BindFragDataLocation(program, 0, ptr)
            );
        }

        let mut vao = 0;
        unsafe {
            gl::GenVertexArrays(1, &mut vao);
        }
        gl::LinkProgram(program);
        let pos = DynamicAttribute::xy(
                program,
                "pos",
                vao
            ).unwrap();
        let color = DynamicAttribute::rgba(
                program,
                "color",
                vao
            ).unwrap();
        XYRGBA {
            vao: vao,
            vertex_shader: vertex_shader,
            fragment_shader: fragment_shader,
            program: program,
            pos: pos,
            color: color,
        }
    }
}

struct XYRGBAUV {
    vertex_shader: GLuint,
    fragment_shader: GLuint,
    program: GLuint,
    vao: GLuint,
    pos: DynamicAttribute,
    color: DynamicAttribute,
    uv: DynamicAttribute,
}

impl Drop for XYRGBAUV {
    fn drop(&mut self) {
        unsafe {
            gl::DeleteVertexArrays(1, &self.vao);
        }
        gl::DeleteProgram(self.program);
        gl::DeleteShader(self.vertex_shader);
        gl::DeleteShader(self.fragment_shader);
    }
}

impl XYRGBAUV {
    fn new(glsl: glsl::GLSL) -> XYRGBAUV {
        let vertex_shader = match compile_shader(
            gl::VERTEX_SHADER,                  // shader type
            pick_120_150(glsl, VERTEX_SHADER_XY_RGBA_UV_120, VERTEX_SHADER_XY_RGBA_UV_150_CORE)
        ) {
            Ok(id) => id,
            Err(s) => fail!("compile_shader: {}", s)
        };
        let fragment_shader = match compile_shader(
            gl::FRAGMENT_SHADER,                // shader type
            pick_120_150(glsl, FRAGMENT_SHADER_XY_RGBA_UV_120, FRAGMENT_SHADER_XY_RGBA_UV_150_CORE)
        ) {
            Ok(id) => id,
            Err(s) => fail!("compile_shader: {}", s)
        };

        let program = gl::CreateProgram();
        gl::AttachShader(program, vertex_shader);
        gl::AttachShader(program, fragment_shader);

        unsafe {
            "out_color".with_c_str(
                |ptr| gl::BindFragDataLocation(program, 0, ptr)
            );
        }

        let mut vao = 0;
        unsafe {
            gl::GenVertexArrays(1, &mut vao);
        }
        gl::LinkProgram(program);
        let pos = DynamicAttribute::xy(
                program,
                "pos",
                vao
            ).unwrap();
        let color = DynamicAttribute::rgba(
                program,
                "color",
                vao
            ).unwrap();
        let uv = DynamicAttribute::uv(
                program,
                "uv",
                vao
            ).unwrap();
        XYRGBAUV {
            vao: vao,
            vertex_shader: vertex_shader,
            fragment_shader: fragment_shader,
            program: program,
            pos: pos,
            color: color,
            uv: uv,
        }
    }
}

/// Contains OpenGL data.
pub struct Gl {
    xy_rgba: XYRGBA,
    xy_rgba_uv: XYRGBAUV,
    // Keeps track of the current shader program.
    current_program: Option<GLuint>,
}

impl<'a> Gl {
    /// Creates a new OpenGl back-end.
    pub fn new(opengl: opengl::OpenGL) -> Gl {
        let glsl = opengl.to_GLSL();
        // Load the vertices, color and texture coord buffers.
        Gl {
            xy_rgba: XYRGBA::new(glsl),
            xy_rgba_uv: XYRGBAUV::new(glsl),
            current_program: None,
       }
    }

    /// Sets viewport with normalized coordinates and center as origin.
    pub fn viewport(
        &mut self,
        x: i32,
        y: i32,
        w: i32,
        h: i32
    ) {
        gl::Viewport(x as GLint, y as GLint, w as GLsizei, h as GLsizei);
    }

    /// Sets the current program only if the program is not in use.
    pub fn use_program(&mut self, program: GLuint) {
        match self.current_program {
            None => {},
            Some(current_program) => {
                if program == current_program { return; }
            },
        }

        gl::UseProgram(program);
        self.current_program = Some(program);
    }

    /// Unset the current program.
    ///
    /// This forces the current program to be set on next drawing call.
    pub fn clear_program(&mut self) {
        self.current_program = None
    }
}

impl BackEnd<Texture> for Gl {
    fn supports_clear_rgba(&self) -> bool { true }

    fn clear_rgba(&mut self, r: f32, g: f32, b: f32, a: f32) {
        gl::ClearColor(r, g, b, a);
        gl::Clear(gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT);
    }

    fn enable_alpha_blend(&mut self) {
        gl::Enable(gl::BLEND);
        gl::BlendFunc(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA);
    }

    fn disable_alpha_blend(&mut self) {
        gl::Disable(gl::BLEND);
    }

    fn supports_single_texture(&self) -> bool { true }

    fn enable_single_texture(&mut self, texture: &Texture) {
        let texture = texture.get_id();
        gl::BindTexture(gl::TEXTURE_2D, texture);
    }

    fn disable_single_texture(&mut self) {}

    // Assume all textures has alpha channel for now.
    fn has_texture_alpha(&self, _texture: &Texture) -> bool { true }

    fn supports_tri_list_xy_f32_rgba_f32(&self) -> bool { true }

    fn tri_list_xy_f32_rgba_f32(
        &mut self,
        vertices: &[f32],
        colors: &[f32]
    ) {
        {
            // Set shader program.
            let shader_program = self.xy_rgba.program;
            self.use_program(shader_program);
        }
        let ref mut shader = self.xy_rgba;
        gl::BindVertexArray(shader.vao);

        // xy makes two floats.
        let size_vertices: i32 = 2;

        unsafe {
            shader.pos.set(vertices);
            shader.color.set(colors);
        }

        // Render triangles whether they are facing
        // clockwise or counter clockwise.
        gl::CullFace(gl::FRONT_AND_BACK);

        let items: i32 = vertices.len() as i32 / size_vertices;
        gl::DrawArrays(gl::TRIANGLES, 0, items);

        gl::BindVertexArray(0);
    }

    fn supports_tri_list_xy_f32_rgba_f32_uv_f32(&self) -> bool { true }

    fn tri_list_xy_f32_rgba_f32_uv_f32(
        &mut self,
        vertices: &[f32],
        colors: &[f32],
        texture_coords: &[f32]
    ) {
        {
            // Set shader program.
            let shader_program = self.xy_rgba_uv.program;
            self.use_program(shader_program);
        }
        let ref mut shader = self.xy_rgba_uv;
        gl::BindVertexArray(shader.vao);

        let size_vertices: i32 = 2;

        unsafe {
            shader.pos.set(vertices);
            shader.color.set(colors);
            shader.uv.set(texture_coords);
        }

        // Render triangles whether they are facing
        // clockwise or counter clockwise.
        gl::CullFace(gl::FRONT_AND_BACK);

        let items: i32 = vertices.len() as i32 / size_vertices;
        gl::DrawArrays(gl::TRIANGLES, 0, items);

        gl::BindVertexArray(0);
    }
}

<commit_msg>fail -> panic<commit_after>//! OpenGL back-end for Rust-Graphics.

// External crates.
use shader_version::{opengl, glsl};
use graphics::BackEnd;
use gl;
use gl::types::{
    GLint,
    GLsizei,
    GLuint,
};
use shader_utils::{
    compile_shader,
    DynamicAttribute,
};

// Local crate.
use Texture;

static VERTEX_SHADER_XY_RGBA_120: &'static str = "
#version 120
attribute vec4 pos;
attribute vec4 color;

varying vec4 v_color;

void main()
{
    v_color = color;
    gl_Position = pos;
}
";

static VERTEX_SHADER_XY_RGBA_150_CORE: &'static str = "
#version 150 core
in vec4 pos;
in vec4 color;

out vec4 v_color;

void main()
{
    v_color = color;
    gl_Position = pos;
}
";

static FRAGMENT_SHADER_XY_RGBA_120: &'static str = "
#version 120
varying vec4 v_color;

void main()
{
    gl_FragColor = v_color;
}
";

static FRAGMENT_SHADER_XY_RGBA_150_CORE: &'static str = "
#version 150 core
out vec4 out_color;
in vec4 v_color;

void main()
{
    out_color = v_color;
}
";

static VERTEX_SHADER_XY_RGBA_UV_120: &'static str = "
#version 120
attribute vec4 pos;
attribute vec4 color;
attribute vec2 uv;

uniform sampler2D s_texture;

varying vec2 v_uv;
varying vec4 v_color;

void main()
{
    v_uv = uv;
    v_color = color;
    gl_Position = pos;
}
";

static VERTEX_SHADER_XY_RGBA_UV_150_CORE: &'static str = "
#version 150 core
in vec4 pos;
in vec4 color;
in vec2 uv;

uniform sampler2D s_texture;

out vec2 v_uv;
out vec4 v_color;

void main()
{
    v_uv = uv;
    v_color = color;
    gl_Position = pos;
}
";

static FRAGMENT_SHADER_XY_RGBA_UV_120: &'static str = "
#version 120
uniform sampler2D s_texture;

varying vec2 v_uv;
varying vec4 v_color;

void main()
{
    gl_FragColor = texture2D(s_texture, v_uv) * v_color;
}
";

static FRAGMENT_SHADER_XY_RGBA_UV_150_CORE: &'static str = "
#version 150 core
out vec4 out_color;

uniform sampler2D s_texture;

in vec2 v_uv;
in vec4 v_color;

void main()
{
    out_color = texture(s_texture, v_uv) * v_color;
}
";

fn pick_120_150<T>(glsl: glsl::GLSL, for_120: T, for_150: T) -> T {
    match glsl {
        glsl::GLSL_1_10 => panic!("GLSL 1.10 not supported"),
        glsl::GLSL_1_20
      | glsl::GLSL_1_30
      | glsl::GLSL_1_40 => for_120,
        _ => for_150,
    }
}

struct XYRGBA {
    vao: GLuint,
    vertex_shader: GLuint,
    fragment_shader: GLuint,
    program: GLuint,
    pos: DynamicAttribute,
    color: DynamicAttribute,
}

impl Drop for XYRGBA {
    fn drop(&mut self) {
        unsafe {
            gl::DeleteVertexArrays(1, &self.vao);
        }
        gl::DeleteProgram(self.program);
        gl::DeleteShader(self.vertex_shader);
        gl::DeleteShader(self.fragment_shader);
    }
}

impl XYRGBA {
    fn new(glsl: glsl::GLSL) -> XYRGBA {
        let vertex_shader = match compile_shader(
            gl::VERTEX_SHADER,                  // shader type
            pick_120_150(glsl, VERTEX_SHADER_XY_RGBA_120, VERTEX_SHADER_XY_RGBA_150_CORE)
        ) {
            Ok(id) => id,
            Err(s) => panic!("compile_shader: {}", s)
        };
        let fragment_shader = match compile_shader(
            gl::FRAGMENT_SHADER,                // shader type
            pick_120_150(glsl, FRAGMENT_SHADER_XY_RGBA_120, FRAGMENT_SHADER_XY_RGBA_150_CORE)
        ) {
            Ok(id) => id,
            Err(s) => panic!("compile_shader: {}", s)
        };
        let program = gl::CreateProgram();

        gl::AttachShader(program, vertex_shader);
        gl::AttachShader(program, fragment_shader);

        unsafe {
            "out_color".with_c_str(
                |ptr| gl::BindFragDataLocation(program, 0, ptr)
            );
        }

        let mut vao = 0;
        unsafe {
            gl::GenVertexArrays(1, &mut vao);
        }
        gl::LinkProgram(program);
        let pos = DynamicAttribute::xy(
                program,
                "pos",
                vao
            ).unwrap();
        let color = DynamicAttribute::rgba(
                program,
                "color",
                vao
            ).unwrap();
        XYRGBA {
            vao: vao,
            vertex_shader: vertex_shader,
            fragment_shader: fragment_shader,
            program: program,
            pos: pos,
            color: color,
        }
    }
}

struct XYRGBAUV {
    vertex_shader: GLuint,
    fragment_shader: GLuint,
    program: GLuint,
    vao: GLuint,
    pos: DynamicAttribute,
    color: DynamicAttribute,
    uv: DynamicAttribute,
}

impl Drop for XYRGBAUV {
    fn drop(&mut self) {
        unsafe {
            gl::DeleteVertexArrays(1, &self.vao);
        }
        gl::DeleteProgram(self.program);
        gl::DeleteShader(self.vertex_shader);
        gl::DeleteShader(self.fragment_shader);
    }
}

impl XYRGBAUV {
    fn new(glsl: glsl::GLSL) -> XYRGBAUV {
        let vertex_shader = match compile_shader(
            gl::VERTEX_SHADER,                  // shader type
            pick_120_150(glsl, VERTEX_SHADER_XY_RGBA_UV_120, VERTEX_SHADER_XY_RGBA_UV_150_CORE)
        ) {
            Ok(id) => id,
            Err(s) => panic!("compile_shader: {}", s)
        };
        let fragment_shader = match compile_shader(
            gl::FRAGMENT_SHADER,                // shader type
            pick_120_150(glsl, FRAGMENT_SHADER_XY_RGBA_UV_120, FRAGMENT_SHADER_XY_RGBA_UV_150_CORE)
        ) {
            Ok(id) => id,
            Err(s) => panic!("compile_shader: {}", s)
        };

        let program = gl::CreateProgram();
        gl::AttachShader(program, vertex_shader);
        gl::AttachShader(program, fragment_shader);

        unsafe {
            "out_color".with_c_str(
                |ptr| gl::BindFragDataLocation(program, 0, ptr)
            );
        }

        let mut vao = 0;
        unsafe {
            gl::GenVertexArrays(1, &mut vao);
        }
        gl::LinkProgram(program);
        let pos = DynamicAttribute::xy(
                program,
                "pos",
                vao
            ).unwrap();
        let color = DynamicAttribute::rgba(
                program,
                "color",
                vao
            ).unwrap();
        let uv = DynamicAttribute::uv(
                program,
                "uv",
                vao
            ).unwrap();
        XYRGBAUV {
            vao: vao,
            vertex_shader: vertex_shader,
            fragment_shader: fragment_shader,
            program: program,
            pos: pos,
            color: color,
            uv: uv,
        }
    }
}

/// Contains OpenGL data.
pub struct Gl {
    xy_rgba: XYRGBA,
    xy_rgba_uv: XYRGBAUV,
    // Keeps track of the current shader program.
    current_program: Option<GLuint>,
}

impl<'a> Gl {
    /// Creates a new OpenGl back-end.
    pub fn new(opengl: opengl::OpenGL) -> Gl {
        let glsl = opengl.to_GLSL();
        // Load the vertices, color and texture coord buffers.
        Gl {
            xy_rgba: XYRGBA::new(glsl),
            xy_rgba_uv: XYRGBAUV::new(glsl),
            current_program: None,
       }
    }

    /// Sets viewport with normalized coordinates and center as origin.
    pub fn viewport(
        &mut self,
        x: i32,
        y: i32,
        w: i32,
        h: i32
    ) {
        gl::Viewport(x as GLint, y as GLint, w as GLsizei, h as GLsizei);
    }

    /// Sets the current program only if the program is not in use.
    pub fn use_program(&mut self, program: GLuint) {
        match self.current_program {
            None => {},
            Some(current_program) => {
                if program == current_program { return; }
            },
        }

        gl::UseProgram(program);
        self.current_program = Some(program);
    }

    /// Unset the current program.
    ///
    /// This forces the current program to be set on next drawing call.
    pub fn clear_program(&mut self) {
        self.current_program = None
    }
}

impl BackEnd<Texture> for Gl {
    fn supports_clear_rgba(&self) -> bool { true }

    fn clear_rgba(&mut self, r: f32, g: f32, b: f32, a: f32) {
        gl::ClearColor(r, g, b, a);
        gl::Clear(gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT);
    }

    fn enable_alpha_blend(&mut self) {
        gl::Enable(gl::BLEND);
        gl::BlendFunc(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA);
    }

    fn disable_alpha_blend(&mut self) {
        gl::Disable(gl::BLEND);
    }

    fn supports_single_texture(&self) -> bool { true }

    fn enable_single_texture(&mut self, texture: &Texture) {
        let texture = texture.get_id();
        gl::BindTexture(gl::TEXTURE_2D, texture);
    }

    fn disable_single_texture(&mut self) {}

    // Assume all textures has alpha channel for now.
    fn has_texture_alpha(&self, _texture: &Texture) -> bool { true }

    fn supports_tri_list_xy_f32_rgba_f32(&self) -> bool { true }

    fn tri_list_xy_f32_rgba_f32(
        &mut self,
        vertices: &[f32],
        colors: &[f32]
    ) {
        {
            // Set shader program.
            let shader_program = self.xy_rgba.program;
            self.use_program(shader_program);
        }
        let ref mut shader = self.xy_rgba;
        gl::BindVertexArray(shader.vao);

        // xy makes two floats.
        let size_vertices: i32 = 2;

        unsafe {
            shader.pos.set(vertices);
            shader.color.set(colors);
        }

        // Render triangles whether they are facing
        // clockwise or counter clockwise.
        gl::CullFace(gl::FRONT_AND_BACK);

        let items: i32 = vertices.len() as i32 / size_vertices;
        gl::DrawArrays(gl::TRIANGLES, 0, items);

        gl::BindVertexArray(0);
    }

    fn supports_tri_list_xy_f32_rgba_f32_uv_f32(&self) -> bool { true }

    fn tri_list_xy_f32_rgba_f32_uv_f32(
        &mut self,
        vertices: &[f32],
        colors: &[f32],
        texture_coords: &[f32]
    ) {
        {
            // Set shader program.
            let shader_program = self.xy_rgba_uv.program;
            self.use_program(shader_program);
        }
        let ref mut shader = self.xy_rgba_uv;
        gl::BindVertexArray(shader.vao);

        let size_vertices: i32 = 2;

        unsafe {
            shader.pos.set(vertices);
            shader.color.set(colors);
            shader.uv.set(texture_coords);
        }

        // Render triangles whether they are facing
        // clockwise or counter clockwise.
        gl::CullFace(gl::FRONT_AND_BACK);

        let items: i32 = vertices.len() as i32 / size_vertices;
        gl::DrawArrays(gl::TRIANGLES, 0, items);

        gl::BindVertexArray(0);
    }
}

<|endoftext|>


<commit_before>use std::path::Path;
use std::collections::HashMap;
use graphics::character::{ CharacterCache, Character };
use graphics::types::FontSize;
use glium::Texture2d;
use glium::backend::Facade;
use image::{ Rgba, ImageBuffer };
use freetype::{ self, Face };
use ::back_end::DrawTexture;

fn load_character<F: Facade>(face: &Face, facade: &F, font_size: FontSize,
                             character: char)
    -> Character<DrawTexture>
{
    face.set_pixel_sizes(0, font_size).unwrap();
    face.load_char(character as usize, freetype::face::DEFAULT).unwrap();
    let glyph = face.glyph().get_glyph().unwrap();
    let bitmap_glyph = glyph.to_bitmap(freetype::render_mode::RenderMode::Normal, None).unwrap();
    let bitmap = bitmap_glyph.bitmap();
    let texture = Texture2d::new(
        facade,
        ImageBuffer::<Rgba<u8>, _>::from_raw(
            bitmap.width() as u32, bitmap.rows() as u32,
            bitmap.buffer().iter()
                .flat_map(|&pix| vec![255, 255, 255, pix].into_iter())
                .collect::<Vec<_>>()
        ).expect("failed to create glyph texture")
    );
    let glyph_size = glyph.advance();
    Character {
        offset: [
            bitmap_glyph.left() as f64,
            bitmap_glyph.top() as f64
        ],
        size: [
            (glyph_size.x >> 16) as f64,
            (glyph_size.y >> 16) as f64
        ],
        texture: DrawTexture::new(texture),
    }
}

pub struct GlyphCache<F> {
    face: Face<'static>,
    data: HashMap<FontSize, HashMap<char, Character<DrawTexture>>>,
    facade: F,
}

impl<F> GlyphCache<F> {
     /// Constructor for a GlyphCache.
    pub fn new(font: &Path, facade: F) -> Result<Self, freetype::error::Error> {
        let freetype = try!(freetype::Library::init());
        let face = try!(freetype.new_face(font, 0));
        Ok(GlyphCache {
            face: face,
            data: HashMap::new(),
            facade: facade,
        })
    }
}

impl<F: Facade> CharacterCache for GlyphCache<F> {
    type Texture = DrawTexture;

    fn character<'a>(&'a mut self, font_size: FontSize, character: char)
        -> &'a Character<DrawTexture>
    {
        use std::collections::hash_map::Entry::{Vacant, Occupied};
        let size_cache: &'a mut HashMap<char, _> = match self.data.entry(font_size) {
            Vacant(entry) => entry.insert(HashMap::new()),
            Occupied(entry) => entry.into_mut(),
        };
        match size_cache.entry(character) {
            Vacant(entry) => entry.insert(load_character(&self.face, &self.facade, font_size, character)),
            Occupied(entry) => entry.into_mut(),
        }
    }
}
<commit_msg>Fix issue with whitespace characters<commit_after>use std::path::Path;
use std::collections::HashMap;
use graphics::character::{ CharacterCache, Character };
use graphics::types::FontSize;
use glium::Texture2d;
use glium::backend::Facade;
use image::{ Rgba, ImageBuffer };
use freetype::{ self, Face };
use ::back_end::DrawTexture;

fn load_character<F: Facade>(face: &Face, facade: &F, font_size: FontSize,
                             character: char)
    -> Character<DrawTexture>
{
    face.set_pixel_sizes(0, font_size).unwrap();
    face.load_char(character as usize, freetype::face::DEFAULT).unwrap();
    let glyph = face.glyph().get_glyph().unwrap();
    let bitmap_glyph = glyph.to_bitmap(freetype::render_mode::RenderMode::Normal, None).unwrap();
    let bitmap = bitmap_glyph.bitmap();
    let texture =
        if bitmap.width() != 0 { Texture2d::new(
            facade,
            ImageBuffer::<Rgba<u8>, _>::from_raw(
                bitmap.width() as u32, bitmap.rows() as u32,
                bitmap.buffer().iter()
                    .flat_map(|&pix| vec![255, 255, 255, pix].into_iter())
                    .collect::<Vec<_>>()
            ).expect("failed to create glyph texture")
        ) }
        else { Texture2d::empty(facade, 1, 1) };
    let glyph_size = glyph.advance();
    Character {
        offset: [
            bitmap_glyph.left() as f64,
            bitmap_glyph.top() as f64
        ],
        size: [
            (glyph_size.x >> 16) as f64,
            (glyph_size.y >> 16) as f64
        ],
        texture: DrawTexture::new(texture),
    }
}

pub struct GlyphCache<F> {
    face: Face<'static>,
    data: HashMap<FontSize, HashMap<char, Character<DrawTexture>>>,
    facade: F,
}

impl<F> GlyphCache<F> {
     /// Constructor for a GlyphCache.
    pub fn new(font: &Path, facade: F) -> Result<Self, freetype::error::Error> {
        let freetype = try!(freetype::Library::init());
        let face = try!(freetype.new_face(font, 0));
        Ok(GlyphCache {
            face: face,
            data: HashMap::new(),
            facade: facade,
        })
    }
}

impl<F: Facade> CharacterCache for GlyphCache<F> {
    type Texture = DrawTexture;

    fn character<'a>(&'a mut self, font_size: FontSize, character: char)
        -> &'a Character<DrawTexture>
    {
        use std::collections::hash_map::Entry::{Vacant, Occupied};
        let size_cache: &'a mut HashMap<char, _> = match self.data.entry(font_size) {
            Vacant(entry) => entry.insert(HashMap::new()),
            Occupied(entry) => entry.into_mut(),
        };
        match size_cache.entry(character) {
            Vacant(entry) => entry.insert(load_character(&self.face, &self.facade, font_size, character)),
            Occupied(entry) => entry.into_mut(),
        }
    }
}
<|endoftext|>


<commit_before>use std::marker;
use std::collections::HashMap;

use solicit::http::client::RequestStream;
use solicit::http::client::ClientSession;
use solicit::http::session::Client;
use solicit::http::session::SessionState;
use solicit::http::session::DefaultSessionState;
use solicit::http::session::DefaultStream;
use solicit::http::session::StreamState;
use solicit::http::session::StreamDataError;
use solicit::http::session::StreamDataChunk;
use solicit::http::session::Stream as solicit_Stream;
use solicit::http::session::StreamIter;
use solicit::http::connection::HttpConnection;
use solicit::http::connection::SendStatus;
use solicit::http::connection::EndStream;
use solicit::http::connection::DataChunk;
use solicit::http::priority::SimplePrioritizer;
use solicit::http::frame::RawFrame;
use solicit::http::StreamId;
use solicit::http::HttpScheme;
use solicit::http::HttpError;
use solicit::http::Header;
use solicit::http::StaticHeader;

use futures;
use futures::Future;
use futures::stream::Stream;

use tokio_core;
use tokio_core::TcpStream;
use tokio_core::io as tokio_io;
use tokio_core::io::TaskIo;
use tokio_core::io::TaskIoRead;
use tokio_core::io::TaskIoWrite;
use tokio_core::LoopHandle;

use error::*;
use futures_misc::*;

use solicit_async::*;

struct GrpcHttp2ClientStream2 {
    state: StreamState,
}

impl solicit_Stream for GrpcHttp2ClientStream2 {
    fn new_data_chunk(&mut self, data: &[u8]) {
    }

    fn set_headers<'n, 'v>(&mut self, headers: Vec<Header<'n, 'v>>) {
    }

    fn set_state(&mut self, state: StreamState) {
        self.state = state;
    }

    fn get_data_chunk(&mut self, buf: &mut [u8]) -> Result<StreamDataChunk, StreamDataError> {
        unimplemented!()
    }

    fn state(&self) -> StreamState {
        self.state
    }
}

struct Inner<H : ResponseHandler> {
    host: String,
    conn: HttpConnection,
    streams: HashMap<StreamId, GrpcHttp2ClientStream2>,
    next_stream_id: StreamId,
    _phantom_data: marker::PhantomData<H>,
}

impl<H : ResponseHandler> SessionState for Inner<H> {
    type Stream = GrpcHttp2ClientStream2;

    fn insert_outgoing(&mut self, stream: Self::Stream) -> StreamId {
        let id = self.next_stream_id;
        self.streams.insert(id, stream);
        self.next_stream_id += 2;
        id
    }

    fn insert_incoming(&mut self, stream_id: StreamId, stream: Self::Stream) -> Result<(), ()> {
        // TODO: assert parity
        // TODO: Assert that the stream IDs are monotonically increasing!
        self.streams.insert(stream_id, stream);
        Ok(())
    }

    #[inline]
    fn get_stream_ref(&self, stream_id: StreamId) -> Option<&Self::Stream> {
        self.streams.get(&stream_id)
    }

    #[inline]
    fn get_stream_mut(&mut self, stream_id: StreamId) -> Option<&mut Self::Stream> {
        self.streams.get_mut(&stream_id)
    }

    #[inline]
    fn remove_stream(&mut self, stream_id: StreamId) -> Option<Self::Stream> {
        self.streams.remove(&stream_id)
    }

    #[inline]
    fn iter(&mut self) -> StreamIter<GrpcHttp2ClientStream2> {
        // https://github.com/mlalic/solicit/pull/34
        unimplemented!()
    }

    /// Number of currently active streams
    #[inline]
    fn len(&self) -> usize {
        self.streams.len()
    }
}

struct HttpConnectionAsync<H : ResponseHandler> {
    inner: TaskRcMut<Inner<H>>,
    call_tx: tokio_core::Sender<ToWriteMessage<H>>,
}

trait ResponseHandler : Send + 'static {
    fn headers(self) -> HttpFuture<Self>;
    fn body_chunk(self) -> HttpFuture<Self>;
    fn end(self) -> HttpFuture<()>;
}

struct StartRequestMessage<H : ResponseHandler> {
    headers: Vec<()>,
    body: HttpStream<Vec<u8>>,
    response_handler: H,
}

struct ReadToWriteMessage {
    buf: Vec<u8>,
}

enum ToWriteMessage<H : ResponseHandler> {
    Start(StartRequestMessage<H>),
    FromRead(ReadToWriteMessage),
}

struct WriteLoop<H : ResponseHandler> {
    write: TaskIoWrite<TcpStream>,
    inner: TaskRcMut<Inner<H>>,
}

impl<H : ResponseHandler> WriteLoop<H> {
    fn process_from_read(self, message: ReadToWriteMessage) -> HttpFuture<Self> {
        let WriteLoop { write, inner } = self;

        tokio_io::write_all(write, message.buf)
            .map(move |(write, _)| WriteLoop { write: write, inner: inner })
            .map_err(HttpError::from)
            .boxed()
    }

    fn process_start(self, start: StartRequestMessage<H>) -> HttpFuture<Self> {
        futures::finished(self).boxed()
    }

    fn process_message(self, message: ToWriteMessage<H>) -> HttpFuture<Self> {
        match message {
            ToWriteMessage::Start(start) => self.process_start(start),
            ToWriteMessage::FromRead(from_read) => self.process_from_read(from_read),
        }
    }

    fn run(self, requests: tokio_core::Receiver<ToWriteMessage<H>>) -> HttpFuture<()> {
        let requests = requests.map_err(HttpError::from);
        requests
            .fold(self, move |wl, message: ToWriteMessage<H>| {
                wl.process_message(message)
            })
            .map(|_| ())
            .boxed()
    }
}

struct ReadLoop<H : ResponseHandler> {
    read: TaskIoRead<TcpStream>,
    inner: TaskRcMut<Inner<H>>,
}

impl<H : ResponseHandler> ReadLoop<H> {
    fn recv_raw_frame(self) -> HttpFuture<(Self, RawFrame<'static>)> {
        let ReadLoop { read, inner } = self;
        recv_raw_frame(read)
            .map(|(read, frame)| (ReadLoop { read: read, inner: inner}, frame))
            .map_err(HttpError::from)
            .boxed()
    }

    fn read_process_frame(self) -> HttpFuture<Self> {
        self.recv_raw_frame()
            .map(|(rl, frame)| rl)
            .boxed()
    }

    fn run(self) -> HttpFuture<()> {
        let stream = stream_repeat(());

        let future = stream.fold(self, |rl, _| {
            rl.read_process_frame()
        });

        future.map(|_| ()).boxed()
    }
}

impl<H : ResponseHandler> HttpConnectionAsync<H> {
    fn new(lh: LoopHandle, conn: TcpStream) -> HttpFuture<(Self, HttpFuture<()>)> {
        let (call_tx, call_rx) = lh.channel();

        let handshake = client_handshake(conn);
        handshake.and_then(move |conn| {
            let (read, write) = TaskIo::new(conn).split();

            let inner = TaskRcMut::new(Inner {
                host: "localhost".to_owned(), // TODO
                conn: HttpConnection::new(HttpScheme::Http),
                next_stream_id: 1,
                streams: HashMap::new(),
                _phantom_data: marker::PhantomData,
            });

            let write_inner = inner.clone();
            let run_write = call_rx.map_err(HttpError::from).and_then(move |call_rx| WriteLoop { write: write, inner: write_inner }.run(call_rx));
            let run_read = ReadLoop { read: read, inner: inner.clone() }.run();

            let c = HttpConnectionAsync {
                inner: inner,
                call_tx: call_tx,
            };
            Ok((c, run_write.join(run_read).map(|_| ()).boxed()))
        }).boxed()
    }

    fn start_request(
        &self,
        headers: Vec<()>,
        body: HttpStream<Vec<u8>>,
        response_handler: H)
            -> HttpFuture<()>
    {
        self.call_tx.send(ToWriteMessage::Start(StartRequestMessage {
            headers: headers,
            body: body,
            response_handler: response_handler,
        }));
        futures::finished(()).boxed()
    }
}
<commit_msg>work on http client<commit_after>use std::marker;
use std::collections::HashMap;

use solicit::http::client::RequestStream;
use solicit::http::client::ClientSession;
use solicit::http::session::Client;
use solicit::http::session::SessionState;
use solicit::http::session::DefaultSessionState;
use solicit::http::session::DefaultStream;
use solicit::http::session::StreamState;
use solicit::http::session::StreamDataError;
use solicit::http::session::StreamDataChunk;
use solicit::http::session::Stream as solicit_Stream;
use solicit::http::session::StreamIter;
use solicit::http::connection::HttpConnection;
use solicit::http::connection::SendStatus;
use solicit::http::connection::EndStream;
use solicit::http::connection::DataChunk;
use solicit::http::priority::SimplePrioritizer;
use solicit::http::frame::RawFrame;
use solicit::http::StreamId;
use solicit::http::HttpScheme;
use solicit::http::HttpError;
use solicit::http::Header;
use solicit::http::StaticHeader;

use futures;
use futures::Future;
use futures::stream::Stream;

use tokio_core;
use tokio_core::TcpStream;
use tokio_core::io as tokio_io;
use tokio_core::io::TaskIo;
use tokio_core::io::TaskIoRead;
use tokio_core::io::TaskIoWrite;
use tokio_core::LoopHandle;

use error::*;
use futures_misc::*;

use solicit_async::*;
use solicit_misc::*;

struct GrpcHttp2ClientStream2 {
    state: StreamState,
}

impl solicit_Stream for GrpcHttp2ClientStream2 {
    fn new_data_chunk(&mut self, data: &[u8]) {
    }

    fn set_headers<'n, 'v>(&mut self, headers: Vec<Header<'n, 'v>>) {
    }

    fn set_state(&mut self, state: StreamState) {
        self.state = state;
    }

    fn get_data_chunk(&mut self, buf: &mut [u8]) -> Result<StreamDataChunk, StreamDataError> {
        unimplemented!()
    }

    fn state(&self) -> StreamState {
        self.state
    }
}

struct Inner<H : ResponseHandler> {
    host: String,
    conn: HttpConnection,
    streams: HashMap<StreamId, GrpcHttp2ClientStream2>,
    next_stream_id: StreamId,
    _phantom_data: marker::PhantomData<H>,
}

impl<H : ResponseHandler> SessionState for Inner<H> {
    type Stream = GrpcHttp2ClientStream2;

    fn insert_outgoing(&mut self, stream: Self::Stream) -> StreamId {
        let id = self.next_stream_id;
        self.streams.insert(id, stream);
        self.next_stream_id += 2;
        id
    }

    fn insert_incoming(&mut self, stream_id: StreamId, stream: Self::Stream) -> Result<(), ()> {
        // TODO: assert parity
        // TODO: Assert that the stream IDs are monotonically increasing!
        self.streams.insert(stream_id, stream);
        Ok(())
    }

    #[inline]
    fn get_stream_ref(&self, stream_id: StreamId) -> Option<&Self::Stream> {
        self.streams.get(&stream_id)
    }

    #[inline]
    fn get_stream_mut(&mut self, stream_id: StreamId) -> Option<&mut Self::Stream> {
        self.streams.get_mut(&stream_id)
    }

    #[inline]
    fn remove_stream(&mut self, stream_id: StreamId) -> Option<Self::Stream> {
        self.streams.remove(&stream_id)
    }

    #[inline]
    fn iter(&mut self) -> StreamIter<GrpcHttp2ClientStream2> {
        // https://github.com/mlalic/solicit/pull/34
        unimplemented!()
    }

    /// Number of currently active streams
    #[inline]
    fn len(&self) -> usize {
        self.streams.len()
    }
}

struct HttpConnectionAsync<H : ResponseHandler> {
    inner: TaskRcMut<Inner<H>>,
    call_tx: tokio_core::Sender<ToWriteMessage<H>>,
}

trait ResponseHandler : Send + 'static {
    fn headers(self) -> HttpFuture<Self>;
    fn body_chunk(self) -> HttpFuture<Self>;
    fn end(self) -> HttpFuture<()>;
}

struct StartRequestMessage<H : ResponseHandler> {
    headers: Vec<StaticHeader>,
    body: HttpStream<Vec<u8>>,
    response_handler: H,
}

struct ReadToWriteMessage {
    buf: Vec<u8>,
}

enum ToWriteMessage<H : ResponseHandler> {
    Start(StartRequestMessage<H>),
    FromRead(ReadToWriteMessage),
}

struct WriteLoop<H : ResponseHandler> {
    write: TaskIoWrite<TcpStream>,
    inner: TaskRcMut<Inner<H>>,
}

impl<H : ResponseHandler> WriteLoop<H> {
    fn write_all(self, buf: Vec<u8>) -> HttpFuture<Self> {
        let WriteLoop { write, inner } = self;

        tokio_io::write_all(write, buf)
            .map(move |(write, _)| WriteLoop { write: write, inner: inner })
            .map_err(HttpError::from)
            .boxed()
    }

    fn process_from_read(self, message: ReadToWriteMessage) -> HttpFuture<Self> {
        self.write_all(message.buf)
    }

    fn process_start(self, start: StartRequestMessage<H>) -> HttpFuture<Self> {
        let buf = self.inner.with(|inner: &mut Inner<H>| {

            let stream = GrpcHttp2ClientStream2 {
                state: StreamState::Open,
            };
            let stream_id = inner.insert_outgoing(stream);

            let send_buf = {

                let headers = vec![
                    // TODO
                    Header::new(b":method", b"POST"),
                    Header::new(b":path", b"/aaa"),
                    Header::new(b":authority", b"localhost"),
                    Header::new(b":scheme", inner.conn.scheme.as_bytes().to_vec()),
                ];

                let mut send_buf = VecSendFrame(Vec::new());

                inner.conn.sender(&mut send_buf).send_headers(headers, stream_id, EndStream::No).unwrap();

                send_buf.0
            };

            send_buf
        });

        self.write_all(buf) // TODO: more
    }

    fn process_message(self, message: ToWriteMessage<H>) -> HttpFuture<Self> {
        match message {
            ToWriteMessage::Start(start) => self.process_start(start),
            ToWriteMessage::FromRead(from_read) => self.process_from_read(from_read),
        }
    }

    fn run(self, requests: tokio_core::Receiver<ToWriteMessage<H>>) -> HttpFuture<()> {
        let requests = requests.map_err(HttpError::from);
        requests
            .fold(self, move |wl, message: ToWriteMessage<H>| {
                wl.process_message(message)
            })
            .map(|_| ())
            .boxed()
    }
}

struct ReadLoop<H : ResponseHandler> {
    read: TaskIoRead<TcpStream>,
    inner: TaskRcMut<Inner<H>>,
}

impl<H : ResponseHandler> ReadLoop<H> {
    fn recv_raw_frame(self) -> HttpFuture<(Self, RawFrame<'static>)> {
        let ReadLoop { read, inner } = self;
        recv_raw_frame(read)
            .map(|(read, frame)| (ReadLoop { read: read, inner: inner}, frame))
            .map_err(HttpError::from)
            .boxed()
    }

    fn read_process_frame(self) -> HttpFuture<Self> {
        self.recv_raw_frame()
            .map(|(rl, frame)| rl)
            .boxed()
    }

    fn run(self) -> HttpFuture<()> {
        let stream = stream_repeat(());

        let future = stream.fold(self, |rl, _| {
            rl.read_process_frame()
        });

        future.map(|_| ()).boxed()
    }
}

impl<H : ResponseHandler> HttpConnectionAsync<H> {
    fn new(lh: LoopHandle, conn: TcpStream) -> HttpFuture<(Self, HttpFuture<()>)> {
        let (call_tx, call_rx) = lh.channel();

        let handshake = client_handshake(conn);
        handshake.and_then(move |conn| {
            let (read, write) = TaskIo::new(conn).split();

            let inner = TaskRcMut::new(Inner {
                host: "localhost".to_owned(), // TODO
                conn: HttpConnection::new(HttpScheme::Http),
                next_stream_id: 1,
                streams: HashMap::new(),
                _phantom_data: marker::PhantomData,
            });

            let write_inner = inner.clone();
            let run_write = call_rx.map_err(HttpError::from).and_then(move |call_rx| WriteLoop { write: write, inner: write_inner }.run(call_rx));
            let run_read = ReadLoop { read: read, inner: inner.clone() }.run();

            let c = HttpConnectionAsync {
                inner: inner,
                call_tx: call_tx,
            };
            Ok((c, run_write.join(run_read).map(|_| ()).boxed()))
        }).boxed()
    }

    fn start_request(
        &self,
        headers: Vec<StaticHeader>,
        body: HttpStream<Vec<u8>>,
        response_handler: H)
            -> HttpFuture<()>
    {
        self.call_tx.send(ToWriteMessage::Start(StartRequestMessage {
            headers: headers,
            body: body,
            response_handler: response_handler,
        }));
        futures::finished(()).boxed()
    }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use common::Sexp::{self, Atom};
use common::Atom::{Number, Symbol, Nil};

#[derive(Debug, PartialEq)]
enum EvalError{
    EvalError
}

fn eval(s: Sexp, e: &HashMap<String, Sexp>) -> Result<Sexp, EvalError> {
    match s {
        Atom(Number(_)) | Atom(Nil) => {
            Ok(s)
        },
        Atom(Symbol(ref name)) => {
            if let Some(s) = e.get(name) {
                Ok(s.clone())
            } else {
                Err(EvalError::EvalError)
            }
        }
        _ => {
            Err(EvalError::EvalError)
        }
    }
}

#[cfg(test)]
mod tests {
    use std::collections::HashMap;
    use common::Atom::{Number, Symbol, Nil};
    use common::Sexp::Atom;
    use super::EvalError::EvalError;
    use super::eval;

    #[test]
    fn test_eval_atom_number() {
        let number = 10f64;
        let expected_result = Atom(Number(number));
        let actual_result = eval(Atom(Number(number)), &HashMap::new());
        assert_eq!(expected_result, actual_result.ok().unwrap());
    }

    #[test]
    fn test_eval_atom_symbol_to_number() {
        let num = 10f64;
        let mut env = HashMap::new();
        env.insert("a".to_string(), Atom(Number(num)));
        let expected_result = Atom(Number(num));
        let actual_result = eval(Atom(Symbol("a".to_string())), &env);
        assert_eq!(expected_result, actual_result.ok().unwrap());
    }

    #[test]
    fn test_eval_atom_symbol_to_nil() {
        let mut env = HashMap::new();
        env.insert("a".to_string(), Atom(Nil));
        let expected_result = Atom(Nil);
        let actual_result = eval(Atom(Symbol("a".to_string())), &env);
        assert_eq!(expected_result, actual_result.ok().unwrap());
    }

    #[test]
    fn test_eval_atom_symbol_to_non_value() {
        let env = HashMap::new();
        let expected_result = EvalError;
        let actual_result = eval(Atom(Symbol("a".to_string())), &env);
        assert_eq!(expected_result, actual_result.err().unwrap());
    }

    #[test]
    fn test_eval_atom_nil_to_itself() {
        let env = HashMap::new();
        let expected_result = Atom(Nil);
        let actual_result = eval(Atom(Nil), &env);
        assert_eq!(expected_result, actual_result.ok().unwrap());
    }
}
<commit_msg>Eval empty list to nil<commit_after>use std::collections::HashMap;
use common::Atom::{Number, Symbol, Nil};
use common::Sexp::{self, Atom, List};

#[derive(Debug, PartialEq)]
enum EvalError{
    EvalError
}

fn eval(s: Sexp, e: &HashMap<String, Sexp>) -> Result<Sexp, EvalError> {
    match s {
        Atom(Number(_)) | Atom(Nil) => {
            Ok(s)
        },
        Atom(Symbol(ref name)) => {
            if let Some(s) = e.get(name) {
                Ok(s.clone())
            } else {
                Err(EvalError::EvalError)
            }
        },
        List(ref l) if l.is_empty() => {
            Ok(Atom(Nil))
        }
        _ => {
            Err(EvalError::EvalError)
        }
    }
}

#[cfg(test)]
mod tests {
    use std::collections::HashMap;
    use common::Atom::{Number, Symbol, Nil};
    use common::Sexp::{Atom, List};
    use super::EvalError::EvalError;
    use super::eval;

    #[test]
    fn test_eval_atom_number_to_itself() {
        let number = 10f64;
        let expected_result = Atom(Number(number));
        let actual_result = eval(Atom(Number(number)), &HashMap::new());
        assert_eq!(expected_result, actual_result.ok().unwrap());
    }

    #[test]
    fn test_eval_atom_symbol_to_number() {
        let num = 10f64;
        let mut env = HashMap::new();
        env.insert("a".to_string(), Atom(Number(num)));
        let expected_result = Atom(Number(num));
        let actual_result = eval(Atom(Symbol("a".to_string())), &env);
        assert_eq!(expected_result, actual_result.ok().unwrap());
    }

    #[test]
    fn test_eval_atom_symbol_to_nil() {
        let mut env = HashMap::new();
        env.insert("a".to_string(), Atom(Nil));
        let expected_result = Atom(Nil);
        let actual_result = eval(Atom(Symbol("a".to_string())), &env);
        assert_eq!(expected_result, actual_result.ok().unwrap());
    }

    #[test]
    fn test_eval_atom_symbol_to_non_value() {
        let env = HashMap::new();
        let expected_result = EvalError;
        let actual_result = eval(Atom(Symbol("a".to_string())), &env);
        assert_eq!(expected_result, actual_result.err().unwrap());
    }

    #[test]
    fn test_eval_atom_nil_to_itself() {
        let env = HashMap::new();
        let expected_result = Atom(Nil);
        let actual_result = eval(Atom(Nil), &env);
        assert_eq!(expected_result, actual_result.ok().unwrap());
    }

    #[test]
    fn test_eval_empty_list_to_nil() {
        let env = HashMap::new();
        let expected_result = Atom(Nil);
        let actual_result = eval(List(vec![]), &env);
        assert_eq!(expected_result, actual_result.ok().unwrap());
    }
}
<|endoftext|>


<commit_before>use ::util::string::StringUtil;

fn read_message_code(string: &str) -> Option<String> {
    // TODO: Replace with slice pattern syntax when it becomes stable
    let mut words = string.splitn(3, ' ');

    if let Some("PING") = words.next() {
        Some("PING".to_string())
    } else if let Some(code) = words.next() {
        Some(code.to_string())
    } else {
        None
    }
}

pub type Code   = String;
pub type Reason = String;

#[derive(Debug)]
pub enum MessageError {
    NoCode,
    UnknownCode(Code),
    Parse(Reason),
}

impl From<String> for MessageError {
    fn from(err: String) -> MessageError {
        MessageError::Parse(err)
    }
}

pub type Reply    = String;
pub type Contents = String;

#[derive(Debug)]
pub enum Message {
    Message { nick: String, channel: String, msg: String},
    Join    { nick: String, channel: String },
    Leave   { nick: String, channel: String },
    Ping(Reply),
    Notice(Contents),
}

impl Message {
    pub fn parse(string: &str) -> Result<Message, MessageError> {
        let get_nick = || string.between(":", "!");
        let code = read_message_code(string).ok_or(MessageError::NoCode)?;

        use self::Message::*;

        match code.as_str() {
            "PRIVMSG" => Ok(
                Message {
                    nick:    get_nick()?,
                    channel: string.between("#", " :")?,
                    msg:     string.after(" :")?,
                }
            ),
            "JOIN" => Ok(
                Join {
                    nick:    get_nick()?,
                    channel: string.after(" #")?,
                }
            ),
            "PART" => Ok(
                Leave {
                    nick:    get_nick()?,
                    channel: string.after(" #")?,
                }
            ),
            "PING"   => Ok(Ping(string.after("PING ")?)),
            "NOTICE" => Ok(Notice(string.after(" :")?)),
            _        => Err(MessageError::UnknownCode(code)),
        }
    }
}<commit_msg>Add handling for initial channel viewers Rename Join and Leave message types<commit_after>use ::util::string::StringUtil;

fn read_message_code(string: &str) -> Option<String> {
    // TODO: Replace with slice pattern syntax when it becomes stable
    let mut words = string.splitn(3, ' ');

    if let Some("PING") = words.next() {
        Some("PING".to_string())
    } else if let Some(code) = words.next() {
        Some(code.to_string())
    } else {
        None
    }
}

pub type Code   = String;
pub type Reason = String;

#[derive(Debug)]
pub enum MessageError {
    NoCode,
    UnknownCode(Code),
    Parse(Reason),
}

impl From<String> for MessageError {
    fn from(err: String) -> MessageError {
        MessageError::Parse(err)
    }
}

pub type Reply    = String;
pub type Contents = String;
pub type Nick     = String;

#[derive(Debug)]
pub enum Message {
    Message { nick: String, channel: String, msg: String},
    Joined  { nick: String, channel: String },
    Left    { nick: String, channel: String },
    Viewers { channel: String, viewers: Vec<Nick> },
    Ping(Reply),
    Notice(Contents),
}

impl Message {
    pub fn parse(string: &str) -> Result<Message, MessageError> {
        let get_nick = || string.between(":", "!");
        let code = read_message_code(string).ok_or(MessageError::NoCode)?;

        use self::Message::*;

        match code.as_str() {
            "PRIVMSG" => Ok(
                Message {
                    nick:    get_nick()?,
                    channel: string.between("#", " :")?,
                    msg:     string.after(" :")?,
                }
            ),
            "JOIN" => Ok(
                Joined {
                    nick:    get_nick()?,
                    channel: string.after(" #")?,
                }
            ),
            "PART" => Ok(
                Left {
                    nick:    get_nick()?,
                    channel: string.after(" #")?,
                }
            ),
            // List of initial channel viewers
            "353" => Ok(
                Viewers {
                    channel: string.between("#", " :")?,
                    viewers:
                        string
                        .after(" :")?
                        .split(' ')
                        .map(String::from)
                        .collect(),
                }
            ),
            "PING"   => Ok(Ping(string.after("PING ")?)),
            "NOTICE" => Ok(Notice(string.after(" :")?)),
            _        => Err(MessageError::UnknownCode(code)),
        }
    }
}<|endoftext|>