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

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

use crate::document::AcquiredNodes;
use crate::drawing_ctx::DrawingCtx;
use crate::element::{Draw, Element, ElementResult, SetAttributes};
use crate::error::*;
use crate::node::{Node, NodeBorrow};
use crate::parsers::{NumberList, NumberListLength, Parse, ParseValue};
use crate::surface_utils::{
    iterators::Pixels, shared_surface::ExclusiveImageSurface, ImageSurfaceDataExt, Pixel,
};
use crate::util::clamp;
use crate::xml::Attributes;

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

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

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

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

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

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

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

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

#[derive(Debug, PartialEq)]
struct Functions {
    r: FeFuncR,
    g: FeFuncG,
    b: FeFuncB,
    a: FeFuncA,
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        impl SetAttributes for $func_name {
            #[inline]
            fn set_attributes(&mut self, attrs: &Attributes) -> ElementResult {
                for (attr, value) in attrs.iter() {
                    match attr.expanded() {
                        expanded_name!("", "type") => self.function_type = attr.parse(value)?,
                        expanded_name!("", "tableValues") => {
                            let NumberList(v) =
                                NumberList::parse_str(value, NumberListLength::Unbounded)
                                    .attribute(attr)?;
                            self.table_values = v;
                        }
                        expanded_name!("", "slope") => self.slope = attr.parse(value)?,
                        expanded_name!("", "intercept") => self.intercept = attr.parse(value)?,
                        expanded_name!("", "amplitude") => self.amplitude = attr.parse(value)?,
                        expanded_name!("", "exponent") => self.exponent = attr.parse(value)?,
                        expanded_name!("", "offset") => self.offset = attr.parse(value)?,

                        _ => (),
                    }
                }

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

                Ok(())
            }
        }

        impl Draw for $func_name {}
    };
}

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

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

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

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

macro_rules! func_or_default {
    ($func_node:ident, $func_type:ident) => {
        match $func_node {
            Some(ref f) => match *f.borrow_element() {
                Element::$func_type(ref e) => e.element_impl.clone(),
                _ => unreachable!(),
            },
            _ => $func_type::default(),
        };
    };
}

macro_rules! get_func_x_node {
    ($func_node:ident, $func_type:ident, $channel:expr) => {
        $func_node
            .children()
            .rev()
            .filter(|c| c.is_element())
            .find(|c| match *c.borrow_element() {
                Element::$func_type(ref f) => f.channel() == $channel,
                _ => false,
            })
    };
}

impl FilterRender for FeComponentTransfer {
    fn render(
        &self,
        node: &Node,
        ctx: &FilterContext,
        acquired_nodes: &mut AcquiredNodes<'_>,
        draw_ctx: &mut DrawingCtx,
    ) -> Result<FilterResult, FilterError> {
        let functions = get_parameters(node)?;

        let input = self.base.get_input(ctx, acquired_nodes, draw_ctx)?;
        let bounds = self
            .base
            .get_bounds(ctx)?
            .add_input(&input)
            .into_irect(ctx, draw_ctx);

        // Create the output surface.
        let mut surface = ExclusiveImageSurface::new(
            ctx.source_graphic().width(),
            ctx.source_graphic().height(),
            input.surface().surface_type(),
        )?;

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

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

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

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

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

        let compute_r = compute_func::<FeFuncR>(&functions.r);
        let compute_g = compute_func::<FeFuncG>(&functions.g);
        let compute_b = compute_func::<FeFuncB>(&functions.b);

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

        // Do the actual processing.
        surface.modify(&mut |data, stride| {
            for (x, y, pixel) in Pixels::within(input.surface(), bounds) {
                let alpha = f64::from(pixel.a) / 255f64;
                let new_alpha = compute_a(alpha);

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

                data.set_pixel(stride, output_pixel, x, y);
            }
        });

        Ok(FilterResult {
            name: self.base.result.clone(),
            output: FilterOutput {
                surface: surface.share()?,
                bounds,
            },
        })
    }
}

impl FilterEffect for FeComponentTransfer {
    fn is_affected_by_color_interpolation_filters(&self) -> bool {
        true
    }
}

/// Takes a feComponentTransfer and walks its children to produce the feFuncX arguments.
fn get_parameters(node: &Node) -> Result<Functions, FilterError> {
    let func_r_node = get_func_x_node!(node, FeFuncR, Channel::R);
    let func_g_node = get_func_x_node!(node, FeFuncG, Channel::G);
    let func_b_node = get_func_x_node!(node, FeFuncB, Channel::B);
    let func_a_node = get_func_x_node!(node, FeFuncA, Channel::A);

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

    let r = func_or_default!(func_r_node, FeFuncR);
    let g = func_or_default!(func_g_node, FeFuncG);
    let b = func_or_default!(func_b_node, FeFuncB);
    let a = func_or_default!(func_a_node, FeFuncA);

    Ok(Functions { r, g, b, a })
}

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

    #[test]
    fn extracts_functions() {
        let document = Document::load_from_bytes(
            br#"<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg">
  <filter id="filter">
    <feComponentTransfer id="component_transfer">
      <!-- no feFuncR so it should get the defaults -->

      <feFuncG type="table" tableValues="0.0 1.0 2.0"/>

      <feFuncB type="table"/>
      <!-- duplicate this to test that last-one-wins -->
      <feFuncB type="discrete" tableValues="0.0, 1.0" slope="1.0" intercept="2.0" amplitude="3.0" exponent="4.0" offset="5.0"/>

      <!-- no feFuncA so it should get the defaults -->
    </feComponentTransfer>
  </filter>
</svg>
"#
        );

        let component_transfer = document.lookup_internal_node("component_transfer").unwrap();
        let functions = get_parameters(&component_transfer).unwrap();

        assert_eq!(
            functions,
            Functions {
                r: FeFuncR::default(),

                g: FeFuncG {
                    function_type: FunctionType::Table,
                    table_values: vec![0.0, 1.0, 2.0],
                    ..FeFuncG::default()
                },

                b: FeFuncB {
                    function_type: FunctionType::Discrete,
                    table_values: vec![0.0, 1.0],
                    slope: 1.0,
                    intercept: 2.0,
                    amplitude: 3.0,
                    exponent: 4.0,
                    offset: 5.0,
                    ..FeFuncB::default()
                },

                a: FeFuncA::default(),
            }
        );
    }
}
<commit_msg>Remove unnecessary lifetime<commit_after>use std::cmp::min;

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

use crate::document::AcquiredNodes;
use crate::drawing_ctx::DrawingCtx;
use crate::element::{Draw, Element, ElementResult, SetAttributes};
use crate::error::*;
use crate::node::{Node, NodeBorrow};
use crate::parsers::{NumberList, NumberListLength, Parse, ParseValue};
use crate::surface_utils::{
    iterators::Pixels, shared_surface::ExclusiveImageSurface, ImageSurfaceDataExt, Pixel,
};
use crate::util::clamp;
use crate::xml::Attributes;

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

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

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

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

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

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

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

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

#[derive(Debug, PartialEq)]
struct Functions {
    r: FeFuncR,
    g: FeFuncG,
    b: FeFuncB,
    a: FeFuncA,
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        impl SetAttributes for $func_name {
            #[inline]
            fn set_attributes(&mut self, attrs: &Attributes) -> ElementResult {
                for (attr, value) in attrs.iter() {
                    match attr.expanded() {
                        expanded_name!("", "type") => self.function_type = attr.parse(value)?,
                        expanded_name!("", "tableValues") => {
                            let NumberList(v) =
                                NumberList::parse_str(value, NumberListLength::Unbounded)
                                    .attribute(attr)?;
                            self.table_values = v;
                        }
                        expanded_name!("", "slope") => self.slope = attr.parse(value)?,
                        expanded_name!("", "intercept") => self.intercept = attr.parse(value)?,
                        expanded_name!("", "amplitude") => self.amplitude = attr.parse(value)?,
                        expanded_name!("", "exponent") => self.exponent = attr.parse(value)?,
                        expanded_name!("", "offset") => self.offset = attr.parse(value)?,

                        _ => (),
                    }
                }

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

                Ok(())
            }
        }

        impl Draw for $func_name {}
    };
}

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

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

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

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

macro_rules! func_or_default {
    ($func_node:ident, $func_type:ident) => {
        match $func_node {
            Some(ref f) => match *f.borrow_element() {
                Element::$func_type(ref e) => e.element_impl.clone(),
                _ => unreachable!(),
            },
            _ => $func_type::default(),
        };
    };
}

macro_rules! get_func_x_node {
    ($func_node:ident, $func_type:ident, $channel:expr) => {
        $func_node
            .children()
            .rev()
            .filter(|c| c.is_element())
            .find(|c| match *c.borrow_element() {
                Element::$func_type(ref f) => f.channel() == $channel,
                _ => false,
            })
    };
}

impl FilterRender for FeComponentTransfer {
    fn render(
        &self,
        node: &Node,
        ctx: &FilterContext,
        acquired_nodes: &mut AcquiredNodes<'_>,
        draw_ctx: &mut DrawingCtx,
    ) -> Result<FilterResult, FilterError> {
        let functions = get_parameters(node)?;

        let input = self.base.get_input(ctx, acquired_nodes, draw_ctx)?;
        let bounds = self
            .base
            .get_bounds(ctx)?
            .add_input(&input)
            .into_irect(ctx, draw_ctx);

        // Create the output surface.
        let mut surface = ExclusiveImageSurface::new(
            ctx.source_graphic().width(),
            ctx.source_graphic().height(),
            input.surface().surface_type(),
        )?;

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

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

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

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

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

        let compute_r = compute_func::<FeFuncR>(&functions.r);
        let compute_g = compute_func::<FeFuncG>(&functions.g);
        let compute_b = compute_func::<FeFuncB>(&functions.b);

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

        // Do the actual processing.
        surface.modify(&mut |data, stride| {
            for (x, y, pixel) in Pixels::within(input.surface(), bounds) {
                let alpha = f64::from(pixel.a) / 255f64;
                let new_alpha = compute_a(alpha);

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

                data.set_pixel(stride, output_pixel, x, y);
            }
        });

        Ok(FilterResult {
            name: self.base.result.clone(),
            output: FilterOutput {
                surface: surface.share()?,
                bounds,
            },
        })
    }
}

impl FilterEffect for FeComponentTransfer {
    fn is_affected_by_color_interpolation_filters(&self) -> bool {
        true
    }
}

/// Takes a feComponentTransfer and walks its children to produce the feFuncX arguments.
fn get_parameters(node: &Node) -> Result<Functions, FilterError> {
    let func_r_node = get_func_x_node!(node, FeFuncR, Channel::R);
    let func_g_node = get_func_x_node!(node, FeFuncG, Channel::G);
    let func_b_node = get_func_x_node!(node, FeFuncB, Channel::B);
    let func_a_node = get_func_x_node!(node, FeFuncA, Channel::A);

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

    let r = func_or_default!(func_r_node, FeFuncR);
    let g = func_or_default!(func_g_node, FeFuncG);
    let b = func_or_default!(func_b_node, FeFuncB);
    let a = func_or_default!(func_a_node, FeFuncA);

    Ok(Functions { r, g, b, a })
}

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

    #[test]
    fn extracts_functions() {
        let document = Document::load_from_bytes(
            br#"<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg">
  <filter id="filter">
    <feComponentTransfer id="component_transfer">
      <!-- no feFuncR so it should get the defaults -->

      <feFuncG type="table" tableValues="0.0 1.0 2.0"/>

      <feFuncB type="table"/>
      <!-- duplicate this to test that last-one-wins -->
      <feFuncB type="discrete" tableValues="0.0, 1.0" slope="1.0" intercept="2.0" amplitude="3.0" exponent="4.0" offset="5.0"/>

      <!-- no feFuncA so it should get the defaults -->
    </feComponentTransfer>
  </filter>
</svg>
"#
        );

        let component_transfer = document.lookup_internal_node("component_transfer").unwrap();
        let functions = get_parameters(&component_transfer).unwrap();

        assert_eq!(
            functions,
            Functions {
                r: FeFuncR::default(),

                g: FeFuncG {
                    function_type: FunctionType::Table,
                    table_values: vec![0.0, 1.0, 2.0],
                    ..FeFuncG::default()
                },

                b: FeFuncB {
                    function_type: FunctionType::Discrete,
                    table_values: vec![0.0, 1.0],
                    slope: 1.0,
                    intercept: 2.0,
                    amplitude: 3.0,
                    exponent: 4.0,
                    offset: 5.0,
                    ..FeFuncB::default()
                },

                a: FeFuncA::default(),
            }
        );
    }
}
<|endoftext|>


<commit_before>use gfx_app::{Window, GameStatus};
use gfx_app::renderer::{DeviceRenderer, EncoderQueue};
use gfx_app::system::DrawSystem;
use gfx;
use std::time;
use std::sync::mpsc;
use terrain;
use specs;
use specs::{World};
use specs::DispatcherBuilder;
use graphics::{Dimensions, DeltaTime};
use gfx_app::controls::TilemapControls;
use gfx_app::mouse_controls::{MouseControlSystem, MouseInputState};
use graphics::camera::CameraControlSystem;
use character::controls::CharacterControlSystem;
use critter::{CharacterSprite, ZombieSprite};
use character;
use zombie;
use graphics;

pub fn run<W, D, F>(window: &mut W) -> GameStatus
  where W: Window<D, F>,
        D: gfx::Device + 'static,
        F: gfx::Factory<D::Resources>,
        D::CommandBuffer: Send {
  let (mut device_renderer, enc_queue) = DeviceRenderer::new(window.create_buffers(2));

  let mut w = specs::World::new();
  setup_world(&mut w, window.get_viewport_size());
  dispatch_loop(window, &mut device_renderer, &mut w, enc_queue)
}

fn setup_world(world: &mut World, viewport_size: (u32, u32)) {
  let view_matrix = Dimensions::get_view_matrix();
  world.register::<terrain::TerrainDrawable>();
  world.register::<graphics::camera::CameraInputState>();
  world.register::<character::CharacterDrawable>();
  world.register::<zombie::ZombieDrawable>();
  world.register::<CharacterSprite>();
  world.register::<ZombieSprite>();
  world.register::<character::controls::CharacterInputState>();
  world.register::<MouseInputState>();

  world.add_resource(terrain::tilemap::generate());
  world.add_resource(Dimensions::new(viewport_size.0, viewport_size.1));
  world.add_resource(graphics::camera::CameraInputState::new());
  world.add_resource(character::controls::CharacterInputState::new());
  world.add_resource(MouseInputState::new());
  world.add_resource(character::CharacterDrawable::new(view_matrix));
  world.add_resource(zombie::ZombieDrawable::new(view_matrix));
  world.add_resource(zombie::ZombieDrawable::new(view_matrix));
  world.add_resource(CharacterSprite::new());
  world.add_resource(ZombieSprite::new());
  world.add_resource(DeltaTime(0.0));
  world.create_entity()
    .with(terrain::TerrainDrawable::new(view_matrix))
    .with(character::CharacterDrawable::new(view_matrix))
    .with(zombie::ZombieDrawable::new(view_matrix))
    .with(CharacterSprite::new())
    .with(ZombieSprite::new())
    .with(graphics::camera::CameraInputState::new())
    .with(character::controls::CharacterInputState::new())
    .with(MouseInputState::new()).build();
}

fn dispatch_loop<W, D, F>(window: &mut W,
                          device_renderer: &mut DeviceRenderer<D>,
                          w: &mut World,
                          encoder_queue: EncoderQueue<D>) -> GameStatus
  where W: Window<D, F>,
        D: gfx::Device + 'static,
        F: gfx::Factory<D::Resources>,
        D::CommandBuffer: Send {

  let draw = {
    let rtv = window.get_render_target_view();
    let dsv = window.get_depth_stencil_view();
    DrawSystem::new(window.get_factory(), rtv, dsv, encoder_queue)
  };

  let (_, game_state) = mpsc::channel::<GameStatus>();

  let (terrain_system, terrain_control) = CameraControlSystem::new();
  let (character_system, character_control) = CharacterControlSystem::new();
  let (mouse_system, mouse_control) = MouseControlSystem::new();
  let controls = TilemapControls::new(terrain_control, character_control, mouse_control);

  let mut dispatcher = DispatcherBuilder::new()
    .add(draw, "drawing", &[])
    .add(terrain::PreDrawSystem::new(), "draw-prep-terrain", &["drawing"])
    .add(character::PreDrawSystem::new(), "draw-prep-character", &["drawing"])
    .add(zombie::PreDrawSystem::new(), "draw-prep-zombie", &["drawing"])
    .add(terrain_system, "terrain-system", &[])
    .add(character_system, "character-system", &[])
    .add(mouse_system, "mouse-system", &[])
    .build();

  window.set_controls(controls);

  let mut last_time = time::Instant::now();
  loop {
    let elapsed = last_time.elapsed();
    let delta = f64::from(elapsed.subsec_nanos()) / 1e9 + elapsed.as_secs() as f64;
    last_time = time::Instant::now();

    dispatcher.dispatch(&w.res);

    device_renderer.draw(window.get_device());
    w.maintain();

    let mut d = w.write_resource::<DeltaTime>();
    *d = DeltaTime(delta);
    window.swap_window();

    if let Some(quit_status) = window.poll_events() {
      return quit_status;
    }

    if let Ok(quit_status) = game_state.try_recv() {
      return quit_status;
    }
  }
}
<commit_msg>Reorder functions in game loop<commit_after>use gfx_app::{Window, GameStatus};
use gfx_app::renderer::{DeviceRenderer, EncoderQueue};
use gfx_app::system::DrawSystem;
use gfx;
use std::time;
use std::sync::mpsc;
use terrain;
use specs;
use specs::{World};
use specs::DispatcherBuilder;
use graphics::{Dimensions, DeltaTime};
use gfx_app::controls::TilemapControls;
use gfx_app::mouse_controls::{MouseControlSystem, MouseInputState};
use graphics::camera::CameraControlSystem;
use character::controls::CharacterControlSystem;
use critter::{CharacterSprite, ZombieSprite};
use character;
use zombie;
use graphics;

pub fn run<W, D, F>(window: &mut W) -> GameStatus
  where W: Window<D, F>,
        D: gfx::Device + 'static,
        F: gfx::Factory<D::Resources>,
        D::CommandBuffer: Send {
  let (mut device_renderer, enc_queue) = DeviceRenderer::new(window.create_buffers(2));

  let mut w = specs::World::new();
  setup_world(&mut w, window.get_viewport_size());
  dispatch_loop(window, &mut device_renderer, &mut w, enc_queue)
}

fn setup_world(world: &mut World, viewport_size: (u32, u32)) {
  let view_matrix = Dimensions::get_view_matrix();
  world.register::<terrain::TerrainDrawable>();
  world.register::<graphics::camera::CameraInputState>();
  world.register::<character::CharacterDrawable>();
  world.register::<zombie::ZombieDrawable>();
  world.register::<CharacterSprite>();
  world.register::<ZombieSprite>();
  world.register::<character::controls::CharacterInputState>();
  world.register::<MouseInputState>();

  world.add_resource(terrain::tilemap::generate());
  world.add_resource(Dimensions::new(viewport_size.0, viewport_size.1));
  world.add_resource(graphics::camera::CameraInputState::new());
  world.add_resource(character::controls::CharacterInputState::new());
  world.add_resource(MouseInputState::new());
  world.add_resource(character::CharacterDrawable::new(view_matrix));
  world.add_resource(zombie::ZombieDrawable::new(view_matrix));
  world.add_resource(zombie::ZombieDrawable::new(view_matrix));
  world.add_resource(CharacterSprite::new());
  world.add_resource(ZombieSprite::new());
  world.add_resource(DeltaTime(0.0));
  world.create_entity()
    .with(terrain::TerrainDrawable::new(view_matrix))
    .with(character::CharacterDrawable::new(view_matrix))
    .with(zombie::ZombieDrawable::new(view_matrix))
    .with(CharacterSprite::new())
    .with(ZombieSprite::new())
    .with(graphics::camera::CameraInputState::new())
    .with(character::controls::CharacterInputState::new())
    .with(MouseInputState::new()).build();
}

fn dispatch_loop<W, D, F>(window: &mut W,
                          device_renderer: &mut DeviceRenderer<D>,
                          w: &mut World,
                          encoder_queue: EncoderQueue<D>) -> GameStatus
  where W: Window<D, F>,
        D: gfx::Device + 'static,
        F: gfx::Factory<D::Resources>,
        D::CommandBuffer: Send {

  let draw = {
    let rtv = window.get_render_target_view();
    let dsv = window.get_depth_stencil_view();
    DrawSystem::new(window.get_factory(), rtv, dsv, encoder_queue)
  };

  let (_, game_state) = mpsc::channel::<GameStatus>();

  let (terrain_system, terrain_control) = CameraControlSystem::new();
  let (character_system, character_control) = CharacterControlSystem::new();
  let (mouse_system, mouse_control) = MouseControlSystem::new();
  let controls = TilemapControls::new(terrain_control, character_control, mouse_control);

  let mut dispatcher = DispatcherBuilder::new()
    .add(draw, "drawing", &[])
    .add(terrain::PreDrawSystem::new(), "draw-prep-terrain", &["drawing"])
    .add(character::PreDrawSystem::new(), "draw-prep-character", &["drawing"])
    .add(zombie::PreDrawSystem::new(), "draw-prep-zombie", &["drawing"])
    .add(terrain_system, "terrain-system", &[])
    .add(character_system, "character-system", &[])
    .add(mouse_system, "mouse-system", &[])
    .build();

  window.set_controls(controls);

  let mut last_time = time::Instant::now();
  loop {
    let elapsed = last_time.elapsed();
    let delta = f64::from(elapsed.subsec_nanos()) / 1e9 + elapsed.as_secs() as f64;
    last_time = time::Instant::now();

    dispatcher.dispatch(&w.res);
    w.maintain();

    let mut d = w.write_resource::<DeltaTime>();
    *d = DeltaTime(delta);

    device_renderer.draw(window.get_device());
    window.swap_window();

    if let Some(quit_status) = window.poll_events() {
      return quit_status;
    }

    if let Ok(quit_status) = game_state.try_recv() {
      return quit_status;
    }
  }
}
<|endoftext|>


<commit_before>use gfx_app::{Window, GameStatus};
use gfx_app::renderer::{DeviceRenderer, EncoderQueue};
use gfx_app::system::DrawSystem;
use gfx;
use std::time;
use std::sync::mpsc;
use terrain;
use specs;
use physics::{Dimensions, Planner};
use gfx_app::controls::{TilemapControls};
use terrain::controls::{TerrainControlSystem};
use character;

pub fn run<W, D, F>(window: &mut W) -> GameStatus
  where W: Window<D, F>,
        D: gfx::Device + 'static,
        F: gfx::Factory<D::Resources>,
        D::CommandBuffer: Send {
  let (mut device_renderer, enc_queue) = DeviceRenderer::new(window.create_buffers(2));

  let mut w = specs::World::new();
  setup_world(&mut w, window.get_viewport_size());
  let mut plan = specs::Planner::new(w);
  let mut receiver = setup_planner(window, &mut plan, enc_queue);

  dispatch_loop(window, &mut device_renderer, plan, &mut receiver)
}

fn setup_world(world: &mut specs::World, viewport_size: (u32, u32)) {
  world.register::<terrain::Drawable>();
  world.register::<terrain::controls::InputState>();
  world.register::<character::Drawable>();
  world.register::<character::character::Character>();

  let dimensions = Dimensions::new(viewport_size.0, viewport_size.1);
  world.add_resource(terrain::terrain::generate());
  world.add_resource(dimensions);
  world.add_resource(terrain::controls::InputState::new());
  world.add_resource(character::Drawable::new());
  world.create()
    .with(terrain::Drawable::new())
    .with(character::Drawable::new())
    .with(terrain::controls::InputState::new()).build();
}

fn setup_planner<W, D, F>(window: &mut W, planner: &mut Planner, encoder_queue: EncoderQueue<D>)
                          -> mpsc::Receiver<GameStatus>
  where W: Window<D, F>,
        D: gfx::Device + 'static,
        F: gfx::Factory<D::Resources>,
        D::CommandBuffer: Send {
  let draw = {
    let rtv = window.get_render_target_view();
    let dsv = window.get_depth_stencil_view();
    DrawSystem::new(window.get_factory(), rtv, dsv, encoder_queue)
  };

  let (_, rx) = mpsc::channel();

  planner.add_system(draw, "drawing", 10);
  planner.add_system(terrain::PreDrawSystem::new(), "draw-prep-terrain", 15);
  planner.add_system(character::PreDrawSystem::new(), "draw-prep-character", 20);

  let map_control = create_controls(planner);
  window.set_controls(map_control);

  rx
}

fn create_controls(planner: &mut Planner) -> TilemapControls {
  let (terrain_system, terrain_control) = TerrainControlSystem::new();
  let controls = TilemapControls::new(terrain_control);
  planner.add_system(terrain_system, "terrain-system", 20);
  controls
}

fn dispatch_loop<W, D, F>(window: &mut W,
                          device_renderer: &mut DeviceRenderer<D>,
                          mut planner: specs::Planner<f32>,
                          game_state: &mut mpsc::Receiver<GameStatus>)
                          -> GameStatus
  where W: Window<D, F>,
        D: gfx::Device + 'static,
        F: gfx::Factory<D::Resources>,
        D::CommandBuffer: Send
{
  let mut last_time = time::Instant::now();
  loop {
    let elapsed = last_time.elapsed();
    let delta = elapsed.subsec_nanos() as f32 / 1e9 + elapsed.as_secs() as f32;
    last_time = time::Instant::now();

    planner.dispatch(delta);

    device_renderer.draw(window.get_device());
    window.swap_window();

    if let Some(quit_status) = window.poll_events() {
      return quit_status;
    }
    planner.wait();
    if let Ok(quit_status) = game_state.try_recv() {
      return quit_status;
    }
  }
}
<commit_msg>Use same draw priority for both terrain & character<commit_after>use gfx_app::{Window, GameStatus};
use gfx_app::renderer::{DeviceRenderer, EncoderQueue};
use gfx_app::system::DrawSystem;
use gfx;
use std::time;
use std::sync::mpsc;
use terrain;
use specs;
use physics::{Dimensions, Planner};
use gfx_app::controls::{TilemapControls};
use terrain::controls::{TerrainControlSystem};
use character;

pub fn run<W, D, F>(window: &mut W) -> GameStatus
  where W: Window<D, F>,
        D: gfx::Device + 'static,
        F: gfx::Factory<D::Resources>,
        D::CommandBuffer: Send {
  let (mut device_renderer, enc_queue) = DeviceRenderer::new(window.create_buffers(2));

  let mut w = specs::World::new();
  setup_world(&mut w, window.get_viewport_size());
  let mut plan = specs::Planner::new(w);
  let mut receiver = setup_planner(window, &mut plan, enc_queue);

  dispatch_loop(window, &mut device_renderer, plan, &mut receiver)
}

fn setup_world(world: &mut specs::World, viewport_size: (u32, u32)) {
  world.register::<terrain::Drawable>();
  world.register::<terrain::controls::InputState>();
  world.register::<character::Drawable>();
  world.register::<character::character::Character>();

  let dimensions = Dimensions::new(viewport_size.0, viewport_size.1);
  world.add_resource(terrain::terrain::generate());
  world.add_resource(dimensions);
  world.add_resource(terrain::controls::InputState::new());
  world.add_resource(character::Drawable::new());
  world.create()
    .with(terrain::Drawable::new())
    .with(character::Drawable::new())
    .with(terrain::controls::InputState::new()).build();
}

fn setup_planner<W, D, F>(window: &mut W, planner: &mut Planner, encoder_queue: EncoderQueue<D>)
                          -> mpsc::Receiver<GameStatus>
  where W: Window<D, F>,
        D: gfx::Device + 'static,
        F: gfx::Factory<D::Resources>,
        D::CommandBuffer: Send {
  let draw = {
    let rtv = window.get_render_target_view();
    let dsv = window.get_depth_stencil_view();
    DrawSystem::new(window.get_factory(), rtv, dsv, encoder_queue)
  };

  let (_, rx) = mpsc::channel();

  planner.add_system(draw, "drawing", 10);
  planner.add_system(terrain::PreDrawSystem::new(), "draw-prep-terrain", 15);
  planner.add_system(character::PreDrawSystem::new(), "draw-prep-character", 15);

  let map_control = create_controls(planner);
  window.set_controls(map_control);

  rx
}

fn create_controls(planner: &mut Planner) -> TilemapControls {
  let (terrain_system, terrain_control) = TerrainControlSystem::new();
  let controls = TilemapControls::new(terrain_control);
  planner.add_system(terrain_system, "terrain-system", 20);
  controls
}

fn dispatch_loop<W, D, F>(window: &mut W,
                          device_renderer: &mut DeviceRenderer<D>,
                          mut planner: specs::Planner<f32>,
                          game_state: &mut mpsc::Receiver<GameStatus>)
                          -> GameStatus
  where W: Window<D, F>,
        D: gfx::Device + 'static,
        F: gfx::Factory<D::Resources>,
        D::CommandBuffer: Send
{
  let mut last_time = time::Instant::now();
  loop {
    let elapsed = last_time.elapsed();
    let delta = elapsed.subsec_nanos() as f32 / 1e9 + elapsed.as_secs() as f32;
    last_time = time::Instant::now();

    planner.dispatch(delta);

    device_renderer.draw(window.get_device());
    window.swap_window();

    if let Some(quit_status) = window.poll_events() {
      return quit_status;
    }
    planner.wait();
    if let Ok(quit_status) = game_state.try_recv() {
      return quit_status;
    }
  }
}
<|endoftext|>


<commit_before>use sozu::channel::Channel;
use sozu::messages::{Order, Instance};
use sozu_command::data::{AnswerData,ConfigCommand,ConfigMessage,ConfigMessageAnswer,ConfigMessageStatus,RunState};

use std::collections::HashSet;
use rand::{thread_rng, Rng};

fn generate_id() -> String {
  let s: String = thread_rng().gen_ascii_chars().take(6).collect();
  format!("ID-{}", s)
}

pub fn save_state(channel: &mut Channel<ConfigMessage,ConfigMessageAnswer>, path: &str) {
  let id = generate_id();
  channel.write_message(&ConfigMessage::new(
    id.clone(),
    ConfigCommand::SaveState(path.to_string()),
    None,
  ));

  match channel.read_message() {
    None          => println!("the proxy didn't answer"),
    Some(message) => {
      if id != message.id {
        println!("received message with invalid id: {:?}", message);
        return;
      }
      match message.status {
        ConfigMessageStatus::Processing => {
          // do nothing here
          // for other messages, we would loop over read_message
          // until an error or ok message was sent
        },
        ConfigMessageStatus::Error => {
          println!("could not save proxy state: {}", message.message);
        },
        ConfigMessageStatus::Ok => {
          println!("Proxy state saved to {}", path);
        }
      }
    }
  }
}

pub fn load_state(channel: &mut Channel<ConfigMessage,ConfigMessageAnswer>, path: &str) {
  let id = generate_id();
  channel.write_message(&ConfigMessage::new(
    id.clone(),
    ConfigCommand::LoadState(path.to_string()),
    None,
  ));

  match channel.read_message() {
    None          => println!("the proxy didn't answer"),
    Some(message) => {
      if id != message.id {
        println!("received message with invalid id: {:?}", message);
        return;
      }
      match message.status {
        ConfigMessageStatus::Processing => {
          // do nothing here
          // for other messages, we would loop over read_message
          // until an error or ok message was sent
        },
        ConfigMessageStatus::Error => {
          println!("could not save proxy state: {}", message.message);
        },
        ConfigMessageStatus::Ok => {
          println!("Proxy state saved to {}", path);
        }
      }
    }
  }
}

pub fn dump_state(channel: &mut Channel<ConfigMessage,ConfigMessageAnswer>) {
  let id = generate_id();
  channel.write_message(&ConfigMessage::new(
    id.clone(),
    ConfigCommand::DumpState,
    None,
  ));

  match channel.read_message() {
    None          => println!("the proxy didn't answer"),
    Some(message) => {
      if id != message.id {
        println!("received message with invalid id: {:?}", message);
        return;
      }
      match message.status {
        ConfigMessageStatus::Processing => {
          // do nothing here
          // for other messages, we would loop over read_message
          // until an error or ok message was sent
        },
        ConfigMessageStatus::Error => {
          println!("could not dump proxy state: {}", message.message);
        },
        ConfigMessageStatus::Ok => {
          println!("Proxy state:\n{}", message.message);
        }
      }
    }
  }
}

pub fn soft_stop(channel: &mut Channel<ConfigMessage,ConfigMessageAnswer>) {
  println!("shutting down proxy");
  let id = generate_id();
  channel.write_message(&ConfigMessage::new(
    id.clone(),
    ConfigCommand::ProxyConfiguration(Order::SoftStop),
    //FIXME: we should be able to soft stop one specific worker
    None,
  ));

  loop {
    match channel.read_message() {
      None          => println!("the proxy didn't answer"),
      Some(message) => {
        if &id != &message.id {
          println!("received message with invalid id: {:?}", message);
          return;
        }
        match message.status {
          ConfigMessageStatus::Processing => {
            println!("Proxy is processing: {}", message.message);
          },
          ConfigMessageStatus::Error => {
            println!("could not stop the proxy: {}", message.message);
          },
          ConfigMessageStatus::Ok => {
            println!("Proxy shut down: {}", message.message);
            break;
          }
        }
      }
    }
  }
}

pub fn hard_stop(channel: &mut Channel<ConfigMessage,ConfigMessageAnswer>) {
  println!("shutting down proxy");
  let id = generate_id();
  channel.write_message(&ConfigMessage::new(
    id.clone(),
    ConfigCommand::ProxyConfiguration(Order::HardStop),
    //FIXME: we should be able to soft stop one specific worker
    None,
  ));

  loop {
    match channel.read_message() {
      None          => println!("the proxy didn't answer"),
      Some(message) => {
        match message.status {
          ConfigMessageStatus::Processing => {
            println!("Proxy is processing: {}", message.message);
          },
          ConfigMessageStatus::Error => {
            println!("could not stop the proxy: {}", message.message);
          },
          ConfigMessageStatus::Ok => {
            if &id == &message.id {
              println!("Proxy shut down: {}", message.message);
              break;
            }
          }
        }
      }
    }
  }
}

pub fn upgrade(channel: &mut Channel<ConfigMessage,ConfigMessageAnswer>) {
  let id = generate_id();
  channel.write_message(&ConfigMessage::new(
    id.clone(),
    ConfigCommand::ListWorkers,
    None,
  ));

  match channel.read_message() {
    None          => println!("the proxy didn't answer"),
    Some(message) => {
      if id != message.id {
        println!("received message with invalid id: {:?}", message);
        return;
      }
      match message.status {
        ConfigMessageStatus::Processing => {
          println!("should have obtained an answer immediately");
          return;
        },
        ConfigMessageStatus::Error => {
          println!("could not get the worker list: {}", message.message);
          return
        },
        ConfigMessageStatus::Ok => {
          println!("Worker list:\n{:?}", message.data);
          if let Some(AnswerData::Workers(ref workers)) = message.data {
            let mut launching: HashSet<String> = HashSet::new();
            let mut stopping:  HashSet<String> = HashSet::new();

            for ref worker in workers.iter().filter(|worker| worker.run_state == RunState::Running) {
              let id = generate_id();
              let msg = ConfigMessage::new(
                id.clone(),
                ConfigCommand::LaunchWorker("BLAH".to_string()),
                None,
              );
              println!("sending message: {:?}", msg);
              channel.write_message(&msg);
              launching.insert(id);
            }

            for ref worker in workers.iter().filter(|worker| worker.run_state == RunState::Running) {
              let id = generate_id();
              let msg = ConfigMessage::new(
                id.clone(),
                ConfigCommand::ProxyConfiguration(Order::SoftStop),
                Some(worker.id),
              );
              println!("sending message: {:?}", msg);
              channel.write_message(&msg);
              stopping.insert(id);
            }

            loop {
              println!("launching: {:?}\nstopping: {:?}", launching, stopping);
              if launching.is_empty() && stopping.is_empty() {
                break;
              }
              match channel.read_message() {
                None          => println!("the proxy didn't answer"),
                Some(message) => {
                  println!("received message: {:?}", message);
                  match message.status {
                    ConfigMessageStatus::Processing => {
                    },
                    ConfigMessageStatus::Error => {
                      println!("error for message[{}]: {}", message.id, message.message);
                      if launching.contains(&message.id) {
                        launching.remove(&message.id);
                        println!("launch message with ID {} done", message.id);
                      }
                      if stopping.contains(&message.id) {
                        stopping.remove(&message.id);
                        println!("stop message with ID {} done", message.id);
                      }
                    },
                    ConfigMessageStatus::Ok => {
                      if launching.contains(&message.id) {
                        launching.remove(&message.id);
                        println!("launch message with ID {} done", message.id);
                      }
                      if stopping.contains(&message.id) {
                        stopping.remove(&message.id);
                        println!("stop message with ID {} done", message.id);
                      }
                    }
                  }
                }
              }
            }

            println!("worker upgrade done");
            let id = generate_id();
            channel.write_message(&ConfigMessage::new(
              id.clone(),
              ConfigCommand::UpgradeMaster,
              None,
            ));

            loop {
              match channel.read_message() {
                None          => println!("the proxy didn't answer"),
                Some(message) => {
                  if &id != &message.id {
                    println!("received message with invalid id: {:?}", message);
                    return;
                  }
                  match message.status {
                    ConfigMessageStatus::Processing => {
                      println!("master is processing: {}", message.message);
                    },
                    ConfigMessageStatus::Error => {
                      println!("could not upgrade the master: {}", message.message);
                      return;
                    },
                    ConfigMessageStatus::Ok => {
                      println!("successfully upgraded the master: {}", message.message);
                      return;
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}

pub fn status(channel: &mut Channel<ConfigMessage,ConfigMessageAnswer>) {
  let id = generate_id();
  channel.write_message(&ConfigMessage::new(
    id.clone(),
    ConfigCommand::ListWorkers,
    None,
  ));

  match channel.read_message() {
    None          => println!("the proxy didn't answer"),
    Some(message) => {
      if id != message.id {
        println!("received message with invalid id: {:?}", message);
        return;
      }
      match message.status {
        ConfigMessageStatus::Processing => {
          println!("should have obtained an answer immediately");
          return;
        },
        ConfigMessageStatus::Error => {
          println!("could not get the worker list: {}", message.message);
          return
        },
        ConfigMessageStatus::Ok => {
          println!("Worker list:\n{:?}", message.data);
          if let Some(AnswerData::Workers(ref workers)) = message.data {
            let mut expecting: HashSet<String> = HashSet::new();

            for ref worker in workers.iter().filter(|worker| worker.run_state == RunState::Running) {
              let id = generate_id();
              let msg = ConfigMessage::new(
                id.clone(),
                ConfigCommand::ProxyConfiguration(Order::Status),
                Some(worker.id),
              );
              println!("sending message: {:?}", msg);
              channel.write_message(&msg);
              expecting.insert(id);
            }

            loop {
              println!("expecting: {:?}", expecting);
              if expecting.is_empty() {
                break;
              }
              match channel.read_message() {
                None          => println!("the proxy didn't answer"),
                Some(message) => {
                  println!("received message: {:?}", message);
                  match message.status {
                    ConfigMessageStatus::Processing => {
                    },
                    ConfigMessageStatus::Error => {
                      println!("error for message[{}]: {}", message.id, message.message);
                      if expecting.contains(&message.id) {
                        expecting.remove(&message.id);
                        println!("status message with ID {} done", message.id);
                      }
                    },
                    ConfigMessageStatus::Ok => {
                      if expecting.contains(&message.id) {
                        expecting.remove(&message.id);
                        println!("status message with ID {} done", message.id);
                      }
                    }
                  }
                }
              }
            }

            println!("worker upgrade done");
          }
        }
      }
    }
  }
}

pub fn remove_backend(channel: &mut Channel<ConfigMessage,ConfigMessageAnswer>, app_id: &str, ip: &str, port: u16) {
    let instance = Instance {
      app_id: String::from(app_id),
      ip_address: String::from(ip),
      port: port
    };

    order_command(channel, Order::RemoveInstance(instance));
}

pub fn add_backend(channel: &mut Channel<ConfigMessage,ConfigMessageAnswer>, app_id: &str, ip: &str, port: u16) {
  let instance = Instance {
      app_id: String::from(app_id),
      ip_address: String::from(ip),
      port: port
    };

  order_command(channel, Order::AddInstance(instance));
}

fn order_command(channel: &mut Channel<ConfigMessage,ConfigMessageAnswer>, order: Order) {
  let id = generate_id();
  channel.write_message(&ConfigMessage::new(
    id.clone(),
    ConfigCommand::ProxyConfiguration(order.clone()),
    None,
  ));

  match channel.read_message() {
    None          => println!("the proxy didn't answer"),
    Some(message) => {
      if id != message.id {
        println!("received message with invalid id: {:?}", message);
        return;
      }
      match message.status {
        ConfigMessageStatus::Processing => {
          // do nothing here
          // for other messages, we would loop over read_message
          // until an error or ok message was sent
        },
        ConfigMessageStatus::Error => {
          match order {
            Order::AddInstance(_) => println!("could not add backend : {}", message.message),
            Order::RemoveInstance(_) => println!("could not remove backend : {}", message.message),
            _ => {
              // do nothing for now
            }
          }
        },
        ConfigMessageStatus::Ok => {
          match order {
            Order::AddInstance(i) => println!("backend {}:{} added for app : {} ", i.ip_address, i.port, i.app_id),
            Order::RemoveInstance(i) => println!("backend {}:{} removed for app : {} ", i.ip_address, i.port, i.app_id),
            _ => {
              // do nothing for now
            }
          }
        }
      }
    }
  }
}
<commit_msg>some stuff<commit_after>use sozu::channel::Channel;
use sozu::messages::{Order, Instance, HttpFront, CertificateAndKey, CertFingerprint, TcpFront};
use sozu_command::data::{AnswerData,ConfigCommand,ConfigMessage,ConfigMessageAnswer,ConfigMessageStatus,RunState};

use std::collections::HashSet;
use rand::{thread_rng, Rng};

fn generate_id() -> String {
  let s: String = thread_rng().gen_ascii_chars().take(6).collect();
  format!("ID-{}", s)
}

pub fn save_state(channel: &mut Channel<ConfigMessage,ConfigMessageAnswer>, path: &str) {
  let id = generate_id();
  channel.write_message(&ConfigMessage::new(
    id.clone(),
    ConfigCommand::SaveState(path.to_string()),
    None,
  ));

  match channel.read_message() {
    None          => println!("the proxy didn't answer"),
    Some(message) => {
      if id != message.id {
        println!("received message with invalid id: {:?}", message);
        return;
      }
      match message.status {
        ConfigMessageStatus::Processing => {
          // do nothing here
          // for other messages, we would loop over read_message
          // until an error or ok message was sent
        },
        ConfigMessageStatus::Error => {
          println!("could not save proxy state: {}", message.message);
        },
        ConfigMessageStatus::Ok => {
          println!("Proxy state saved to {}", path);
        }
      }
    }
  }
}

pub fn load_state(channel: &mut Channel<ConfigMessage,ConfigMessageAnswer>, path: &str) {
  let id = generate_id();
  channel.write_message(&ConfigMessage::new(
    id.clone(),
    ConfigCommand::LoadState(path.to_string()),
    None,
  ));

  match channel.read_message() {
    None          => println!("the proxy didn't answer"),
    Some(message) => {
      if id != message.id {
        println!("received message with invalid id: {:?}", message);
        return;
      }
      match message.status {
        ConfigMessageStatus::Processing => {
          // do nothing here
          // for other messages, we would loop over read_message
          // until an error or ok message was sent
        },
        ConfigMessageStatus::Error => {
          println!("could not save proxy state: {}", message.message);
        },
        ConfigMessageStatus::Ok => {
          println!("Proxy state saved to {}", path);
        }
      }
    }
  }
}

pub fn dump_state(channel: &mut Channel<ConfigMessage,ConfigMessageAnswer>) {
  let id = generate_id();
  channel.write_message(&ConfigMessage::new(
    id.clone(),
    ConfigCommand::DumpState,
    None,
  ));

  match channel.read_message() {
    None          => println!("the proxy didn't answer"),
    Some(message) => {
      if id != message.id {
        println!("received message with invalid id: {:?}", message);
        return;
      }
      match message.status {
        ConfigMessageStatus::Processing => {
          // do nothing here
          // for other messages, we would loop over read_message
          // until an error or ok message was sent
        },
        ConfigMessageStatus::Error => {
          println!("could not dump proxy state: {}", message.message);
        },
        ConfigMessageStatus::Ok => {
          println!("Proxy state:\n{}", message.message);
        }
      }
    }
  }
}

pub fn soft_stop(channel: &mut Channel<ConfigMessage,ConfigMessageAnswer>) {
  println!("shutting down proxy");
  let id = generate_id();
  channel.write_message(&ConfigMessage::new(
    id.clone(),
    ConfigCommand::ProxyConfiguration(Order::SoftStop),
    //FIXME: we should be able to soft stop one specific worker
    None,
  ));

  loop {
    match channel.read_message() {
      None          => println!("the proxy didn't answer"),
      Some(message) => {
        if &id != &message.id {
          println!("received message with invalid id: {:?}", message);
          return;
        }
        match message.status {
          ConfigMessageStatus::Processing => {
            println!("Proxy is processing: {}", message.message);
          },
          ConfigMessageStatus::Error => {
            println!("could not stop the proxy: {}", message.message);
          },
          ConfigMessageStatus::Ok => {
            println!("Proxy shut down: {}", message.message);
            break;
          }
        }
      }
    }
  }
}

pub fn hard_stop(channel: &mut Channel<ConfigMessage,ConfigMessageAnswer>) {
  println!("shutting down proxy");
  let id = generate_id();
  channel.write_message(&ConfigMessage::new(
    id.clone(),
    ConfigCommand::ProxyConfiguration(Order::HardStop),
    //FIXME: we should be able to soft stop one specific worker
    None,
  ));

  loop {
    match channel.read_message() {
      None          => println!("the proxy didn't answer"),
      Some(message) => {
        match message.status {
          ConfigMessageStatus::Processing => {
            println!("Proxy is processing: {}", message.message);
          },
          ConfigMessageStatus::Error => {
            println!("could not stop the proxy: {}", message.message);
          },
          ConfigMessageStatus::Ok => {
            if &id == &message.id {
              println!("Proxy shut down: {}", message.message);
              break;
            }
          }
        }
      }
    }
  }
}

pub fn upgrade(channel: &mut Channel<ConfigMessage,ConfigMessageAnswer>) {
  let id = generate_id();
  channel.write_message(&ConfigMessage::new(
    id.clone(),
    ConfigCommand::ListWorkers,
    None,
  ));

  match channel.read_message() {
    None          => println!("the proxy didn't answer"),
    Some(message) => {
      if id != message.id {
        println!("received message with invalid id: {:?}", message);
        return;
      }
      match message.status {
        ConfigMessageStatus::Processing => {
          println!("should have obtained an answer immediately");
          return;
        },
        ConfigMessageStatus::Error => {
          println!("could not get the worker list: {}", message.message);
          return
        },
        ConfigMessageStatus::Ok => {
          println!("Worker list:\n{:?}", message.data);
          if let Some(AnswerData::Workers(ref workers)) = message.data {
            let mut launching: HashSet<String> = HashSet::new();
            let mut stopping:  HashSet<String> = HashSet::new();

            for ref worker in workers.iter().filter(|worker| worker.run_state == RunState::Running) {
              let id = generate_id();
              let msg = ConfigMessage::new(
                id.clone(),
                ConfigCommand::LaunchWorker("BLAH".to_string()),
                None,
              );
              println!("sending message: {:?}", msg);
              channel.write_message(&msg);
              launching.insert(id);
            }

            for ref worker in workers.iter().filter(|worker| worker.run_state == RunState::Running) {
              let id = generate_id();
              let msg = ConfigMessage::new(
                id.clone(),
                ConfigCommand::ProxyConfiguration(Order::SoftStop),
                Some(worker.id),
              );
              println!("sending message: {:?}", msg);
              channel.write_message(&msg);
              stopping.insert(id);
            }

            loop {
              println!("launching: {:?}\nstopping: {:?}", launching, stopping);
              if launching.is_empty() && stopping.is_empty() {
                break;
              }
              match channel.read_message() {
                None          => println!("the proxy didn't answer"),
                Some(message) => {
                  println!("received message: {:?}", message);
                  match message.status {
                    ConfigMessageStatus::Processing => {
                    },
                    ConfigMessageStatus::Error => {
                      println!("error for message[{}]: {}", message.id, message.message);
                      if launching.contains(&message.id) {
                        launching.remove(&message.id);
                        println!("launch message with ID {} done", message.id);
                      }
                      if stopping.contains(&message.id) {
                        stopping.remove(&message.id);
                        println!("stop message with ID {} done", message.id);
                      }
                    },
                    ConfigMessageStatus::Ok => {
                      if launching.contains(&message.id) {
                        launching.remove(&message.id);
                        println!("launch message with ID {} done", message.id);
                      }
                      if stopping.contains(&message.id) {
                        stopping.remove(&message.id);
                        println!("stop message with ID {} done", message.id);
                      }
                    }
                  }
                }
              }
            }

            println!("worker upgrade done");
            let id = generate_id();
            channel.write_message(&ConfigMessage::new(
              id.clone(),
              ConfigCommand::UpgradeMaster,
              None,
            ));

            loop {
              match channel.read_message() {
                None          => println!("the proxy didn't answer"),
                Some(message) => {
                  if &id != &message.id {
                    println!("received message with invalid id: {:?}", message);
                    return;
                  }
                  match message.status {
                    ConfigMessageStatus::Processing => {
                      println!("master is processing: {}", message.message);
                    },
                    ConfigMessageStatus::Error => {
                      println!("could not upgrade the master: {}", message.message);
                      return;
                    },
                    ConfigMessageStatus::Ok => {
                      println!("successfully upgraded the master: {}", message.message);
                      return;
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}

pub fn status(channel: &mut Channel<ConfigMessage,ConfigMessageAnswer>) {
  let id = generate_id();
  channel.write_message(&ConfigMessage::new(
    id.clone(),
    ConfigCommand::ListWorkers,
    None,
  ));

  match channel.read_message() {
    None          => println!("the proxy didn't answer"),
    Some(message) => {
      if id != message.id {
        println!("received message with invalid id: {:?}", message);
        return;
      }
      match message.status {
        ConfigMessageStatus::Processing => {
          println!("should have obtained an answer immediately");
          return;
        },
        ConfigMessageStatus::Error => {
          println!("could not get the worker list: {}", message.message);
          return
        },
        ConfigMessageStatus::Ok => {
          println!("Worker list:\n{:?}", message.data);
          if let Some(AnswerData::Workers(ref workers)) = message.data {
            let mut expecting: HashSet<String> = HashSet::new();

            for ref worker in workers.iter().filter(|worker| worker.run_state == RunState::Running) {
              let id = generate_id();
              let msg = ConfigMessage::new(
                id.clone(),
                ConfigCommand::ProxyConfiguration(Order::Status),
                Some(worker.id),
              );
              println!("sending message: {:?}", msg);
              channel.write_message(&msg);
              expecting.insert(id);
            }

            loop {
              println!("expecting: {:?}", expecting);
              if expecting.is_empty() {
                break;
              }
              match channel.read_message() {
                None          => println!("the proxy didn't answer"),
                Some(message) => {
                  println!("received message: {:?}", message);
                  match message.status {
                    ConfigMessageStatus::Processing => {
                    },
                    ConfigMessageStatus::Error => {
                      println!("error for message[{}]: {}", message.id, message.message);
                      if expecting.contains(&message.id) {
                        expecting.remove(&message.id);
                        println!("status message with ID {} done", message.id);
                      }
                    },
                    ConfigMessageStatus::Ok => {
                      if expecting.contains(&message.id) {
                        expecting.remove(&message.id);
                        println!("status message with ID {} done", message.id);
                      }
                    }
                  }
                }
              }
            }

            println!("worker upgrade done");
          }
        }
      }
    }
  }
}

pub fn remove_backend(channel: &mut Channel<ConfigMessage,ConfigMessageAnswer>, app_id: &str, ip: &str, port: u16) {
    order_command(channel, Order::RemoveInstance(Instance {
      app_id: String::from(app_id),
      ip_address: String::from(ip),
      port: port
    }));
}

pub fn add_backend(channel: &mut Channel<ConfigMessage,ConfigMessageAnswer>, app_id: &str, ip: &str, port: u16) {
  order_command(channel, Order::AddInstance(Instance {
      app_id: String::from(app_id),
      ip_address: String::from(ip),
      port: port
    }));
}

pub fn add_front(channel: &mut Channel<ConfigMessage,ConfigMessageAnswer>, app_id: &str, hostname: &str, path_begin: &str) {
  order_command(channel, Order::AddHttpFront(HttpFront {
    app_id: String::from(app_id),
    hostname: String::from(hostname),
    path_begin: String::from(path_begin)
  }));
}

pub fn add_certificate(channel: &mut Channel<ConfigMessage,ConfigMessageAnswer>, certificate: &str, certificate_chain: Vec<String>, key: &str) {
  order_command(channel, Order::AddCertificate(CertificateAndKey {
    certificate: String::from(certificate),
    certificate_chain: certificate_chain,
    key: String::from(key)
  }));
}

pub fn remove_certificate(channel: &mut Channel<ConfigMessage,ConfigMessageAnswer>, cert_fingerprint: CertFingerprint) {
  order_command(channel, Order::RemoveCertificate(cert_fingerprint));
}

pub fn add_tcp_front(channel: &mut Channel<ConfigMessage,ConfigMessageAnswer>, app_id: &str, ip_address: &str, port: u16) {
  order_command(channel, Order::AddTcpFront(TcpFront {
    app_id: String::from(app_id),
    ip_address: String::from(ip_address),
    port: port
  }));
}

pub fn remove_tcp_front(channel: &mut Channel<ConfigMessage,ConfigMessageAnswer>, app_id: &str, ip_address: &str, port: u16) {
  order_command(channel, Order::RemoveTcpFront(TcpFront {
    app_id: String::from(app_id),
    ip_address: String::from(ip_address),
    port: port
  }));
}

fn order_command(channel: &mut Channel<ConfigMessage,ConfigMessageAnswer>, order: Order) {
  let id = generate_id();
  channel.write_message(&ConfigMessage::new(
    id.clone(),
    ConfigCommand::ProxyConfiguration(order.clone()),
    None,
  ));

  match channel.read_message() {
    None          => println!("the proxy didn't answer"),
    Some(message) => {
      if id != message.id {
        println!("received message with invalid id: {:?}", message);
        return;
      }
      match message.status {
        ConfigMessageStatus::Processing => {
          // do nothing here
          // for other messages, we would loop over read_message
          // until an error or ok message was sent
        },
        ConfigMessageStatus::Error => {
          println!("could not execute order: {}", message.message);
        },
        ConfigMessageStatus::Ok => {
          match order {
            Order::AddInstance(_) => println!("backend added : {}", message.message),
            Order::RemoveInstance(_) => println!("backend removed : {} ", message.message),
            Order::AddCertificate(_) => println!("certificate added: {}", message.message),
            Order::RemoveCertificate(_) => println!("certificate removed: {}", message.message),
            Order::AddHttpFront(_) => println!("front added: {}", message.message),
            Order::RemoveHttpFront(_) => println!("front removed: {}", message.message),
            _ => {
              // do nothing for now
            }
          }
        }
      }
    }
  }
}
<|endoftext|>