<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */

use crate::canvas_data::{
    Backend, CanvasPaintState, Color, CompositionOp, DrawOptions, ExtendMode, Filter,
    GenericDrawTarget, GenericPathBuilder, GradientStop, GradientStops, Path, Pattern,
    SourceSurface, StrokeOptions, SurfaceFormat,
};
use crate::canvas_paint_thread::AntialiasMode;
use canvas_traits::canvas::*;
use cssparser::RGBA;
use euclid::default::{Point2D, Rect, Size2D, Transform2D, Vector2D};
use std::marker::PhantomData;

pub struct RaqoteBackend;

impl Backend for RaqoteBackend {
    fn get_composition_op(&self, _opts: &DrawOptions) -> CompositionOp {
        unimplemented!()
    }

    fn need_to_draw_shadow(&self, color: &Color) -> bool {
        color.as_raqote().a != 0
    }

    fn size_from_pattern(&self, rect: &Rect<f32>, pattern: &Pattern) -> Option<Size2D<f32>> {
        match pattern {
            Pattern::Raqote(raqote::Source::Image(image, extend, ..)) => {
                match extend {
                    raqote::ExtendMode::Repeat => {
                        Some(rect.size)
                    },
                    _ => Some(Size2D::new(image.width as f32, image.height as f32))
                }
            },
            _ => None
        }
    }

    fn set_shadow_color<'a>(&mut self, _color: RGBA, _state: &mut CanvasPaintState<'a>) {
        unimplemented!()
    }

    fn set_fill_style<'a>(
        &mut self,
        style: FillOrStrokeStyle,
        state: &mut CanvasPaintState<'a>,
        _drawtarget: &dyn GenericDrawTarget,
    ) {
        if let Some(source) = style.to_raqote_source() {
            state.fill_style = Pattern::Raqote(source);
        }
    }

    fn set_stroke_style<'a>(
        &mut self,
        _style: FillOrStrokeStyle,
        _state: &mut CanvasPaintState<'a>,
        _drawtarget: &dyn GenericDrawTarget,
    ) {
        unimplemented!()
    }

    fn set_global_composition<'a>(
        &mut self,
        _op: CompositionOrBlending,
        _state: &mut CanvasPaintState<'a>,
    ) {
        unimplemented!()
    }

    fn create_drawtarget(&self, size: Size2D<u64>) -> Box<dyn GenericDrawTarget> {
        Box::new(raqote::DrawTarget::new(
            size.width as i32,
            size.height as i32,
        ))
    }

    fn recreate_paint_state<'a>(&self, _state: &CanvasPaintState<'a>) -> CanvasPaintState<'a> {
        CanvasPaintState::new(AntialiasMode::Default)
    }
}

impl<'a> CanvasPaintState<'a> {
    pub fn new(_antialias: AntialiasMode) -> CanvasPaintState<'a> {
        let solid_src = raqote::SolidSource { r: 0, g: 0, b: 0, a: 255 };
        CanvasPaintState {
            draw_options: DrawOptions::Raqote(raqote::DrawOptions::new()),
            fill_style: Pattern::Raqote(raqote::Source::Solid(solid_src)),
            stroke_style: Pattern::Raqote(raqote::Source::Solid(solid_src)),
            stroke_opts: StrokeOptions::Raqote(Default::default(), PhantomData),
            transform: Transform2D::identity(),
            shadow_offset_x: 0.0,
            shadow_offset_y: 0.0,
            shadow_blur: 0.0,
            shadow_color: Color::Raqote(raqote::SolidSource { r: 0, g: 0, b: 0, a: 0 }),
        }
    }
}

impl Pattern<'_> {
    pub fn is_zero_size_gradient(&self) -> bool {
        match self {
            Pattern::Raqote(p) => {
                use raqote::Source::*;

                match p {
                    LinearGradient(g, ..) | RadialGradient(g, ..) | TwoCircleRadialGradient(g, ..) => {
                        g.stops.is_empty()
                    },
                    _ => false
                }
            },
        }
    }
    pub fn as_raqote(&self) -> &raqote::Source {
        match self {
            Pattern::Raqote(p) => p,
        }
    }
}

impl<'a> StrokeOptions<'a> {
    pub fn set_line_width(&mut self, _val: f32) {
        match self {
            StrokeOptions::Raqote(options, _) => options.width = _val,
        }
    }
    pub fn set_miter_limit(&mut self, _val: f32) {
        match self {
            StrokeOptions::Raqote(options, _) => options.miter_limit = _val,
        }
    }
    pub fn set_line_join(&mut self, _val: LineJoinStyle) {
        match self {
            StrokeOptions::Raqote(options, _) => options.join = _val.to_raqote_style(),
        }
    }
    pub fn set_line_cap(&mut self, _val: LineCapStyle) {
        match self {
            StrokeOptions::Raqote(options, _) => options.cap = _val.to_raqote_style(),
        }
    }
    pub fn as_raqote(&self) -> &raqote::StrokeStyle {
        match self {
            StrokeOptions::Raqote(options, _) => options,
        }
    }
}

impl DrawOptions {
    pub fn set_alpha(&mut self, _val: f32) {
        match self {
            DrawOptions::Raqote(draw_options) => draw_options.alpha = _val,
        }
    }
    pub fn as_raqote(&self) -> &raqote::DrawOptions {
        match self {
            DrawOptions::Raqote(options) => options,
        }
    }
}

impl Path {
    pub fn transformed_copy_to_builder(
        &self,
        _transform: &Transform2D<f32>,
    ) -> Box<dyn GenericPathBuilder> {
        unimplemented!()
    }

    pub fn contains_point(&self, _x: f64, _y: f64, _path_transform: &Transform2D<f32>) -> bool {
        unimplemented!()
    }

    pub fn copy_to_builder(&self) -> Box<dyn GenericPathBuilder> {
        unimplemented!()
    }

    pub fn as_raqote(&self) -> &raqote::Path {
        match self {
            Path::Raqote(p) => p,
        }
    }
}

impl GenericDrawTarget for raqote::DrawTarget {
    fn clear_rect(&self, _rect: &Rect<f32>) {
        unimplemented!();
    }
    fn copy_surface(
        &mut self,
        _surface: SourceSurface,
        _source: Rect<i32>,
        _destination: Point2D<i32>,
    ) {
        unimplemented!();
    }
    fn create_gradient_stops(
        &self,
        _gradient_stops: Vec<GradientStop>,
        _extend_mode: ExtendMode,
    ) -> GradientStops {
        unimplemented!();
    }
    fn create_path_builder(&self) -> Box<dyn GenericPathBuilder> {
        Box::new(PathBuilder::new())
    }
    fn create_similar_draw_target(
        &self,
        _size: &Size2D<i32>,
        _format: SurfaceFormat,
    ) -> Box<dyn GenericDrawTarget> {
        unimplemented!();
    }
    fn create_source_surface_from_data(
        &self,
        _data: &[u8],
        _size: Size2D<i32>,
        _stride: i32,
    ) -> Option<SourceSurface> {
        unimplemented!();
    }
    fn draw_surface(
        &self,
        _surface: SourceSurface,
        _dest: Rect<f64>,
        _source: Rect<f64>,
        _filter: Filter,
        _draw_options: &DrawOptions,
    ) {
        unimplemented!();
    }
    fn draw_surface_with_shadow(
        &self,
        _surface: SourceSurface,
        _dest: &Point2D<f32>,
        _color: &Color,
        _offset: &Vector2D<f32>,
        _sigma: f32,
        _operator: CompositionOp,
    ) {
        unimplemented!();
    }
    fn fill(&mut self, path: &Path, pattern: Pattern, draw_options: &DrawOptions) {
        self.fill(path.as_raqote(), pattern.as_raqote(), draw_options.as_raqote());
    }
    fn fill_rect(
        &mut self,
        rect: &Rect<f32>,
        pattern: Pattern,
        _draw_options: Option<&DrawOptions>,
    ) {
        let mut pb = raqote::PathBuilder::new();
        pb.rect(
            rect.origin.x,
            rect.origin.y,
            rect.size.width,
            rect.size.height,
        );
        raqote::DrawTarget::fill(
            self,
            &pb.finish(),
            pattern.as_raqote(),
            &raqote::DrawOptions::new(),
        );
    }
    fn get_format(&self) -> SurfaceFormat {
        unimplemented!();
    }
    fn get_size(&self) -> Size2D<i32> {
        unimplemented!();
    }
    fn get_transform(&self) -> Transform2D<f32> {
        *self.get_transform()
    }
    fn pop_clip(&mut self) {
        self.pop_clip();
    }
    fn push_clip(&mut self, path: &Path) {
        self.push_clip(path.as_raqote());
    }
    fn set_transform(&mut self, matrix: &Transform2D<f32>) {
        self.set_transform(matrix);
    }
    fn snapshot(&self) -> SourceSurface {
        unimplemented!();
    }
    fn stroke(
        &mut self,
        path: &Path,
        pattern: Pattern,
        stroke_options: &StrokeOptions,
        draw_options: &DrawOptions,
    ) {
        self.stroke(
            path.as_raqote(),
            pattern.as_raqote(),
            stroke_options.as_raqote(),
            draw_options.as_raqote(),
            );
    }
    fn stroke_line(
        &mut self,
        _start: Point2D<f32>,
        _end: Point2D<f32>,
        _pattern: Pattern,
        _stroke_options: &StrokeOptions,
        _draw_options: &DrawOptions,
    ) {
        unimplemented!();
    }
    fn stroke_rect(
        &mut self,
        _rect: &Rect<f32>,
        _pattern: Pattern,
        _stroke_options: &StrokeOptions,
        _draw_options: &DrawOptions,
    ) {
        unimplemented!();
    }
    #[allow(unsafe_code)]
    fn snapshot_data(&self, f: &dyn Fn(&[u8]) -> Vec<u8>) -> Vec<u8> {
        let v = self.get_data();
        f(unsafe {
            std::slice::from_raw_parts(
                v.as_ptr() as *const u8,
                v.len() * std::mem::size_of::<u32>(),
            )
        })
    }
    fn snapshot_data_owned(&self) -> Vec<u8> {
        unimplemented!();
    }
}

struct PathBuilder(Option<raqote::PathBuilder>);

impl PathBuilder {
    fn new() -> PathBuilder {
        PathBuilder(Some(raqote::PathBuilder::new()))
    }
}

impl GenericPathBuilder for PathBuilder {
    fn arc(
        &mut self,
        origin: Point2D<f32>,
        radius: f32,
        start_angle: f32,
        end_angle: f32,
        _anticlockwise: bool,
    ) {
        self.0.as_mut().unwrap().arc(origin.x, origin.y, radius, start_angle, end_angle);
    }
    fn bezier_curve_to(
        &mut self,
        control_point1: &Point2D<f32>,
        control_point2: &Point2D<f32>,
        control_point3: &Point2D<f32>,
    ) {
        self.0.as_mut().unwrap().cubic_to(
            control_point1.x,
            control_point1.y,
            control_point2.x,
            control_point2.y,
            control_point3.x,
            control_point3.y,
        );
    }
    fn close(&mut self) {
        self.0.as_mut().unwrap().close();
    }
    fn ellipse(
        &mut self,
        _origin: Point2D<f32>,
        _radius_x: f32,
        _radius_y: f32,
        _rotation_angle: f32,
        _start_angle: f32,
        _end_angle: f32,
        _anticlockwise: bool,
    ) {
        unimplemented!();
    }
    fn get_current_point(&mut self) -> Point2D<f32> {
        unimplemented!();
    }
    fn line_to(&mut self, point: Point2D<f32>) {
        self.0.as_mut().unwrap().line_to(point.x, point.y);
    }
    fn move_to(&mut self, point: Point2D<f32>) {
        self.0.as_mut().unwrap().move_to(point.x, point.y);
    }
    fn quadratic_curve_to(&mut self, control_point: &Point2D<f32>, end_point: &Point2D<f32>) {
        self.0.as_mut().unwrap().quad_to(control_point.x, control_point.y, end_point.x, end_point.y);
    }
    fn finish(&mut self) -> Path {
        Path::Raqote(self.0.take().unwrap().finish())
    }
}

pub trait ToRaqoteStyle {
    type Target;

    fn to_raqote_style(self) -> Self::Target;
}

impl ToRaqoteStyle for LineJoinStyle {
    type Target = raqote::LineJoin;

    fn to_raqote_style(self) -> raqote::LineJoin {
        match self {
            LineJoinStyle::Round => raqote::LineJoin::Round,
            LineJoinStyle::Bevel => raqote::LineJoin::Bevel,
            LineJoinStyle::Miter => raqote::LineJoin::Miter,
        }
    }
}

impl ToRaqoteStyle for LineCapStyle {
    type Target = raqote::LineCap;

    fn to_raqote_style(self) -> raqote::LineCap {
        match self {
            LineCapStyle::Butt => raqote::LineCap::Butt,
            LineCapStyle::Round => raqote::LineCap::Round,
            LineCapStyle::Square => raqote::LineCap::Square,
        }
    }
}

// TODO(pylbrecht)
#[cfg(feature = "raqote_backend")]
impl Clone for Pattern<'_> {
    fn clone(&self) -> Self {
        unimplemented!();
    }
}

pub trait ToRaqoteSource<'a> {
    fn to_raqote_source(self) -> Option<raqote::Source<'a>>;
}

impl<'a> ToRaqoteSource<'a> for FillOrStrokeStyle {
    fn to_raqote_source(self) -> Option<raqote::Source<'a>> {
        use canvas_traits::canvas::FillOrStrokeStyle::*;

        match self {
            Color(rgba) => Some(raqote::Source::Solid(raqote::SolidSource {
                r: rgba.red,
                g: rgba.green,
                b: rgba.blue,
                a: rgba.alpha,
            })),
            LinearGradient(_) => unimplemented!(),
            RadialGradient(_) => unimplemented!(),
            Surface(_) => unimplemented!(),
        }
    }
}

impl Color {
    fn as_raqote(&self) -> &raqote::SolidSource {
        match self {
            Color::Raqote(s) => s,
        }
    }
}
<commit_msg>Make tidy happy<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */

use crate::canvas_data::{
    Backend, CanvasPaintState, Color, CompositionOp, DrawOptions, ExtendMode, Filter,
    GenericDrawTarget, GenericPathBuilder, GradientStop, GradientStops, Path, Pattern,
    SourceSurface, StrokeOptions, SurfaceFormat,
};
use crate::canvas_paint_thread::AntialiasMode;
use canvas_traits::canvas::*;
use cssparser::RGBA;
use euclid::default::{Point2D, Rect, Size2D, Transform2D, Vector2D};
use std::marker::PhantomData;

pub struct RaqoteBackend;

impl Backend for RaqoteBackend {
    fn get_composition_op(&self, _opts: &DrawOptions) -> CompositionOp {
        unimplemented!()
    }

    fn need_to_draw_shadow(&self, color: &Color) -> bool {
        color.as_raqote().a != 0
    }

    fn size_from_pattern(&self, rect: &Rect<f32>, pattern: &Pattern) -> Option<Size2D<f32>> {
        match pattern {
            Pattern::Raqote(raqote::Source::Image(image, extend, ..)) => match extend {
                raqote::ExtendMode::Repeat => Some(rect.size),
                _ => Some(Size2D::new(image.width as f32, image.height as f32)),
            },
            _ => None,
        }
    }

    fn set_shadow_color<'a>(&mut self, _color: RGBA, _state: &mut CanvasPaintState<'a>) {
        unimplemented!()
    }

    fn set_fill_style<'a>(
        &mut self,
        style: FillOrStrokeStyle,
        state: &mut CanvasPaintState<'a>,
        _drawtarget: &dyn GenericDrawTarget,
    ) {
        if let Some(source) = style.to_raqote_source() {
            state.fill_style = Pattern::Raqote(source);
        }
    }

    fn set_stroke_style<'a>(
        &mut self,
        _style: FillOrStrokeStyle,
        _state: &mut CanvasPaintState<'a>,
        _drawtarget: &dyn GenericDrawTarget,
    ) {
        unimplemented!()
    }

    fn set_global_composition<'a>(
        &mut self,
        _op: CompositionOrBlending,
        _state: &mut CanvasPaintState<'a>,
    ) {
        unimplemented!()
    }

    fn create_drawtarget(&self, size: Size2D<u64>) -> Box<dyn GenericDrawTarget> {
        Box::new(raqote::DrawTarget::new(
            size.width as i32,
            size.height as i32,
        ))
    }

    fn recreate_paint_state<'a>(&self, _state: &CanvasPaintState<'a>) -> CanvasPaintState<'a> {
        CanvasPaintState::new(AntialiasMode::Default)
    }
}

impl<'a> CanvasPaintState<'a> {
    pub fn new(_antialias: AntialiasMode) -> CanvasPaintState<'a> {
        let solid_src = raqote::SolidSource {
            r: 0,
            g: 0,
            b: 0,
            a: 255,
        };
        CanvasPaintState {
            draw_options: DrawOptions::Raqote(raqote::DrawOptions::new()),
            fill_style: Pattern::Raqote(raqote::Source::Solid(solid_src)),
            stroke_style: Pattern::Raqote(raqote::Source::Solid(solid_src)),
            stroke_opts: StrokeOptions::Raqote(Default::default(), PhantomData),
            transform: Transform2D::identity(),
            shadow_offset_x: 0.0,
            shadow_offset_y: 0.0,
            shadow_blur: 0.0,
            shadow_color: Color::Raqote(raqote::SolidSource {
                r: 0,
                g: 0,
                b: 0,
                a: 0,
            }),
        }
    }
}

impl Pattern<'_> {
    pub fn is_zero_size_gradient(&self) -> bool {
        match self {
            Pattern::Raqote(p) => {
                use raqote::Source::*;

                match p {
                    LinearGradient(g, ..) |
                    RadialGradient(g, ..) |
                    TwoCircleRadialGradient(g, ..) => g.stops.is_empty(),
                    _ => false,
                }
            },
        }
    }
    pub fn as_raqote(&self) -> &raqote::Source {
        match self {
            Pattern::Raqote(p) => p,
        }
    }
}

impl<'a> StrokeOptions<'a> {
    pub fn set_line_width(&mut self, _val: f32) {
        match self {
            StrokeOptions::Raqote(options, _) => options.width = _val,
        }
    }
    pub fn set_miter_limit(&mut self, _val: f32) {
        match self {
            StrokeOptions::Raqote(options, _) => options.miter_limit = _val,
        }
    }
    pub fn set_line_join(&mut self, _val: LineJoinStyle) {
        match self {
            StrokeOptions::Raqote(options, _) => options.join = _val.to_raqote_style(),
        }
    }
    pub fn set_line_cap(&mut self, _val: LineCapStyle) {
        match self {
            StrokeOptions::Raqote(options, _) => options.cap = _val.to_raqote_style(),
        }
    }
    pub fn as_raqote(&self) -> &raqote::StrokeStyle {
        match self {
            StrokeOptions::Raqote(options, _) => options,
        }
    }
}

impl DrawOptions {
    pub fn set_alpha(&mut self, _val: f32) {
        match self {
            DrawOptions::Raqote(draw_options) => draw_options.alpha = _val,
        }
    }
    pub fn as_raqote(&self) -> &raqote::DrawOptions {
        match self {
            DrawOptions::Raqote(options) => options,
        }
    }
}

impl Path {
    pub fn transformed_copy_to_builder(
        &self,
        _transform: &Transform2D<f32>,
    ) -> Box<dyn GenericPathBuilder> {
        unimplemented!()
    }

    pub fn contains_point(&self, _x: f64, _y: f64, _path_transform: &Transform2D<f32>) -> bool {
        unimplemented!()
    }

    pub fn copy_to_builder(&self) -> Box<dyn GenericPathBuilder> {
        unimplemented!()
    }

    pub fn as_raqote(&self) -> &raqote::Path {
        match self {
            Path::Raqote(p) => p,
        }
    }
}

impl GenericDrawTarget for raqote::DrawTarget {
    fn clear_rect(&self, _rect: &Rect<f32>) {
        unimplemented!();
    }
    fn copy_surface(
        &mut self,
        _surface: SourceSurface,
        _source: Rect<i32>,
        _destination: Point2D<i32>,
    ) {
        unimplemented!();
    }
    fn create_gradient_stops(
        &self,
        _gradient_stops: Vec<GradientStop>,
        _extend_mode: ExtendMode,
    ) -> GradientStops {
        unimplemented!();
    }
    fn create_path_builder(&self) -> Box<dyn GenericPathBuilder> {
        Box::new(PathBuilder::new())
    }
    fn create_similar_draw_target(
        &self,
        _size: &Size2D<i32>,
        _format: SurfaceFormat,
    ) -> Box<dyn GenericDrawTarget> {
        unimplemented!();
    }
    fn create_source_surface_from_data(
        &self,
        _data: &[u8],
        _size: Size2D<i32>,
        _stride: i32,
    ) -> Option<SourceSurface> {
        unimplemented!();
    }
    fn draw_surface(
        &self,
        _surface: SourceSurface,
        _dest: Rect<f64>,
        _source: Rect<f64>,
        _filter: Filter,
        _draw_options: &DrawOptions,
    ) {
        unimplemented!();
    }
    fn draw_surface_with_shadow(
        &self,
        _surface: SourceSurface,
        _dest: &Point2D<f32>,
        _color: &Color,
        _offset: &Vector2D<f32>,
        _sigma: f32,
        _operator: CompositionOp,
    ) {
        unimplemented!();
    }
    fn fill(&mut self, path: &Path, pattern: Pattern, draw_options: &DrawOptions) {
        self.fill(
            path.as_raqote(),
            pattern.as_raqote(),
            draw_options.as_raqote(),
        );
    }
    fn fill_rect(
        &mut self,
        rect: &Rect<f32>,
        pattern: Pattern,
        _draw_options: Option<&DrawOptions>,
    ) {
        let mut pb = raqote::PathBuilder::new();
        pb.rect(
            rect.origin.x,
            rect.origin.y,
            rect.size.width,
            rect.size.height,
        );
        raqote::DrawTarget::fill(
            self,
            &pb.finish(),
            pattern.as_raqote(),
            &raqote::DrawOptions::new(),
        );
    }
    fn get_format(&self) -> SurfaceFormat {
        unimplemented!();
    }
    fn get_size(&self) -> Size2D<i32> {
        unimplemented!();
    }
    fn get_transform(&self) -> Transform2D<f32> {
        *self.get_transform()
    }
    fn pop_clip(&mut self) {
        self.pop_clip();
    }
    fn push_clip(&mut self, path: &Path) {
        self.push_clip(path.as_raqote());
    }
    fn set_transform(&mut self, matrix: &Transform2D<f32>) {
        self.set_transform(matrix);
    }
    fn snapshot(&self) -> SourceSurface {
        unimplemented!();
    }
    fn stroke(
        &mut self,
        path: &Path,
        pattern: Pattern,
        stroke_options: &StrokeOptions,
        draw_options: &DrawOptions,
    ) {
        self.stroke(
            path.as_raqote(),
            pattern.as_raqote(),
            stroke_options.as_raqote(),
            draw_options.as_raqote(),
        );
    }
    fn stroke_line(
        &mut self,
        _start: Point2D<f32>,
        _end: Point2D<f32>,
        _pattern: Pattern,
        _stroke_options: &StrokeOptions,
        _draw_options: &DrawOptions,
    ) {
        unimplemented!();
    }
    fn stroke_rect(
        &mut self,
        _rect: &Rect<f32>,
        _pattern: Pattern,
        _stroke_options: &StrokeOptions,
        _draw_options: &DrawOptions,
    ) {
        unimplemented!();
    }
    #[allow(unsafe_code)]
    fn snapshot_data(&self, f: &dyn Fn(&[u8]) -> Vec<u8>) -> Vec<u8> {
        let v = self.get_data();
        f(unsafe {
            std::slice::from_raw_parts(
                v.as_ptr() as *const u8,
                v.len() * std::mem::size_of::<u32>(),
            )
        })
    }
    fn snapshot_data_owned(&self) -> Vec<u8> {
        unimplemented!();
    }
}

struct PathBuilder(Option<raqote::PathBuilder>);

impl PathBuilder {
    fn new() -> PathBuilder {
        PathBuilder(Some(raqote::PathBuilder::new()))
    }
}

impl GenericPathBuilder for PathBuilder {
    fn arc(
        &mut self,
        origin: Point2D<f32>,
        radius: f32,
        start_angle: f32,
        end_angle: f32,
        _anticlockwise: bool,
    ) {
        self.0
            .as_mut()
            .unwrap()
            .arc(origin.x, origin.y, radius, start_angle, end_angle);
    }
    fn bezier_curve_to(
        &mut self,
        control_point1: &Point2D<f32>,
        control_point2: &Point2D<f32>,
        control_point3: &Point2D<f32>,
    ) {
        self.0.as_mut().unwrap().cubic_to(
            control_point1.x,
            control_point1.y,
            control_point2.x,
            control_point2.y,
            control_point3.x,
            control_point3.y,
        );
    }
    fn close(&mut self) {
        self.0.as_mut().unwrap().close();
    }
    fn ellipse(
        &mut self,
        _origin: Point2D<f32>,
        _radius_x: f32,
        _radius_y: f32,
        _rotation_angle: f32,
        _start_angle: f32,
        _end_angle: f32,
        _anticlockwise: bool,
    ) {
        unimplemented!();
    }
    fn get_current_point(&mut self) -> Point2D<f32> {
        unimplemented!();
    }
    fn line_to(&mut self, point: Point2D<f32>) {
        self.0.as_mut().unwrap().line_to(point.x, point.y);
    }
    fn move_to(&mut self, point: Point2D<f32>) {
        self.0.as_mut().unwrap().move_to(point.x, point.y);
    }
    fn quadratic_curve_to(&mut self, control_point: &Point2D<f32>, end_point: &Point2D<f32>) {
        self.0.as_mut().unwrap().quad_to(
            control_point.x,
            control_point.y,
            end_point.x,
            end_point.y,
        );
    }
    fn finish(&mut self) -> Path {
        Path::Raqote(self.0.take().unwrap().finish())
    }
}

pub trait ToRaqoteStyle {
    type Target;

    fn to_raqote_style(self) -> Self::Target;
}

impl ToRaqoteStyle for LineJoinStyle {
    type Target = raqote::LineJoin;

    fn to_raqote_style(self) -> raqote::LineJoin {
        match self {
            LineJoinStyle::Round => raqote::LineJoin::Round,
            LineJoinStyle::Bevel => raqote::LineJoin::Bevel,
            LineJoinStyle::Miter => raqote::LineJoin::Miter,
        }
    }
}

impl ToRaqoteStyle for LineCapStyle {
    type Target = raqote::LineCap;

    fn to_raqote_style(self) -> raqote::LineCap {
        match self {
            LineCapStyle::Butt => raqote::LineCap::Butt,
            LineCapStyle::Round => raqote::LineCap::Round,
            LineCapStyle::Square => raqote::LineCap::Square,
        }
    }
}

// TODO(pylbrecht)
#[cfg(feature = "raqote_backend")]
impl Clone for Pattern<'_> {
    fn clone(&self) -> Self {
        unimplemented!();
    }
}

pub trait ToRaqoteSource<'a> {
    fn to_raqote_source(self) -> Option<raqote::Source<'a>>;
}

impl<'a> ToRaqoteSource<'a> for FillOrStrokeStyle {
    fn to_raqote_source(self) -> Option<raqote::Source<'a>> {
        use canvas_traits::canvas::FillOrStrokeStyle::*;

        match self {
            Color(rgba) => Some(raqote::Source::Solid(raqote::SolidSource {
                r: rgba.red,
                g: rgba.green,
                b: rgba.blue,
                a: rgba.alpha,
            })),
            LinearGradient(_) => unimplemented!(),
            RadialGradient(_) => unimplemented!(),
            Surface(_) => unimplemented!(),
        }
    }
}

impl Color {
    fn as_raqote(&self) -> &raqote::SolidSource {
        match self {
            Color::Raqote(s) => s,
        }
    }
}
<|endoftext|>


<commit_before>/**********************************************************\
|                                                          |
|                          hprose                          |
|                                                          |
| Official WebSite: http://www.hprose.com/                 |
|                   http://www.hprose.org/                 |
|                                                          |
\**********************************************************/
/**********************************************************\
 *                                                        *
 * io/writer.rs                                           *
 *                                                        *
 * hprose writer for Rust.                                *
 *                                                        *
 * LastModified: Sep 12, 2016                             *
 * Author: Chen Fei <cf@hprose.com>                       *
 *                                                        *
\**********************************************************/

extern crate test;
extern crate dtoa;

use std::i32;
use std::io::Write;
use std::ptr;
use std::string::String;

use super::tags::*;
use super::util::*;
use super::encoder::*;
use super::writer_refer::WriterRefer;

pub struct Writer {
    buf: Vec<u8>,
    refer: Option<WriterRefer>
}

impl Writer {
    #[inline]
    pub fn serialize<T: Encodable + ?Sized>(&mut self, v: &T) -> &mut Writer {
        self.write_value(v);
        self
    }

    #[inline]
    pub fn write_value<T: Encodable + ?Sized>(&mut self, v: &T) {
        v.encode(self);
    }

    pub fn clear(&mut self) {
        self.buf.clear();
    }

    pub fn bytes(&mut self) -> Vec<u8> {
        self.buf.clone()
    }

    pub fn string(&mut self) -> String {
        String::from_utf8(self.buf.clone()).unwrap()
    }

    #[inline]
    pub fn new(simple: bool) -> Writer {
        Writer {
            buf: Vec::with_capacity(1024),
            refer: if simple { None } else { Some(WriterRefer::new()) }
        }
    }

    // private functions

    fn write_string(&mut self, s: &str, length: i64) {
        self.buf.push(TAG_STRING);
        let mut buf: [u8; 20] = [0; 20];
        self.buf.extend_from_slice(get_int_bytes(&mut buf, length));
        self.buf.push(TAG_QUOTE);
        self.buf.extend_from_slice(s.as_bytes());
        self.buf.push(TAG_QUOTE);
    }

    fn write_list_header(&mut self, len: usize) {
        self.buf.push(TAG_LIST);
        let mut buf: [u8; 20] = [0; 20];
        self.buf.extend_from_slice(get_uint_bytes(&mut buf, len as u64));
        self.buf.push(TAG_OPENBRACE);
    }

    #[inline]
    fn write_list_footer(&mut self) {
        self.buf.push(TAG_CLOSEBRACE);
    }

    #[inline]
    fn write_empty_list(&mut self) {
        self.buf.push(TAG_LIST);
        self.buf.push(TAG_OPENBRACE);
        self.buf.push(TAG_CLOSEBRACE);
    }
}

impl Encoder for Writer {
    #[inline]
    fn write_nil(&mut self) {
        self.buf.push(TAG_NULL);
    }

    #[inline]
    fn write_bool(&mut self, b: bool) {
        self.buf.push(if b { TAG_TRUE } else { TAG_FALSE });
    }

    fn write_int(&mut self, i: i64) {
        if i >= 0 && i <= 9 {
            self.buf.push(b'0' + i as u8);
            return
        }
        if i >= i32::MIN as i64 && i <= i32::MAX as i64 {
            self.buf.push(TAG_INTEGER);
        } else {
            self.buf.push(TAG_LONG);
        }
        let mut buf: [u8; 20] = [0; 20];
        self.buf.extend_from_slice(get_int_bytes(&mut buf, i));
        self.buf.push(TAG_SEMICOLON);
    }

    fn write_uint(&mut self, i: u64) {
        if i <= 9 {
            self.buf.push(b'0' + i as u8);
            return
        }
        if i <= i32::MAX as u64 {
            self.buf.push(TAG_INTEGER);
        } else {
            self.buf.push(TAG_LONG);
        }
        let mut buf: [u8; 20] = [0; 20];
        self.buf.extend_from_slice(get_uint_bytes(&mut buf, i));
        self.buf.push(TAG_SEMICOLON);
    }

    fn write_f32(&mut self, f: f32) {
        if f.is_nan() {
            self.buf.push(TAG_NAN);
            return
        }
        if f.is_infinite() {
            self.buf.push(TAG_INFINITY);
            self.buf.push(if f.is_sign_negative() { TAG_NEG } else { TAG_POS });
            return
        }
        self.buf.push(TAG_DOUBLE);
        dtoa::write(&mut self.buf, f).unwrap();
        self.buf.push(TAG_SEMICOLON);
    }

    fn write_f64(&mut self, f: f64) {
        if f.is_nan() {
            self.buf.push(TAG_NAN);
            return
        }
        if f.is_infinite() {
            self.buf.push(TAG_INFINITY);
            self.buf.push(if f.is_sign_negative() { TAG_NEG } else { TAG_POS });
            return
        }
        self.buf.push(TAG_DOUBLE);
        dtoa::write(&mut self.buf, f).unwrap();
        self.buf.push(TAG_SEMICOLON);
    }

    fn write_char(&mut self, c: char) {}

    fn write_str(&mut self, s: &str) {
        let length = utf16_length(s);
        match length {
            0 => self.buf.push(TAG_EMPTY),
            - 1 => self.write_bytes(s.as_bytes()),
            1 => {
                self.buf.push(TAG_UTF8_CHAR);
                self.buf.extend_from_slice(s.as_bytes());
            },
            _ => {
                self.set_ref(ptr::null::<&str>());
                self.write_string(s, length)
            }
        }
    }

    fn write_bytes(&mut self, bytes: &[u8]) {
        let count = bytes.len();
        if count == 0 {
            self.buf.push(TAG_BYTES);
            self.buf.push(TAG_QUOTE);
            self.buf.push(TAG_QUOTE);
            return
        }
        self.buf.push(TAG_BYTES);
        let mut buf: [u8; 20] = [0; 20];
        self.buf.extend_from_slice(get_int_bytes(&mut buf, count as i64));
        self.buf.push(TAG_QUOTE);
        self.buf.extend_from_slice(bytes);
        self.buf.push(TAG_QUOTE);
    }

    fn write_seq<F>(&mut self, len: usize, f: F) where F: FnOnce(&mut Writer) {
        if len == 0 {
            self.write_empty_list();
            return
        }
        self.write_list_header(len);
        f(self);
        self.write_list_footer();
    }

    #[inline]
    fn write_ref<T>(&mut self, p: *const T) -> bool {
        let buf = &mut self.buf;
        self.refer.as_mut().map_or(true, |r| r.write(buf, p))
    }

    #[inline]
    fn set_ref<T>(&mut self, p: *const T) {
        self.refer.as_mut().map(|r| r.set(p));
    }
}

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

    use std::{f32, f64};

    #[test]
    fn test_serialize_bool() {
        let mut w = Writer::new(true);
        w.serialize(&true);
        assert_eq!(w.string(), "t");
        w.clear();
        w.serialize(&false);
        assert_eq!(w.string(), "f");
    }

    #[bench]
    fn benchmark_serialize_bool(b: &mut Bencher) {
        let mut w = Writer::new(true);
        b.bytes = 1;
        b.iter(|| {
            w.serialize(&true);
        });
    }

    #[test]
    fn test_serialize_int() {
        let mut w = Writer::new(true);
        w.serialize(&8);
        assert_eq!(w.string(), "8");
        w.clear();
        w.serialize(&88);
        assert_eq!(w.string(), "i88;");
    }

    #[bench]
    fn benchmark_serialize_int(b: &mut Bencher) {
        let mut w = Writer::new(true);
        let mut i: i64 = 1;
        b.iter(|| {
            w.serialize(&i);
            i += 1;
        });
    }

    #[test]
    fn test_serialize_f32() {
        let test_cases = [
            (f32::NAN, "N"),
            (f32::INFINITY, "I+"),
            (f32::NEG_INFINITY, "I-"),
            (f32::consts::PI, "d3.1415927;")
        ];
        let mut w = Writer::new(true);
        for test_case in &test_cases {
            w.serialize(&test_case.0);
            assert_eq!(w.string(), test_case.1);
            w.clear();
        }
    }

    #[bench]
    fn benchmark_serialize_f32(b: &mut Bencher) {
        let mut w = Writer::new(true);
        let mut i: f32 = 1.0;
        b.iter(|| {
            w.serialize(&i);
            i += 1.0;
        });
    }

    #[test]
    fn test_serialize_f64() {
        let test_cases = [
            (f64::NAN, "N"),
            (f64::INFINITY, "I+"),
            (f64::NEG_INFINITY, "I-"),
            (f64::consts::PI, "d3.141592653589793;")
        ];
        let mut w = Writer::new(true);
        for test_case in &test_cases {
            w.serialize(&test_case.0);
            assert_eq!(w.string(), test_case.1);
            w.clear();
        }
    }

    #[bench]
    fn benchmark_serialize_f64(b: &mut Bencher) {
        let mut w = Writer::new(true);
        let mut i: f64 = 1.0;
        b.iter(|| {
            w.serialize(&i);
            i += 1.0;
        });
    }

    #[test]
    fn test_serialize_str() {
        let test_cases = [
            ("", "e"),
            ("π", "uπ"),
            ("你", "u你"),
            ("你好", "s2\"你好\""),
            ("你好啊,hello!", "s10\"你好啊,hello!\""),
            ("🇨🇳", "s4\"🇨🇳\"")
        ];
        let mut w = Writer::new(true);
        for test_case in &test_cases {
            w.serialize(test_case.0);
            assert_eq!(w.string(), test_case.1);
            w.clear();
        }
    }

    #[bench]
    fn benchmark_serialize_str(b: &mut Bencher) {
        let mut w = Writer::new(true);
        let s = "你好,hello!";
        b.bytes = s.len() as u64;
        b.iter(|| {
            w.serialize(s);
        });
    }

    #[test]
    fn test_serialize_bytes() {
        let test_cases = [
            ("hello".as_bytes(), "b5\"hello\""),
            ("".as_bytes(), "b\"\"")
        ];
        let mut w = Writer::new(true);
        for test_case in &test_cases {
            w.serialize(test_case.0);
            assert_eq!(w.string(), test_case.1);
            w.clear();
        }
    }

    #[bench]
    fn benchmark_serialize_bytes(b: &mut Bencher) {
        let mut w = Writer::new(true);
        let bytes = "你好,hello!".as_bytes();
        b.bytes = bytes.len() as u64;
        b.iter(|| {
            w.serialize(bytes);
        });
    }

    #[bench]
    fn benchmark_serialize_int_slice(b: &mut Bencher) {
        let mut w = Writer::new(true);
        let array = [0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 1, 2, 3, 4, 0, 1, 2, 3, 4];
        let slice = &array[..];
        b.iter(|| {
            w.serialize(slice);
        });
    }
}
<commit_msg>Float optimization for fraction 0.<commit_after>/**********************************************************\
|                                                          |
|                          hprose                          |
|                                                          |
| Official WebSite: http://www.hprose.com/                 |
|                   http://www.hprose.org/                 |
|                                                          |
\**********************************************************/
/**********************************************************\
 *                                                        *
 * io/writer.rs                                           *
 *                                                        *
 * hprose writer for Rust.                                *
 *                                                        *
 * LastModified: Sep 13, 2016                             *
 * Author: Chen Fei <cf@hprose.com>                       *
 *                                                        *
\**********************************************************/

extern crate test;
extern crate dtoa;

use std::{i32, f32, f64};
use std::io::Write;
use std::num::FpCategory as Fp;
use std::ptr;
use std::string::String;

use super::tags::*;
use super::util::*;
use super::encoder::*;
use super::writer_refer::WriterRefer;

pub struct Writer {
    buf: Vec<u8>,
    refer: Option<WriterRefer>
}

impl Writer {
    #[inline]
    pub fn serialize<T: Encodable + ?Sized>(&mut self, v: &T) -> &mut Writer {
        self.write_value(v);
        self
    }

    #[inline]
    pub fn write_value<T: Encodable + ?Sized>(&mut self, v: &T) {
        v.encode(self);
    }

    pub fn clear(&mut self) {
        self.buf.clear();
    }

    pub fn bytes(&mut self) -> Vec<u8> {
        self.buf.clone()
    }

    pub fn string(&mut self) -> String {
        String::from_utf8(self.buf.clone()).unwrap()
    }

    #[inline]
    pub fn new(simple: bool) -> Writer {
        Writer {
            buf: Vec::with_capacity(1024),
            refer: if simple { None } else { Some(WriterRefer::new()) }
        }
    }

    // private functions

    fn write_string(&mut self, s: &str, length: i64) {
        self.buf.push(TAG_STRING);
        let mut buf: [u8; 20] = [0; 20];
        self.buf.extend_from_slice(get_int_bytes(&mut buf, length));
        self.buf.push(TAG_QUOTE);
        self.buf.extend_from_slice(s.as_bytes());
        self.buf.push(TAG_QUOTE);
    }

    fn write_list_header(&mut self, len: usize) {
        self.buf.push(TAG_LIST);
        let mut buf: [u8; 20] = [0; 20];
        self.buf.extend_from_slice(get_uint_bytes(&mut buf, len as u64));
        self.buf.push(TAG_OPENBRACE);
    }

    #[inline]
    fn write_list_footer(&mut self) {
        self.buf.push(TAG_CLOSEBRACE);
    }

    #[inline]
    fn write_empty_list(&mut self) {
        self.buf.push(TAG_LIST);
        self.buf.push(TAG_OPENBRACE);
        self.buf.push(TAG_CLOSEBRACE);
    }
}

impl Encoder for Writer {
    #[inline]
    fn write_nil(&mut self) {
        self.buf.push(TAG_NULL);
    }

    #[inline]
    fn write_bool(&mut self, b: bool) {
        self.buf.push(if b { TAG_TRUE } else { TAG_FALSE });
    }

    fn write_int(&mut self, i: i64) {
        if i >= 0 && i <= 9 {
            self.buf.push(b'0' + i as u8);
            return
        }
        if i >= i32::MIN as i64 && i <= i32::MAX as i64 {
            self.buf.push(TAG_INTEGER);
        } else {
            self.buf.push(TAG_LONG);
        }
        let mut buf: [u8; 20] = [0; 20];
        self.buf.extend_from_slice(get_int_bytes(&mut buf, i));
        self.buf.push(TAG_SEMICOLON);
    }

    fn write_uint(&mut self, i: u64) {
        if i <= 9 {
            self.buf.push(b'0' + i as u8);
            return
        }
        if i <= i32::MAX as u64 {
            self.buf.push(TAG_INTEGER);
        } else {
            self.buf.push(TAG_LONG);
        }
        let mut buf: [u8; 20] = [0; 20];
        self.buf.extend_from_slice(get_uint_bytes(&mut buf, i));
        self.buf.push(TAG_SEMICOLON);
    }

    fn write_f32(&mut self, f: f32) {
        match f.classify() {
            Fp::Nan => self.buf.push(TAG_NAN),
            Fp::Infinite => {
                self.buf.push(TAG_INFINITY);
                self.buf.push(if f == f32::NEG_INFINITY { TAG_NEG } else { TAG_POS });
            },
            _ if f.fract() != 0f32 => {
                self.buf.push(TAG_DOUBLE);
                dtoa::write(&mut self.buf, f).unwrap();
                self.buf.push(TAG_SEMICOLON);
            }
            _ => {
                self.buf.push(TAG_DOUBLE);
                let mut buf: [u8; 20] = [0; 20];
                self.buf.extend_from_slice(get_int_bytes(&mut buf, f as i64));
                self.buf.push(TAG_SEMICOLON);
            }
        };
    }

    fn write_f64(&mut self, f: f64) {
        match f.classify() {
            Fp::Nan => self.buf.push(TAG_NAN),
            Fp::Infinite => {
                self.buf.push(TAG_INFINITY);
                self.buf.push(if f == f64::NEG_INFINITY { TAG_NEG } else { TAG_POS });
            },
            _ if f.fract() != 0f64 => {
                self.buf.push(TAG_DOUBLE);
                dtoa::write(&mut self.buf, f).unwrap();
                self.buf.push(TAG_SEMICOLON);
            }
            _ => {
                self.buf.push(TAG_DOUBLE);
                let mut buf: [u8; 20] = [0; 20];
                self.buf.extend_from_slice(get_int_bytes(&mut buf, f as i64));
                self.buf.push(TAG_SEMICOLON);
            }
        };
    }

    fn write_char(&mut self, c: char) {}

    fn write_str(&mut self, s: &str) {
        let length = utf16_length(s);
        match length {
            0 => self.buf.push(TAG_EMPTY),
            - 1 => self.write_bytes(s.as_bytes()),
            1 => {
                self.buf.push(TAG_UTF8_CHAR);
                self.buf.extend_from_slice(s.as_bytes());
            },
            _ => {
                self.set_ref(ptr::null::<&str>());
                self.write_string(s, length)
            }
        }
    }

    fn write_bytes(&mut self, bytes: &[u8]) {
        let count = bytes.len();
        if count == 0 {
            self.buf.push(TAG_BYTES);
            self.buf.push(TAG_QUOTE);
            self.buf.push(TAG_QUOTE);
            return
        }
        self.buf.push(TAG_BYTES);
        let mut buf: [u8; 20] = [0; 20];
        self.buf.extend_from_slice(get_int_bytes(&mut buf, count as i64));
        self.buf.push(TAG_QUOTE);
        self.buf.extend_from_slice(bytes);
        self.buf.push(TAG_QUOTE);
    }

    fn write_seq<F>(&mut self, len: usize, f: F) where F: FnOnce(&mut Writer) {
        if len == 0 {
            self.write_empty_list();
            return
        }
        self.write_list_header(len);
        f(self);
        self.write_list_footer();
    }

    #[inline]
    fn write_ref<T>(&mut self, p: *const T) -> bool {
        let buf = &mut self.buf;
        self.refer.as_mut().map_or(true, |r| r.write(buf, p))
    }

    #[inline]
    fn set_ref<T>(&mut self, p: *const T) {
        self.refer.as_mut().map(|r| r.set(p));
    }
}

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

    use std::{f32, f64};

    #[test]
    fn test_serialize_bool() {
        let mut w = Writer::new(true);
        w.serialize(&true);
        assert_eq!(w.string(), "t");
        w.clear();
        w.serialize(&false);
        assert_eq!(w.string(), "f");
    }

    #[bench]
    fn benchmark_serialize_bool(b: &mut Bencher) {
        let mut w = Writer::new(true);
        b.bytes = 1;
        b.iter(|| {
            w.serialize(&true);
        });
    }

    #[test]
    fn test_serialize_int() {
        let mut w = Writer::new(true);
        w.serialize(&8);
        assert_eq!(w.string(), "8");
        w.clear();
        w.serialize(&88);
        assert_eq!(w.string(), "i88;");
    }

    #[bench]
    fn benchmark_serialize_int(b: &mut Bencher) {
        let mut w = Writer::new(true);
        let mut i: i64 = 1;
        b.iter(|| {
            w.serialize(&i);
            i += 1;
        });
    }

    #[test]
    fn test_serialize_f32() {
        let test_cases = [
            (f32::NAN, "N"),
            (f32::INFINITY, "I+"),
            (f32::NEG_INFINITY, "I-"),
            (f32::consts::PI, "d3.1415927;")
        ];
        let mut w = Writer::new(true);
        for test_case in &test_cases {
            w.serialize(&test_case.0);
            assert_eq!(w.string(), test_case.1);
            w.clear();
        }
    }

    #[bench]
    fn benchmark_serialize_f32(b: &mut Bencher) {
        let mut w = Writer::new(true);
        let mut i: f32 = 1.0;
        b.iter(|| {
            w.serialize(&i);
            i += 1.1;
        });
    }

    #[test]
    fn test_serialize_f64() {
        let test_cases = [
            (f64::NAN, "N"),
            (f64::INFINITY, "I+"),
            (f64::NEG_INFINITY, "I-"),
            (f64::consts::PI, "d3.141592653589793;")
        ];
        let mut w = Writer::new(true);
        for test_case in &test_cases {
            w.serialize(&test_case.0);
            assert_eq!(w.string(), test_case.1);
            w.clear();
        }
    }

    #[bench]
    fn benchmark_serialize_f64(b: &mut Bencher) {
        let mut w = Writer::new(true);
        let mut i: f64 = 1.0;
        b.iter(|| {
            w.serialize(&i);
            i += 1.1;
        });
    }

    #[test]
    fn test_serialize_str() {
        let test_cases = [
            ("", "e"),
            ("π", "uπ"),
            ("你", "u你"),
            ("你好", "s2\"你好\""),
            ("你好啊,hello!", "s10\"你好啊,hello!\""),
            ("🇨🇳", "s4\"🇨🇳\"")
        ];
        let mut w = Writer::new(true);
        for test_case in &test_cases {
            w.serialize(test_case.0);
            assert_eq!(w.string(), test_case.1);
            w.clear();
        }
    }

    #[bench]
    fn benchmark_serialize_str(b: &mut Bencher) {
        let mut w = Writer::new(true);
        let s = "你好,hello!";
        b.bytes = s.len() as u64;
        b.iter(|| {
            w.serialize(s);
        });
    }

    #[test]
    fn test_serialize_bytes() {
        let test_cases = [
            ("hello".as_bytes(), "b5\"hello\""),
            ("".as_bytes(), "b\"\"")
        ];
        let mut w = Writer::new(true);
        for test_case in &test_cases {
            w.serialize(test_case.0);
            assert_eq!(w.string(), test_case.1);
            w.clear();
        }
    }

    #[bench]
    fn benchmark_serialize_bytes(b: &mut Bencher) {
        let mut w = Writer::new(true);
        let bytes = "你好,hello!".as_bytes();
        b.bytes = bytes.len() as u64;
        b.iter(|| {
            w.serialize(bytes);
        });
    }

    #[bench]
    fn benchmark_serialize_int_slice(b: &mut Bencher) {
        let mut w = Writer::new(true);
        let array = [0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 1, 2, 3, 4, 0, 1, 2, 3, 4];
        let slice = &array[..];
        b.iter(|| {
            w.serialize(slice);
        });
    }
}
<|endoftext|>


<commit_before>extern crate serde;
extern crate serde_xml;

use glusterfs_exporter::errors::*;
use glusterfs_exporter::types::*;
use glusterfs_exporter::metrics::*;
use std::process::{Command, Output};
use std::io::Read;
use std::str;

fn fail_if_not_successful(cmd_string: &str, cmd: &Output) -> Result<(), GEError> {
    if !cmd.status.success() {
        err!("Failed to run command {} with exitcode {}.",
             cmd_string,
             cmd.status)
    } else {
        Ok(())
    }
}

pub fn gluster_volumes() -> Result<Vec<String>, GEError> {
    let cmd = try!(Command::new("gluster")
        .arg("volume")
        .arg("list")
        .output());
    try!(fail_if_not_successful("volume list", &cmd));
    let out = try!(str::from_utf8(&cmd.stdout));
    Ok(out.lines().map(str::to_string).collect())
}

// This assumes valid xml output as of Glusterfs 3.8
pub fn gluster_vol_info(vol: &str) -> Result<VolProfile, GEError> {
    let cmd = try!(Command::new("gluster")
        .arg("volume")
        .arg("profile")
        .arg(vol)
        .arg("info")
        .arg("cumulative")
        .arg("--xml")
        .spawn());
    let stdout = try!(cmd.stdout.ok_or("Can't capture stdout"));
    serde_xml::de::from_iter(stdout.bytes())
        .map_err(From::from)
        .map(|cli: CliOutput| cli.vol_profile)
}

pub fn collect_stats() -> Result<(), GEError> {
    for vol in try!(gluster_volumes()).iter() {
        info!("collection profile info for volume {}.", vol);
        let VolProfile{profile_op, bricks, ..} = try!(gluster_vol_info(vol));
        PROFILE_OP_GAUGE.with_label_values(&[vol]).set(profile_op as f64);

        for Brick{brick_name, cumulative_stats} in bricks {
            let brick = &brick_name;
            debug!("collecting info for brick {}.", brick_name);
            BLOCKSTATS_TOTAL_COUNTER.with_label_values(&[vol, brick, "read"]).set(cumulative_stats.total_read as f64);
            BLOCKSTATS_TOTAL_COUNTER.with_label_values(&[vol, brick, "write"]).set(cumulative_stats.total_write as f64);
            for blockstat in cumulative_stats.block_stats.blocks {
                let size_bucket = &format!("{}", blockstat.size);
                BLOCKSTATS_COUNTER.with_label_values(&[vol, brick, "read", size_bucket]).set(blockstat.reads as f64);
                BLOCKSTATS_COUNTER.with_label_values(&[vol, brick, "write", size_bucket]).set(blockstat.writes as f64);
            }
            for fopstat in cumulative_stats.fop_stats.fops {
                let fop_name = &fopstat.name;
                FOPSTATS_AVGLATENCY.with_label_values(&[vol, brick, fop_name]).set(fopstat.avg_latency);
                FOPSTATS_MINLATENCY.with_label_values(&[vol, brick, fop_name]).set(fopstat.min_latency);
                FOPSTATS_MAXLATENCY.with_label_values(&[vol, brick, fop_name]).set(fopstat.max_latency);
                FOPSTATS_COUNTER.with_label_values(&[vol, brick, fop_name]).set(fopstat.hits as f64);
            }
        }
    }
    Ok(())
}
<commit_msg>actually capture stdout<commit_after>extern crate serde;
extern crate serde_xml;

use glusterfs_exporter::errors::*;
use glusterfs_exporter::types::*;
use glusterfs_exporter::metrics::*;
use std::process::{Command, Output};
use std::io::Read;
use std::str;

fn fail_if_not_successful(cmd_string: &str, cmd: &Output) -> Result<(), GEError> {
    if !cmd.status.success() {
        err!("Failed to run command {} with exitcode {}.",
             cmd_string,
             cmd.status)
    } else {
        Ok(())
    }
}

pub fn gluster_volumes() -> Result<Vec<String>, GEError> {
    let cmd = try!(Command::new("gluster")
        .arg("volume")
        .arg("list")
        .output());
    try!(fail_if_not_successful("volume list", &cmd));
    let out = try!(str::from_utf8(&cmd.stdout));
    Ok(out.lines().map(str::to_string).collect())
}

// This assumes valid xml output as of Glusterfs 3.8
pub fn gluster_vol_info(vol: &str) -> Result<VolProfile, GEError> {
    let cmd = try!(Command::new("gluster")
        .arg("volume")
        .arg("profile")
        .arg(vol)
        .arg("info")
        .arg("cumulative")
        .arg("--xml")
        .output());
    serde_xml::de::from_iter(cmd.stdout.bytes())
        .map_err(From::from)
        .map(|cli: CliOutput| cli.vol_profile)
}

pub fn collect_stats() -> Result<(), GEError> {
    for vol in try!(gluster_volumes()).iter() {
        info!("collection profile info for volume {}.", vol);
        let VolProfile{profile_op, bricks, ..} = try!(gluster_vol_info(vol));
        PROFILE_OP_GAUGE.with_label_values(&[vol]).set(profile_op as f64);

        for Brick{brick_name, cumulative_stats} in bricks {
            let brick = &brick_name;
            debug!("collecting info for brick {}.", brick_name);
            BLOCKSTATS_TOTAL_COUNTER.with_label_values(&[vol, brick, "read"]).set(cumulative_stats.total_read as f64);
            BLOCKSTATS_TOTAL_COUNTER.with_label_values(&[vol, brick, "write"]).set(cumulative_stats.total_write as f64);
            for blockstat in cumulative_stats.block_stats.blocks {
                let size_bucket = &format!("{}", blockstat.size);
                BLOCKSTATS_COUNTER.with_label_values(&[vol, brick, "read", size_bucket]).set(blockstat.reads as f64);
                BLOCKSTATS_COUNTER.with_label_values(&[vol, brick, "write", size_bucket]).set(blockstat.writes as f64);
            }
            for fopstat in cumulative_stats.fop_stats.fops {
                let fop_name = &fopstat.name;
                FOPSTATS_AVGLATENCY.with_label_values(&[vol, brick, fop_name]).set(fopstat.avg_latency);
                FOPSTATS_MINLATENCY.with_label_values(&[vol, brick, fop_name]).set(fopstat.min_latency);
                FOPSTATS_MAXLATENCY.with_label_values(&[vol, brick, fop_name]).set(fopstat.max_latency);
                FOPSTATS_COUNTER.with_label_values(&[vol, brick, fop_name]).set(fopstat.hits as f64);
            }
        }
    }
    Ok(())
}
<|endoftext|>


<commit_before><commit_msg>screen : count number of successful push<commit_after><|endoftext|>


<commit_before>use cliargsparser::CliArgsParser;
use dirs;
use gettextrs::gettext;
use libc;
use logger::{self, Level};
use std::fs::{self, DirBuilder};
use std::io;
use std::os::unix::fs::DirBuilderExt;
use std::path::{Path, PathBuf};
use xdg;

pub const NEWSBOAT_SUBDIR_XDG: &str = "newsboat";
pub const NEWSBOAT_CONFIG_SUBDIR: &str = ".newsboat";
pub const NEWSBEUTER_SUBDIR_XDG: &str = "newsbeuter";
pub const NEWSBEUTER_CONFIG_SUBDIR: &str = ".newsbeuter";
pub const LOCK_SUFFIX: &str = ".lock";

#[derive(Debug)]
pub struct ConfigPaths {
    m_env_home: PathBuf,
    m_error_message: String,

    m_data_dir: PathBuf,
    m_config_dir: PathBuf,

    m_url_file: String,
    m_cache_file: String,
    m_config_file: String,
    m_lock_file: String,
    m_queue_file: String,
    m_search_file: String,
    m_cmdline_file: String,

    m_silent: bool,
    m_using_nonstandard_configs: bool,
}

impl ConfigPaths {
    pub fn new() -> ConfigPaths {
        let mut config_paths = ConfigPaths {
            m_env_home: PathBuf::new(),
            m_error_message: String::new(),

            m_data_dir: PathBuf::new(),
            m_config_dir: PathBuf::new(),

            m_url_file: String::from("urls"),
            m_cache_file: String::from("cache.db"),
            m_config_file: String::from("config"),
            m_lock_file: String::new(),
            m_queue_file: String::from("queue"),
            m_search_file: String::new(),
            m_cmdline_file: String::new(),

            m_silent: false,
            m_using_nonstandard_configs: false,
        };

        let m_env_home = dirs::home_dir();
        if m_env_home.is_none() {
            let uid = unsafe { libc::getuid() };

            config_paths.m_error_message = fmt!(
                &gettext(
                    "Fatal error: couldn't determine home directory!\n\
                     Please set the HOME environment variable or add \
                     a valid user for UID %u!"
                ),
                uid
            );

            return config_paths;
        }

        // hitting this branch means we found a home directory
        // we can now safely call unwrap on all other functions in the dirs crate

        config_paths.m_env_home = m_env_home.unwrap();
        config_paths.find_dirs();
        config_paths
    }

    fn migrate_data_from_newsbeuter_xdg(&mut self) -> bool {
        // This can't panic because we've tested we can find the home directory in ConfigPaths::new
        // This should be replaced with proper error handling after this is not used by c++ anymore
        let xdg_dirs = xdg::BaseDirectories::new().unwrap();
        let xdg_config_dir = xdg_dirs.get_config_home();
        let xdg_data_dir = xdg_dirs.get_data_home();

        let newsbeuter_config_dir = xdg_config_dir.join(NEWSBEUTER_SUBDIR_XDG);
        let newsbeuter_data_dir = xdg_data_dir.join(NEWSBEUTER_SUBDIR_XDG);

        let newsboat_config_dir = xdg_config_dir.join(NEWSBOAT_SUBDIR_XDG);
        let newsboat_data_dir = xdg_data_dir.join(NEWSBOAT_SUBDIR_XDG);

        if !newsbeuter_config_dir.is_dir() {
            return false;
        }

        fn exists(path: &Path) -> bool {
            let exists = path.exists();
            if exists {
                log!(
                    Level::Debug,
                    "{:?} already exists, aborting XDG migration.",
                    path
                );
            }
            return exists;
        }

        if exists(&newsboat_config_dir) {
            return false;
        }

        if exists(&newsboat_data_dir) {
            return false;
        }

        self.m_config_dir = newsboat_config_dir;
        self.m_data_dir = newsboat_data_dir;

        if !self.m_silent {
            eprintln!("Migrating configs and data from Newsbeuter's XDG dirs...");
        }

        if !try_mkdir(&self.m_config_dir) {
            return false;
        }

        if !try_mkdir(&self.m_data_dir) {
            return false;
        }

        // We ignore the return codes because it's okay if some files are missing.

        // in config
        let _ = migrate_file(&newsbeuter_config_dir, &self.m_config_dir, "urls");
        let _ = migrate_file(&newsbeuter_config_dir, &self.m_config_dir, "config");

        // in data
        let _ = migrate_file(&newsbeuter_data_dir, &self.m_data_dir, "cache.db");
        let _ = migrate_file(&newsbeuter_data_dir, &self.m_data_dir, "queue");
        let _ = migrate_file(&newsbeuter_data_dir, &self.m_data_dir, "history.search");
        let _ = migrate_file(&newsbeuter_data_dir, &self.m_data_dir, "history.cmdline");

        return true;
    }

    fn migrate_data_from_newsbeuter_simple(&self) -> bool {
        let newsbeuter_dir = self.m_env_home.join(NEWSBEUTER_CONFIG_SUBDIR);

        if !newsbeuter_dir.is_dir() {
            return false;
        }

        let newsboat_dir = self.m_env_home.join(NEWSBOAT_CONFIG_SUBDIR);

        if newsboat_dir.exists() {
            log!(
                Level::Debug,
                "{:?} already exists, aborting migration.",
                newsboat_dir
            );
            return false;
        }

        if !self.m_silent {
            eprintln!("Migrating configs and data from Newsbeuter's dotdir...");
        }

        match mkdir(&newsboat_dir, 0o700) {
            Ok(_) => (),
            Err(ref e) if e.kind() == io::ErrorKind::AlreadyExists => (),
            Err(err) => {
                if !self.m_silent {
                    eprintln!(
                        "Aborting migration because mkdir on {:?} failed: {}",
                        newsboat_dir, err
                    );
                }
                return false;
            }
        };

        // We ignore the return codes because it's okay if some files are missing.
        let _ = migrate_file(&newsbeuter_dir, &newsboat_dir, "urls");
        let _ = migrate_file(&newsbeuter_dir, &newsboat_dir, "cache.db");
        let _ = migrate_file(&newsbeuter_dir, &newsboat_dir, "config");
        let _ = migrate_file(&newsbeuter_dir, &newsboat_dir, "queue");
        let _ = migrate_file(&newsbeuter_dir, &newsboat_dir, "history.search");
        let _ = migrate_file(&newsbeuter_dir, &newsboat_dir, "history.cmdline");

        return true;
    }

    fn migrate_data_from_newsbeuter(&mut self) -> bool {
        let mut migrated = self.migrate_data_from_newsbeuter_xdg();

        if migrated {
            // Re-running to pick up XDG dirs
            self.m_url_file = "urls".into();
            self.m_cache_file = "cache.db".into();
            self.m_config_file = "config".into();
            self.m_queue_file = "queue".into();
            self.find_dirs();
        } else {
            migrated = self.migrate_data_from_newsbeuter_simple();
        }

        migrated
    }

    pub fn create_dirs(&self) -> bool {
        return try_mkdir(&self.m_config_dir) && try_mkdir(&self.m_data_dir);
    }

    fn find_dirs(&mut self) {
        self.m_config_dir = self.m_env_home.join(NEWSBOAT_CONFIG_SUBDIR);

        self.m_data_dir = self.m_config_dir.clone();

        // Will change config_dir and data_dir to point to XDG if XDG
        // directories are available.
        self.find_dirs_xdg();

        // in config
        self.m_url_file = self
            .m_config_dir
            .join(&self.m_url_file)
            .to_string_lossy()
            .into();
        self.m_config_file = self
            .m_config_dir
            .join(&self.m_config_file)
            .to_string_lossy()
            .into();

        // in data
        self.m_cache_file = self
            .m_data_dir
            .join(&self.m_cache_file)
            .to_string_lossy()
            .into();
        self.m_lock_file = self.m_cache_file.clone() + LOCK_SUFFIX;
        self.m_queue_file = self
            .m_data_dir
            .join(&self.m_queue_file)
            .to_string_lossy()
            .into();
        self.m_search_file = self
            .m_data_dir
            .join("history.search")
            .to_string_lossy()
            .into();
        self.m_cmdline_file = self
            .m_data_dir
            .join("history.cmdline")
            .to_string_lossy()
            .into();
    }

    fn find_dirs_xdg(&mut self) {
        // This can't panic because we've tested we can find the home directory in ConfigPaths::new
        // This should be replaced with proper error handling after this is not used by c++ anymore
        let xdg_dirs = xdg::BaseDirectories::new().unwrap();
        let config_dir = xdg_dirs.get_config_home().join(NEWSBOAT_SUBDIR_XDG);
        let data_dir = xdg_dirs.get_data_home().join(NEWSBOAT_SUBDIR_XDG);

        if !config_dir.is_dir() {
            return;
        }

        /* Invariant: config dir exists.
         *
         * At this point, we're confident we'll be using XDG. We don't check if
         * data dir exists, because if it doesn't we'll create it. */

        self.m_config_dir = config_dir;
        self.m_data_dir = data_dir;
    }

    /// Indicates if the object can be used.
    ///
    /// If this method returned `false`, the cause for initialization failure can be found using
    /// `error_message()`.
    pub fn initialized(&self) -> bool {
        !self.m_env_home.as_os_str().is_empty()
    }

    /// Returns explanation why initialization failed.
    ///
    /// \note You shouldn't call this unless `initialized()` returns `false`.
    pub fn error_message(&self) -> &str {
        &self.m_error_message
    }

    /// Initializes paths to config, cache etc. from CLI arguments.
    pub fn process_args(&mut self, args: &CliArgsParser) {
        if let Some(ref url_file) = args.url_file {
            self.m_url_file = url_file.to_string();
        }

        if let Some(ref cache_file) = args.cache_file {
            self.m_cache_file = cache_file.to_string();
        }

        if let Some(ref lock_file) = args.lock_file {
            self.m_lock_file = lock_file.to_string();
        }

        if let Some(ref config_file) = args.config_file {
            self.m_config_file = config_file.to_string();
        }

        self.m_silent = args.silent;
        self.m_using_nonstandard_configs = args.using_nonstandard_configs();
    }

    /// Migrate configs and data from Newsbeuter if they exist. Return `true` if migrated
    /// something, `false` otherwise.
    pub fn try_migrate_from_newsbeuter(&mut self) -> bool {
        if !self.m_using_nonstandard_configs && !Path::new(&self.m_url_file).exists() {
            return self.migrate_data_from_newsbeuter();
        }

        // No migration occurred.
        false
    }

    /// Path to the URLs file.
    pub fn url_file(&self) -> String {
        self.m_url_file.clone()
    }

    /// Path to the cache file.
    pub fn cache_file(&self) -> String {
        self.m_cache_file.clone()
    }

    /// Sets path to the cache file.
    // FIXME: this is actually a kludge that lets Controller change the path
    // midway. That logic should be moved into ConfigPaths, and this method
    // removed.
    pub fn set_cache_file(&mut self, path: String) {
        self.m_cache_file = path.clone();
        self.m_lock_file = path + LOCK_SUFFIX;
    }

    /// Path to the config file.
    pub fn config_file(&self) -> String {
        self.m_config_file.clone()
    }

    /// Path to the lock file.
    ///
    /// \note This changes when path to config file changes.
    pub fn lock_file(&self) -> String {
        self.m_lock_file.clone()
    }

    /// \brief Path to the queue file.
    ///
    /// Queue file stores enqueued podcasts. It's written by Newsboat, and read by Podboat.
    pub fn queue_file(&self) -> String {
        self.m_queue_file.clone()
    }

    /// Path to the file with previous search queries.
    pub fn search_file(&self) -> String {
        self.m_search_file.clone()
    }

    /// Path to the file with command-line history.
    pub fn cmdline_file(&self) -> String {
        self.m_cmdline_file.clone()
    }
}

fn try_mkdir<R: AsRef<Path>>(path: R) -> bool {
    DirBuilder::new()
        .recursive(true)
        .mode(0o700)
        .create(path.as_ref())
        .is_ok()
}

fn mkdir<R: AsRef<Path>>(path: R, mode: u32) -> io::Result<()> {
    DirBuilder::new().mode(mode).create(path.as_ref())
}

fn migrate_file<R: AsRef<Path>>(newsbeuter_dir: R, newsboat_dir: R, file: &str) -> io::Result<()> {
    let input_filepath = newsbeuter_dir.as_ref().join(file);
    let output_filepath = newsboat_dir.as_ref().join(file);
    eprintln!("{:?} -> {:?}", input_filepath, output_filepath);
    fs::copy(input_filepath, output_filepath).map(|_| ())
}
<commit_msg>ConfigPaths: drop `m_` prefixes from private fields<commit_after>use cliargsparser::CliArgsParser;
use dirs;
use gettextrs::gettext;
use libc;
use logger::{self, Level};
use std::fs::{self, DirBuilder};
use std::io;
use std::os::unix::fs::DirBuilderExt;
use std::path::{Path, PathBuf};
use xdg;

pub const NEWSBOAT_SUBDIR_XDG: &str = "newsboat";
pub const NEWSBOAT_CONFIG_SUBDIR: &str = ".newsboat";
pub const NEWSBEUTER_SUBDIR_XDG: &str = "newsbeuter";
pub const NEWSBEUTER_CONFIG_SUBDIR: &str = ".newsbeuter";
pub const LOCK_SUFFIX: &str = ".lock";

#[derive(Debug)]
pub struct ConfigPaths {
    env_home: PathBuf,
    error_message: String,

    data_dir: PathBuf,
    config_dir: PathBuf,

    url_file: String,
    cache_file: String,
    config_file: String,
    lock_file: String,
    queue_file: String,
    search_file: String,
    cmdline_file: String,

    silent: bool,
    using_nonstandard_configs: bool,
}

impl ConfigPaths {
    pub fn new() -> ConfigPaths {
        let mut config_paths = ConfigPaths {
            env_home: PathBuf::new(),
            error_message: String::new(),

            data_dir: PathBuf::new(),
            config_dir: PathBuf::new(),

            url_file: String::from("urls"),
            cache_file: String::from("cache.db"),
            config_file: String::from("config"),
            lock_file: String::new(),
            queue_file: String::from("queue"),
            search_file: String::new(),
            cmdline_file: String::new(),

            silent: false,
            using_nonstandard_configs: false,
        };

        let env_home = dirs::home_dir();
        if env_home.is_none() {
            let uid = unsafe { libc::getuid() };

            config_paths.error_message = fmt!(
                &gettext(
                    "Fatal error: couldn't determine home directory!\n\
                     Please set the HOME environment variable or add \
                     a valid user for UID %u!"
                ),
                uid
            );

            return config_paths;
        }

        // hitting this branch means we found a home directory
        // we can now safely call unwrap on all other functions in the dirs crate

        config_paths.env_home = env_home.unwrap();
        config_paths.find_dirs();
        config_paths
    }

    fn migrate_data_from_newsbeuter_xdg(&mut self) -> bool {
        // This can't panic because we've tested we can find the home directory in ConfigPaths::new
        // This should be replaced with proper error handling after this is not used by c++ anymore
        let xdg_dirs = xdg::BaseDirectories::new().unwrap();
        let xdg_config_dir = xdg_dirs.get_config_home();
        let xdg_data_dir = xdg_dirs.get_data_home();

        let newsbeuter_config_dir = xdg_config_dir.join(NEWSBEUTER_SUBDIR_XDG);
        let newsbeuter_data_dir = xdg_data_dir.join(NEWSBEUTER_SUBDIR_XDG);

        let newsboat_config_dir = xdg_config_dir.join(NEWSBOAT_SUBDIR_XDG);
        let newsboat_data_dir = xdg_data_dir.join(NEWSBOAT_SUBDIR_XDG);

        if !newsbeuter_config_dir.is_dir() {
            return false;
        }

        fn exists(path: &Path) -> bool {
            let exists = path.exists();
            if exists {
                log!(
                    Level::Debug,
                    "{:?} already exists, aborting XDG migration.",
                    path
                );
            }
            return exists;
        }

        if exists(&newsboat_config_dir) {
            return false;
        }

        if exists(&newsboat_data_dir) {
            return false;
        }

        self.config_dir = newsboat_config_dir;
        self.data_dir = newsboat_data_dir;

        if !self.silent {
            eprintln!("Migrating configs and data from Newsbeuter's XDG dirs...");
        }

        if !try_mkdir(&self.config_dir) {
            return false;
        }

        if !try_mkdir(&self.data_dir) {
            return false;
        }

        // We ignore the return codes because it's okay if some files are missing.

        // in config
        let _ = migrate_file(&newsbeuter_config_dir, &self.config_dir, "urls");
        let _ = migrate_file(&newsbeuter_config_dir, &self.config_dir, "config");

        // in data
        let _ = migrate_file(&newsbeuter_data_dir, &self.data_dir, "cache.db");
        let _ = migrate_file(&newsbeuter_data_dir, &self.data_dir, "queue");
        let _ = migrate_file(&newsbeuter_data_dir, &self.data_dir, "history.search");
        let _ = migrate_file(&newsbeuter_data_dir, &self.data_dir, "history.cmdline");

        return true;
    }

    fn migrate_data_from_newsbeuter_simple(&self) -> bool {
        let newsbeuter_dir = self.env_home.join(NEWSBEUTER_CONFIG_SUBDIR);

        if !newsbeuter_dir.is_dir() {
            return false;
        }

        let newsboat_dir = self.env_home.join(NEWSBOAT_CONFIG_SUBDIR);

        if newsboat_dir.exists() {
            log!(
                Level::Debug,
                "{:?} already exists, aborting migration.",
                newsboat_dir
            );
            return false;
        }

        if !self.silent {
            eprintln!("Migrating configs and data from Newsbeuter's dotdir...");
        }

        match mkdir(&newsboat_dir, 0o700) {
            Ok(_) => (),
            Err(ref e) if e.kind() == io::ErrorKind::AlreadyExists => (),
            Err(err) => {
                if !self.silent {
                    eprintln!(
                        "Aborting migration because mkdir on {:?} failed: {}",
                        newsboat_dir, err
                    );
                }
                return false;
            }
        };

        // We ignore the return codes because it's okay if some files are missing.
        let _ = migrate_file(&newsbeuter_dir, &newsboat_dir, "urls");
        let _ = migrate_file(&newsbeuter_dir, &newsboat_dir, "cache.db");
        let _ = migrate_file(&newsbeuter_dir, &newsboat_dir, "config");
        let _ = migrate_file(&newsbeuter_dir, &newsboat_dir, "queue");
        let _ = migrate_file(&newsbeuter_dir, &newsboat_dir, "history.search");
        let _ = migrate_file(&newsbeuter_dir, &newsboat_dir, "history.cmdline");

        return true;
    }

    fn migrate_data_from_newsbeuter(&mut self) -> bool {
        let mut migrated = self.migrate_data_from_newsbeuter_xdg();

        if migrated {
            // Re-running to pick up XDG dirs
            self.url_file = "urls".into();
            self.cache_file = "cache.db".into();
            self.config_file = "config".into();
            self.queue_file = "queue".into();
            self.find_dirs();
        } else {
            migrated = self.migrate_data_from_newsbeuter_simple();
        }

        migrated
    }

    pub fn create_dirs(&self) -> bool {
        return try_mkdir(&self.config_dir) && try_mkdir(&self.data_dir);
    }

    fn find_dirs(&mut self) {
        self.config_dir = self.env_home.join(NEWSBOAT_CONFIG_SUBDIR);

        self.data_dir = self.config_dir.clone();

        // Will change config_dir and data_dir to point to XDG if XDG
        // directories are available.
        self.find_dirs_xdg();

        // in config
        self.url_file = self
            .config_dir
            .join(&self.url_file)
            .to_string_lossy()
            .into();
        self.config_file = self
            .config_dir
            .join(&self.config_file)
            .to_string_lossy()
            .into();

        // in data
        self.cache_file = self
            .data_dir
            .join(&self.cache_file)
            .to_string_lossy()
            .into();
        self.lock_file = self.cache_file.clone() + LOCK_SUFFIX;
        self.queue_file = self
            .data_dir
            .join(&self.queue_file)
            .to_string_lossy()
            .into();
        self.search_file = self
            .data_dir
            .join("history.search")
            .to_string_lossy()
            .into();
        self.cmdline_file = self
            .data_dir
            .join("history.cmdline")
            .to_string_lossy()
            .into();
    }

    fn find_dirs_xdg(&mut self) {
        // This can't panic because we've tested we can find the home directory in ConfigPaths::new
        // This should be replaced with proper error handling after this is not used by c++ anymore
        let xdg_dirs = xdg::BaseDirectories::new().unwrap();
        let config_dir = xdg_dirs.get_config_home().join(NEWSBOAT_SUBDIR_XDG);
        let data_dir = xdg_dirs.get_data_home().join(NEWSBOAT_SUBDIR_XDG);

        if !config_dir.is_dir() {
            return;
        }

        /* Invariant: config dir exists.
         *
         * At this point, we're confident we'll be using XDG. We don't check if
         * data dir exists, because if it doesn't we'll create it. */

        self.config_dir = config_dir;
        self.data_dir = data_dir;
    }

    /// Indicates if the object can be used.
    ///
    /// If this method returned `false`, the cause for initialization failure can be found using
    /// `error_message()`.
    pub fn initialized(&self) -> bool {
        !self.env_home.as_os_str().is_empty()
    }

    /// Returns explanation why initialization failed.
    ///
    /// \note You shouldn't call this unless `initialized()` returns `false`.
    pub fn error_message(&self) -> &str {
        &self.error_message
    }

    /// Initializes paths to config, cache etc. from CLI arguments.
    pub fn process_args(&mut self, args: &CliArgsParser) {
        if let Some(ref url_file) = args.url_file {
            self.url_file = url_file.to_string();
        }

        if let Some(ref cache_file) = args.cache_file {
            self.cache_file = cache_file.to_string();
        }

        if let Some(ref lock_file) = args.lock_file {
            self.lock_file = lock_file.to_string();
        }

        if let Some(ref config_file) = args.config_file {
            self.config_file = config_file.to_string();
        }

        self.silent = args.silent;
        self.using_nonstandard_configs = args.using_nonstandard_configs();
    }

    /// Migrate configs and data from Newsbeuter if they exist. Return `true` if migrated
    /// something, `false` otherwise.
    pub fn try_migrate_from_newsbeuter(&mut self) -> bool {
        if !self.using_nonstandard_configs && !Path::new(&self.url_file).exists() {
            return self.migrate_data_from_newsbeuter();
        }

        // No migration occurred.
        false
    }

    /// Path to the URLs file.
    pub fn url_file(&self) -> String {
        self.url_file.clone()
    }

    /// Path to the cache file.
    pub fn cache_file(&self) -> String {
        self.cache_file.clone()
    }

    /// Sets path to the cache file.
    // FIXME: this is actually a kludge that lets Controller change the path
    // midway. That logic should be moved into ConfigPaths, and this method
    // removed.
    pub fn set_cache_file(&mut self, path: String) {
        self.cache_file = path.clone();
        self.lock_file = path + LOCK_SUFFIX;
    }

    /// Path to the config file.
    pub fn config_file(&self) -> String {
        self.config_file.clone()
    }

    /// Path to the lock file.
    ///
    /// \note This changes when path to config file changes.
    pub fn lock_file(&self) -> String {
        self.lock_file.clone()
    }

    /// \brief Path to the queue file.
    ///
    /// Queue file stores enqueued podcasts. It's written by Newsboat, and read by Podboat.
    pub fn queue_file(&self) -> String {
        self.queue_file.clone()
    }

    /// Path to the file with previous search queries.
    pub fn search_file(&self) -> String {
        self.search_file.clone()
    }

    /// Path to the file with command-line history.
    pub fn cmdline_file(&self) -> String {
        self.cmdline_file.clone()
    }
}

fn try_mkdir<R: AsRef<Path>>(path: R) -> bool {
    DirBuilder::new()
        .recursive(true)
        .mode(0o700)
        .create(path.as_ref())
        .is_ok()
}

fn mkdir<R: AsRef<Path>>(path: R, mode: u32) -> io::Result<()> {
    DirBuilder::new().mode(mode).create(path.as_ref())
}

fn migrate_file<R: AsRef<Path>>(newsbeuter_dir: R, newsboat_dir: R, file: &str) -> io::Result<()> {
    let input_filepath = newsbeuter_dir.as_ref().join(file);
    let output_filepath = newsboat_dir.as_ref().join(file);
    eprintln!("{:?} -> {:?}", input_filepath, output_filepath);
    fs::copy(input_filepath, output_filepath).map(|_| ())
}
<|endoftext|>


<commit_before>//! Primitive data types.

#![allow(non_snake_case)]

use std::io::Read;
use std::{mem, ptr};

use Result;
use band::{Band, Value};

pub type BYTE = u8;
pub type CHAR = i8;

pub type USHORT = u16;
pub type SHORT = i16;

pub type UFWORD = USHORT;
pub type FWORD = SHORT;

pub type ULONG = u32;

#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub struct Fixed(pub u32);

pub type LONGDATETIME = i64;

impl Fixed {
    pub fn as_f32(&self) -> f32 {
        ((self.0 as f32) * 0.0000152587890625 * 1000.0).round() / 1000.0
    }
}

#[cfg(target_endian = "big")]
macro_rules! convert(
    ($data:ident) => ();
);

#[cfg(target_endian = "little")]
macro_rules! convert(
    ($data:ident) => ($data.reverse());
);

macro_rules! read(
    ($band:ident, $count:expr) => (unsafe {
        let mut buffer: [u8; $count] = mem::uninitialized();
        if try!($band.read(&mut buffer)) != $count {
            return raise!("failed to read as much as needed");
        }
        convert!(buffer);
        Ok(ptr::read(buffer.as_ptr() as *const _))
    });
);

impl Value for BYTE {
    fn read<T: Band>(band: &mut T) -> Result<Self> {
        read!(band, 1)
    }
}

impl Value for CHAR {
    fn read<T: Band>(band: &mut T) -> Result<Self> {
        read!(band, 1)
    }
}

impl Value for USHORT {
    fn read<T: Band>(band: &mut T) -> Result<Self> {
        read!(band, 2)
    }
}

impl Value for SHORT {
    fn read<T: Band>(band: &mut T) -> Result<Self> {
        read!(band, 2)
    }
}

impl Value for ULONG {
    fn read<T: Band>(band: &mut T) -> Result<Self> {
        read!(band, 4)
    }
}

impl Value for Fixed {
    fn read<T: Band>(band: &mut T) -> Result<Self> {
        read!(band, 4)
    }
}

impl Value for LONGDATETIME {
    fn read<T: Band>(band: &mut T) -> Result<Self> {
        read!(band, 8)
    }
}
<commit_msg>Dry a bit the primitives<commit_after>//! Primitive data types.

#![allow(non_snake_case)]

use std::io::Read;
use std::{mem, ptr};

use Result;
use band::{Band, Value};

pub type BYTE = u8;
pub type CHAR = i8;

pub type USHORT = u16;
pub type SHORT = i16;

pub type UFWORD = USHORT;
pub type FWORD = SHORT;

pub type ULONG = u32;

#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub struct Fixed(pub u32);

pub type LONGDATETIME = i64;

impl Fixed {
    pub fn as_f32(&self) -> f32 {
        ((self.0 as f32) * 0.0000152587890625 * 1000.0).round() / 1000.0
    }
}

#[cfg(target_endian = "big")]
macro_rules! convert(
    ($data:ident) => ();
);

#[cfg(target_endian = "little")]
macro_rules! convert(
    ($data:ident) => ($data.reverse());
);

macro_rules! read(
    ($band:ident, $count:expr) => (unsafe {
        let mut buffer: [u8; $count] = mem::uninitialized();
        if try!($band.read(&mut buffer)) != $count {
            return raise!("failed to read as much as needed");
        }
        convert!(buffer);
        Ok(ptr::read(buffer.as_ptr() as *const _))
    });
);

macro_rules! implement {
    ($name:ty, $count:expr) => (
        impl Value for $name {
            fn read<T: Band>(band: &mut T) -> Result<Self> {
                read!(band, $count)
            }
        }
    );
}

implement!(BYTE, 1);
implement!(CHAR, 1);

implement!(USHORT, 2);
implement!(SHORT, 2);

implement!(ULONG, 4);

implement!(Fixed, 4);

implement!(LONGDATETIME, 8);
<|endoftext|>


<commit_before>use CHIP8_HEIGHT;
use CHIP8_WIDTH;
use CHIP8_RAM;

const OPCODE_SIZE: usize = 2;

pub struct OutputState<'a> {
    pub vram: &'a [[u8; CHIP8_WIDTH]; CHIP8_HEIGHT],
    pub vram_changed: bool,
    pub beep: bool,
}

pub struct Processor {
    vram: [[u8; CHIP8_WIDTH]; CHIP8_HEIGHT],
    vram_changed: bool,
    ram: [u8; CHIP8_RAM],
    stack: [usize; 16],
    v: [u8; 16],
    i: usize,
    pc: usize,
    sp: usize,
    delay_timer: u8,
    sound_timer: u8,
}

impl Processor {
    pub fn new() -> Self {
        Processor {
            vram: [[0; CHIP8_WIDTH]; CHIP8_HEIGHT],
            vram_changed: false,
            ram: [0; CHIP8_RAM],
            stack: [0; 16],
            v: [0; 16],
            i: 0,
            pc: 0x200,
            sp: 0,
            delay_timer: 0,
            sound_timer: 0,
        }
    }

    pub fn tick(&mut self, keypad: &[bool; 16]) -> OutputState {
        self.vram_changed = false;
        let opcode = self.get_opcode();
        self.run_opcode(opcode);
        OutputState {
            vram: &self.vram,
            vram_changed: self.vram_changed,
            beep: self.sound_timer > 0,
        }
    }

    fn get_opcode(&self) -> u16 {
        (self.ram[self.pc] as u16) << 8 | (self.ram[self.pc + 1] as u16)
    }

    fn run_opcode(&mut self, opcode: u16) {

        let nibbles = (
            (opcode & 0xF000) >> 12 as u8,
            (opcode & 0x0F00) >> 8 as u8,
            (opcode & 0x00F0) >> 4 as u8,
            (opcode & 0x000F) as u8,
        );

        let nnn = (opcode & 0x0FFF) as usize;
        let kk = (opcode & 0x00FF) as u8;
        let x = nibbles.1 as usize;
        let y = nibbles.2 as usize;
        let n = nibbles.3 as u8;

        match nibbles {
            (0x00, 0x00, 0x0e, 0x00) => self.op_00e0(),
            (0x00, 0x00, 0x0e, 0x0e) => self.op_00ee(),
            (0x01, _, _, _) => self.op_1nnn(nnn),
            (0x02, _, _, _) => self.op_2nnn(nnn),
            (0x03, _, _, _) => self.op_3xkk(x, kk),
            (0x04, _, _, _) => self.op_4xkk(x, kk),
            (0x05, _, _, 0x00) => self.op_5xy0(x, y),
            (0x06, _, _, _) => self.op_6xkk(x, kk),
            (0x07, _, _, _) => self.op_7xkk(x, kk),
            (0x08, _, _, 0x00) => self.op_8xy0(x, y),
            (0x08, _, _, 0x01) => self.op_8xy1(x, y),
            (0x08, _, _, 0x02) => self.op_8xy2(x, y),
            (0x08, _, _, 0x03) => self.op_8xy3(x, y),
            (0x08, _, _, 0x04) => self.op_8xy4(x, y),
            (0x08, _, _, 0x05) => self.op_8xy5(x, y),
            (0x08, _, _, 0x06) => self.op_8xy6(x, y),
            (0x08, _, _, 0x07) => self.op_8xy7(x, y),
            (0x08, _, _, 0x0e) => self.op_8xye(x, y),
            (0x09, _, _, 0x00) => self.op_9xy0(x, y),
            (0x0a, _, _, _) => self.op_annn(nnn),
            (0x0b, _, _, _) => self.op_bnnn(nnn),
            (0x0c, _, _, _) => self.op_cxkk(x, kk),
            (0x0d, _, _, _) => self.op_dxyn(x, y, n),
            (0x0e, _, 0x09, 0x0e) => self.op_ex9e(x),
            (0x0e, _, 0x0a, 0x01) => self.op_exa1(x),
            (0x0f, _, 0x00, 0x07) => self.op_fx07(x),
            (0x0f, _, 0x00, 0x0a) => self.op_fx0a(x),
            (0x0f, _, 0x01, 0x05) => self.op_fx15(x),
            (0x0f, _, 0x01, 0x08) => self.op_fx18(x),
            (0x0f, _, 0x01, 0x0e) => self.op_fx1e(x),
            (0x0f, _, 0x02, 0x09) => self.op_fx29(x),
            (0x0f, _, 0x03, 0x03) => self.op_fx33(x),
            (0x0f, _, 0x05, 0x05) => self.op_fx55(x),
            (0x0f, _, 0x06, 0x05) => self.op_fx65(x),
            _ => return,
        }

    }
    // CLS: Clear the display.
    fn op_00e0(&mut self) {
        for y in 0..CHIP8_HEIGHT {
            for x in 0..CHIP8_WIDTH {
                self.vram[y][x] = 0;
            }
        }
        self.vram_changed = true;
        self.pc += OPCODE_SIZE;
    }

    // RET:  Return from a subroutine.
    fn op_00ee(&mut self) {
        self.sp -= 1;
        self.pc = self.stack[self.sp];
    }

    // JP addr
    fn op_1nnn(&mut self, nnn: usize) {
        self.pc = nnn;
    }

    // CALL addr
    fn op_2nnn(&mut self, nnn: usize) {
        self.stack[self.sp] = self.pc;
        self.sp += 1;
        self.pc = nnn;
    }

    // SE Vx, byte:  Skip next instruction if Vx = kk.
    fn op_3xkk(&mut self, x: usize, kk: u8) {
        self.pc += OPCODE_SIZE * (if self.v[x] == kk { 2 } else { 1 });
    }

    // SNE Vx, byte
    fn op_4xkk(&mut self, x: usize, kk: u8) {}
    // SE Vx, Vy
    fn op_5xy0(&mut self, x: usize, y: usize) {}
    // LD Vx, byte
    fn op_6xkk(&mut self, x: usize, kk: u8) {}
    // ADD Vx, byte
    fn op_7xkk(&mut self, x: usize, kk: u8) {}
    // LD Vx, Vy
    fn op_8xy0(&mut self, x: usize, y: usize) {}
    // OR Vx, Vy
    fn op_8xy1(&mut self, x: usize, y: usize) {}
    // AND Vx, Vy
    fn op_8xy2(&mut self, x: usize, y: usize) {}
    // XOR Vx, Vy
    fn op_8xy3(&mut self, x: usize, y: usize) {}
    // ADD Vx, Vy
    fn op_8xy4(&mut self, x: usize, y: usize) {}
    // SUB Vx, Vy
    fn op_8xy5(&mut self, x: usize, y: usize) {}
    // SHR Vx {, Vy}
    fn op_8xy6(&mut self, x: usize, y: usize) {}
    // SUBN Vx, Vy
    fn op_8xy7(&mut self, x: usize, y: usize) {}
    // SHL Vx {, Vy}
    fn op_8xye(&mut self, x: usize, y: usize) {}
    // SNE Vx, Vy
    fn op_9xy0(&mut self, x: usize, y: usize) {}
    // LD I, addr
    fn op_annn(&mut self, nnn: usize) {}
    // JP V0, addr
    fn op_bnnn(&mut self, nnn: usize) {}
    // RND Vx, byte
    fn op_cxkk(&mut self, x: usize, kk: u8) {}
    // DRW Vx, Vy, nibble
    fn op_dxyn(&mut self, x: usize, y: usize, n: u8) {}
    // SKP Vx
    fn op_ex9e(&mut self, x: usize) {}
    // SKNP Vx
    fn op_exa1(&mut self, x: usize) {}
    // LD Vx, DT
    fn op_fx07(&mut self, x: usize) {}
    // LD Vx, K
    fn op_fx0a(&mut self, x: usize) {}
    // LD DT, Vx
    fn op_fx15(&mut self, x: usize) {}
    // LD ST, Vx
    fn op_fx18(&mut self, x: usize) {}
    // ADD I, Vx
    fn op_fx1e(&mut self, x: usize) {}
    // LD F, Vx
    fn op_fx29(&mut self, x: usize) {}
    // LD B, Vx
    fn op_fx33(&mut self, x: usize) {}
    // LD [I], Vx
    fn op_fx55(&mut self, x: usize) {}
    // LD Vx, [I]
    fn op_fx65(&mut self, x: usize) {}
}

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

    #[test]
    fn test_initial_state() {
        let processor = Processor::new();
        assert_eq!(processor.sp, 0);
        assert_eq!(processor.stack, [0; 16]);
    }

    // CLS
    #[test]
    fn test_op_00e0() {
        let mut processor = Processor::new();
        processor.vram = [[128; CHIP8_WIDTH]; CHIP8_HEIGHT];
        processor.pc = 0x600;
        processor.run_opcode(0x00e0);
        for y in 0..CHIP8_HEIGHT {
            for x in 0..CHIP8_WIDTH {
                assert_eq!(processor.vram[y][x], 0);
            }
        }
        assert_eq!(processor.pc, 0x600 + OPCODE_SIZE);
    }

    // RET
    #[test]
    fn test_op_00ee() {
        let mut processor = Processor::new();
        processor.sp = 5;
        processor.stack[4] = 0x6666;
        processor.run_opcode(0x00ee);
        assert_eq!(processor.sp, 4);
        assert_eq!(processor.pc, 0x6666);
    }

    // JP
    #[test]
    fn test_op_1nnn() {
        let mut processor = Processor::new();
        processor.run_opcode(0x1666);
        assert_eq!(processor.pc, 0x0666);
    }

    // CALL
    #[test]
    fn test_op_2nnn() {
        let mut processor = Processor::new();
        processor.pc = 0x400;
        processor.run_opcode(0x2666);
        assert_eq!(processor.pc, 0x0666);
        assert_eq!(processor.sp, 1);
        assert_eq!(processor.stack[0], 0x400);
    }

    // SE VX, byte
    #[test]
    fn test_op_3xkk() {
        let mut processor = Processor::new();
        processor.pc = 0x400;
        processor.v[5] = 0xfb;
        processor.run_opcode(0x35fb);
        assert_eq!(processor.pc, 0x0400 + (2 * OPCODE_SIZE));
    }

}
<commit_msg>add SNE Vx, byte and SE Vx, Vy<commit_after>use CHIP8_HEIGHT;
use CHIP8_WIDTH;
use CHIP8_RAM;

const OPCODE_SIZE: usize = 2;

pub struct OutputState<'a> {
    pub vram: &'a [[u8; CHIP8_WIDTH]; CHIP8_HEIGHT],
    pub vram_changed: bool,
    pub beep: bool,
}

pub struct Processor {
    vram: [[u8; CHIP8_WIDTH]; CHIP8_HEIGHT],
    vram_changed: bool,
    ram: [u8; CHIP8_RAM],
    stack: [usize; 16],
    v: [u8; 16],
    i: usize,
    pc: usize,
    sp: usize,
    delay_timer: u8,
    sound_timer: u8,
}

impl Processor {
    pub fn new() -> Self {
        Processor {
            vram: [[0; CHIP8_WIDTH]; CHIP8_HEIGHT],
            vram_changed: false,
            ram: [0; CHIP8_RAM],
            stack: [0; 16],
            v: [0; 16],
            i: 0,
            pc: 0x200,
            sp: 0,
            delay_timer: 0,
            sound_timer: 0,
        }
    }

    pub fn tick(&mut self, keypad: &[bool; 16]) -> OutputState {
        self.vram_changed = false;
        let opcode = self.get_opcode();
        self.run_opcode(opcode);
        OutputState {
            vram: &self.vram,
            vram_changed: self.vram_changed,
            beep: self.sound_timer > 0,
        }
    }

    fn get_opcode(&self) -> u16 {
        (self.ram[self.pc] as u16) << 8 | (self.ram[self.pc + 1] as u16)
    }

    fn run_opcode(&mut self, opcode: u16) {

        let nibbles = (
            (opcode & 0xF000) >> 12 as u8,
            (opcode & 0x0F00) >> 8 as u8,
            (opcode & 0x00F0) >> 4 as u8,
            (opcode & 0x000F) as u8,
        );

        let nnn = (opcode & 0x0FFF) as usize;
        let kk = (opcode & 0x00FF) as u8;
        let x = nibbles.1 as usize;
        let y = nibbles.2 as usize;
        let n = nibbles.3 as u8;

        match nibbles {
            (0x00, 0x00, 0x0e, 0x00) => self.op_00e0(),
            (0x00, 0x00, 0x0e, 0x0e) => self.op_00ee(),
            (0x01, _, _, _) => self.op_1nnn(nnn),
            (0x02, _, _, _) => self.op_2nnn(nnn),
            (0x03, _, _, _) => self.op_3xkk(x, kk),
            (0x04, _, _, _) => self.op_4xkk(x, kk),
            (0x05, _, _, 0x00) => self.op_5xy0(x, y),
            (0x06, _, _, _) => self.op_6xkk(x, kk),
            (0x07, _, _, _) => self.op_7xkk(x, kk),
            (0x08, _, _, 0x00) => self.op_8xy0(x, y),
            (0x08, _, _, 0x01) => self.op_8xy1(x, y),
            (0x08, _, _, 0x02) => self.op_8xy2(x, y),
            (0x08, _, _, 0x03) => self.op_8xy3(x, y),
            (0x08, _, _, 0x04) => self.op_8xy4(x, y),
            (0x08, _, _, 0x05) => self.op_8xy5(x, y),
            (0x08, _, _, 0x06) => self.op_8xy6(x, y),
            (0x08, _, _, 0x07) => self.op_8xy7(x, y),
            (0x08, _, _, 0x0e) => self.op_8xye(x, y),
            (0x09, _, _, 0x00) => self.op_9xy0(x, y),
            (0x0a, _, _, _) => self.op_annn(nnn),
            (0x0b, _, _, _) => self.op_bnnn(nnn),
            (0x0c, _, _, _) => self.op_cxkk(x, kk),
            (0x0d, _, _, _) => self.op_dxyn(x, y, n),
            (0x0e, _, 0x09, 0x0e) => self.op_ex9e(x),
            (0x0e, _, 0x0a, 0x01) => self.op_exa1(x),
            (0x0f, _, 0x00, 0x07) => self.op_fx07(x),
            (0x0f, _, 0x00, 0x0a) => self.op_fx0a(x),
            (0x0f, _, 0x01, 0x05) => self.op_fx15(x),
            (0x0f, _, 0x01, 0x08) => self.op_fx18(x),
            (0x0f, _, 0x01, 0x0e) => self.op_fx1e(x),
            (0x0f, _, 0x02, 0x09) => self.op_fx29(x),
            (0x0f, _, 0x03, 0x03) => self.op_fx33(x),
            (0x0f, _, 0x05, 0x05) => self.op_fx55(x),
            (0x0f, _, 0x06, 0x05) => self.op_fx65(x),
            _ => return,
        }

    }
    // CLS: Clear the display.
    fn op_00e0(&mut self) {
        for y in 0..CHIP8_HEIGHT {
            for x in 0..CHIP8_WIDTH {
                self.vram[y][x] = 0;
            }
        }
        self.vram_changed = true;
        self.pc += OPCODE_SIZE;
    }

    // RET:  Return from a subroutine.
    fn op_00ee(&mut self) {
        self.sp -= 1;
        self.pc = self.stack[self.sp];
    }

    // JP addr
    fn op_1nnn(&mut self, nnn: usize) {
        self.pc = nnn;
    }

    // CALL addr
    fn op_2nnn(&mut self, nnn: usize) {
        self.stack[self.sp] = self.pc;
        self.sp += 1;
        self.pc = nnn;
    }

    // SE Vx, byte:  Skip next instruction if Vx = kk.
    fn op_3xkk(&mut self, x: usize, kk: u8) {
        self.pc += OPCODE_SIZE * (if self.v[x] == kk { 2 } else { 1 });
    }

    // SNE Vx, byte. Skip next instruction if Vx != kk.
    fn op_4xkk(&mut self, x: usize, kk: u8) {
        self.pc += OPCODE_SIZE * (if self.v[x] != kk { 2 } else { 1 });
    }

    // SE Vx, Vy
    fn op_5xy0(&mut self, x: usize, y: usize) {
        self.pc += OPCODE_SIZE * (if self.v[x] == self.v[y] { 2 } else { 1 });
    }
    // LD Vx, byte
    fn op_6xkk(&mut self, x: usize, kk: u8) {}
    // ADD Vx, byte
    fn op_7xkk(&mut self, x: usize, kk: u8) {}
    // LD Vx, Vy
    fn op_8xy0(&mut self, x: usize, y: usize) {}
    // OR Vx, Vy
    fn op_8xy1(&mut self, x: usize, y: usize) {}
    // AND Vx, Vy
    fn op_8xy2(&mut self, x: usize, y: usize) {}
    // XOR Vx, Vy
    fn op_8xy3(&mut self, x: usize, y: usize) {}
    // ADD Vx, Vy
    fn op_8xy4(&mut self, x: usize, y: usize) {}
    // SUB Vx, Vy
    fn op_8xy5(&mut self, x: usize, y: usize) {}
    // SHR Vx {, Vy}
    fn op_8xy6(&mut self, x: usize, y: usize) {}
    // SUBN Vx, Vy
    fn op_8xy7(&mut self, x: usize, y: usize) {}
    // SHL Vx {, Vy}
    fn op_8xye(&mut self, x: usize, y: usize) {}
    // SNE Vx, Vy
    fn op_9xy0(&mut self, x: usize, y: usize) {}
    // LD I, addr
    fn op_annn(&mut self, nnn: usize) {}
    // JP V0, addr
    fn op_bnnn(&mut self, nnn: usize) {}
    // RND Vx, byte
    fn op_cxkk(&mut self, x: usize, kk: u8) {}
    // DRW Vx, Vy, nibble
    fn op_dxyn(&mut self, x: usize, y: usize, n: u8) {}
    // SKP Vx
    fn op_ex9e(&mut self, x: usize) {}
    // SKNP Vx
    fn op_exa1(&mut self, x: usize) {}
    // LD Vx, DT
    fn op_fx07(&mut self, x: usize) {}
    // LD Vx, K
    fn op_fx0a(&mut self, x: usize) {}
    // LD DT, Vx
    fn op_fx15(&mut self, x: usize) {}
    // LD ST, Vx
    fn op_fx18(&mut self, x: usize) {}
    // ADD I, Vx
    fn op_fx1e(&mut self, x: usize) {}
    // LD F, Vx
    fn op_fx29(&mut self, x: usize) {}
    // LD B, Vx
    fn op_fx33(&mut self, x: usize) {}
    // LD [I], Vx
    fn op_fx55(&mut self, x: usize) {}
    // LD Vx, [I]
    fn op_fx65(&mut self, x: usize) {}
}

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

    #[test]
    fn test_initial_state() {
        let processor = Processor::new();
        assert_eq!(processor.sp, 0);
        assert_eq!(processor.stack, [0; 16]);
    }

    // CLS
    #[test]
    fn test_op_00e0() {
        let mut processor = Processor::new();
        processor.vram = [[128; CHIP8_WIDTH]; CHIP8_HEIGHT];
        processor.pc = 0x600;
        processor.run_opcode(0x00e0);
        for y in 0..CHIP8_HEIGHT {
            for x in 0..CHIP8_WIDTH {
                assert_eq!(processor.vram[y][x], 0);
            }
        }
        assert_eq!(processor.pc, 0x600 + OPCODE_SIZE);
    }

    // RET
    #[test]
    fn test_op_00ee() {
        let mut processor = Processor::new();
        processor.sp = 5;
        processor.stack[4] = 0x6666;
        processor.run_opcode(0x00ee);
        assert_eq!(processor.sp, 4);
        assert_eq!(processor.pc, 0x6666);
    }

    // JP
    #[test]
    fn test_op_1nnn() {
        let mut processor = Processor::new();
        processor.run_opcode(0x1666);
        assert_eq!(processor.pc, 0x0666);
    }

    // CALL
    #[test]
    fn test_op_2nnn() {
        let mut processor = Processor::new();
        processor.pc = 0x400;
        processor.run_opcode(0x2666);
        assert_eq!(processor.pc, 0x0666);
        assert_eq!(processor.sp, 1);
        assert_eq!(processor.stack[0], 0x400);
    }

    // SE VX, byte
    #[test]
    fn test_op_3xkk() {
        let mut processor = Processor::new();
        processor.pc = 0x400;
        processor.v[5] = 0xfb;
        processor.run_opcode(0x35fb);
        assert_eq!(processor.pc, 0x0400 + (2 * OPCODE_SIZE));
        processor.pc = 0x400;
        processor.run_opcode(0x35fc);
        assert_eq!(processor.pc, 0x0400 + OPCODE_SIZE);
    }

    // SNE VX, byte
    #[test]
    fn test_op_4xkk() {
        let mut processor = Processor::new();
        processor.pc = 0x400;
        processor.v[5] = 0xfb;
        processor.run_opcode(0x45fc);
        assert_eq!(processor.pc, 0x0400 + (2 * OPCODE_SIZE));
        processor.pc = 0x400;
        processor.run_opcode(0x45fb);
        assert_eq!(processor.pc, 0x0400 + OPCODE_SIZE);
    }

    // SE VX, VY
    #[test]
    fn test_op_5xy0() {
        let mut processor = Processor::new();
        processor.pc = 0x400;
        processor.v[5] = 0xfb;
        processor.v[4] = 0xfb;
        processor.run_opcode(0x5540);
        assert_eq!(processor.pc, 0x0400 + (2 * OPCODE_SIZE));
        processor.pc = 0x400;
        processor.run_opcode(0x5500);
        assert_eq!(processor.pc, 0x0400 + OPCODE_SIZE);
    }

}
<|endoftext|>