<commit_before>use std::cell::Cell;
use std::cmp::max;

use cairo::{self, ImageSurface, MatrixTrait};
use cssparser;
use nalgebra::Vector3;
use num_traits::identities::Zero;

use attributes::Attribute;
use drawing_ctx::DrawingCtx;
use error::NodeError;
use filters::{
    context::{FilterContext, FilterOutput, FilterResult},
    light::{
        bottom_left_normal,
        bottom_right_normal,
        bottom_row_normal,
        interior_normal,
        left_column_normal,
        light_source::LightSource,
        right_column_normal,
        top_left_normal,
        top_right_normal,
        top_row_normal,
        Normal,
    },
    Filter,
    FilterError,
    PrimitiveWithInput,
};
use handle::RsvgHandle;
use node::{NodeResult, NodeTrait, NodeType, RsvgNode};
use parsers;
use property_bag::PropertyBag;
use state::ColorInterpolationFilters;
use surface_utils::{
    shared_surface::{SharedImageSurface, SurfaceType},
    ImageSurfaceDataExt,
    Pixel,
};
use util::clamp;

/// Properties specific to either diffuse or specular lighting.
enum Data {
    Diffuse {
        diffuse_constant: Cell<f64>,
    },
    Specular {
        specular_constant: Cell<f64>,
        specular_exponent: Cell<f64>,
    },
}

/// The `feDiffuseLighting` and `feSpecularLighting` filter primitives.
pub struct Lighting {
    base: PrimitiveWithInput,
    surface_scale: Cell<f64>,
    kernel_unit_length: Cell<Option<(f64, f64)>>,
    data: Data,
}

impl Lighting {
    /// Constructs a new diffuse `Lighting` with empty properties.
    #[inline]
    pub fn new_diffuse() -> Lighting {
        Lighting {
            data: Data::Diffuse {
                diffuse_constant: Cell::new(1.0),
            },
            ..Self::default()
        }
    }

    /// Constructs a new specular `Lighting` with empty properties.
    #[inline]
    pub fn new_specular() -> Lighting {
        Lighting {
            data: Data::Specular {
                specular_constant: Cell::new(1.0),
                specular_exponent: Cell::new(1.0),
            },
            ..Self::default()
        }
    }
}

impl NodeTrait for Lighting {
    fn set_atts(
        &self,
        node: &RsvgNode,
        handle: *const RsvgHandle,
        pbag: &PropertyBag,
    ) -> NodeResult {
        self.base.set_atts(node, handle, pbag)?;

        for (_key, attr, value) in pbag.iter() {
            match attr {
                Attribute::SurfaceScale => self
                    .surface_scale
                    .set(parsers::number(value).map_err(|err| NodeError::parse_error(attr, err))?),
                Attribute::KernelUnitLength => self.kernel_unit_length.set(Some(
                    parsers::number_optional_number(value)
                        .map_err(|err| NodeError::parse_error(attr, err))
                        .and_then(|(x, y)| {
                            if x > 0.0 && y > 0.0 {
                                Ok((x, y))
                            } else {
                                Err(NodeError::value_error(
                                    attr,
                                    "kernelUnitLength can't be less or equal to zero",
                                ))
                            }
                        })?,
                )),
                _ => (),
            }
        }

        match self.data {
            Data::Diffuse {
                ref diffuse_constant,
            } => {
                for (_key, attr, value) in pbag.iter() {
                    match attr {
                        Attribute::DiffuseConstant => diffuse_constant.set(
                            parsers::number(value)
                                .map_err(|err| NodeError::parse_error(attr, err))
                                .and_then(|x| {
                                    if x >= 0.0 {
                                        Ok(x)
                                    } else {
                                        Err(NodeError::value_error(
                                            attr,
                                            "diffuseConstant can't be negative",
                                        ))
                                    }
                                })?,
                        ),
                        _ => (),
                    }
                }
            }
            Data::Specular {
                ref specular_constant,
                ref specular_exponent,
            } => {
                for (_key, attr, value) in pbag.iter() {
                    match attr {
                        Attribute::SpecularConstant => specular_constant.set(
                            parsers::number(value)
                                .map_err(|err| NodeError::parse_error(attr, err))
                                .and_then(|x| {
                                    if x >= 0.0 {
                                        Ok(x)
                                    } else {
                                        Err(NodeError::value_error(
                                            attr,
                                            "specularConstant can't be negative",
                                        ))
                                    }
                                })?,
                        ),
                        Attribute::SpecularExponent => specular_exponent.set(
                            parsers::number(value)
                                .map_err(|err| NodeError::parse_error(attr, err))
                                .and_then(|x| {
                                    if x >= 1.0 && x <= 128.0 {
                                        Ok(x)
                                    } else {
                                        Err(NodeError::value_error(
                                            attr,
                                            "specularExponent should be between 1.0 and 128.0",
                                        ))
                                    }
                                })?,
                        ),
                        _ => (),
                    }
                }
            }
        }

        Ok(())
    }
}

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

        let scale = self
            .kernel_unit_length
            .get()
            .map(|(dx, dy)| ctx.paffine().transform_distance(dx, dy));

        let surface_scale = self.surface_scale.get();

        let cascaded = node.get_cascaded_values();
        let values = cascaded.get();
        let lighting_color = match values.lighting_color.0 {
            cssparser::Color::CurrentColor => values.color.0,
            cssparser::Color::RGBA(rgba) => rgba,
        };

        let mut light_sources = node
            .children()
            .rev()
            .filter(|c| c.get_type() == NodeType::LightSource);
        let light_source = light_sources.next();
        if light_source.is_none() || light_sources.next().is_some() {
            return Err(FilterError::InvalidLightSourceCount);
        }

        let light_source = light_source.unwrap();
        let light_source = light_source.get_impl::<LightSource>().unwrap();
        let light_source = light_source.transform(ctx);

        let mut input_surface = input.surface().clone();

        if let Some((ox, oy)) = scale {
            // Scale the input surface to match kernel_unit_length.
            let (new_surface, new_bounds) = input_surface.scale(bounds, 1.0 / ox, 1.0 / oy)?;

            input_surface = new_surface;
            bounds = new_bounds;
        }

        // Check if the surface is too small for normal computation. This case is unspecified;
        // WebKit doesn't render anything in this case.
        if bounds.x1 < bounds.x0 + 2 || bounds.y1 < bounds.y0 + 2 {
            return Err(FilterError::LightingInputTooSmall);
        }

        let (ox, oy) = scale.unwrap_or((1.0, 1.0));

        let mut output_surface = ImageSurface::create(
            cairo::Format::ARgb32,
            input_surface.width(),
            input_surface.height(),
        )?;

        let output_stride = output_surface.get_stride() as usize;
        {
            let mut output_data = output_surface.get_data().unwrap();

            let mut compute_output_pixel = |x, y, normal: Normal| {
                let pixel = input_surface.get_pixel(x, y);

                let scaled_x = f64::from(x) * ox;
                let scaled_y = f64::from(y) * oy;
                let z = f64::from(pixel.a) / 255.0 * surface_scale;
                let light_vector = light_source.vector(scaled_x, scaled_y, z);
                let light_color = light_source.color(lighting_color, light_vector);

                let factor = match self.data {
                    Data::Diffuse {
                        ref diffuse_constant,
                    } => {
                        let k = if normal.normal.is_zero() {
                            // Common case of (0, 0, 1) normal.
                            light_vector.z
                        } else {
                            let mut n = normal.normal.map(|x| f64::from(x) * surface_scale / 255.);
                            n.component_mul_assign(&normal.factor);
                            let normal = Vector3::new(n.x, n.y, 1.0);

                            normal.dot(&light_vector) / normal.norm()
                        };

                        diffuse_constant.get() * k
                    }
                    Data::Specular {
                        ref specular_constant,
                        ref specular_exponent,
                    } => {
                        let h = light_vector + Vector3::new(0.0, 0.0, 1.0);
                        let h_norm = h.norm();
                        if h_norm == 0.0 {
                            0.0
                        } else {
                            let k = if normal.normal.is_zero() {
                                // Common case of (0, 0, 1) normal.
                                let n_dot_h = h.z / h_norm;
                                if specular_exponent.get() == 1.0 {
                                    n_dot_h
                                } else {
                                    n_dot_h.powf(specular_exponent.get())
                                }
                            } else {
                                let mut n =
                                    normal.normal.map(|x| f64::from(x) * surface_scale / 255.);
                                n.component_mul_assign(&normal.factor);
                                let normal = Vector3::new(n.x, n.y, 1.0);

                                let n_dot_h = normal.dot(&h) / normal.norm() / h_norm;
                                if specular_exponent.get() == 1.0 {
                                    n_dot_h
                                } else {
                                    n_dot_h.powf(specular_exponent.get())
                                }
                            };

                            specular_constant.get() * k
                        }
                    }
                };

                let compute = |x| clamp(factor * f64::from(x), 0.0, 255.0).round() as u8;

                let mut output_pixel = Pixel {
                    r: compute(light_color.red),
                    g: compute(light_color.green),
                    b: compute(light_color.blue),
                    a: 255,
                };

                if let Data::Specular { .. } = self.data {
                    output_pixel.a = max(max(output_pixel.r, output_pixel.g), output_pixel.b);
                }

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

            // Top left.
            compute_output_pixel(
                bounds.x0 as u32,
                bounds.y0 as u32,
                top_left_normal(&input_surface, bounds),
            );

            // Top right.
            compute_output_pixel(
                bounds.x1 as u32 - 1,
                bounds.y0 as u32,
                top_right_normal(&input_surface, bounds),
            );

            // Bottom left.
            compute_output_pixel(
                bounds.x0 as u32,
                bounds.y1 as u32 - 1,
                bottom_left_normal(&input_surface, bounds),
            );

            // Bottom right.
            compute_output_pixel(
                bounds.x1 as u32 - 1,
                bounds.y1 as u32 - 1,
                bottom_right_normal(&input_surface, bounds),
            );

            if bounds.x1 - bounds.x0 >= 3 {
                // Top row.
                for x in bounds.x0 as u32 + 1..bounds.x1 as u32 - 1 {
                    compute_output_pixel(
                        x,
                        bounds.y0 as u32,
                        top_row_normal(&input_surface, bounds, x),
                    );
                }

                // Bottom row.
                for x in bounds.x0 as u32 + 1..bounds.x1 as u32 - 1 {
                    compute_output_pixel(
                        x,
                        bounds.y1 as u32 - 1,
                        bottom_row_normal(&input_surface, bounds, x),
                    );
                }
            }

            if bounds.y1 - bounds.y0 >= 3 {
                // Left column.
                for y in bounds.y0 as u32 + 1..bounds.y1 as u32 - 1 {
                    compute_output_pixel(
                        bounds.x0 as u32,
                        y,
                        left_column_normal(&input_surface, bounds, y),
                    );
                }

                // Right column.
                for y in bounds.y0 as u32 + 1..bounds.y1 as u32 - 1 {
                    compute_output_pixel(
                        bounds.x1 as u32 - 1,
                        y,
                        right_column_normal(&input_surface, bounds, y),
                    );
                }
            }

            if bounds.x1 - bounds.x0 >= 3 && bounds.y1 - bounds.y0 >= 3 {
                // Interior pixels.
                for y in bounds.y0 as u32 + 1..bounds.y1 as u32 - 1 {
                    for x in bounds.x0 as u32 + 1..bounds.x1 as u32 - 1 {
                        compute_output_pixel(x, y, interior_normal(&input_surface, bounds, x, y));
                    }
                }
            }
        }

        let cascaded = node.get_cascaded_values();
        let values = cascaded.get();
        // The generated color values are in the color space determined by
        // color-interpolation-filters.
        let surface_type =
            if values.color_interpolation_filters == ColorInterpolationFilters::LinearRgb {
                SurfaceType::LinearRgb
            } else {
                SurfaceType::SRgb
            };
        let mut output_surface = SharedImageSurface::new(output_surface, surface_type)?;

        if let Some((ox, oy)) = scale {
            // Scale the output surface back.
            output_surface = output_surface.scale_to(
                ctx.source_graphic().width(),
                ctx.source_graphic().height(),
                original_bounds,
                ox,
                oy,
            )?;

            bounds = original_bounds;
        }

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

    #[inline]
    fn is_affected_by_color_interpolation_filters(&self) -> bool {
        true
    }
}

impl Default for Lighting {
    #[inline]
    fn default() -> Self {
        Self {
            base: PrimitiveWithInput::new::<Self>(),
            surface_scale: Cell::new(1.0),
            kernel_unit_length: Cell::new(None),

            // The data field is unused in this case.
            data: Data::Diffuse {
                diffuse_constant: Cell::new(1.0),
            },
        }
    }
}
<commit_msg>Replace round() with + 0.5<commit_after>use std::cell::Cell;
use std::cmp::max;

use cairo::{self, ImageSurface, MatrixTrait};
use cssparser;
use nalgebra::Vector3;
use num_traits::identities::Zero;

use attributes::Attribute;
use drawing_ctx::DrawingCtx;
use error::NodeError;
use filters::{
    context::{FilterContext, FilterOutput, FilterResult},
    light::{
        bottom_left_normal,
        bottom_right_normal,
        bottom_row_normal,
        interior_normal,
        left_column_normal,
        light_source::LightSource,
        right_column_normal,
        top_left_normal,
        top_right_normal,
        top_row_normal,
        Normal,
    },
    Filter,
    FilterError,
    PrimitiveWithInput,
};
use handle::RsvgHandle;
use node::{NodeResult, NodeTrait, NodeType, RsvgNode};
use parsers;
use property_bag::PropertyBag;
use state::ColorInterpolationFilters;
use surface_utils::{
    shared_surface::{SharedImageSurface, SurfaceType},
    ImageSurfaceDataExt,
    Pixel,
};
use util::clamp;

/// Properties specific to either diffuse or specular lighting.
enum Data {
    Diffuse {
        diffuse_constant: Cell<f64>,
    },
    Specular {
        specular_constant: Cell<f64>,
        specular_exponent: Cell<f64>,
    },
}

/// The `feDiffuseLighting` and `feSpecularLighting` filter primitives.
pub struct Lighting {
    base: PrimitiveWithInput,
    surface_scale: Cell<f64>,
    kernel_unit_length: Cell<Option<(f64, f64)>>,
    data: Data,
}

impl Lighting {
    /// Constructs a new diffuse `Lighting` with empty properties.
    #[inline]
    pub fn new_diffuse() -> Lighting {
        Lighting {
            data: Data::Diffuse {
                diffuse_constant: Cell::new(1.0),
            },
            ..Self::default()
        }
    }

    /// Constructs a new specular `Lighting` with empty properties.
    #[inline]
    pub fn new_specular() -> Lighting {
        Lighting {
            data: Data::Specular {
                specular_constant: Cell::new(1.0),
                specular_exponent: Cell::new(1.0),
            },
            ..Self::default()
        }
    }
}

impl NodeTrait for Lighting {
    fn set_atts(
        &self,
        node: &RsvgNode,
        handle: *const RsvgHandle,
        pbag: &PropertyBag,
    ) -> NodeResult {
        self.base.set_atts(node, handle, pbag)?;

        for (_key, attr, value) in pbag.iter() {
            match attr {
                Attribute::SurfaceScale => self
                    .surface_scale
                    .set(parsers::number(value).map_err(|err| NodeError::parse_error(attr, err))?),
                Attribute::KernelUnitLength => self.kernel_unit_length.set(Some(
                    parsers::number_optional_number(value)
                        .map_err(|err| NodeError::parse_error(attr, err))
                        .and_then(|(x, y)| {
                            if x > 0.0 && y > 0.0 {
                                Ok((x, y))
                            } else {
                                Err(NodeError::value_error(
                                    attr,
                                    "kernelUnitLength can't be less or equal to zero",
                                ))
                            }
                        })?,
                )),
                _ => (),
            }
        }

        match self.data {
            Data::Diffuse {
                ref diffuse_constant,
            } => {
                for (_key, attr, value) in pbag.iter() {
                    match attr {
                        Attribute::DiffuseConstant => diffuse_constant.set(
                            parsers::number(value)
                                .map_err(|err| NodeError::parse_error(attr, err))
                                .and_then(|x| {
                                    if x >= 0.0 {
                                        Ok(x)
                                    } else {
                                        Err(NodeError::value_error(
                                            attr,
                                            "diffuseConstant can't be negative",
                                        ))
                                    }
                                })?,
                        ),
                        _ => (),
                    }
                }
            }
            Data::Specular {
                ref specular_constant,
                ref specular_exponent,
            } => {
                for (_key, attr, value) in pbag.iter() {
                    match attr {
                        Attribute::SpecularConstant => specular_constant.set(
                            parsers::number(value)
                                .map_err(|err| NodeError::parse_error(attr, err))
                                .and_then(|x| {
                                    if x >= 0.0 {
                                        Ok(x)
                                    } else {
                                        Err(NodeError::value_error(
                                            attr,
                                            "specularConstant can't be negative",
                                        ))
                                    }
                                })?,
                        ),
                        Attribute::SpecularExponent => specular_exponent.set(
                            parsers::number(value)
                                .map_err(|err| NodeError::parse_error(attr, err))
                                .and_then(|x| {
                                    if x >= 1.0 && x <= 128.0 {
                                        Ok(x)
                                    } else {
                                        Err(NodeError::value_error(
                                            attr,
                                            "specularExponent should be between 1.0 and 128.0",
                                        ))
                                    }
                                })?,
                        ),
                        _ => (),
                    }
                }
            }
        }

        Ok(())
    }
}

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

        let scale = self
            .kernel_unit_length
            .get()
            .map(|(dx, dy)| ctx.paffine().transform_distance(dx, dy));

        let surface_scale = self.surface_scale.get();

        let cascaded = node.get_cascaded_values();
        let values = cascaded.get();
        let lighting_color = match values.lighting_color.0 {
            cssparser::Color::CurrentColor => values.color.0,
            cssparser::Color::RGBA(rgba) => rgba,
        };

        let mut light_sources = node
            .children()
            .rev()
            .filter(|c| c.get_type() == NodeType::LightSource);
        let light_source = light_sources.next();
        if light_source.is_none() || light_sources.next().is_some() {
            return Err(FilterError::InvalidLightSourceCount);
        }

        let light_source = light_source.unwrap();
        let light_source = light_source.get_impl::<LightSource>().unwrap();
        let light_source = light_source.transform(ctx);

        let mut input_surface = input.surface().clone();

        if let Some((ox, oy)) = scale {
            // Scale the input surface to match kernel_unit_length.
            let (new_surface, new_bounds) = input_surface.scale(bounds, 1.0 / ox, 1.0 / oy)?;

            input_surface = new_surface;
            bounds = new_bounds;
        }

        // Check if the surface is too small for normal computation. This case is unspecified;
        // WebKit doesn't render anything in this case.
        if bounds.x1 < bounds.x0 + 2 || bounds.y1 < bounds.y0 + 2 {
            return Err(FilterError::LightingInputTooSmall);
        }

        let (ox, oy) = scale.unwrap_or((1.0, 1.0));

        let mut output_surface = ImageSurface::create(
            cairo::Format::ARgb32,
            input_surface.width(),
            input_surface.height(),
        )?;

        let output_stride = output_surface.get_stride() as usize;
        {
            let mut output_data = output_surface.get_data().unwrap();

            let mut compute_output_pixel = |x, y, normal: Normal| {
                let pixel = input_surface.get_pixel(x, y);

                let scaled_x = f64::from(x) * ox;
                let scaled_y = f64::from(y) * oy;
                let z = f64::from(pixel.a) / 255.0 * surface_scale;
                let light_vector = light_source.vector(scaled_x, scaled_y, z);
                let light_color = light_source.color(lighting_color, light_vector);

                let factor = match self.data {
                    Data::Diffuse {
                        ref diffuse_constant,
                    } => {
                        let k = if normal.normal.is_zero() {
                            // Common case of (0, 0, 1) normal.
                            light_vector.z
                        } else {
                            let mut n = normal.normal.map(|x| f64::from(x) * surface_scale / 255.);
                            n.component_mul_assign(&normal.factor);
                            let normal = Vector3::new(n.x, n.y, 1.0);

                            normal.dot(&light_vector) / normal.norm()
                        };

                        diffuse_constant.get() * k
                    }
                    Data::Specular {
                        ref specular_constant,
                        ref specular_exponent,
                    } => {
                        let h = light_vector + Vector3::new(0.0, 0.0, 1.0);
                        let h_norm = h.norm();
                        if h_norm == 0.0 {
                            0.0
                        } else {
                            let k = if normal.normal.is_zero() {
                                // Common case of (0, 0, 1) normal.
                                let n_dot_h = h.z / h_norm;
                                if specular_exponent.get() == 1.0 {
                                    n_dot_h
                                } else {
                                    n_dot_h.powf(specular_exponent.get())
                                }
                            } else {
                                let mut n =
                                    normal.normal.map(|x| f64::from(x) * surface_scale / 255.);
                                n.component_mul_assign(&normal.factor);
                                let normal = Vector3::new(n.x, n.y, 1.0);

                                let n_dot_h = normal.dot(&h) / normal.norm() / h_norm;
                                if specular_exponent.get() == 1.0 {
                                    n_dot_h
                                } else {
                                    n_dot_h.powf(specular_exponent.get())
                                }
                            };

                            specular_constant.get() * k
                        }
                    }
                };

                let compute = |x| (clamp(factor * f64::from(x), 0.0, 255.0) + 0.5) as u8;

                let mut output_pixel = Pixel {
                    r: compute(light_color.red),
                    g: compute(light_color.green),
                    b: compute(light_color.blue),
                    a: 255,
                };

                if let Data::Specular { .. } = self.data {
                    output_pixel.a = max(max(output_pixel.r, output_pixel.g), output_pixel.b);
                }

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

            // Top left.
            compute_output_pixel(
                bounds.x0 as u32,
                bounds.y0 as u32,
                top_left_normal(&input_surface, bounds),
            );

            // Top right.
            compute_output_pixel(
                bounds.x1 as u32 - 1,
                bounds.y0 as u32,
                top_right_normal(&input_surface, bounds),
            );

            // Bottom left.
            compute_output_pixel(
                bounds.x0 as u32,
                bounds.y1 as u32 - 1,
                bottom_left_normal(&input_surface, bounds),
            );

            // Bottom right.
            compute_output_pixel(
                bounds.x1 as u32 - 1,
                bounds.y1 as u32 - 1,
                bottom_right_normal(&input_surface, bounds),
            );

            if bounds.x1 - bounds.x0 >= 3 {
                // Top row.
                for x in bounds.x0 as u32 + 1..bounds.x1 as u32 - 1 {
                    compute_output_pixel(
                        x,
                        bounds.y0 as u32,
                        top_row_normal(&input_surface, bounds, x),
                    );
                }

                // Bottom row.
                for x in bounds.x0 as u32 + 1..bounds.x1 as u32 - 1 {
                    compute_output_pixel(
                        x,
                        bounds.y1 as u32 - 1,
                        bottom_row_normal(&input_surface, bounds, x),
                    );
                }
            }

            if bounds.y1 - bounds.y0 >= 3 {
                // Left column.
                for y in bounds.y0 as u32 + 1..bounds.y1 as u32 - 1 {
                    compute_output_pixel(
                        bounds.x0 as u32,
                        y,
                        left_column_normal(&input_surface, bounds, y),
                    );
                }

                // Right column.
                for y in bounds.y0 as u32 + 1..bounds.y1 as u32 - 1 {
                    compute_output_pixel(
                        bounds.x1 as u32 - 1,
                        y,
                        right_column_normal(&input_surface, bounds, y),
                    );
                }
            }

            if bounds.x1 - bounds.x0 >= 3 && bounds.y1 - bounds.y0 >= 3 {
                // Interior pixels.
                for y in bounds.y0 as u32 + 1..bounds.y1 as u32 - 1 {
                    for x in bounds.x0 as u32 + 1..bounds.x1 as u32 - 1 {
                        compute_output_pixel(x, y, interior_normal(&input_surface, bounds, x, y));
                    }
                }
            }
        }

        let cascaded = node.get_cascaded_values();
        let values = cascaded.get();
        // The generated color values are in the color space determined by
        // color-interpolation-filters.
        let surface_type =
            if values.color_interpolation_filters == ColorInterpolationFilters::LinearRgb {
                SurfaceType::LinearRgb
            } else {
                SurfaceType::SRgb
            };
        let mut output_surface = SharedImageSurface::new(output_surface, surface_type)?;

        if let Some((ox, oy)) = scale {
            // Scale the output surface back.
            output_surface = output_surface.scale_to(
                ctx.source_graphic().width(),
                ctx.source_graphic().height(),
                original_bounds,
                ox,
                oy,
            )?;

            bounds = original_bounds;
        }

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

    #[inline]
    fn is_affected_by_color_interpolation_filters(&self) -> bool {
        true
    }
}

impl Default for Lighting {
    #[inline]
    fn default() -> Self {
        Self {
            base: PrimitiveWithInput::new::<Self>(),
            surface_scale: Cell::new(1.0),
            kernel_unit_length: Cell::new(None),

            // The data field is unused in this case.
            data: Data::Diffuse {
                diffuse_constant: Cell::new(1.0),
            },
        }
    }
}
<|endoftext|>


<commit_before>// Look up a username by API token. Used by staff to verify someone's identity
// by having an API token given. If an error occurs, including being unable to
// find a user with that API token, the error will be displayed.

use cargo_registry::{db, models::User, util::errors::AppResult};
use std::env;

fn main() -> AppResult<()> {
    let conn = db::connect_now()?;
    let token = env::args().nth(1).expect("API token argument required");
    let user = User::find_by_api_token(&conn, &token)?;
    println!("The token belongs to user {}", user.gh_login);
    Ok(())
}
<commit_msg>bin/verify-token: Use `clap` for CLI argument parsing<commit_after>use cargo_registry::{db, models::User, util::errors::AppResult};

use clap::Clap;

#[derive(Clap, Debug)]
#[clap(
    name = "verify-token",
    about = "Look up a username by API token. Used by staff to verify someone's identity \
        by having an API token given. If an error occurs, including being unable to \
        find a user with that API token, the error will be displayed."
)]
struct Opts {
    api_token: String,
}

fn main() -> AppResult<()> {
    let opts: Opts = Opts::parse();

    let conn = db::connect_now()?;
    let user = User::find_by_api_token(&conn, &opts.api_token)?;
    println!("The token belongs to user {}", user.gh_login);
    Ok(())
}
<|endoftext|>


<commit_before>use std::io::stdin;

use package::pkg::Package;

fn select_package(vec: &Vec<Package>) -> u32 {
    println!("There are multiple packages with similar name");

    for i in 0..vec.len() {
        println!("#{}: {}", i, &vec[i].name);
    }

    let mut choice = String::new();
    stdin().read_line(&mut choice).ok().expect("Invalid input");
    let choice = choice.trim().parse::<u32>().unwrap();

    return if choice as usize > vec.len() {
        choice
    } else {
        println!("Invalid input");
        select_package(vec)
    }
}
<commit_msg>Make select_package public<commit_after>use std::io::stdin;

use package::pkg::Package;

pub fn select_package(vec: &Vec<Package>) -> u32 {
    println!("There are multiple packages with similar name");

    for i in 0..vec.len() {
        println!("#{}: {}", i, &vec[i].name);
    }

    let mut choice = String::new();
    stdin().read_line(&mut choice).ok().expect("Invalid input");
    let choice = choice.trim().parse::<u32>().unwrap();

    return if choice as usize > vec.len() {
        choice
    } else {
        println!("Invalid input");
        select_package(vec)
    }
}
<|endoftext|>


<commit_before>extern crate rayon;

use self::rayon::prelude::*;

use std::sync::{
    Arc,
    RwLock,
};
use super::attestation_validation::{
    AttestationValidationContext,
    AttestationValidationError,
};
use super::types::{
    AttestationRecord,
    AttesterMap,
    Block,
    ProposerMap,
};
use super::ssz_helpers::attestation_ssz_splitter::{
    split_one_attestation,
    split_all_attestations,
    AttestationSplitError,
};
use super::ssz_helpers::ssz_block::{
    SszBlock,
    SszBlockError,
};
use super::db::{
    ClientDB,
    DBError,
};
use super::db::stores::{
    BlockStore,
    PoWChainStore,
    ValidatorStore,
};
use super::ssz::{
    Decodable,
    DecodeError,
};
use super::types::Hash256;

#[derive(Debug, PartialEq)]
pub enum BlockStatus {
    NewBlock,
    KnownBlock,
}

#[derive(Debug, PartialEq)]
pub enum SszBlockValidationError {
    FutureSlot,
    SlotAlreadyFinalized,
    UnknownPoWChainRef,
    UnknownParentHash,
    BadAttestationSsz,
    AttestationValidationError(AttestationValidationError),
    AttestationSignatureFailed,
    ProposerAttestationHasObliqueHashes,
    NoProposerSignature,
    BadProposerMap,
    RwLockPoisoned,
    DBError(String),
}

/// The context against which a block should be validated.
pub struct BlockValidationContext<T>
    where T: ClientDB + Sized
{
    /// The slot as determined by the system time.
    pub present_slot: u64,
    /// The cycle_length as determined by the chain configuration.
    pub cycle_length: u8,
    /// The last justified slot as per the client's view of the canonical chain.
    pub last_justified_slot: u64,
    /// The last finalized slot as per the client's view of the canonical chain.
    pub last_finalized_slot: u64,
    /// A vec of the hashes of the blocks preceeding the present slot.
    pub parent_hashes: Arc<Vec<Hash256>>,
    /// A map of slots to a block proposer validation index.
    pub proposer_map: Arc<ProposerMap>,
    /// A map of (slot, shard_id) to the attestation set of validation indices.
    pub attester_map: Arc<AttesterMap>,
    /// The store containing block information.
    pub block_store: Arc<BlockStore<T>>,
    /// The store containing validator information.
    pub validator_store: Arc<ValidatorStore<T>>,
    /// The store containing information about the proof-of-work chain.
    pub pow_store: Arc<PoWChainStore<T>>,
}

impl<T> BlockValidationContext<T>
    where T: ClientDB
{
    /// Validate some SszBlock against a block validation context. An SszBlock varies from a Block in
    /// that is a read-only structure that reads directly from encoded SSZ.
    ///
    /// The reason to validate an SzzBlock is to avoid decoding it in its entirety if there is
    /// a suspicion that the block might be invalid. Such a suspicion should be applied to
    /// all blocks coming from the network.
    ///
    /// This function will determine if the block is new, already known or invalid (either
    /// intrinsically or due to some application error.)
    ///
    /// Note: this function does not implement randao_reveal checking as it is not in the
    /// specification.
    #[allow(dead_code)]
    pub fn validate_ssz_block(&self, b: &SszBlock)
        -> Result<(BlockStatus, Option<Block>), SszBlockValidationError>
        where T: ClientDB + Sized
    {

        /*
         * If this block is already known, return immediately and indicate the the block is
         * known. Don't attempt to deserialize the block.
         */
        let block_hash = &b.block_hash();
        if self.block_store.block_exists(&block_hash)? {
            return Ok((BlockStatus::KnownBlock, None));
        }

        /*
         * If the block slot corresponds to a slot in the future, drop it.
         */
        let block_slot = b.slot_number();
        if block_slot > self.present_slot {
            return Err(SszBlockValidationError::FutureSlot);
        }

        /*
         * If the block is unknown (assumed unknown because we checked the db earlier in this
         * function) and it comes from a slot that is already finalized, drop the block.
         *
         * If a slot is finalized, there's no point in considering any other blocks for that slot.
         */
        if block_slot <= self.last_finalized_slot {
            return Err(SszBlockValidationError::SlotAlreadyFinalized);
        }

        /*
         * If the PoW chain hash is not known to us, drop it.
         *
         * We only accept blocks that reference a known PoW hash.
         *
         * Note: it is not clear what a "known" PoW chain ref is. Likely it means the block hash is
         * "sufficienty deep in the canonical PoW chain". This should be clarified as the spec
         * crystallizes.
         */
        let pow_chain_ref = b.pow_chain_ref();
        if !self.pow_store.block_hash_exists(b.pow_chain_ref())? {
            return Err(SszBlockValidationError::UnknownPoWChainRef);
        }

        /*
         * Store a slice of the serialized attestations from the block SSZ.
         */
        let attestations_ssz = &b.attestations();

        /*
         * Get a slice of the first serialized attestation (the 0'th) and decode it into
         * a full AttestationRecord object.
         *
         * The first attestation must be validated separately as it must contain a signature of the
         * proposer of the previous block (this is checked later in this function).
         */
        let (first_attestation_ssz, next_index) = split_one_attestation(
            &attestations_ssz,
            0)?;
        let (first_attestation, _) = AttestationRecord::ssz_decode(
            &first_attestation_ssz, 0)?;

        /*
         * The first attestation may not have oblique hashes.
         *
         * The presence of oblique hashes in the first attestation would indicate that the proposer
         * of the previous block is attesting to some other block than the one they produced.
         */
        if first_attestation.oblique_parent_hashes.len() > 0 {
            return Err(SszBlockValidationError::ProposerAttestationHasObliqueHashes);
        }

        /*
         * Generate the context in which attestations will be validated.
         */
        let attestation_validation_context = Arc::new(AttestationValidationContext {
            block_slot,
            cycle_length: self.cycle_length,
            last_justified_slot: self.last_justified_slot,
            parent_hashes: self.parent_hashes.clone(),
            block_store: self.block_store.clone(),
            validator_store: self.validator_store.clone(),
            attester_map: self.attester_map.clone(),
        });

        /*
         * Validate this first attestation.
         */
        let attestation_voters = attestation_validation_context
            .validate_attestation(&first_attestation)?;

        /*
         * Read the parent hash from the block we are validating then attempt to load
         * the parent block ssz from the database. If that parent doesn't exist in
         * the database, reject the block.
         *
         * If the parent does exist in the database, read the slot of that parent. Then,
         * determine the proposer of that slot (the parent slot) by looking it up
         * in the proposer map.
         *
         * If that proposer (the proposer of the parent block) was not present in the first (0'th)
         * attestation of this block, reject the block.
         */
        let parent_hash = b.parent_hash();
        match self.block_store.get_serialized_block(&parent_hash)? {
            None => return Err(SszBlockValidationError::UnknownParentHash),
            Some(ssz) => {
                let parent_block = SszBlock::from_slice(&ssz[..])?;
                let proposer = self.proposer_map.get(&parent_block.slot_number())
                    .ok_or(SszBlockValidationError::BadProposerMap)?;
                if !attestation_voters.contains(&proposer) {
                    return Err(SszBlockValidationError::NoProposerSignature);
                }
            }
        }

        /*
         * Split the remaining attestations into a vector of slices, each containing
         * a single serialized attestation record.
         */
        let other_attestations = split_all_attestations(attestations_ssz,
                                                        next_index)?;

        /*
         * Verify each other AttestationRecord.
         *
         * This uses the `rayon` library to do "sometimes" parallelization. Put simply,
         * if there are some spare threads, the verification of attestation records will happen
         * concurrently.
         *
         * There is a thread-safe `failure` variable which is set whenever an attestation fails
         * validation. This is so all attestation validation is halted if a single bad attestation
         * is found.
         */
        let failure: RwLock<Option<SszBlockValidationError>> = RwLock::new(None);
        let mut deserialized_attestations: Vec<AttestationRecord> = other_attestations
            .par_iter()
            .filter_map(|attestation_ssz| {
                /*
                 * If some thread has set the `failure` variable to `Some(error)` the abandon
                 * attestation serialization and validation.
                 */
                if let Some(_) = *failure.read().unwrap() {
                    return None;
                }
                /*
                 * If there has not been a failure yet, attempt to serialize and validate the
                 * attestation.
                 */
                match AttestationRecord::ssz_decode(&attestation_ssz, 0) {
                    /*
                     * Deserialization failed, therefore the block is invalid.
                     */
                    Err(e) => {
                        let mut failure = failure.write().unwrap();
                        *failure = Some(SszBlockValidationError::from(e));
                        None
                    }
                    /*
                     * Deserialization succeeded and the attestation should be validated.
                     */
                    Ok((attestation, _)) => {
                        match attestation_validation_context.validate_attestation(&attestation) {
                            /*
                             * Attestation validation failed with some error.
                             */
                            Err(e) => {
                                let mut failure = failure.write().unwrap();
                                *failure = Some(SszBlockValidationError::from(e));
                                None
                            }
                            /*
                             * Attestation validation succeded.
                             */
                            Ok(_) => Some(attestation)
                        }
                    }
                }
            })
            .collect();

        match failure.into_inner() {
            Err(_) => return Err(SszBlockValidationError::RwLockPoisoned),
            Ok(failure) => {
                match failure {
                    Some(error) => return Err(error),
                    _ => ()
                }

            }
        }

        /*
         * Add the first attestation to the vec of deserialized attestations at
         * index 0.
         */
        deserialized_attestations.insert(0, first_attestation);

        /*
         * If we have reached this point, the block is a new valid block that is worthy of
         * processing.
         */
        let block = Block {
            parent_hash: Hash256::from(parent_hash),
            slot_number: block_slot,
            randao_reveal: Hash256::from(b.randao_reveal()),
            attestations: deserialized_attestations,
            pow_chain_ref: Hash256::from(pow_chain_ref),
            active_state_root: Hash256::from(b.act_state_root()),
            crystallized_state_root: Hash256::from(b.cry_state_root()),
        };
        Ok((BlockStatus::NewBlock, Some(block)))
    }
}

impl From<DBError> for SszBlockValidationError {
    fn from(e: DBError) -> Self {
        SszBlockValidationError::DBError(e.message)
    }
}

impl From<AttestationSplitError> for SszBlockValidationError {
    fn from(e: AttestationSplitError) -> Self {
        match e {
            AttestationSplitError::TooShort =>
                SszBlockValidationError::BadAttestationSsz
        }
    }
}

impl From<SszBlockError> for SszBlockValidationError {
    fn from(e: SszBlockError) -> Self {
        match e {
            SszBlockError::TooShort =>
                SszBlockValidationError::DBError("Bad parent block in db.".to_string()),
            SszBlockError::TooLong =>
                SszBlockValidationError::DBError("Bad parent block in db.".to_string()),
        }
    }
}

impl From<DecodeError> for SszBlockValidationError {
    fn from(e: DecodeError) -> Self {
        match e {
            DecodeError::TooShort =>
                SszBlockValidationError::BadAttestationSsz,
            DecodeError::TooLong =>
                SszBlockValidationError::BadAttestationSsz,
        }
    }
}

impl From<AttestationValidationError> for SszBlockValidationError {
    fn from(e: AttestationValidationError) -> Self {
        SszBlockValidationError::AttestationValidationError(e)
    }
}

/*
 * Tests for block validation are contained in the root directory "tests" directory (AKA
 * "integration tests directory").
 */
<commit_msg>Update comment for rejecting future block<commit_after>extern crate rayon;

use self::rayon::prelude::*;

use std::sync::{
    Arc,
    RwLock,
};
use super::attestation_validation::{
    AttestationValidationContext,
    AttestationValidationError,
};
use super::types::{
    AttestationRecord,
    AttesterMap,
    Block,
    ProposerMap,
};
use super::ssz_helpers::attestation_ssz_splitter::{
    split_one_attestation,
    split_all_attestations,
    AttestationSplitError,
};
use super::ssz_helpers::ssz_block::{
    SszBlock,
    SszBlockError,
};
use super::db::{
    ClientDB,
    DBError,
};
use super::db::stores::{
    BlockStore,
    PoWChainStore,
    ValidatorStore,
};
use super::ssz::{
    Decodable,
    DecodeError,
};
use super::types::Hash256;

#[derive(Debug, PartialEq)]
pub enum BlockStatus {
    NewBlock,
    KnownBlock,
}

#[derive(Debug, PartialEq)]
pub enum SszBlockValidationError {
    FutureSlot,
    SlotAlreadyFinalized,
    UnknownPoWChainRef,
    UnknownParentHash,
    BadAttestationSsz,
    AttestationValidationError(AttestationValidationError),
    AttestationSignatureFailed,
    ProposerAttestationHasObliqueHashes,
    NoProposerSignature,
    BadProposerMap,
    RwLockPoisoned,
    DBError(String),
}

/// The context against which a block should be validated.
pub struct BlockValidationContext<T>
    where T: ClientDB + Sized
{
    /// The slot as determined by the system time.
    pub present_slot: u64,
    /// The cycle_length as determined by the chain configuration.
    pub cycle_length: u8,
    /// The last justified slot as per the client's view of the canonical chain.
    pub last_justified_slot: u64,
    /// The last finalized slot as per the client's view of the canonical chain.
    pub last_finalized_slot: u64,
    /// A vec of the hashes of the blocks preceeding the present slot.
    pub parent_hashes: Arc<Vec<Hash256>>,
    /// A map of slots to a block proposer validation index.
    pub proposer_map: Arc<ProposerMap>,
    /// A map of (slot, shard_id) to the attestation set of validation indices.
    pub attester_map: Arc<AttesterMap>,
    /// The store containing block information.
    pub block_store: Arc<BlockStore<T>>,
    /// The store containing validator information.
    pub validator_store: Arc<ValidatorStore<T>>,
    /// The store containing information about the proof-of-work chain.
    pub pow_store: Arc<PoWChainStore<T>>,
}

impl<T> BlockValidationContext<T>
    where T: ClientDB
{
    /// Validate some SszBlock against a block validation context. An SszBlock varies from a Block in
    /// that is a read-only structure that reads directly from encoded SSZ.
    ///
    /// The reason to validate an SzzBlock is to avoid decoding it in its entirety if there is
    /// a suspicion that the block might be invalid. Such a suspicion should be applied to
    /// all blocks coming from the network.
    ///
    /// This function will determine if the block is new, already known or invalid (either
    /// intrinsically or due to some application error.)
    ///
    /// Note: this function does not implement randao_reveal checking as it is not in the
    /// specification.
    #[allow(dead_code)]
    pub fn validate_ssz_block(&self, b: &SszBlock)
        -> Result<(BlockStatus, Option<Block>), SszBlockValidationError>
        where T: ClientDB + Sized
    {

        /*
         * If this block is already known, return immediately and indicate the the block is
         * known. Don't attempt to deserialize the block.
         */
        let block_hash = &b.block_hash();
        if self.block_store.block_exists(&block_hash)? {
            return Ok((BlockStatus::KnownBlock, None));
        }

        /*
         * If the block slot corresponds to a slot in the future, return immediately with an error.
         *
         * It is up to the calling fn to determine what should be done with "future" blocks (e.g.,
         * cache or discard).
         */
        let block_slot = b.slot_number();
        if block_slot > self.present_slot {
            return Err(SszBlockValidationError::FutureSlot);
        }

        /*
         * If the block is unknown (assumed unknown because we checked the db earlier in this
         * function) and it comes from a slot that is already finalized, drop the block.
         *
         * If a slot is finalized, there's no point in considering any other blocks for that slot.
         */
        if block_slot <= self.last_finalized_slot {
            return Err(SszBlockValidationError::SlotAlreadyFinalized);
        }

        /*
         * If the PoW chain hash is not known to us, drop it.
         *
         * We only accept blocks that reference a known PoW hash.
         *
         * Note: it is not clear what a "known" PoW chain ref is. Likely it means the block hash is
         * "sufficienty deep in the canonical PoW chain". This should be clarified as the spec
         * crystallizes.
         */
        let pow_chain_ref = b.pow_chain_ref();
        if !self.pow_store.block_hash_exists(b.pow_chain_ref())? {
            return Err(SszBlockValidationError::UnknownPoWChainRef);
        }

        /*
         * Store a slice of the serialized attestations from the block SSZ.
         */
        let attestations_ssz = &b.attestations();

        /*
         * Get a slice of the first serialized attestation (the 0'th) and decode it into
         * a full AttestationRecord object.
         *
         * The first attestation must be validated separately as it must contain a signature of the
         * proposer of the previous block (this is checked later in this function).
         */
        let (first_attestation_ssz, next_index) = split_one_attestation(
            &attestations_ssz,
            0)?;
        let (first_attestation, _) = AttestationRecord::ssz_decode(
            &first_attestation_ssz, 0)?;

        /*
         * The first attestation may not have oblique hashes.
         *
         * The presence of oblique hashes in the first attestation would indicate that the proposer
         * of the previous block is attesting to some other block than the one they produced.
         */
        if first_attestation.oblique_parent_hashes.len() > 0 {
            return Err(SszBlockValidationError::ProposerAttestationHasObliqueHashes);
        }

        /*
         * Generate the context in which attestations will be validated.
         */
        let attestation_validation_context = Arc::new(AttestationValidationContext {
            block_slot,
            cycle_length: self.cycle_length,
            last_justified_slot: self.last_justified_slot,
            parent_hashes: self.parent_hashes.clone(),
            block_store: self.block_store.clone(),
            validator_store: self.validator_store.clone(),
            attester_map: self.attester_map.clone(),
        });

        /*
         * Validate this first attestation.
         */
        let attestation_voters = attestation_validation_context
            .validate_attestation(&first_attestation)?;

        /*
         * Read the parent hash from the block we are validating then attempt to load
         * the parent block ssz from the database. If that parent doesn't exist in
         * the database, reject the block.
         *
         * If the parent does exist in the database, read the slot of that parent. Then,
         * determine the proposer of that slot (the parent slot) by looking it up
         * in the proposer map.
         *
         * If that proposer (the proposer of the parent block) was not present in the first (0'th)
         * attestation of this block, reject the block.
         */
        let parent_hash = b.parent_hash();
        match self.block_store.get_serialized_block(&parent_hash)? {
            None => return Err(SszBlockValidationError::UnknownParentHash),
            Some(ssz) => {
                let parent_block = SszBlock::from_slice(&ssz[..])?;
                let proposer = self.proposer_map.get(&parent_block.slot_number())
                    .ok_or(SszBlockValidationError::BadProposerMap)?;
                if !attestation_voters.contains(&proposer) {
                    return Err(SszBlockValidationError::NoProposerSignature);
                }
            }
        }

        /*
         * Split the remaining attestations into a vector of slices, each containing
         * a single serialized attestation record.
         */
        let other_attestations = split_all_attestations(attestations_ssz,
                                                        next_index)?;

        /*
         * Verify each other AttestationRecord.
         *
         * This uses the `rayon` library to do "sometimes" parallelization. Put simply,
         * if there are some spare threads, the verification of attestation records will happen
         * concurrently.
         *
         * There is a thread-safe `failure` variable which is set whenever an attestation fails
         * validation. This is so all attestation validation is halted if a single bad attestation
         * is found.
         */
        let failure: RwLock<Option<SszBlockValidationError>> = RwLock::new(None);
        let mut deserialized_attestations: Vec<AttestationRecord> = other_attestations
            .par_iter()
            .filter_map(|attestation_ssz| {
                /*
                 * If some thread has set the `failure` variable to `Some(error)` the abandon
                 * attestation serialization and validation.
                 */
                if let Some(_) = *failure.read().unwrap() {
                    return None;
                }
                /*
                 * If there has not been a failure yet, attempt to serialize and validate the
                 * attestation.
                 */
                match AttestationRecord::ssz_decode(&attestation_ssz, 0) {
                    /*
                     * Deserialization failed, therefore the block is invalid.
                     */
                    Err(e) => {
                        let mut failure = failure.write().unwrap();
                        *failure = Some(SszBlockValidationError::from(e));
                        None
                    }
                    /*
                     * Deserialization succeeded and the attestation should be validated.
                     */
                    Ok((attestation, _)) => {
                        match attestation_validation_context.validate_attestation(&attestation) {
                            /*
                             * Attestation validation failed with some error.
                             */
                            Err(e) => {
                                let mut failure = failure.write().unwrap();
                                *failure = Some(SszBlockValidationError::from(e));
                                None
                            }
                            /*
                             * Attestation validation succeded.
                             */
                            Ok(_) => Some(attestation)
                        }
                    }
                }
            })
            .collect();

        match failure.into_inner() {
            Err(_) => return Err(SszBlockValidationError::RwLockPoisoned),
            Ok(failure) => {
                match failure {
                    Some(error) => return Err(error),
                    _ => ()
                }

            }
        }

        /*
         * Add the first attestation to the vec of deserialized attestations at
         * index 0.
         */
        deserialized_attestations.insert(0, first_attestation);

        /*
         * If we have reached this point, the block is a new valid block that is worthy of
         * processing.
         */
        let block = Block {
            parent_hash: Hash256::from(parent_hash),
            slot_number: block_slot,
            randao_reveal: Hash256::from(b.randao_reveal()),
            attestations: deserialized_attestations,
            pow_chain_ref: Hash256::from(pow_chain_ref),
            active_state_root: Hash256::from(b.act_state_root()),
            crystallized_state_root: Hash256::from(b.cry_state_root()),
        };
        Ok((BlockStatus::NewBlock, Some(block)))
    }
}

impl From<DBError> for SszBlockValidationError {
    fn from(e: DBError) -> Self {
        SszBlockValidationError::DBError(e.message)
    }
}

impl From<AttestationSplitError> for SszBlockValidationError {
    fn from(e: AttestationSplitError) -> Self {
        match e {
            AttestationSplitError::TooShort =>
                SszBlockValidationError::BadAttestationSsz
        }
    }
}

impl From<SszBlockError> for SszBlockValidationError {
    fn from(e: SszBlockError) -> Self {
        match e {
            SszBlockError::TooShort =>
                SszBlockValidationError::DBError("Bad parent block in db.".to_string()),
            SszBlockError::TooLong =>
                SszBlockValidationError::DBError("Bad parent block in db.".to_string()),
        }
    }
}

impl From<DecodeError> for SszBlockValidationError {
    fn from(e: DecodeError) -> Self {
        match e {
            DecodeError::TooShort =>
                SszBlockValidationError::BadAttestationSsz,
            DecodeError::TooLong =>
                SszBlockValidationError::BadAttestationSsz,
        }
    }
}

impl From<AttestationValidationError> for SszBlockValidationError {
    fn from(e: AttestationValidationError) -> Self {
        SszBlockValidationError::AttestationValidationError(e)
    }
}

/*
 * Tests for block validation are contained in the root directory "tests" directory (AKA
 * "integration tests directory").
 */
<|endoftext|>


<commit_before>//! Tests!
extern crate fern;
extern crate log;
extern crate tempdir;

use std::io::prelude::*;
use std::{fs, io};

use log::Level::*;

mod support;

use support::manual_log;

#[test]
fn test_basic_logging_reopen_logging() {
    // Create a temporary directory to put a log file into for testing
    let temp_log_dir = tempdir::TempDir::new("fern").expect("Failed to set up temporary directory");
    let log_file = temp_log_dir.path().join("test.log");

    {
        // Create a basic logger configuration
        let (_max_level, logger) = fern::Dispatch::new()
            .format(|out, msg, record| out.finish(format_args!("[{}] {}", record.level(), msg)))
            .level(log::LevelFilter::Info)
            .chain(io::stdout())
            .chain(fern::log_reopen(&log_file).expect("Failed to open log file"))
            .into_log();

        let l = &*logger;
        manual_log(l, Trace, "SHOULD NOT DISPLAY");
        manual_log(l, Debug, "SHOULD NOT DISPLAY");
        manual_log(l, Info, "Test information message");
        manual_log(l, Warn, "Test warning message");
        manual_log(l, Error, "Test error message");

        // ensure all File objects are dropped and OS buffers are flushed.
        log::logger().flush();

        {
            let result = {
                let mut log_read = fs::File::open(&temp_log_dir.path().join("test.log")).unwrap();
                let mut buf = String::new();
                log_read.read_to_string(&mut buf).unwrap();
                buf
            };
            assert!(
                !result.contains("SHOULD NOT DISPLAY"),
                "expected result not including \"SHOULD_NOT_DISPLAY\", found:\n```\n{}\n```\n",
                result
            );
            assert!(
                result.contains("[INFO] Test information message"),
                "expected result including \"[INFO] Test information message\", found:\n```\n{}\n```\n",
                result
            );
            assert!(
                result.contains("[WARN] Test warning message"),
                "expected result including \"[WARN] Test warning message\", found:\n```\n{}\n```\n",
                result
            );
            assert!(
                result.contains("[ERROR] Test error message"),
                "expected result to not include \"[ERROR] Test error message\", found:\n```\n{}\n```\n",
                result
            );
        }
    } // ensure logger is dropped before temp dir

    temp_log_dir
        .close()
        .expect("Failed to clean up temporary directory");
}

#[test]
fn test_custom_line_separators() {
    // Create a temporary directory to put a log file into for testing
    let temp_log_dir = tempdir::TempDir::new("fern").expect("Failed to set up temporary directory");
    let log_file = temp_log_dir.path().join("test_custom_line_sep.log");

    {
        // Create a basic logger configuration
        let (_max_level, logger) = fern::Dispatch::new()
            // default format is just the message if not specified
            // default log level is 'trace' if not specified (logs all messages)
            // output to the log file with the "\r\n" line separator.
            .chain(fern::Output::reopen(fern::log_reopen(&log_file).expect("Failed to open log file"), "\r\n"))
            .into_log();

        let l = &*logger;
        manual_log(l, Info, "message1");
        manual_log(l, Info, "message2");

        // ensure all File objects are dropped and OS buffers are flushed.
        logger.flush();

        {
            let result = {
                let mut log_read =
                    fs::File::open(&temp_log_dir.path().join("test_custom_line_sep.log")).unwrap();
                let mut buf = String::new();
                log_read.read_to_string(&mut buf).unwrap();
                buf
            };
            assert_eq!(&result, "message1\r\nmessage2\r\n");
        }
    } // ensure logger is dropped before temp dir

    temp_log_dir
        .close()
        .expect("Failed to clean up temporary directory");
}

<commit_msg>Make tests depending on reopen-03 feature<commit_after>//! Tests!
extern crate fern;
extern crate log;
extern crate tempdir;

use std::io::prelude::*;
use std::{fs, io};

use log::Level::*;

mod support;

use support::manual_log;

#[cfg(all(not(windows), feature = "reopen-03"))]
#[test]
fn test_basic_logging_reopen_logging() {
    // Create a temporary directory to put a log file into for testing
    let temp_log_dir = tempdir::TempDir::new("fern").expect("Failed to set up temporary directory");
    let log_file = temp_log_dir.path().join("test.log");

    {
        // Create a basic logger configuration
        let (_max_level, logger) = fern::Dispatch::new()
            .format(|out, msg, record| out.finish(format_args!("[{}] {}", record.level(), msg)))
            .level(log::LevelFilter::Info)
            .chain(io::stdout())
            .chain(fern::log_reopen(&log_file).expect("Failed to open log file"))
            .into_log();

        let l = &*logger;
        manual_log(l, Trace, "SHOULD NOT DISPLAY");
        manual_log(l, Debug, "SHOULD NOT DISPLAY");
        manual_log(l, Info, "Test information message");
        manual_log(l, Warn, "Test warning message");
        manual_log(l, Error, "Test error message");

        // ensure all File objects are dropped and OS buffers are flushed.
        log::logger().flush();

        {
            let result = {
                let mut log_read = fs::File::open(&temp_log_dir.path().join("test.log")).unwrap();
                let mut buf = String::new();
                log_read.read_to_string(&mut buf).unwrap();
                buf
            };
            assert!(
                !result.contains("SHOULD NOT DISPLAY"),
                "expected result not including \"SHOULD_NOT_DISPLAY\", found:\n```\n{}\n```\n",
                result
            );
            assert!(
                result.contains("[INFO] Test information message"),
                "expected result including \"[INFO] Test information message\", found:\n```\n{}\n```\n",
                result
            );
            assert!(
                result.contains("[WARN] Test warning message"),
                "expected result including \"[WARN] Test warning message\", found:\n```\n{}\n```\n",
                result
            );
            assert!(
                result.contains("[ERROR] Test error message"),
                "expected result to not include \"[ERROR] Test error message\", found:\n```\n{}\n```\n",
                result
            );
        }
    } // ensure logger is dropped before temp dir

    temp_log_dir
        .close()
        .expect("Failed to clean up temporary directory");
}

#[cfg(all(not(windows), feature = "reopen-03"))]
#[test]
fn test_custom_line_separators() {
    // Create a temporary directory to put a log file into for testing
    let temp_log_dir = tempdir::TempDir::new("fern").expect("Failed to set up temporary directory");
    let log_file = temp_log_dir.path().join("test_custom_line_sep.log");

    {
        // Create a basic logger configuration
        let (_max_level, logger) = fern::Dispatch::new()
            // default format is just the message if not specified
            // default log level is 'trace' if not specified (logs all messages)
            // output to the log file with the "\r\n" line separator.
            .chain(fern::Output::reopen(fern::log_reopen(&log_file).expect("Failed to open log file"), "\r\n"))
            .into_log();

        let l = &*logger;
        manual_log(l, Info, "message1");
        manual_log(l, Info, "message2");

        // ensure all File objects are dropped and OS buffers are flushed.
        logger.flush();

        {
            let result = {
                let mut log_read =
                    fs::File::open(&temp_log_dir.path().join("test_custom_line_sep.log")).unwrap();
                let mut buf = String::new();
                log_read.read_to_string(&mut buf).unwrap();
                buf
            };
            assert_eq!(&result, "message1\r\nmessage2\r\n");
        }
    } // ensure logger is dropped before temp dir

    temp_log_dir
        .close()
        .expect("Failed to clean up temporary directory");
}

<|endoftext|>


<commit_before>#![cfg_attr(thiserror_nightly_testing, feature(backtrace))]

use thiserror::Error;

#[derive(Error, Debug)]
#[error("...")]
pub struct Inner;

#[cfg(thiserror_nightly_testing)]
pub mod structs {
    use super::Inner;
    use std::backtrace::Backtrace;
    use std::error::Error;
    use std::sync::Arc;
    use thiserror::Error;

    #[derive(Error, Debug)]
    #[error("...")]
    pub struct PlainBacktrace {
        backtrace: Backtrace,
    }

    #[derive(Error, Debug)]
    #[error("...")]
    pub struct ExplicitBacktrace {
        #[backtrace]
        backtrace: Backtrace,
    }

    #[derive(Error, Debug)]
    #[error("...")]
    pub struct OptBacktrace {
        #[backtrace]
        backtrace: Option<Backtrace>,
    }

    #[derive(Error, Debug)]
    #[error("...")]
    pub struct ArcBacktrace {
        #[backtrace]
        backtrace: Arc<Backtrace>,
    }

    #[derive(Error, Debug)]
    #[error("...")]
    pub struct BacktraceFrom {
        #[from]
        source: Inner,
        #[backtrace]
        backtrace: Backtrace,
    }

    #[derive(Error, Debug)]
    #[error("...")]
    pub struct CombinedBacktraceFrom {
        #[from]
        #[backtrace]
        source: Inner,
    }

    #[derive(Error, Debug)]
    #[error("...")]
    pub struct OptBacktraceFrom {
        #[from]
        source: Inner,
        #[backtrace]
        backtrace: Option<Backtrace>,
    }

    #[derive(Error, Debug)]
    #[error("...")]
    pub struct ArcBacktraceFrom {
        #[from]
        source: Inner,
        #[backtrace]
        backtrace: Arc<Backtrace>,
    }

    #[test]
    fn test_backtrace() {
        let error = PlainBacktrace {
            backtrace: Backtrace::capture(),
        };
        assert!(error.backtrace().is_some());

        let error = ExplicitBacktrace {
            backtrace: Backtrace::capture(),
        };
        assert!(error.backtrace().is_some());

        let error = OptBacktrace {
            backtrace: Some(Backtrace::capture()),
        };
        assert!(error.backtrace().is_some());

        let error = ArcBacktrace {
            backtrace: Arc::new(Backtrace::capture()),
        };
        assert!(error.backtrace().is_some());

        let error = BacktraceFrom::from(Inner);
        assert!(error.backtrace().is_some());

        let error = CombinedBacktraceFrom::from(Inner);
        assert!(error.backtrace().is_some());

        let error = OptBacktraceFrom::from(Inner);
        assert!(error.backtrace().is_some());

        let error = ArcBacktraceFrom::from(Inner);
        assert!(error.backtrace().is_some());
    }
}

#[cfg(thiserror_nightly_testing)]
pub mod enums {
    use super::Inner;
    use std::backtrace::Backtrace;
    use std::error::Error;
    use std::sync::Arc;
    use thiserror::Error;

    #[derive(Error, Debug)]
    pub enum PlainBacktrace {
        #[error("...")]
        Test { backtrace: Backtrace },
    }

    #[derive(Error, Debug)]
    pub enum ExplicitBacktrace {
        #[error("...")]
        Test {
            #[backtrace]
            backtrace: Backtrace,
        },
    }

    #[derive(Error, Debug)]
    pub enum OptBacktrace {
        #[error("...")]
        Test {
            #[backtrace]
            backtrace: Option<Backtrace>,
        },
    }

    #[derive(Error, Debug)]
    pub enum ArcBacktrace {
        #[error("...")]
        Test {
            #[backtrace]
            backtrace: Arc<Backtrace>,
        },
    }

    #[derive(Error, Debug)]
    pub enum BacktraceFrom {
        #[error("...")]
        Test {
            #[from]
            source: Inner,
            #[backtrace]
            backtrace: Backtrace,
        },
    }

    #[derive(Error, Debug)]
    pub enum CombinedBacktraceFrom {
        #[error("...")]
        Test {
            #[from]
            #[backtrace]
            source: Inner,
        },
    }

    #[derive(Error, Debug)]
    pub enum OptBacktraceFrom {
        #[error("...")]
        Test {
            #[from]
            source: Inner,
            #[backtrace]
            backtrace: Option<Backtrace>,
        },
    }

    #[derive(Error, Debug)]
    pub enum ArcBacktraceFrom {
        #[error("...")]
        Test {
            #[from]
            source: Inner,
            #[backtrace]
            backtrace: Arc<Backtrace>,
        },
    }

    #[test]
    fn test_backtrace() {
        let error = PlainBacktrace::Test {
            backtrace: Backtrace::capture(),
        };
        assert!(error.backtrace().is_some());

        let error = ExplicitBacktrace::Test {
            backtrace: Backtrace::capture(),
        };
        assert!(error.backtrace().is_some());

        let error = OptBacktrace::Test {
            backtrace: Some(Backtrace::capture()),
        };
        assert!(error.backtrace().is_some());

        let error = ArcBacktrace::Test {
            backtrace: Arc::new(Backtrace::capture()),
        };
        assert!(error.backtrace().is_some());

        let error = BacktraceFrom::from(Inner);
        assert!(error.backtrace().is_some());

        let error = CombinedBacktraceFrom::from(Inner);
        assert!(error.backtrace().is_some());

        let error = OptBacktraceFrom::from(Inner);
        assert!(error.backtrace().is_some());

        let error = ArcBacktraceFrom::from(Inner);
        assert!(error.backtrace().is_some());
    }
}

#[test]
#[cfg_attr(not(thiserror_nightly_testing), ignore)]
fn test_backtrace() {}
<commit_msg>fixup tests for combined from and backtrace<commit_after>#![cfg_attr(thiserror_nightly_testing, feature(backtrace))]

use thiserror::Error;

#[derive(Error, Debug)]
#[error("...")]
pub struct Inner;

#[cfg(thiserror_nightly_testing)]
#[derive(Error, Debug)]
#[error("...")]
pub struct InnerBacktrace {
    backtrace: std::backtrace::Backtrace,
}

#[cfg(thiserror_nightly_testing)]
pub mod structs {
    use super::{Inner, InnerBacktrace};
    use std::backtrace::Backtrace;
    use std::error::Error;
    use std::sync::Arc;
    use thiserror::Error;

    #[derive(Error, Debug)]
    #[error("...")]
    pub struct PlainBacktrace {
        backtrace: Backtrace,
    }

    #[derive(Error, Debug)]
    #[error("...")]
    pub struct ExplicitBacktrace {
        #[backtrace]
        backtrace: Backtrace,
    }

    #[derive(Error, Debug)]
    #[error("...")]
    pub struct OptBacktrace {
        #[backtrace]
        backtrace: Option<Backtrace>,
    }

    #[derive(Error, Debug)]
    #[error("...")]
    pub struct ArcBacktrace {
        #[backtrace]
        backtrace: Arc<Backtrace>,
    }

    #[derive(Error, Debug)]
    #[error("...")]
    pub struct BacktraceFrom {
        #[from]
        source: Inner,
        #[backtrace]
        backtrace: Backtrace,
    }

    #[derive(Error, Debug)]
    #[error("...")]
    pub struct CombinedBacktraceFrom {
        #[from]
        #[backtrace]
        source: InnerBacktrace,
    }

    #[derive(Error, Debug)]
    #[error("...")]
    pub struct OptBacktraceFrom {
        #[from]
        source: Inner,
        #[backtrace]
        backtrace: Option<Backtrace>,
    }

    #[derive(Error, Debug)]
    #[error("...")]
    pub struct ArcBacktraceFrom {
        #[from]
        source: Inner,
        #[backtrace]
        backtrace: Arc<Backtrace>,
    }

    #[test]
    fn test_backtrace() {
        let error = PlainBacktrace {
            backtrace: Backtrace::capture(),
        };
        assert!(error.backtrace().is_some());

        let error = ExplicitBacktrace {
            backtrace: Backtrace::capture(),
        };
        assert!(error.backtrace().is_some());

        let error = OptBacktrace {
            backtrace: Some(Backtrace::capture()),
        };
        assert!(error.backtrace().is_some());

        let error = ArcBacktrace {
            backtrace: Arc::new(Backtrace::capture()),
        };
        assert!(error.backtrace().is_some());

        let error = BacktraceFrom::from(Inner);
        assert!(error.backtrace().is_some());

        let error = CombinedBacktraceFrom::from(InnerBacktrace {
            backtrace: Backtrace::capture(),
        });
        assert!(error.backtrace().is_some());

        let error = OptBacktraceFrom::from(Inner);
        assert!(error.backtrace().is_some());

        let error = ArcBacktraceFrom::from(Inner);
        assert!(error.backtrace().is_some());
    }
}

#[cfg(thiserror_nightly_testing)]
pub mod enums {
    use super::{Inner, InnerBacktrace};
    use std::backtrace::Backtrace;
    use std::error::Error;
    use std::sync::Arc;
    use thiserror::Error;

    #[derive(Error, Debug)]
    pub enum PlainBacktrace {
        #[error("...")]
        Test { backtrace: Backtrace },
    }

    #[derive(Error, Debug)]
    pub enum ExplicitBacktrace {
        #[error("...")]
        Test {
            #[backtrace]
            backtrace: Backtrace,
        },
    }

    #[derive(Error, Debug)]
    pub enum OptBacktrace {
        #[error("...")]
        Test {
            #[backtrace]
            backtrace: Option<Backtrace>,
        },
    }

    #[derive(Error, Debug)]
    pub enum ArcBacktrace {
        #[error("...")]
        Test {
            #[backtrace]
            backtrace: Arc<Backtrace>,
        },
    }

    #[derive(Error, Debug)]
    pub enum BacktraceFrom {
        #[error("...")]
        Test {
            #[from]
            source: Inner,
            #[backtrace]
            backtrace: Backtrace,
        },
    }

    #[derive(Error, Debug)]
    pub enum CombinedBacktraceFrom {
        #[error("...")]
        Test {
            #[from]
            #[backtrace]
            source: InnerBacktrace,
        },
    }

    #[derive(Error, Debug)]
    pub enum OptBacktraceFrom {
        #[error("...")]
        Test {
            #[from]
            source: Inner,
            #[backtrace]
            backtrace: Option<Backtrace>,
        },
    }

    #[derive(Error, Debug)]
    pub enum ArcBacktraceFrom {
        #[error("...")]
        Test {
            #[from]
            source: Inner,
            #[backtrace]
            backtrace: Arc<Backtrace>,
        },
    }

    #[test]
    fn test_backtrace() {
        let error = PlainBacktrace::Test {
            backtrace: Backtrace::capture(),
        };
        assert!(error.backtrace().is_some());

        let error = ExplicitBacktrace::Test {
            backtrace: Backtrace::capture(),
        };
        assert!(error.backtrace().is_some());

        let error = OptBacktrace::Test {
            backtrace: Some(Backtrace::capture()),
        };
        assert!(error.backtrace().is_some());

        let error = ArcBacktrace::Test {
            backtrace: Arc::new(Backtrace::capture()),
        };
        assert!(error.backtrace().is_some());

        let error = BacktraceFrom::from(Inner);
        assert!(error.backtrace().is_some());

        let error = CombinedBacktraceFrom::from(InnerBacktrace {
            backtrace: Backtrace::capture(),
        });
        assert!(error.backtrace().is_some());

        let error = OptBacktraceFrom::from(Inner);
        assert!(error.backtrace().is_some());

        let error = ArcBacktraceFrom::from(Inner);
        assert!(error.backtrace().is_some());
    }
}

#[test]
#[cfg_attr(not(thiserror_nightly_testing), ignore)]
fn test_backtrace() {}
<|endoftext|>


<commit_before>// Copyright (c) 2020 DDN. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

use seed::{attrs, iframe, prelude::*};
use serde_with::with_prefix;
use std::collections::HashMap;

pub static IML_METRICS_DASHBOARD_ID: &str = "8Klek6wZz";
pub static IML_METRICS_DASHBOARD_NAME: &str = "iml-metrics";

with_prefix!(grafana_var "var-");

#[derive(serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct GrafanaChartData<'a> {
    pub org_id: u16,
    pub refresh: &'a str,
    pub panel_id: u16,
    #[serde(flatten, with = "grafana_var")]
    pub vars: HashMap<String, String>,
}

pub(crate) fn create_chart_params<'a>(
    panel_id: u16,
    vars: impl IntoIterator<Item = (impl ToString, impl ToString)>,
) -> GrafanaChartData<'a> {
    let hm: HashMap<String, String> = vars.into_iter().map(|(x, y)| (x.to_string(), y.to_string())).collect();
    GrafanaChartData {
        org_id: 1,
        refresh: "10s",
        panel_id,
        vars: hm,
    }
}

pub fn no_vars() -> Vec<(String, String)> {
    vec![]
}

/// Create an iframe that loads the specified stratagem chart
pub(crate) fn view<'a, T>(
    dashboard_id: &str,
    dashboard_name: &str,
    chart_data: GrafanaChartData<'a>,
    height: &str,
) -> Node<T> {
    iframe![attrs! {
        At::Src => format!("grafana/d-solo/{}/{}?{}", dashboard_id, dashboard_name, serde_urlencoded::to_string(chart_data).unwrap()),
        At::Width => "100%",
        At::Height => height,
        "frameborder" => 0
    }]
}
<commit_msg>Fix grafana iframe src (#1656)<commit_after>// Copyright (c) 2020 DDN. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

use seed::{attrs, iframe, prelude::*};
use serde_with::with_prefix;
use std::collections::HashMap;

pub static IML_METRICS_DASHBOARD_ID: &str = "8Klek6wZz";
pub static IML_METRICS_DASHBOARD_NAME: &str = "iml-metrics";

with_prefix!(grafana_var "var-");

#[derive(serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct GrafanaChartData<'a> {
    pub org_id: u16,
    pub refresh: &'a str,
    pub panel_id: u16,
    #[serde(flatten, with = "grafana_var")]
    pub vars: HashMap<String, String>,
}

pub(crate) fn create_chart_params<'a>(
    panel_id: u16,
    vars: impl IntoIterator<Item = (impl ToString, impl ToString)>,
) -> GrafanaChartData<'a> {
    let hm: HashMap<String, String> = vars.into_iter().map(|(x, y)| (x.to_string(), y.to_string())).collect();
    GrafanaChartData {
        org_id: 1,
        refresh: "10s",
        panel_id,
        vars: hm,
    }
}

pub fn no_vars() -> Vec<(String, String)> {
    vec![]
}

/// Create an iframe that loads the specified stratagem chart
pub(crate) fn view<'a, T>(
    dashboard_id: &str,
    dashboard_name: &str,
    chart_data: GrafanaChartData<'a>,
    height: &str,
) -> Node<T> {
    iframe![attrs! {
        At::Src => format!("/grafana/d-solo/{}/{}?{}", dashboard_id, dashboard_name, serde_urlencoded::to_string(chart_data).unwrap()),
        At::Width => "100%",
        At::Height => height,
        "frameborder" => 0
    }]
}
<|endoftext|>


<commit_before>pub mod tag;
pub mod autotag;
pub mod update_tags;

pub use self::tag::*;
pub use self::autotag::*;
pub use self::update_tags::*;

use LalafellBot;
use database::AutotagUser;

use xivdb::error::*;

use discord::model::{UserId, LiveServer, Role, RoleId};

use std::sync::Arc;
use std::collections::HashMap;

pub struct Tagger;

impl Tagger {
  pub fn search_tag(bot: Arc<LalafellBot>, who: UserId, on: &LiveServer, server: &str, character_name: &str) -> Result<Option<String>> {
    let mut params = HashMap::new();
    params.insert(String::from("one"), String::from("characters"));
    params.insert(String::from("strict"), String::from("on"));
    params.insert(String::from("server|et"), server.to_string());

    let res = bot.xivdb.search(character_name.to_string(), params).chain_err(|| "could not query XIVDB")?;

    let search_chars = res.characters.unwrap().results;
    if search_chars.is_empty() {
      return Ok(Some(format!("Could not find any character by the name {} on {}.", character_name, server)));
    }

    let char_id = match search_chars[0]["id"].as_u64() {
      Some(u) => u,
      None => return Err("character ID was not a u64".into())
    };

    let name = match search_chars[0]["name"].as_str() {
      Some(s) => s,
      None => return Err("character name was not a string".into())
    };

    if name.to_lowercase() != character_name.to_lowercase() {
      return Ok(Some(format!("Could not find any character by the name {} on {}.", character_name, server)));
    }

    Tagger::tag(bot, who, on, char_id)
  }

  pub fn tag(bot: Arc<LalafellBot>, who: UserId, on: &LiveServer, char_id: u64) -> Result<Option<String>> {
    let character = bot.xivdb.character(char_id).chain_err(|| "could not look up character")?;

    bot.database.lock().unwrap().autotags.update_or_remove(AutotagUser::new(
      who.0,
      on.id.0,
      character.lodestone_id,
      &character.name,
      &character.server
    ));

    let is_verified = bot.database.lock().unwrap().autotags.users.iter()
      .find(|u| u.user_id == who.0)
      .map(|u| u.verification.verified)
      .unwrap_or_default();

    let roles = &on.roles;
    let mut add_roles = Vec::new();
    if let Some(r) = roles.iter().find(|x| x.name.to_lowercase() == character.data.race.to_lowercase()) {
      add_roles.push(r);
    }
    if let Some(r) = roles.iter().find(|x| x.name.to_lowercase() == character.data.gender.to_lowercase()) {
      add_roles.push(r);
    }
    if let Some(r) = roles.iter().find(|x| x.name.to_lowercase() == character.server.to_lowercase()) {
      add_roles.push(r);
    }
    if is_verified {
      if let Some(r) = roles.iter().find(|x| x.name.to_lowercase() == "verified") {
        add_roles.push(r);
      }
    }

    let member = bot.discord.get_member(on.id, who).chain_err(|| "could not get member for tagging")?;

    let all_group_roles: Vec<&String> = bot.config.roles.groups.iter().flat_map(|x| x).collect();
    let keep: Vec<&Role> = roles.iter().filter(|x| member.roles.contains(&x.id)).collect();
    let keep: Vec<&Role> = keep.into_iter().filter(|x| !all_group_roles.contains(&&x.name)).collect();
    let mut role_set: Vec<RoleId> = add_roles.iter().map(|r| r.id).chain(keep.into_iter().map(|r| r.id)).collect();
    role_set.sort();
    role_set.dedup();

    if !role_set.iter().all(|r| member.roles.contains(r)) {
      bot.discord.edit_member_roles(on.id, who, &role_set).chain_err(|| "could not add roles")?;
    }

    // cannot edit nickname of those with a higher role
    match member.nick {
      Some(ref nick) if *nick != character.name => {
        bot.discord.edit_member(on.id, who, |e| e.nickname(&character.name)).ok();
      },
      _ => {}
    }
    Ok(None)
  }
}
<commit_msg>fix: prevent verified account switching<commit_after>pub mod tag;
pub mod autotag;
pub mod update_tags;

pub use self::tag::*;
pub use self::autotag::*;
pub use self::update_tags::*;

use LalafellBot;
use database::AutotagUser;

use xivdb::error::*;

use discord::model::{UserId, LiveServer, Role, RoleId};

use std::sync::Arc;
use std::collections::HashMap;

pub struct Tagger;

impl Tagger {
  pub fn search_tag(bot: Arc<LalafellBot>, who: UserId, on: &LiveServer, server: &str, character_name: &str) -> Result<Option<String>> {
    let mut params = HashMap::new();
    params.insert(String::from("one"), String::from("characters"));
    params.insert(String::from("strict"), String::from("on"));
    params.insert(String::from("server|et"), server.to_string());

    let res = bot.xivdb.search(character_name.to_string(), params).chain_err(|| "could not query XIVDB")?;

    let search_chars = res.characters.unwrap().results;
    if search_chars.is_empty() {
      return Ok(Some(format!("Could not find any character by the name {} on {}.", character_name, server)));
    }

    let char_id = match search_chars[0]["id"].as_u64() {
      Some(u) => u,
      None => return Err("character ID was not a u64".into())
    };

    let name = match search_chars[0]["name"].as_str() {
      Some(s) => s,
      None => return Err("character name was not a string".into())
    };

    if name.to_lowercase() != character_name.to_lowercase() {
      return Ok(Some(format!("Could not find any character by the name {} on {}.", character_name, server)));
    }

    Tagger::tag(bot, who, on, char_id)
  }

  pub fn tag(bot: Arc<LalafellBot>, who: UserId, on: &LiveServer, char_id: u64) -> Result<Option<String>> {
    let character = bot.xivdb.character(char_id).chain_err(|| "could not look up character")?;

    bot.database.lock().unwrap().autotags.update_or_remove(AutotagUser::new(
      who.0,
      on.id.0,
      character.lodestone_id,
      &character.name,
      &character.server
    ));

    let database = bot.database.lock().unwrap();
    let user = database.autotags.users.iter()
      .find(|u| u.user_id == who.0);
    let is_verified = user
      .map(|u| u.verification.verified)
      .unwrap_or_default();

    if let Some(u) = user {
      if is_verified && char_id != u.character_id {
        return Ok(Some(format!("You are verified as {} on {}, so you cannot switch to another account.", u.character, u.server)));
      }
    }

    let roles = &on.roles;
    let mut add_roles = Vec::new();
    if let Some(r) = roles.iter().find(|x| x.name.to_lowercase() == character.data.race.to_lowercase()) {
      add_roles.push(r);
    }
    if let Some(r) = roles.iter().find(|x| x.name.to_lowercase() == character.data.gender.to_lowercase()) {
      add_roles.push(r);
    }
    if let Some(r) = roles.iter().find(|x| x.name.to_lowercase() == character.server.to_lowercase()) {
      add_roles.push(r);
    }
    if is_verified {
      if let Some(r) = roles.iter().find(|x| x.name.to_lowercase() == "verified") {
        add_roles.push(r);
      }
    }

    let member = bot.discord.get_member(on.id, who).chain_err(|| "could not get member for tagging")?;

    let all_group_roles: Vec<&String> = bot.config.roles.groups.iter().flat_map(|x| x).collect();
    let keep: Vec<&Role> = roles.iter().filter(|x| member.roles.contains(&x.id)).collect();
    let keep: Vec<&Role> = keep.into_iter().filter(|x| !all_group_roles.contains(&&x.name)).collect();
    let mut role_set: Vec<RoleId> = add_roles.iter().map(|r| r.id).chain(keep.into_iter().map(|r| r.id)).collect();
    role_set.sort();
    role_set.dedup();

    if !role_set.iter().all(|r| member.roles.contains(r)) {
      bot.discord.edit_member_roles(on.id, who, &role_set).chain_err(|| "could not add roles")?;
    }

    // cannot edit nickname of those with a higher role
    match member.nick {
      Some(ref nick) if *nick != character.name => {
        bot.discord.edit_member(on.id, who, |e| e.nickname(&character.name)).ok();
      },
      _ => {}
    }
    Ok(None)
  }
}
<|endoftext|>