<commit_before>use std::collections::HashMap;
use std::ffi::CStr;

use cairo::prelude::SurfaceExt;
use cairo::{self, MatrixTrait};
use cairo_sys::cairo_surface_t;
use glib::translate::{from_glib_none, ToGlibPtr};
use glib_sys::*;

use bbox::BoundingBox;
use coord_units::CoordUnits;
use drawing_ctx::{self, RsvgDrawingCtx};
use length::RsvgLength;
use node::RsvgNode;

use super::input::Input;
use super::node::NodeFilter;
use super::RsvgFilterPrimitive;

// Required by the C code until all filters are ported to Rust.
// Keep this in sync with
// ../../librsvg/librsvg/rsvg-filter.h:RsvgIRect
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct IRect {
    pub x0: i32,
    pub y0: i32,
    pub x1: i32,
    pub y1: i32,
}

// Required by the C code until all filters are ported to Rust.
// Keep this in sync with
// ../../librsvg/librsvg/filters/common.h:_RsvgFilterPrimitiveOutput
#[repr(C)]
pub struct RsvgFilterPrimitiveOutput {
    surface: *mut cairo_surface_t,
    bounds: IRect,
}

/// A filter primitive output.
#[derive(Debug, Clone)]
pub struct FilterOutput {
    /// The surface after the filter primitive was applied.
    pub surface: cairo::ImageSurface,

    /// The filter primitive subregion.
    pub bounds: IRect,
}

/// A filter primitive result.
#[derive(Debug, Clone)]
pub struct FilterResult {
    /// The name of this result: the value of the `result` attribute.
    pub name: Option<String>,

    /// The output.
    pub output: FilterOutput,
}

pub type RsvgFilterContext = FilterContext;

/// The filter rendering context.
pub struct FilterContext {
    /// The <filter> node.
    node: RsvgNode,
    /// The source graphic surface.
    source_surface: cairo::ImageSurface,
    /// Output of the last filter primitive.
    last_result: Option<FilterOutput>,
    /// Surfaces of the previous filter primitives by name.
    previous_results: HashMap<String, FilterOutput>,

    affine: cairo::Matrix,
    paffine: cairo::Matrix,
    drawing_ctx: *mut RsvgDrawingCtx,
    channelmap: [i32; 4],
}

impl FilterContext {
    /// Creates a new `FilterContext`.
    pub fn new(
        filter_node: &RsvgNode,
        source_surface: cairo::ImageSurface,
        draw_ctx: *mut RsvgDrawingCtx,
        channelmap: [i32; 4],
    ) -> Self {
        let cr_affine = drawing_ctx::get_cairo_context(draw_ctx).get_matrix();
        let bbox = drawing_ctx::get_bbox(draw_ctx);
        let bbox_rect = bbox.rect.unwrap();

        let filter = filter_node.get_impl::<NodeFilter>().unwrap();

        let affine = match filter.filterunits.get() {
            CoordUnits::UserSpaceOnUse => cr_affine,
            CoordUnits::ObjectBoundingBox => {
                let affine = cairo::Matrix::new(
                    bbox_rect.width,
                    0f64,
                    0f64,
                    bbox_rect.height,
                    bbox_rect.x,
                    bbox_rect.y,
                );
                cairo::Matrix::multiply(&affine, &cr_affine)
            }
        };

        let paffine = match filter.primitiveunits.get() {
            CoordUnits::UserSpaceOnUse => cr_affine,
            CoordUnits::ObjectBoundingBox => {
                let affine = cairo::Matrix::new(
                    bbox_rect.width,
                    0f64,
                    0f64,
                    bbox_rect.height,
                    bbox_rect.x,
                    bbox_rect.y,
                );
                cairo::Matrix::multiply(&affine, &cr_affine)
            }
        };

        let mut rv = Self {
            node: filter_node.clone(),
            source_surface,
            last_result: None,
            previous_results: HashMap::new(),
            affine,
            paffine,
            drawing_ctx: draw_ctx,
            channelmap,
        };

        let last_result = FilterOutput {
            surface: rv.source_surface.clone(),
            bounds: rv.compute_bounds(None, None, None, None),
        };

        rv.last_result = Some(last_result);
        rv
    }

    /// Returns the <filter> node for this context.
    #[inline]
    pub fn get_filter_node(&self) -> RsvgNode {
        self.node.clone()
    }

    /// Returns the surface corresponding to the last filter primitive's result.
    #[inline]
    pub fn last_result(&self) -> Option<&FilterOutput> {
        self.last_result.as_ref()
    }

    /// Returns the surface corresponding to the source graphic.
    #[inline]
    pub fn source_graphic(&self) -> &cairo::ImageSurface {
        &self.source_surface
    }

    /// Returns the surface corresponding to the background image snapshot.
    #[inline]
    pub fn background_image(&self) -> &cairo::ImageSurface {
        unimplemented!()
    }

    /// Returns the output of the filter primitive by its result name.
    #[inline]
    pub fn filter_output(&self, name: &str) -> Option<&FilterOutput> {
        self.previous_results.get(name)
    }

    /// Converts this `FilterContext` into the surface corresponding to the output of the filter
    /// chain.
    #[inline]
    pub fn into_output(self) -> cairo::ImageSurface {
        self.last_result
            .map(|FilterOutput { surface, .. }| surface)
            .unwrap_or(self.source_surface)
    }

    /// Stores a filter primitive result into the context.
    #[inline]
    pub fn store_result(&mut self, result: FilterResult) {
        if let Some(name) = result.name {
            self.previous_results.insert(name, result.output.clone());
        }

        self.last_result = Some(result.output);
    }

    /// Returns the drawing context for this filter context.
    #[inline]
    pub fn drawing_context(&self) -> *const RsvgDrawingCtx {
        self.drawing_ctx
    }

    /// Returns the paffine matrix.
    #[inline]
    pub fn paffine(&self) -> cairo::Matrix {
        self.paffine
    }

    /// Computes and returns the filter primitive bounds.
    pub fn compute_bounds(
        &self,
        x: Option<RsvgLength>,
        y: Option<RsvgLength>,
        width: Option<RsvgLength>,
        height: Option<RsvgLength>,
    ) -> IRect {
        let cascaded = self.node.get_cascaded_values();
        let values = cascaded.get();

        let filter = self.node.get_impl::<NodeFilter>().unwrap();
        let mut bbox = BoundingBox::new(&cairo::Matrix::identity());

        if filter.filterunits.get() == CoordUnits::ObjectBoundingBox {
            drawing_ctx::push_view_box(self.drawing_ctx, 1f64, 1f64);
        }

        let rect = cairo::Rectangle {
            x: filter.x.get().normalize(values, self.drawing_ctx),
            y: filter.y.get().normalize(values, self.drawing_ctx),
            width: filter.width.get().normalize(values, self.drawing_ctx),
            height: filter.height.get().normalize(values, self.drawing_ctx),
        };

        if filter.filterunits.get() == CoordUnits::ObjectBoundingBox {
            drawing_ctx::pop_view_box(self.drawing_ctx);
        }

        let other_bbox = BoundingBox::new(&self.affine).with_rect(Some(rect));
        bbox.insert(&other_bbox);

        if x.is_some() || y.is_some() || width.is_some() || height.is_some() {
            if filter.primitiveunits.get() == CoordUnits::ObjectBoundingBox {
                drawing_ctx::push_view_box(self.drawing_ctx, 1f64, 1f64);
            }

            let mut rect = cairo::Rectangle {
                x: x.map(|x| x.normalize(values, self.drawing_ctx))
                    .unwrap_or(0f64),
                y: y.map(|y| y.normalize(values, self.drawing_ctx))
                    .unwrap_or(0f64),
                ..rect
            };

            if width.is_some() || height.is_some() {
                let (vbox_width, vbox_height) = drawing_ctx::get_view_box_size(self.drawing_ctx);

                rect.width = width
                    .map(|w| w.normalize(values, self.drawing_ctx))
                    .unwrap_or(vbox_width);
                rect.height = height
                    .map(|h| h.normalize(values, self.drawing_ctx))
                    .unwrap_or(vbox_height);
            }

            if filter.primitiveunits.get() == CoordUnits::ObjectBoundingBox {
                drawing_ctx::pop_view_box(self.drawing_ctx);
            }

            let other_bbox = BoundingBox::new(&self.paffine).with_rect(Some(rect));
            bbox.clip(&other_bbox);
        }

        let rect = cairo::Rectangle {
            x: 0f64,
            y: 0f64,
            width: f64::from(self.source_surface.get_width()),
            height: f64::from(self.source_surface.get_height()),
        };
        let other_bbox = BoundingBox::new(&cairo::Matrix::identity()).with_rect(Some(rect));
        bbox.clip(&other_bbox);

        let bbox_rect = bbox.rect.unwrap();
        IRect {
            x0: bbox_rect.x.floor() as i32,
            y0: bbox_rect.y.floor() as i32,
            x1: (bbox_rect.x + bbox_rect.width).ceil() as i32,
            y1: (bbox_rect.y + bbox_rect.height).ceil() as i32,
        }
    }

    /// Retrieves the filter input surface according to the SVG rules.
    pub fn get_input(&self, in_: Option<&Input>) -> Option<FilterOutput> {
        if in_.is_none() {
            // No value => use the last result.
            // As per the SVG spec, if the filter primitive is the first in the chain, return the
            // source graphic.
            return Some(self.last_result().cloned().unwrap_or_else(|| FilterOutput {
                surface: self.source_graphic().clone(),
                // TODO
                bounds: IRect {
                    x0: 0,
                    y0: 0,
                    x1: 0,
                    y1: 0,
                },
            }));
        }

        match *in_.unwrap() {
            Input::SourceGraphic => Some(FilterOutput {
                surface: self.source_graphic().clone(),
                // TODO
                bounds: IRect {
                    x0: 0,
                    y0: 0,
                    x1: 0,
                    y1: 0,
                },
            }),
            Input::SourceAlpha => unimplemented!(),
            Input::BackgroundImage => unimplemented!(),
            Input::BackgroundAlpha => unimplemented!(),

            Input::FillPaint => unimplemented!(),
            Input::StrokePaint => unimplemented!(),

            Input::FilterOutput(ref name) => self.filter_output(name).cloned(),
        }
    }
}

#[no_mangle]
pub unsafe extern "C" fn rsvg_filter_context_get_affine(
    ctx: *const RsvgFilterContext,
) -> cairo::Matrix {
    assert!(!ctx.is_null());

    (*ctx).affine
}

#[no_mangle]
pub unsafe extern "C" fn rsvg_filter_context_get_paffine(
    ctx: *const RsvgFilterContext,
) -> cairo::Matrix {
    assert!(!ctx.is_null());

    (*ctx).paffine
}

#[no_mangle]
pub unsafe extern "C" fn rsvg_filter_context_get_drawing_ctx(
    ctx: *mut RsvgFilterContext,
) -> *mut RsvgDrawingCtx {
    assert!(!ctx.is_null());

    (*ctx).drawing_ctx
}

#[no_mangle]
pub unsafe extern "C" fn rsvg_filter_context_get_width(ctx: *const RsvgFilterContext) -> i32 {
    assert!(!ctx.is_null());

    (*ctx).source_surface.get_width()
}

#[no_mangle]
pub unsafe extern "C" fn rsvg_filter_context_get_height(ctx: *const RsvgFilterContext) -> i32 {
    assert!(!ctx.is_null());

    (*ctx).source_surface.get_height()
}

#[no_mangle]
pub unsafe extern "C" fn rsvg_filter_context_get_channelmap(
    ctx: *const RsvgFilterContext,
) -> *const i32 {
    assert!(!ctx.is_null());

    (*ctx).channelmap.as_ptr()
}

#[no_mangle]
pub unsafe extern "C" fn rsvg_filter_context_get_source_surface(
    ctx: *mut RsvgFilterContext,
) -> *mut cairo_surface_t {
    assert!(!ctx.is_null());

    (*ctx).source_surface.to_glib_none().0
}

#[no_mangle]
pub unsafe extern "C" fn rsvg_filter_context_get_bg_surface(
    ctx: *mut RsvgFilterContext,
) -> *mut cairo_surface_t {
    assert!(!ctx.is_null());

    (*ctx).background_image().to_glib_none().0
}

#[no_mangle]
pub unsafe extern "C" fn rsvg_filter_context_get_lastresult(
    ctx: *mut RsvgFilterContext,
) -> RsvgFilterPrimitiveOutput {
    assert!(!ctx.is_null());

    let ctx = &*ctx;

    match ctx.last_result {
        Some(FilterOutput {
            ref surface,
            ref bounds,
        }) => RsvgFilterPrimitiveOutput {
            surface: surface.to_glib_none().0,
            bounds: *bounds,
        },
        None => RsvgFilterPrimitiveOutput {
            surface: ctx.source_surface.to_glib_none().0,
            bounds: ctx.compute_bounds(None, None, None, None),
        },
    }
}

#[no_mangle]
pub unsafe extern "C" fn rsvg_filter_context_get_previous_result(
    name: *mut GString,
    ctx: *mut RsvgFilterContext,
    output: *mut RsvgFilterPrimitiveOutput,
) -> i32 {
    assert!(!name.is_null());
    assert!(!ctx.is_null());
    assert!(!output.is_null());

    if let Some(&FilterOutput {
        ref surface,
        ref bounds,
    }) = (*ctx).filter_output(&CStr::from_ptr((*name).str).to_string_lossy())
    {
        *output = RsvgFilterPrimitiveOutput {
            surface: surface.to_glib_none().0,
            bounds: *bounds,
        };
        1
    } else {
        0
    }
}

#[no_mangle]
pub unsafe extern "C" fn rsvg_filter_store_output(
    name: *mut GString,
    result: RsvgFilterPrimitiveOutput,
    ctx: *mut RsvgFilterContext,
) {
    assert!(!name.is_null());
    assert!(!result.surface.is_null());
    assert!(!ctx.is_null());

    let name = from_glib_none((*name).str);

    let surface: cairo::Surface = from_glib_none(result.surface);
    assert_eq!(surface.get_type(), cairo::SurfaceType::Image);
    let surface = cairo::ImageSurface::from(surface).unwrap();

    let result = FilterResult {
        name: Some(name),
        output: FilterOutput {
            surface,
            bounds: result.bounds,
        },
    };

    (*ctx).store_result(result);
}

#[no_mangle]
pub unsafe extern "C" fn rsvg_filter_primitive_get_bounds(
    primitive: *const RsvgFilterPrimitive,
    ctx: *const RsvgFilterContext,
) -> IRect {
    assert!(!ctx.is_null());

    let ctx = &*ctx;

    let mut x = None;
    let mut y = None;
    let mut width = None;
    let mut height = None;

    if !primitive.is_null() {
        if (*primitive).x_specified != 0 {
            x = Some((*primitive).x)
        };

        if (*primitive).y_specified != 0 {
            y = Some((*primitive).y)
        };

        if (*primitive).width_specified != 0 {
            width = Some((*primitive).width)
        };

        if (*primitive).height_specified != 0 {
            height = Some((*primitive).height)
        };
    }

    ctx.compute_bounds(x, y, width, height)
}
<commit_msg>Return correct bounds from get_input()<commit_after>use std::collections::HashMap;
use std::ffi::CStr;

use cairo::prelude::SurfaceExt;
use cairo::{self, MatrixTrait};
use cairo_sys::cairo_surface_t;
use glib::translate::{from_glib_none, ToGlibPtr};
use glib_sys::*;

use bbox::BoundingBox;
use coord_units::CoordUnits;
use drawing_ctx::{self, RsvgDrawingCtx};
use length::RsvgLength;
use node::RsvgNode;

use super::input::Input;
use super::node::NodeFilter;
use super::RsvgFilterPrimitive;

// Required by the C code until all filters are ported to Rust.
// Keep this in sync with
// ../../librsvg/librsvg/rsvg-filter.h:RsvgIRect
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct IRect {
    pub x0: i32,
    pub y0: i32,
    pub x1: i32,
    pub y1: i32,
}

// Required by the C code until all filters are ported to Rust.
// Keep this in sync with
// ../../librsvg/librsvg/filters/common.h:_RsvgFilterPrimitiveOutput
#[repr(C)]
pub struct RsvgFilterPrimitiveOutput {
    surface: *mut cairo_surface_t,
    bounds: IRect,
}

/// A filter primitive output.
#[derive(Debug, Clone)]
pub struct FilterOutput {
    /// The surface after the filter primitive was applied.
    pub surface: cairo::ImageSurface,

    /// The filter primitive subregion.
    pub bounds: IRect,
}

/// A filter primitive result.
#[derive(Debug, Clone)]
pub struct FilterResult {
    /// The name of this result: the value of the `result` attribute.
    pub name: Option<String>,

    /// The output.
    pub output: FilterOutput,
}

pub type RsvgFilterContext = FilterContext;

/// The filter rendering context.
pub struct FilterContext {
    /// The <filter> node.
    node: RsvgNode,
    /// The source graphic surface.
    source_surface: cairo::ImageSurface,
    /// Output of the last filter primitive.
    last_result: Option<FilterOutput>,
    /// Surfaces of the previous filter primitives by name.
    previous_results: HashMap<String, FilterOutput>,

    affine: cairo::Matrix,
    paffine: cairo::Matrix,
    drawing_ctx: *mut RsvgDrawingCtx,
    channelmap: [i32; 4],
}

impl FilterContext {
    /// Creates a new `FilterContext`.
    pub fn new(
        filter_node: &RsvgNode,
        source_surface: cairo::ImageSurface,
        draw_ctx: *mut RsvgDrawingCtx,
        channelmap: [i32; 4],
    ) -> Self {
        let cr_affine = drawing_ctx::get_cairo_context(draw_ctx).get_matrix();
        let bbox = drawing_ctx::get_bbox(draw_ctx);
        let bbox_rect = bbox.rect.unwrap();

        let filter = filter_node.get_impl::<NodeFilter>().unwrap();

        let affine = match filter.filterunits.get() {
            CoordUnits::UserSpaceOnUse => cr_affine,
            CoordUnits::ObjectBoundingBox => {
                let affine = cairo::Matrix::new(
                    bbox_rect.width,
                    0f64,
                    0f64,
                    bbox_rect.height,
                    bbox_rect.x,
                    bbox_rect.y,
                );
                cairo::Matrix::multiply(&affine, &cr_affine)
            }
        };

        let paffine = match filter.primitiveunits.get() {
            CoordUnits::UserSpaceOnUse => cr_affine,
            CoordUnits::ObjectBoundingBox => {
                let affine = cairo::Matrix::new(
                    bbox_rect.width,
                    0f64,
                    0f64,
                    bbox_rect.height,
                    bbox_rect.x,
                    bbox_rect.y,
                );
                cairo::Matrix::multiply(&affine, &cr_affine)
            }
        };

        let mut rv = Self {
            node: filter_node.clone(),
            source_surface,
            last_result: None,
            previous_results: HashMap::new(),
            affine,
            paffine,
            drawing_ctx: draw_ctx,
            channelmap,
        };

        let last_result = FilterOutput {
            surface: rv.source_surface.clone(),
            bounds: rv.compute_bounds(None, None, None, None),
        };

        rv.last_result = Some(last_result);
        rv
    }

    /// Returns the <filter> node for this context.
    #[inline]
    pub fn get_filter_node(&self) -> RsvgNode {
        self.node.clone()
    }

    /// Returns the surface corresponding to the last filter primitive's result.
    #[inline]
    pub fn last_result(&self) -> Option<&FilterOutput> {
        self.last_result.as_ref()
    }

    /// Returns the surface corresponding to the source graphic.
    #[inline]
    pub fn source_graphic(&self) -> &cairo::ImageSurface {
        &self.source_surface
    }

    /// Returns the surface corresponding to the background image snapshot.
    #[inline]
    pub fn background_image(&self) -> &cairo::ImageSurface {
        unimplemented!()
    }

    /// Returns the output of the filter primitive by its result name.
    #[inline]
    pub fn filter_output(&self, name: &str) -> Option<&FilterOutput> {
        self.previous_results.get(name)
    }

    /// Converts this `FilterContext` into the surface corresponding to the output of the filter
    /// chain.
    #[inline]
    pub fn into_output(self) -> cairo::ImageSurface {
        self.last_result
            .map(|FilterOutput { surface, .. }| surface)
            .unwrap_or(self.source_surface)
    }

    /// Stores a filter primitive result into the context.
    #[inline]
    pub fn store_result(&mut self, result: FilterResult) {
        if let Some(name) = result.name {
            self.previous_results.insert(name, result.output.clone());
        }

        self.last_result = Some(result.output);
    }

    /// Returns the drawing context for this filter context.
    #[inline]
    pub fn drawing_context(&self) -> *const RsvgDrawingCtx {
        self.drawing_ctx
    }

    /// Returns the paffine matrix.
    #[inline]
    pub fn paffine(&self) -> cairo::Matrix {
        self.paffine
    }

    /// Computes and returns the filter primitive bounds.
    pub fn compute_bounds(
        &self,
        x: Option<RsvgLength>,
        y: Option<RsvgLength>,
        width: Option<RsvgLength>,
        height: Option<RsvgLength>,
    ) -> IRect {
        let cascaded = self.node.get_cascaded_values();
        let values = cascaded.get();

        let filter = self.node.get_impl::<NodeFilter>().unwrap();
        let mut bbox = BoundingBox::new(&cairo::Matrix::identity());

        if filter.filterunits.get() == CoordUnits::ObjectBoundingBox {
            drawing_ctx::push_view_box(self.drawing_ctx, 1f64, 1f64);
        }

        let rect = cairo::Rectangle {
            x: filter.x.get().normalize(values, self.drawing_ctx),
            y: filter.y.get().normalize(values, self.drawing_ctx),
            width: filter.width.get().normalize(values, self.drawing_ctx),
            height: filter.height.get().normalize(values, self.drawing_ctx),
        };

        if filter.filterunits.get() == CoordUnits::ObjectBoundingBox {
            drawing_ctx::pop_view_box(self.drawing_ctx);
        }

        let other_bbox = BoundingBox::new(&self.affine).with_rect(Some(rect));
        bbox.insert(&other_bbox);

        if x.is_some() || y.is_some() || width.is_some() || height.is_some() {
            if filter.primitiveunits.get() == CoordUnits::ObjectBoundingBox {
                drawing_ctx::push_view_box(self.drawing_ctx, 1f64, 1f64);
            }

            let mut rect = cairo::Rectangle {
                x: x.map(|x| x.normalize(values, self.drawing_ctx))
                    .unwrap_or(0f64),
                y: y.map(|y| y.normalize(values, self.drawing_ctx))
                    .unwrap_or(0f64),
                ..rect
            };

            if width.is_some() || height.is_some() {
                let (vbox_width, vbox_height) = drawing_ctx::get_view_box_size(self.drawing_ctx);

                rect.width = width
                    .map(|w| w.normalize(values, self.drawing_ctx))
                    .unwrap_or(vbox_width);
                rect.height = height
                    .map(|h| h.normalize(values, self.drawing_ctx))
                    .unwrap_or(vbox_height);
            }

            if filter.primitiveunits.get() == CoordUnits::ObjectBoundingBox {
                drawing_ctx::pop_view_box(self.drawing_ctx);
            }

            let other_bbox = BoundingBox::new(&self.paffine).with_rect(Some(rect));
            bbox.clip(&other_bbox);
        }

        let rect = cairo::Rectangle {
            x: 0f64,
            y: 0f64,
            width: f64::from(self.source_surface.get_width()),
            height: f64::from(self.source_surface.get_height()),
        };
        let other_bbox = BoundingBox::new(&cairo::Matrix::identity()).with_rect(Some(rect));
        bbox.clip(&other_bbox);

        let bbox_rect = bbox.rect.unwrap();
        IRect {
            x0: bbox_rect.x.floor() as i32,
            y0: bbox_rect.y.floor() as i32,
            x1: (bbox_rect.x + bbox_rect.width).ceil() as i32,
            y1: (bbox_rect.y + bbox_rect.height).ceil() as i32,
        }
    }

    /// Retrieves the filter input surface according to the SVG rules.
    pub fn get_input(&self, in_: Option<&Input>) -> Option<FilterOutput> {
        if in_.is_none() {
            // No value => use the last result.
            // As per the SVG spec, if the filter primitive is the first in the chain, return the
            // source graphic.
            return Some(self.last_result().cloned().unwrap_or_else(|| FilterOutput {
                surface: self.source_graphic().clone(),
                bounds: self.compute_bounds(None, None, None, None),
            }));
        }

        match *in_.unwrap() {
            Input::SourceGraphic => Some(FilterOutput {
                surface: self.source_graphic().clone(),
                bounds: self.compute_bounds(None, None, None, None),
            }),
            Input::SourceAlpha => unimplemented!(),
            Input::BackgroundImage => unimplemented!(),
            Input::BackgroundAlpha => unimplemented!(),

            Input::FillPaint => unimplemented!(),
            Input::StrokePaint => unimplemented!(),

            Input::FilterOutput(ref name) => self.filter_output(name).cloned(),
        }
    }
}

#[no_mangle]
pub unsafe extern "C" fn rsvg_filter_context_get_affine(
    ctx: *const RsvgFilterContext,
) -> cairo::Matrix {
    assert!(!ctx.is_null());

    (*ctx).affine
}

#[no_mangle]
pub unsafe extern "C" fn rsvg_filter_context_get_paffine(
    ctx: *const RsvgFilterContext,
) -> cairo::Matrix {
    assert!(!ctx.is_null());

    (*ctx).paffine
}

#[no_mangle]
pub unsafe extern "C" fn rsvg_filter_context_get_drawing_ctx(
    ctx: *mut RsvgFilterContext,
) -> *mut RsvgDrawingCtx {
    assert!(!ctx.is_null());

    (*ctx).drawing_ctx
}

#[no_mangle]
pub unsafe extern "C" fn rsvg_filter_context_get_width(ctx: *const RsvgFilterContext) -> i32 {
    assert!(!ctx.is_null());

    (*ctx).source_surface.get_width()
}

#[no_mangle]
pub unsafe extern "C" fn rsvg_filter_context_get_height(ctx: *const RsvgFilterContext) -> i32 {
    assert!(!ctx.is_null());

    (*ctx).source_surface.get_height()
}

#[no_mangle]
pub unsafe extern "C" fn rsvg_filter_context_get_channelmap(
    ctx: *const RsvgFilterContext,
) -> *const i32 {
    assert!(!ctx.is_null());

    (*ctx).channelmap.as_ptr()
}

#[no_mangle]
pub unsafe extern "C" fn rsvg_filter_context_get_source_surface(
    ctx: *mut RsvgFilterContext,
) -> *mut cairo_surface_t {
    assert!(!ctx.is_null());

    (*ctx).source_surface.to_glib_none().0
}

#[no_mangle]
pub unsafe extern "C" fn rsvg_filter_context_get_bg_surface(
    ctx: *mut RsvgFilterContext,
) -> *mut cairo_surface_t {
    assert!(!ctx.is_null());

    (*ctx).background_image().to_glib_none().0
}

#[no_mangle]
pub unsafe extern "C" fn rsvg_filter_context_get_lastresult(
    ctx: *mut RsvgFilterContext,
) -> RsvgFilterPrimitiveOutput {
    assert!(!ctx.is_null());

    let ctx = &*ctx;

    match ctx.last_result {
        Some(FilterOutput {
            ref surface,
            ref bounds,
        }) => RsvgFilterPrimitiveOutput {
            surface: surface.to_glib_none().0,
            bounds: *bounds,
        },
        None => RsvgFilterPrimitiveOutput {
            surface: ctx.source_surface.to_glib_none().0,
            bounds: ctx.compute_bounds(None, None, None, None),
        },
    }
}

#[no_mangle]
pub unsafe extern "C" fn rsvg_filter_context_get_previous_result(
    name: *mut GString,
    ctx: *mut RsvgFilterContext,
    output: *mut RsvgFilterPrimitiveOutput,
) -> i32 {
    assert!(!name.is_null());
    assert!(!ctx.is_null());
    assert!(!output.is_null());

    if let Some(&FilterOutput {
        ref surface,
        ref bounds,
    }) = (*ctx).filter_output(&CStr::from_ptr((*name).str).to_string_lossy())
    {
        *output = RsvgFilterPrimitiveOutput {
            surface: surface.to_glib_none().0,
            bounds: *bounds,
        };
        1
    } else {
        0
    }
}

#[no_mangle]
pub unsafe extern "C" fn rsvg_filter_store_output(
    name: *mut GString,
    result: RsvgFilterPrimitiveOutput,
    ctx: *mut RsvgFilterContext,
) {
    assert!(!name.is_null());
    assert!(!result.surface.is_null());
    assert!(!ctx.is_null());

    let name = from_glib_none((*name).str);

    let surface: cairo::Surface = from_glib_none(result.surface);
    assert_eq!(surface.get_type(), cairo::SurfaceType::Image);
    let surface = cairo::ImageSurface::from(surface).unwrap();

    let result = FilterResult {
        name: Some(name),
        output: FilterOutput {
            surface,
            bounds: result.bounds,
        },
    };

    (*ctx).store_result(result);
}

#[no_mangle]
pub unsafe extern "C" fn rsvg_filter_primitive_get_bounds(
    primitive: *const RsvgFilterPrimitive,
    ctx: *const RsvgFilterContext,
) -> IRect {
    assert!(!ctx.is_null());

    let ctx = &*ctx;

    let mut x = None;
    let mut y = None;
    let mut width = None;
    let mut height = None;

    if !primitive.is_null() {
        if (*primitive).x_specified != 0 {
            x = Some((*primitive).x)
        };

        if (*primitive).y_specified != 0 {
            y = Some((*primitive).y)
        };

        if (*primitive).width_specified != 0 {
            width = Some((*primitive).width)
        };

        if (*primitive).height_specified != 0 {
            height = Some((*primitive).height)
        };
    }

    ctx.compute_bounds(x, y, width, height)
}
<|endoftext|>


<commit_before>use std::cmp::min;
use std::thread;
use std::time::Duration;

use indicatif::{ProgressBar, ProgressStyle};

fn main() {
    let mut downloaded = 69369369;
    let total_size = 231231231;

    let pb = ProgressBar::new(total_size);
    pb.set_style(
        ProgressStyle::default_bar()
            .template("{spinner:.green} [{wide_bar:.cyan/blue}] {bytes}/{total_bytes} ({eta})")
            .progress_chars("#>-"),
    );

    while downloaded < total_size {
        let new = min(downloaded + 123211, total_size);
        downloaded = new;
        pb.set_position(new);
        thread::sleep(Duration::from_millis(12));
    }

    pb.finish_with_message("downloaded");
}
<commit_msg>Fix `download-continued` ETA (#171)<commit_after>use std::cmp::min;
use std::thread;
use std::time::Duration;

use indicatif::{ProgressBar, ProgressStyle};

fn main() {
    let mut downloaded = 69369369;
    let total_size = 231231231;

    let pb = ProgressBar::new(total_size);
    pb.set_style(
        ProgressStyle::default_bar()
            .template("{spinner:.green} [{wide_bar:.cyan/blue}] {bytes}/{total_bytes} ({eta})")
            .progress_chars("#>-"),
    );
    pb.set_position(downloaded);
    pb.reset_eta();

    while downloaded < total_size {
        downloaded = min(downloaded + 123211, total_size);
        pb.set_position(downloaded);
        thread::sleep(Duration::from_millis(12));
    }

    pb.finish_with_message("downloaded");
}
<|endoftext|>


<commit_before>use std::ops::Deref;

use protobuf::reflect::EnumDescriptor;
use protobuf::reflect::EnumValueDescriptor;
use protobuf::reflect::FieldDescriptor;
use protobuf::reflect::FileDescriptor;
use protobuf::reflect::MessageDescriptor;
use protobuf::reflect::OneofDescriptor;
use protobuf_parse::ProtobufAbsPath;
use protobuf_parse::ProtobufAbsPathRef;
use protobuf_parse::ProtobufIdentRef;
use protobuf_parse::ProtobufRelPath;
use protobuf_parse::ProtobufRelPathRef;

use crate::customize::Customize;
use crate::gen::field::rust_field_name_for_protobuf_field_name;
use crate::gen::file_and_mod::FileAndMod;
use crate::gen::map::map_entry;
use crate::gen::message::message_name_to_nested_mod_name;
use crate::gen::paths::proto_path_to_rust_mod;
use crate::gen::rust::ident::RustIdent;
use crate::gen::rust::ident_with_path::RustIdentWithPath;
use crate::gen::rust::rel_path::RustRelativePath;
use crate::gen::strx::capitalize;

pub(crate) struct RootScope<'a> {
    pub file_descriptors: &'a [FileDescriptor],
}

impl<'a> RootScope<'a> {
    fn packages(&'a self) -> Vec<FileScope<'a>> {
        self.file_descriptors
            .iter()
            .map(|fd| FileScope {
                file_descriptor: fd,
            })
            .collect()
    }

    // find enum by fully qualified name
    pub fn _find_enum(&'a self, fqn: &ProtobufAbsPath) -> EnumWithScope<'a> {
        match self.find_message_or_enum(fqn) {
            MessageOrEnumWithScope::Enum(e) => e,
            _ => panic!("not an enum: {}", fqn),
        }
    }

    // find message by fully qualified name
    pub fn find_message(&'a self, fqn: &ProtobufAbsPath) -> MessageWithScope<'a> {
        match self.find_message_or_enum(fqn) {
            MessageOrEnumWithScope::Message(m) => m,
            _ => panic!("not a message: {}", fqn),
        }
    }

    // find message or enum by fully qualified name
    pub fn find_message_or_enum(&'a self, fqn: &ProtobufAbsPath) -> MessageOrEnumWithScope<'a> {
        assert!(!fqn.is_root());
        self.packages()
            .into_iter()
            .flat_map(|p| p.find_message_or_enum_abs(fqn))
            .next()
            .expect(&format!("enum not found by name: {}", fqn))
    }
}

#[derive(Clone, Debug)]
pub(crate) struct FileScope<'a> {
    pub file_descriptor: &'a FileDescriptor,
}

impl<'a> Deref for FileScope<'a> {
    type Target = FileDescriptor;

    fn deref(&self) -> &Self::Target {
        self.file_descriptor
    }
}

impl<'a> FileScope<'a> {
    fn package(&self) -> ProtobufAbsPath {
        ProtobufAbsPath::package_from_file_descriptor(self.file_descriptor)
    }

    pub fn to_scope(&self) -> Scope<'a> {
        Scope {
            file_scope: self.clone(),
            path: Vec::new(),
        }
    }

    fn find_message_or_enum(
        &self,
        name: &ProtobufRelPathRef,
    ) -> Option<MessageOrEnumWithScope<'a>> {
        self.find_messages_and_enums()
            .into_iter()
            .filter(|e| e.protobuf_name_to_package().as_ref() == name)
            .next()
    }

    fn find_message_or_enum_abs(
        &self,
        name: &ProtobufAbsPathRef,
    ) -> Option<MessageOrEnumWithScope<'a>> {
        let name = name.to_owned();
        match name.remove_prefix(&self.package()) {
            Some(rem) => self.find_message_or_enum(&rem),
            None => None,
        }
    }

    // find all enums in given file descriptor
    pub fn find_enums(&self) -> Vec<EnumWithScope<'a>> {
        let mut r = Vec::new();

        self.to_scope().walk_scopes(|scope| {
            r.extend(scope.enums());
        });

        r
    }

    /// Find all messages in given file descriptor
    pub fn find_messages(&self) -> Vec<MessageWithScope<'a>> {
        let mut r = Vec::new();

        self.to_scope().walk_scopes(|scope| {
            r.extend(scope.messages());
        });

        r
    }

    /// Find all messages in given file descriptor, except map messages
    pub fn find_messages_except_map(&self) -> Vec<MessageWithScope<'a>> {
        self.find_messages()
            .into_iter()
            .filter(|m| !m.is_map())
            .collect()
    }

    /// find all messages and enums in given file descriptor
    pub fn find_messages_and_enums(&self) -> Vec<MessageOrEnumWithScope<'a>> {
        let mut r = Vec::new();

        self.to_scope().walk_scopes(|scope| {
            r.extend(scope.messages_and_enums());
        });

        r
    }
}

#[derive(Clone, Debug)]
pub(crate) struct Scope<'a> {
    pub file_scope: FileScope<'a>,
    pub path: Vec<MessageDescriptor>,
}

impl<'a> Scope<'a> {
    pub(crate) fn file_descriptor(&self) -> FileDescriptor {
        self.file_scope.file_descriptor.clone()
    }

    // get message descriptors in this scope
    fn message_descriptors(&self) -> Vec<MessageDescriptor> {
        if self.path.is_empty() {
            self.file_scope.file_descriptor.messages().collect()
        } else {
            self.path.last().unwrap().nested_messages().collect()
        }
    }

    // get enum descriptors in this scope
    fn enum_descriptors(&self) -> Vec<EnumDescriptor> {
        if self.path.is_empty() {
            self.file_scope.file_descriptor.enums().collect()
        } else {
            self.path.last().unwrap().nested_enums().collect()
        }
    }

    // get messages with attached scopes in this scope
    pub fn messages(&self) -> Vec<MessageWithScope<'a>> {
        self.message_descriptors()
            .into_iter()
            .map(|message| MessageWithScope {
                scope: self.clone(),
                message,
            })
            .collect()
    }

    // get enums with attached scopes in this scope
    pub fn enums(&self) -> Vec<EnumWithScope<'a>> {
        self.enum_descriptors()
            .into_iter()
            .map(|en| EnumWithScope {
                scope: self.clone(),
                en,
            })
            .collect()
    }

    // get messages and enums with attached scopes in this scope
    pub fn messages_and_enums(&self) -> Vec<MessageOrEnumWithScope<'a>> {
        self.messages()
            .into_iter()
            .map(|m| MessageOrEnumWithScope::Message(m))
            .chain(
                self.enums()
                    .into_iter()
                    .map(|m| MessageOrEnumWithScope::Enum(m)),
            )
            .collect()
    }

    // nested scopes, i. e. scopes of nested messages
    fn nested_scopes(&self) -> Vec<Scope<'a>> {
        self.message_descriptors()
            .into_iter()
            .map(|m| {
                let mut nested = self.clone();
                nested.path.push(m);
                nested
            })
            .collect()
    }

    fn walk_scopes_impl<F: FnMut(&Scope<'a>)>(&self, callback: &mut F) {
        (*callback)(self);

        for nested in self.nested_scopes() {
            nested.walk_scopes_impl(callback);
        }
    }

    // apply callback for this scope and all nested scopes
    fn walk_scopes<F>(&self, mut callback: F)
    where
        F: FnMut(&Scope<'a>),
    {
        self.walk_scopes_impl(&mut callback);
    }

    pub fn rust_path_to_file(&self) -> RustRelativePath {
        RustRelativePath::from_idents(
            self.path
                .iter()
                .map(|m| message_name_to_nested_mod_name(m.name())),
        )
    }

    pub fn path_str(&self) -> String {
        let v: Vec<&str> = self.path.iter().map(|m| m.name()).collect();
        v.join(".")
    }

    pub fn prefix(&self) -> String {
        let path_str = self.path_str();
        if path_str.is_empty() {
            path_str
        } else {
            format!("{}.", path_str)
        }
    }

    pub fn protobuf_path_to_file(&self) -> ProtobufRelPath {
        ProtobufRelPath::from_components(self.path.iter().map(|m| ProtobufIdentRef::new(m.name())))
    }

    pub fn protobuf_absolute_path(&self) -> ProtobufAbsPath {
        let mut r = self.file_scope.package();
        r.push_relative(&self.protobuf_path_to_file());
        r
    }

    pub fn file_and_mod(&self, customize: Customize) -> FileAndMod {
        FileAndMod {
            file: self.file_scope.file_descriptor.proto().name().to_owned(),
            relative_mod: self.rust_path_to_file(),
            customize,
        }
    }
}

pub(crate) trait WithScope<'a> {
    fn scope(&self) -> &Scope<'a>;

    fn file_descriptor(&self) -> FileDescriptor {
        self.scope().file_descriptor()
    }

    // message or enum name
    fn name(&self) -> &ProtobufIdentRef;

    fn name_to_package(&self) -> String {
        let mut r = self.scope().prefix();
        r.push_str(&self.name());
        r
    }

    fn protobuf_name_to_package(&self) -> ProtobufRelPath {
        let r = self.scope().protobuf_path_to_file();
        r.append_ident(ProtobufIdentRef::new(self.name()))
    }

    /// Return absolute name starting with dot
    fn name_absolute(&self) -> ProtobufAbsPath {
        let mut path = self.scope().protobuf_absolute_path();
        path.push_simple(self.name());
        path
    }

    // rust type name of this descriptor
    fn rust_name(&self) -> RustIdent {
        let rust_name = capitalize(&self.name());
        RustIdent::new(&rust_name)
    }

    fn rust_name_to_file(&self) -> RustIdentWithPath {
        self.scope()
            .rust_path_to_file()
            .into_path()
            .with_ident(self.rust_name())
    }

    // fully-qualified name of this type
    fn rust_name_with_file(&self) -> RustIdentWithPath {
        let mut r = self.rust_name_to_file();
        r.prepend_ident(proto_path_to_rust_mod(
            self.scope().file_descriptor().name(),
        ));
        r
    }
}

#[derive(Clone, Debug)]
pub(crate) struct MessageWithScope<'a> {
    pub scope: Scope<'a>,
    pub message: MessageDescriptor,
}

impl<'a> WithScope<'a> for MessageWithScope<'a> {
    fn scope(&self) -> &Scope<'a> {
        &self.scope
    }

    fn name(&self) -> &ProtobufIdentRef {
        ProtobufIdentRef::new(self.message.name())
    }
}

impl<'a> MessageWithScope<'a> {
    pub fn into_scope(mut self) -> Scope<'a> {
        self.scope.path.push(self.message);
        self.scope
    }

    pub fn to_scope(&self) -> Scope<'a> {
        self.clone().into_scope()
    }

    pub fn fields(&self) -> Vec<FieldWithContext<'a>> {
        self.message
            .fields()
            .into_iter()
            .map(|field| FieldWithContext {
                field,
                message: self.clone(),
            })
            .collect()
    }

    pub fn oneofs(&self) -> Vec<OneofWithContext<'a>> {
        self.message
            .oneofs()
            .into_iter()
            .map(|oneof| OneofWithContext {
                message: self.clone(),
                oneof,
            })
            .collect()
    }

    pub fn mod_name(&self) -> RustIdent {
        message_name_to_nested_mod_name(self.message.name())
    }

    /// This message is a special message which is a map.
    pub fn is_map(&self) -> bool {
        map_entry(self).is_some()
    }
}

#[derive(Clone, Debug)]
pub(crate) struct EnumWithScope<'a> {
    pub scope: Scope<'a>,
    pub en: EnumDescriptor,
}

impl<'a> EnumWithScope<'a> {
    pub fn values(&self) -> Vec<EnumValueWithContext<'a>> {
        self.en
            .values()
            .into_iter()
            .map(|v| EnumValueWithContext {
                en: self.clone(),
                proto: v,
            })
            .collect()
    }

    // find enum value by protobuf name
    pub fn value_by_name(&self, name: &str) -> EnumValueWithContext<'a> {
        self.values()
            .into_iter()
            .find(|v| v.proto.proto().name() == name)
            .unwrap()
    }
}

#[derive(Clone, Debug)]
pub(crate) struct EnumValueWithContext<'a> {
    pub en: EnumWithScope<'a>,
    pub proto: EnumValueDescriptor,
}

impl<'a> EnumValueWithContext<'a> {
    pub fn rust_name(&self) -> RustIdent {
        // TODO: camel case or something.
        RustIdent::new(self.proto.name())
    }
}

impl<'a> WithScope<'a> for EnumWithScope<'a> {
    fn scope(&self) -> &Scope<'a> {
        &self.scope
    }

    fn name(&self) -> &ProtobufIdentRef {
        ProtobufIdentRef::new(self.en.name())
    }
}

pub(crate) enum MessageOrEnumWithScope<'a> {
    Message(MessageWithScope<'a>),
    Enum(EnumWithScope<'a>),
}

impl<'a> WithScope<'a> for MessageOrEnumWithScope<'a> {
    fn scope(&self) -> &Scope<'a> {
        match self {
            MessageOrEnumWithScope::Message(m) => m.scope(),
            MessageOrEnumWithScope::Enum(e) => e.scope(),
        }
    }

    fn name(&self) -> &ProtobufIdentRef {
        match self {
            MessageOrEnumWithScope::Message(m) => m.name(),
            MessageOrEnumWithScope::Enum(e) => e.name(),
        }
    }
}

#[derive(Clone)]
pub(crate) struct FieldWithContext<'a> {
    pub field: FieldDescriptor,
    pub message: MessageWithScope<'a>,
}

impl<'a> Deref for FieldWithContext<'a> {
    type Target = FieldDescriptor;

    fn deref(&self) -> &Self::Target {
        &self.field
    }
}

impl<'a> FieldWithContext<'a> {
    pub fn is_oneof(&self) -> bool {
        self.field.containing_oneof().is_some()
    }

    pub fn oneof(&self) -> Option<OneofWithContext<'a>> {
        match self.field.containing_oneof() {
            Some(oneof) => Some(OneofWithContext {
                message: self.message.clone(),
                oneof,
            }),
            None => None,
        }
    }
}

#[derive(Clone)]
pub(crate) struct OneofVariantWithContext<'a> {
    pub oneof: &'a OneofWithContext<'a>,
    pub field: FieldDescriptor,
}

#[derive(Clone)]
pub(crate) struct OneofWithContext<'a> {
    pub oneof: OneofDescriptor,
    pub message: MessageWithScope<'a>,
}

impl<'a> OneofWithContext<'a> {
    pub fn field_name(&'a self) -> RustIdent {
        return rust_field_name_for_protobuf_field_name(self.oneof.name());
    }

    // rust type name of enum
    pub fn rust_name(&self) -> RustIdentWithPath {
        // TODO: escape name
        let type_name = RustIdent::from(capitalize(self.oneof.name()));
        self.message
            .to_scope()
            .rust_path_to_file()
            .into_path()
            .with_ident(type_name)
    }

    pub fn variants(&'a self) -> Vec<OneofVariantWithContext<'a>> {
        self.message
            .fields()
            .into_iter()
            .filter(|f| f.field.containing_oneof().as_ref() == Some(&self.oneof))
            .map(|f| OneofVariantWithContext {
                oneof: self,
                field: f.field,
            })
            .collect()
    }
}
<commit_msg>Outdated todo<commit_after>use std::ops::Deref;

use protobuf::reflect::EnumDescriptor;
use protobuf::reflect::EnumValueDescriptor;
use protobuf::reflect::FieldDescriptor;
use protobuf::reflect::FileDescriptor;
use protobuf::reflect::MessageDescriptor;
use protobuf::reflect::OneofDescriptor;
use protobuf_parse::ProtobufAbsPath;
use protobuf_parse::ProtobufAbsPathRef;
use protobuf_parse::ProtobufIdentRef;
use protobuf_parse::ProtobufRelPath;
use protobuf_parse::ProtobufRelPathRef;

use crate::customize::Customize;
use crate::gen::field::rust_field_name_for_protobuf_field_name;
use crate::gen::file_and_mod::FileAndMod;
use crate::gen::map::map_entry;
use crate::gen::message::message_name_to_nested_mod_name;
use crate::gen::paths::proto_path_to_rust_mod;
use crate::gen::rust::ident::RustIdent;
use crate::gen::rust::ident_with_path::RustIdentWithPath;
use crate::gen::rust::rel_path::RustRelativePath;
use crate::gen::strx::capitalize;

pub(crate) struct RootScope<'a> {
    pub file_descriptors: &'a [FileDescriptor],
}

impl<'a> RootScope<'a> {
    fn packages(&'a self) -> Vec<FileScope<'a>> {
        self.file_descriptors
            .iter()
            .map(|fd| FileScope {
                file_descriptor: fd,
            })
            .collect()
    }

    // find enum by fully qualified name
    pub fn _find_enum(&'a self, fqn: &ProtobufAbsPath) -> EnumWithScope<'a> {
        match self.find_message_or_enum(fqn) {
            MessageOrEnumWithScope::Enum(e) => e,
            _ => panic!("not an enum: {}", fqn),
        }
    }

    // find message by fully qualified name
    pub fn find_message(&'a self, fqn: &ProtobufAbsPath) -> MessageWithScope<'a> {
        match self.find_message_or_enum(fqn) {
            MessageOrEnumWithScope::Message(m) => m,
            _ => panic!("not a message: {}", fqn),
        }
    }

    // find message or enum by fully qualified name
    pub fn find_message_or_enum(&'a self, fqn: &ProtobufAbsPath) -> MessageOrEnumWithScope<'a> {
        assert!(!fqn.is_root());
        self.packages()
            .into_iter()
            .flat_map(|p| p.find_message_or_enum_abs(fqn))
            .next()
            .expect(&format!("enum not found by name: {}", fqn))
    }
}

#[derive(Clone, Debug)]
pub(crate) struct FileScope<'a> {
    pub file_descriptor: &'a FileDescriptor,
}

impl<'a> Deref for FileScope<'a> {
    type Target = FileDescriptor;

    fn deref(&self) -> &Self::Target {
        self.file_descriptor
    }
}

impl<'a> FileScope<'a> {
    fn package(&self) -> ProtobufAbsPath {
        ProtobufAbsPath::package_from_file_descriptor(self.file_descriptor)
    }

    pub fn to_scope(&self) -> Scope<'a> {
        Scope {
            file_scope: self.clone(),
            path: Vec::new(),
        }
    }

    fn find_message_or_enum(
        &self,
        name: &ProtobufRelPathRef,
    ) -> Option<MessageOrEnumWithScope<'a>> {
        self.find_messages_and_enums()
            .into_iter()
            .filter(|e| e.protobuf_name_to_package().as_ref() == name)
            .next()
    }

    fn find_message_or_enum_abs(
        &self,
        name: &ProtobufAbsPathRef,
    ) -> Option<MessageOrEnumWithScope<'a>> {
        let name = name.to_owned();
        match name.remove_prefix(&self.package()) {
            Some(rem) => self.find_message_or_enum(&rem),
            None => None,
        }
    }

    // find all enums in given file descriptor
    pub fn find_enums(&self) -> Vec<EnumWithScope<'a>> {
        let mut r = Vec::new();

        self.to_scope().walk_scopes(|scope| {
            r.extend(scope.enums());
        });

        r
    }

    /// Find all messages in given file descriptor
    pub fn find_messages(&self) -> Vec<MessageWithScope<'a>> {
        let mut r = Vec::new();

        self.to_scope().walk_scopes(|scope| {
            r.extend(scope.messages());
        });

        r
    }

    /// Find all messages in given file descriptor, except map messages
    pub fn find_messages_except_map(&self) -> Vec<MessageWithScope<'a>> {
        self.find_messages()
            .into_iter()
            .filter(|m| !m.is_map())
            .collect()
    }

    /// find all messages and enums in given file descriptor
    pub fn find_messages_and_enums(&self) -> Vec<MessageOrEnumWithScope<'a>> {
        let mut r = Vec::new();

        self.to_scope().walk_scopes(|scope| {
            r.extend(scope.messages_and_enums());
        });

        r
    }
}

#[derive(Clone, Debug)]
pub(crate) struct Scope<'a> {
    pub file_scope: FileScope<'a>,
    pub path: Vec<MessageDescriptor>,
}

impl<'a> Scope<'a> {
    pub(crate) fn file_descriptor(&self) -> FileDescriptor {
        self.file_scope.file_descriptor.clone()
    }

    // get message descriptors in this scope
    fn message_descriptors(&self) -> Vec<MessageDescriptor> {
        if self.path.is_empty() {
            self.file_scope.file_descriptor.messages().collect()
        } else {
            self.path.last().unwrap().nested_messages().collect()
        }
    }

    // get enum descriptors in this scope
    fn enum_descriptors(&self) -> Vec<EnumDescriptor> {
        if self.path.is_empty() {
            self.file_scope.file_descriptor.enums().collect()
        } else {
            self.path.last().unwrap().nested_enums().collect()
        }
    }

    // get messages with attached scopes in this scope
    pub fn messages(&self) -> Vec<MessageWithScope<'a>> {
        self.message_descriptors()
            .into_iter()
            .map(|message| MessageWithScope {
                scope: self.clone(),
                message,
            })
            .collect()
    }

    // get enums with attached scopes in this scope
    pub fn enums(&self) -> Vec<EnumWithScope<'a>> {
        self.enum_descriptors()
            .into_iter()
            .map(|en| EnumWithScope {
                scope: self.clone(),
                en,
            })
            .collect()
    }

    // get messages and enums with attached scopes in this scope
    pub fn messages_and_enums(&self) -> Vec<MessageOrEnumWithScope<'a>> {
        self.messages()
            .into_iter()
            .map(|m| MessageOrEnumWithScope::Message(m))
            .chain(
                self.enums()
                    .into_iter()
                    .map(|m| MessageOrEnumWithScope::Enum(m)),
            )
            .collect()
    }

    // nested scopes, i. e. scopes of nested messages
    fn nested_scopes(&self) -> Vec<Scope<'a>> {
        self.message_descriptors()
            .into_iter()
            .map(|m| {
                let mut nested = self.clone();
                nested.path.push(m);
                nested
            })
            .collect()
    }

    fn walk_scopes_impl<F: FnMut(&Scope<'a>)>(&self, callback: &mut F) {
        (*callback)(self);

        for nested in self.nested_scopes() {
            nested.walk_scopes_impl(callback);
        }
    }

    // apply callback for this scope and all nested scopes
    fn walk_scopes<F>(&self, mut callback: F)
    where
        F: FnMut(&Scope<'a>),
    {
        self.walk_scopes_impl(&mut callback);
    }

    pub fn rust_path_to_file(&self) -> RustRelativePath {
        RustRelativePath::from_idents(
            self.path
                .iter()
                .map(|m| message_name_to_nested_mod_name(m.name())),
        )
    }

    pub fn path_str(&self) -> String {
        let v: Vec<&str> = self.path.iter().map(|m| m.name()).collect();
        v.join(".")
    }

    pub fn prefix(&self) -> String {
        let path_str = self.path_str();
        if path_str.is_empty() {
            path_str
        } else {
            format!("{}.", path_str)
        }
    }

    pub fn protobuf_path_to_file(&self) -> ProtobufRelPath {
        ProtobufRelPath::from_components(self.path.iter().map(|m| ProtobufIdentRef::new(m.name())))
    }

    pub fn protobuf_absolute_path(&self) -> ProtobufAbsPath {
        let mut r = self.file_scope.package();
        r.push_relative(&self.protobuf_path_to_file());
        r
    }

    pub fn file_and_mod(&self, customize: Customize) -> FileAndMod {
        FileAndMod {
            file: self.file_scope.file_descriptor.proto().name().to_owned(),
            relative_mod: self.rust_path_to_file(),
            customize,
        }
    }
}

pub(crate) trait WithScope<'a> {
    fn scope(&self) -> &Scope<'a>;

    fn file_descriptor(&self) -> FileDescriptor {
        self.scope().file_descriptor()
    }

    // message or enum name
    fn name(&self) -> &ProtobufIdentRef;

    fn name_to_package(&self) -> String {
        let mut r = self.scope().prefix();
        r.push_str(&self.name());
        r
    }

    fn protobuf_name_to_package(&self) -> ProtobufRelPath {
        let r = self.scope().protobuf_path_to_file();
        r.append_ident(ProtobufIdentRef::new(self.name()))
    }

    /// Return absolute name starting with dot
    fn name_absolute(&self) -> ProtobufAbsPath {
        let mut path = self.scope().protobuf_absolute_path();
        path.push_simple(self.name());
        path
    }

    // rust type name of this descriptor
    fn rust_name(&self) -> RustIdent {
        let rust_name = capitalize(&self.name());
        RustIdent::new(&rust_name)
    }

    fn rust_name_to_file(&self) -> RustIdentWithPath {
        self.scope()
            .rust_path_to_file()
            .into_path()
            .with_ident(self.rust_name())
    }

    // fully-qualified name of this type
    fn rust_name_with_file(&self) -> RustIdentWithPath {
        let mut r = self.rust_name_to_file();
        r.prepend_ident(proto_path_to_rust_mod(
            self.scope().file_descriptor().name(),
        ));
        r
    }
}

#[derive(Clone, Debug)]
pub(crate) struct MessageWithScope<'a> {
    pub scope: Scope<'a>,
    pub message: MessageDescriptor,
}

impl<'a> WithScope<'a> for MessageWithScope<'a> {
    fn scope(&self) -> &Scope<'a> {
        &self.scope
    }

    fn name(&self) -> &ProtobufIdentRef {
        ProtobufIdentRef::new(self.message.name())
    }
}

impl<'a> MessageWithScope<'a> {
    pub fn into_scope(mut self) -> Scope<'a> {
        self.scope.path.push(self.message);
        self.scope
    }

    pub fn to_scope(&self) -> Scope<'a> {
        self.clone().into_scope()
    }

    pub fn fields(&self) -> Vec<FieldWithContext<'a>> {
        self.message
            .fields()
            .into_iter()
            .map(|field| FieldWithContext {
                field,
                message: self.clone(),
            })
            .collect()
    }

    pub fn oneofs(&self) -> Vec<OneofWithContext<'a>> {
        self.message
            .oneofs()
            .into_iter()
            .map(|oneof| OneofWithContext {
                message: self.clone(),
                oneof,
            })
            .collect()
    }

    pub fn mod_name(&self) -> RustIdent {
        message_name_to_nested_mod_name(self.message.name())
    }

    /// This message is a special message which is a map.
    pub fn is_map(&self) -> bool {
        map_entry(self).is_some()
    }
}

#[derive(Clone, Debug)]
pub(crate) struct EnumWithScope<'a> {
    pub scope: Scope<'a>,
    pub en: EnumDescriptor,
}

impl<'a> EnumWithScope<'a> {
    pub fn values(&self) -> Vec<EnumValueWithContext<'a>> {
        self.en
            .values()
            .into_iter()
            .map(|v| EnumValueWithContext {
                en: self.clone(),
                proto: v,
            })
            .collect()
    }

    // find enum value by protobuf name
    pub fn value_by_name(&self, name: &str) -> EnumValueWithContext<'a> {
        self.values()
            .into_iter()
            .find(|v| v.proto.proto().name() == name)
            .unwrap()
    }
}

#[derive(Clone, Debug)]
pub(crate) struct EnumValueWithContext<'a> {
    pub en: EnumWithScope<'a>,
    pub proto: EnumValueDescriptor,
}

impl<'a> EnumValueWithContext<'a> {
    pub fn rust_name(&self) -> RustIdent {
        // TODO: camel case or something.
        RustIdent::new(self.proto.name())
    }
}

impl<'a> WithScope<'a> for EnumWithScope<'a> {
    fn scope(&self) -> &Scope<'a> {
        &self.scope
    }

    fn name(&self) -> &ProtobufIdentRef {
        ProtobufIdentRef::new(self.en.name())
    }
}

pub(crate) enum MessageOrEnumWithScope<'a> {
    Message(MessageWithScope<'a>),
    Enum(EnumWithScope<'a>),
}

impl<'a> WithScope<'a> for MessageOrEnumWithScope<'a> {
    fn scope(&self) -> &Scope<'a> {
        match self {
            MessageOrEnumWithScope::Message(m) => m.scope(),
            MessageOrEnumWithScope::Enum(e) => e.scope(),
        }
    }

    fn name(&self) -> &ProtobufIdentRef {
        match self {
            MessageOrEnumWithScope::Message(m) => m.name(),
            MessageOrEnumWithScope::Enum(e) => e.name(),
        }
    }
}

#[derive(Clone)]
pub(crate) struct FieldWithContext<'a> {
    pub field: FieldDescriptor,
    pub message: MessageWithScope<'a>,
}

impl<'a> Deref for FieldWithContext<'a> {
    type Target = FieldDescriptor;

    fn deref(&self) -> &Self::Target {
        &self.field
    }
}

impl<'a> FieldWithContext<'a> {
    pub fn is_oneof(&self) -> bool {
        self.field.containing_oneof().is_some()
    }

    pub fn oneof(&self) -> Option<OneofWithContext<'a>> {
        match self.field.containing_oneof() {
            Some(oneof) => Some(OneofWithContext {
                message: self.message.clone(),
                oneof,
            }),
            None => None,
        }
    }
}

#[derive(Clone)]
pub(crate) struct OneofVariantWithContext<'a> {
    pub oneof: &'a OneofWithContext<'a>,
    pub field: FieldDescriptor,
}

#[derive(Clone)]
pub(crate) struct OneofWithContext<'a> {
    pub oneof: OneofDescriptor,
    pub message: MessageWithScope<'a>,
}

impl<'a> OneofWithContext<'a> {
    pub fn field_name(&'a self) -> RustIdent {
        return rust_field_name_for_protobuf_field_name(self.oneof.name());
    }

    // rust type name of enum
    pub fn rust_name(&self) -> RustIdentWithPath {
        let type_name = RustIdent::from(capitalize(self.oneof.name()));
        self.message
            .to_scope()
            .rust_path_to_file()
            .into_path()
            .with_ident(type_name)
    }

    pub fn variants(&'a self) -> Vec<OneofVariantWithContext<'a>> {
        self.message
            .fields()
            .into_iter()
            .filter(|f| f.field.containing_oneof().as_ref() == Some(&self.oneof))
            .map(|f| OneofVariantWithContext {
                oneof: self,
                field: f.field,
            })
            .collect()
    }
}
<|endoftext|>


<commit_before>#![feature(test)] // compiletest_rs requires this attribute

use compiletest_rs as compiletest;
use compiletest_rs::common::Mode as TestMode;

use std::env::{self, set_var, var};
use std::ffi::OsStr;
use std::fs;
use std::io;
use std::path::{Path, PathBuf};

mod cargo;

fn host_lib() -> PathBuf {
    option_env!("HOST_LIBS").map_or(cargo::CARGO_TARGET_DIR.join(env!("PROFILE")), PathBuf::from)
}

fn clippy_driver_path() -> PathBuf {
    option_env!("CLIPPY_DRIVER_PATH").map_or(cargo::TARGET_LIB.join("clippy-driver"), PathBuf::from)
}

// When we'll want to use `extern crate ..` for a dependency that is used
// both by the crate and the compiler itself, we can't simply pass -L flags
// as we'll get a duplicate matching versions. Instead, disambiguate with
// `--extern dep=path`.
// See https://github.com/rust-lang/rust-clippy/issues/4015.
//
// FIXME: We cannot use `cargo build --message-format=json` to resolve to dependency files.
//        Because it would force-rebuild if the options passed to `build` command is not the same
//        as what we manually pass to `cargo` invocation
fn third_party_crates() -> String {
    use std::collections::HashMap;
    static CRATES: &[&str] = &["serde", "serde_derive", "regex", "clippy_lints", "syn", "quote"];
    let dep_dir = cargo::TARGET_LIB.join("deps");
    let mut crates: HashMap<&str, PathBuf> = HashMap::with_capacity(CRATES.len());
    for entry in fs::read_dir(dep_dir).unwrap() {
        let path = match entry {
            Ok(entry) => entry.path(),
            Err(_) => continue,
        };
        if let Some(name) = path.file_name().and_then(OsStr::to_str) {
            for dep in CRATES {
                if name.starts_with(&format!("lib{}-", dep)) && name.ends_with(".rlib") {
                    if let Some(old) = crates.insert(dep, path.clone()) {
                        panic!("Found multiple rlibs for crate `{}`: `{:?}` and `{:?}", dep, old, path);
                    }
                    break;
                }
            }
        }
    }

    let v: Vec<_> = crates
        .into_iter()
        .map(|(dep, path)| format!("--extern {}={}", dep, path.display()))
        .collect();
    v.join(" ")
}

fn default_config() -> compiletest::Config {
    let mut config = compiletest::Config::default();

    if let Ok(name) = env::var("TESTNAME") {
        config.filter = Some(name);
    }

    if let Some(path) = option_env!("RUSTC_LIB_PATH") {
        let path = PathBuf::from(path);
        config.run_lib_path = path.clone();
        config.compile_lib_path = path;
    }

    config.target_rustcflags = Some(format!(
        "-L {0} -L {1} -Dwarnings -Zui-testing {2}",
        host_lib().join("deps").display(),
        cargo::TARGET_LIB.join("deps").display(),
        third_party_crates(),
    ));

    config.build_base = if cargo::is_rustc_test_suite() {
        // This make the stderr files go to clippy OUT_DIR on rustc repo build dir
        let mut path = PathBuf::from(env!("OUT_DIR"));
        path.push("test_build_base");
        path
    } else {
        host_lib().join("test_build_base")
    };
    config.rustc_path = clippy_driver_path();
    config
}

fn run_mode(cfg: &mut compiletest::Config) {
    cfg.mode = TestMode::Ui;
    cfg.src_base = Path::new("tests").join("ui");
    compiletest::run_tests(&cfg);
}

fn run_ui_toml(config: &mut compiletest::Config) {
    fn run_tests(config: &compiletest::Config, mut tests: Vec<tester::TestDescAndFn>) -> Result<bool, io::Error> {
        let mut result = true;
        let opts = compiletest::test_opts(config);
        for dir in fs::read_dir(&config.src_base)? {
            let dir = dir?;
            if !dir.file_type()?.is_dir() {
                continue;
            }
            let dir_path = dir.path();
            set_var("CARGO_MANIFEST_DIR", &dir_path);
            for file in fs::read_dir(&dir_path)? {
                let file = file?;
                let file_path = file.path();
                if file.file_type()?.is_dir() {
                    continue;
                }
                if file_path.extension() != Some(OsStr::new("rs")) {
                    continue;
                }
                let paths = compiletest::common::TestPaths {
                    file: file_path,
                    base: config.src_base.clone(),
                    relative_dir: dir_path.file_name().unwrap().into(),
                };
                let test_name = compiletest::make_test_name(&config, &paths);
                let index = tests
                    .iter()
                    .position(|test| test.desc.name == test_name)
                    .expect("The test should be in there");
                result &= tester::run_tests_console(&opts, vec![tests.swap_remove(index)])?;
            }
        }
        Ok(result)
    }

    config.mode = TestMode::Ui;
    config.src_base = Path::new("tests").join("ui-toml").canonicalize().unwrap();

    let tests = compiletest::make_tests(&config);

    let manifest_dir = var("CARGO_MANIFEST_DIR").unwrap_or_default();
    let res = run_tests(&config, tests);
    set_var("CARGO_MANIFEST_DIR", &manifest_dir);
    match res {
        Ok(true) => {},
        Ok(false) => panic!("Some tests failed"),
        Err(e) => {
            panic!("I/O failure during tests: {:?}", e);
        },
    }
}

fn run_ui_cargo(config: &mut compiletest::Config) {
    fn run_tests(
        config: &compiletest::Config,
        filter: &Option<String>,
        mut tests: Vec<tester::TestDescAndFn>,
    ) -> Result<bool, io::Error> {
        let mut result = true;
        let opts = compiletest::test_opts(config);

        for dir in fs::read_dir(&config.src_base)? {
            let dir = dir?;
            if !dir.file_type()?.is_dir() {
                continue;
            }

            // Use the filter if provided
            let dir_path = dir.path();
            match &filter {
                Some(name) if !dir_path.ends_with(name) => continue,
                _ => {},
            }

            for case in fs::read_dir(&dir_path)? {
                let case = case?;
                if !case.file_type()?.is_dir() {
                    continue;
                }

                let src_path = case.path().join("src");

                // When switching between branches, if the previous branch had a test
                // that the current branch does not have, the directory is not removed
                // because an ignored Cargo.lock file exists.
                if !src_path.exists() {
                    continue;
                }

                env::set_current_dir(&src_path)?;
                for file in fs::read_dir(&src_path)? {
                    let file = file?;
                    if file.file_type()?.is_dir() {
                        continue;
                    }

                    // Search for the main file to avoid running a test for each file in the project
                    let file_path = file.path();
                    match file_path.file_name().and_then(OsStr::to_str) {
                        Some("main.rs") => {},
                        _ => continue,
                    }

                    let paths = compiletest::common::TestPaths {
                        file: file_path,
                        base: config.src_base.clone(),
                        relative_dir: src_path.strip_prefix(&config.src_base).unwrap().into(),
                    };
                    let test_name = compiletest::make_test_name(&config, &paths);
                    let index = tests
                        .iter()
                        .position(|test| test.desc.name == test_name)
                        .expect("The test should be in there");
                    result &= tester::run_tests_console(&opts, vec![tests.swap_remove(index)])?;
                }
            }
        }
        Ok(result)
    }

    if cargo::is_rustc_test_suite() {
        return;
    }

    config.mode = TestMode::Ui;
    config.src_base = Path::new("tests").join("ui-cargo").canonicalize().unwrap();

    let tests = compiletest::make_tests(&config);

    let current_dir = env::current_dir().unwrap();
    let filter = env::var("TESTNAME").ok();
    let res = run_tests(&config, &filter, tests);
    env::set_current_dir(current_dir).unwrap();

    match res {
        Ok(true) => {},
        Ok(false) => panic!("Some tests failed"),
        Err(e) => {
            panic!("I/O failure during tests: {:?}", e);
        },
    }
}

fn prepare_env() {
    set_var("CLIPPY_DISABLE_DOCS_LINKS", "true");
    set_var("CLIPPY_TESTS", "true");
    //set_var("RUST_BACKTRACE", "0");
}

#[test]
fn compile_test() {
    prepare_env();
    let mut config = default_config();
    run_mode(&mut config);
    run_ui_toml(&mut config);
    run_ui_cargo(&mut config);
}
<commit_msg>Add emit=metadata to UI tests build flags<commit_after>#![feature(test)] // compiletest_rs requires this attribute

use compiletest_rs as compiletest;
use compiletest_rs::common::Mode as TestMode;

use std::env::{self, set_var, var};
use std::ffi::OsStr;
use std::fs;
use std::io;
use std::path::{Path, PathBuf};

mod cargo;

fn host_lib() -> PathBuf {
    option_env!("HOST_LIBS").map_or(cargo::CARGO_TARGET_DIR.join(env!("PROFILE")), PathBuf::from)
}

fn clippy_driver_path() -> PathBuf {
    option_env!("CLIPPY_DRIVER_PATH").map_or(cargo::TARGET_LIB.join("clippy-driver"), PathBuf::from)
}

// When we'll want to use `extern crate ..` for a dependency that is used
// both by the crate and the compiler itself, we can't simply pass -L flags
// as we'll get a duplicate matching versions. Instead, disambiguate with
// `--extern dep=path`.
// See https://github.com/rust-lang/rust-clippy/issues/4015.
//
// FIXME: We cannot use `cargo build --message-format=json` to resolve to dependency files.
//        Because it would force-rebuild if the options passed to `build` command is not the same
//        as what we manually pass to `cargo` invocation
fn third_party_crates() -> String {
    use std::collections::HashMap;
    static CRATES: &[&str] = &["serde", "serde_derive", "regex", "clippy_lints", "syn", "quote"];
    let dep_dir = cargo::TARGET_LIB.join("deps");
    let mut crates: HashMap<&str, PathBuf> = HashMap::with_capacity(CRATES.len());
    for entry in fs::read_dir(dep_dir).unwrap() {
        let path = match entry {
            Ok(entry) => entry.path(),
            Err(_) => continue,
        };
        if let Some(name) = path.file_name().and_then(OsStr::to_str) {
            for dep in CRATES {
                if name.starts_with(&format!("lib{}-", dep)) && name.ends_with(".rlib") {
                    if let Some(old) = crates.insert(dep, path.clone()) {
                        panic!("Found multiple rlibs for crate `{}`: `{:?}` and `{:?}", dep, old, path);
                    }
                    break;
                }
            }
        }
    }

    let v: Vec<_> = crates
        .into_iter()
        .map(|(dep, path)| format!("--extern {}={}", dep, path.display()))
        .collect();
    v.join(" ")
}

fn default_config() -> compiletest::Config {
    let mut config = compiletest::Config::default();

    if let Ok(name) = env::var("TESTNAME") {
        config.filter = Some(name);
    }

    if let Some(path) = option_env!("RUSTC_LIB_PATH") {
        let path = PathBuf::from(path);
        config.run_lib_path = path.clone();
        config.compile_lib_path = path;
    }

    config.target_rustcflags = Some(format!(
        "--emit=metadata -L {0} -L {1} -Dwarnings -Zui-testing {2}",
        host_lib().join("deps").display(),
        cargo::TARGET_LIB.join("deps").display(),
        third_party_crates(),
    ));

    config.build_base = if cargo::is_rustc_test_suite() {
        // This make the stderr files go to clippy OUT_DIR on rustc repo build dir
        let mut path = PathBuf::from(env!("OUT_DIR"));
        path.push("test_build_base");
        path
    } else {
        host_lib().join("test_build_base")
    };
    config.rustc_path = clippy_driver_path();
    config
}

fn run_mode(cfg: &mut compiletest::Config) {
    cfg.mode = TestMode::Ui;
    cfg.src_base = Path::new("tests").join("ui");
    compiletest::run_tests(&cfg);
}

fn run_ui_toml(config: &mut compiletest::Config) {
    fn run_tests(config: &compiletest::Config, mut tests: Vec<tester::TestDescAndFn>) -> Result<bool, io::Error> {
        let mut result = true;
        let opts = compiletest::test_opts(config);
        for dir in fs::read_dir(&config.src_base)? {
            let dir = dir?;
            if !dir.file_type()?.is_dir() {
                continue;
            }
            let dir_path = dir.path();
            set_var("CARGO_MANIFEST_DIR", &dir_path);
            for file in fs::read_dir(&dir_path)? {
                let file = file?;
                let file_path = file.path();
                if file.file_type()?.is_dir() {
                    continue;
                }
                if file_path.extension() != Some(OsStr::new("rs")) {
                    continue;
                }
                let paths = compiletest::common::TestPaths {
                    file: file_path,
                    base: config.src_base.clone(),
                    relative_dir: dir_path.file_name().unwrap().into(),
                };
                let test_name = compiletest::make_test_name(&config, &paths);
                let index = tests
                    .iter()
                    .position(|test| test.desc.name == test_name)
                    .expect("The test should be in there");
                result &= tester::run_tests_console(&opts, vec![tests.swap_remove(index)])?;
            }
        }
        Ok(result)
    }

    config.mode = TestMode::Ui;
    config.src_base = Path::new("tests").join("ui-toml").canonicalize().unwrap();

    let tests = compiletest::make_tests(&config);

    let manifest_dir = var("CARGO_MANIFEST_DIR").unwrap_or_default();
    let res = run_tests(&config, tests);
    set_var("CARGO_MANIFEST_DIR", &manifest_dir);
    match res {
        Ok(true) => {},
        Ok(false) => panic!("Some tests failed"),
        Err(e) => {
            panic!("I/O failure during tests: {:?}", e);
        },
    }
}

fn run_ui_cargo(config: &mut compiletest::Config) {
    fn run_tests(
        config: &compiletest::Config,
        filter: &Option<String>,
        mut tests: Vec<tester::TestDescAndFn>,
    ) -> Result<bool, io::Error> {
        let mut result = true;
        let opts = compiletest::test_opts(config);

        for dir in fs::read_dir(&config.src_base)? {
            let dir = dir?;
            if !dir.file_type()?.is_dir() {
                continue;
            }

            // Use the filter if provided
            let dir_path = dir.path();
            match &filter {
                Some(name) if !dir_path.ends_with(name) => continue,
                _ => {},
            }

            for case in fs::read_dir(&dir_path)? {
                let case = case?;
                if !case.file_type()?.is_dir() {
                    continue;
                }

                let src_path = case.path().join("src");

                // When switching between branches, if the previous branch had a test
                // that the current branch does not have, the directory is not removed
                // because an ignored Cargo.lock file exists.
                if !src_path.exists() {
                    continue;
                }

                env::set_current_dir(&src_path)?;
                for file in fs::read_dir(&src_path)? {
                    let file = file?;
                    if file.file_type()?.is_dir() {
                        continue;
                    }

                    // Search for the main file to avoid running a test for each file in the project
                    let file_path = file.path();
                    match file_path.file_name().and_then(OsStr::to_str) {
                        Some("main.rs") => {},
                        _ => continue,
                    }

                    let paths = compiletest::common::TestPaths {
                        file: file_path,
                        base: config.src_base.clone(),
                        relative_dir: src_path.strip_prefix(&config.src_base).unwrap().into(),
                    };
                    let test_name = compiletest::make_test_name(&config, &paths);
                    let index = tests
                        .iter()
                        .position(|test| test.desc.name == test_name)
                        .expect("The test should be in there");
                    result &= tester::run_tests_console(&opts, vec![tests.swap_remove(index)])?;
                }
            }
        }
        Ok(result)
    }

    if cargo::is_rustc_test_suite() {
        return;
    }

    config.mode = TestMode::Ui;
    config.src_base = Path::new("tests").join("ui-cargo").canonicalize().unwrap();

    let tests = compiletest::make_tests(&config);

    let current_dir = env::current_dir().unwrap();
    let filter = env::var("TESTNAME").ok();
    let res = run_tests(&config, &filter, tests);
    env::set_current_dir(current_dir).unwrap();

    match res {
        Ok(true) => {},
        Ok(false) => panic!("Some tests failed"),
        Err(e) => {
            panic!("I/O failure during tests: {:?}", e);
        },
    }
}

fn prepare_env() {
    set_var("CLIPPY_DISABLE_DOCS_LINKS", "true");
    set_var("CLIPPY_TESTS", "true");
    //set_var("RUST_BACKTRACE", "0");
}

#[test]
fn compile_test() {
    prepare_env();
    let mut config = default_config();
    run_mode(&mut config);
    run_ui_toml(&mut config);
    run_ui_cargo(&mut config);
}
<|endoftext|>


<commit_before>use std::collections::BTreeMap;
use std::ops::{Deref, DerefMut};
use std::io::BufRead;

use toml::Value;
use uuid::Uuid;

use task_hookrs::task::Task as TTask;
use task_hookrs::import::{import_task, import_tasks};

use libimagstore::store::{FileLockEntry, Store};
use libimagstore::storeid::{IntoStoreId, StoreIdIterator, StoreId};
use module_path::ModuleEntryPath;

use error::{TodoError, TodoErrorKind, MapErrInto};
use result::Result;

/// Task struct containing a `FileLockEntry`
#[derive(Debug)]
pub struct Task<'a>(FileLockEntry<'a>);

impl<'a> Task<'a> {

    /// Concstructs a new `Task` with a `FileLockEntry`
    pub fn new(fle: FileLockEntry<'a>) -> Task<'a> {
        Task(fle)
    }

    pub fn import<R: BufRead>(store: &'a Store, mut r: R) -> Result<(Task<'a>, Uuid)> {
        let mut line = String::new();
        r.read_line(&mut line);
        import_task(&line.as_str())
            .map_err_into(TodoErrorKind::ImportError)
            .and_then(|t| {
                let uuid = t.uuid().clone();
                t.into_task(store).map(|t| (t, uuid))
            })
    }

    /// Get a task from an import string. That is: read the imported string, get the UUID from it
    /// and try to load this UUID from store.
    ///
    /// Possible return values are:
    ///
    /// * Ok(Ok(Task))
    /// * Ok(Err(String)) - where the String is the String read from the `r` parameter
    /// * Err(_)          - where the error is an error that happened during evaluation
    ///
    pub fn get_from_import<R: BufRead>(store: &'a Store, mut r: R) -> Result<RResult<Task<'a>, String>>
    {
        unimplemented!()
    }

    /// Get a task from a String. The String is expected to contain the JSON-representation of the
    /// Task to get from the store (only the UUID really matters in this case)
    pub fn get_from_string(store: &'a Store, s: String) -> Result<Task<'a>> {
        unimplemented!()
    }

    /// Get a task from an UUID.
    pub fn get_from_uuid(store: &'a Store, uuid: Uuid) -> Result<Task<'a>> {
        unimplemented!()
    }

    /// Same as Task::get_from_import() but uses Store::retrieve() rather than Store::get(), to
    /// implicitely create the task if it does not exist.
    pub fn retrieve_from_import<R: BufRead>(store: &'a Store, mut r: R) -> Result<Task<'a>> {
        unimplemented!()
    }

    /// Retrieve a task from a String. The String is expected to contain the JSON-representation of
    /// the Task to retrieve from the store (only the UUID really matters in this case)
    pub fn retrieve_from_string(store: &'a Store, s: String) -> Result<Task<'a>> {
        unimplemented!()
    }

    pub fn delete_by_uuid(store: &Store, uuid: Uuid) -> Result<()> {
        store.delete(ModuleEntryPath::new(format!("taskwarrior/{}", uuid)).into_storeid())
            .map_err(|e| TodoError::new(TodoErrorKind::StoreError, Some(Box::new(e))))
    }

    pub fn all_as_ids(store: &Store) -> Result<StoreIdIterator> {
        store.retrieve_for_module("todo/taskwarrior")
            .map_err(|e| TodoError::new(TodoErrorKind::StoreError, Some(Box::new(e))))
    }

    pub fn all(store: &Store) -> Result<TaskIterator> {
        Task::all_as_ids(store)
            .map(|iter| TaskIterator::new(store, iter))
    }

}

impl<'a> Deref for Task<'a> {
    type Target = FileLockEntry<'a>;

    fn deref(&self) -> &FileLockEntry<'a> {
        &self.0
    }

}

impl<'a> DerefMut for Task<'a> {

    fn deref_mut(&mut self) -> &mut FileLockEntry<'a> {
        &mut self.0
    }

}

/// A trait to get a `libimagtodo::task::Task` out of the implementing object.
/// This Task struct is merely a wrapper for a `FileLockEntry`, therefore the function name
/// `into_filelockentry`.
pub trait IntoTask<'a> {

    /// # Usage
    /// ```ignore
    /// use std::io::stdin;
    ///
    /// use task_hookrs::task::Task;
    /// use task_hookrs::import::import;
    /// use libimagstore::store::{Store, FileLockEntry};
    ///
    /// if let Ok(task_hookrs_task) = import(stdin()) {
    ///     // Store is given at runtime
    ///     let task = task_hookrs_task.into_filelockentry(store);
    ///     println!("Task with uuid: {}", task.flentry.get_header().get("todo.uuid"));
    /// }
    /// ```
    fn into_task(self, store : &'a Store) -> Result<Task<'a>>;

}

impl<'a> IntoTask<'a> for TTask {

    fn into_task(self, store : &'a Store) -> Result<Task<'a>> {
        let uuid     = self.uuid();
        let store_id = ModuleEntryPath::new(format!("taskwarrior/{}", uuid)).into_storeid();

        match store.retrieve(store_id) {
            Err(e) => return Err(TodoError::new(TodoErrorKind::StoreError, Some(Box::new(e)))),
            Ok(mut fle) => {
                {
                    let mut header = fle.get_header_mut();
                    match header.read("todo") {
                        Ok(None) => {
                            if let Err(e) = header.set("todo", Value::Table(BTreeMap::new())) {
                                return Err(TodoError::new(TodoErrorKind::StoreError, Some(Box::new(e))))
                            }
                        }
                        Ok(Some(_)) => { }
                        Err(e) => {
                            return Err(TodoError::new(TodoErrorKind::StoreError, Some(Box::new(e))))
                        }
                    }

                    if let Err(e) = header.set("todo.uuid", Value::String(format!("{}",uuid))) {
                        return Err(TodoError::new(TodoErrorKind::StoreError, Some(Box::new(e))))
                    }
                }

                // If none of the errors above have returned the function, everything is fine
                Ok(Task::new(fle))
            }
        }
    }

}

trait FromStoreId {
    fn from_storeid<'a>(&'a Store, StoreId) -> Result<Task<'a>>;
}

impl<'a> FromStoreId for Task<'a> {

    fn from_storeid<'b>(store: &'b Store, id: StoreId) -> Result<Task<'b>> {
        match store.retrieve(id) {
            Err(e) => Err(TodoError::new(TodoErrorKind::StoreError, Some(Box::new(e)))),
            Ok(c)  => Ok(Task::new( c )),
        }
    }
}

pub struct TaskIterator<'a> {
    store: &'a Store,
    iditer: StoreIdIterator,
}

impl<'a> TaskIterator<'a> {

    pub fn new(store: &'a Store, iditer: StoreIdIterator) -> TaskIterator<'a> {
        TaskIterator {
            store: store,
            iditer: iditer,
        }
    }

}

impl<'a> Iterator for TaskIterator<'a> {
    type Item = Result<Task<'a>>;

    fn next(&mut self) -> Option<Result<Task<'a>>> {
        self.iditer.next().map(|id| Task::from_storeid(self.store, id))
    }
}

<commit_msg>Fix return types of getter functions<commit_after>use std::collections::BTreeMap;
use std::ops::{Deref, DerefMut};
use std::io::BufRead;
use std::result::Result as RResult;

use toml::Value;
use uuid::Uuid;

use task_hookrs::task::Task as TTask;
use task_hookrs::import::{import_task, import_tasks};

use libimagstore::store::{FileLockEntry, Store};
use libimagstore::storeid::{IntoStoreId, StoreIdIterator, StoreId};
use module_path::ModuleEntryPath;

use error::{TodoError, TodoErrorKind, MapErrInto};
use result::Result;

/// Task struct containing a `FileLockEntry`
#[derive(Debug)]
pub struct Task<'a>(FileLockEntry<'a>);

impl<'a> Task<'a> {

    /// Concstructs a new `Task` with a `FileLockEntry`
    pub fn new(fle: FileLockEntry<'a>) -> Task<'a> {
        Task(fle)
    }

    pub fn import<R: BufRead>(store: &'a Store, mut r: R) -> Result<(Task<'a>, Uuid)> {
        let mut line = String::new();
        r.read_line(&mut line);
        import_task(&line.as_str())
            .map_err_into(TodoErrorKind::ImportError)
            .and_then(|t| {
                let uuid = t.uuid().clone();
                t.into_task(store).map(|t| (t, uuid))
            })
    }

    /// Get a task from an import string. That is: read the imported string, get the UUID from it
    /// and try to load this UUID from store.
    ///
    /// Possible return values are:
    ///
    /// * Ok(Ok(Task))
    /// * Ok(Err(String)) - where the String is the String read from the `r` parameter
    /// * Err(_)          - where the error is an error that happened during evaluation
    ///
    pub fn get_from_import<R: BufRead>(store: &'a Store, mut r: R) -> Result<RResult<Task<'a>, String>>
    {
        unimplemented!()
    }

    /// Get a task from a String. The String is expected to contain the JSON-representation of the
    /// Task to get from the store (only the UUID really matters in this case)
    ///
    /// For an explanation on the return values see `Task::get_from_import()`.
    pub fn get_from_string(store: &'a Store, s: String) -> Result<RResult<Task<'a>, String>> {
        unimplemented!()
    }

    /// Get a task from an UUID.
    ///
    /// If there is no task with this UUID, this returns `Ok(None)`.
    pub fn get_from_uuid(store: &'a Store, uuid: Uuid) -> Result<Option<Task<'a>>> {
        unimplemented!()
    }

    /// Same as Task::get_from_import() but uses Store::retrieve() rather than Store::get(), to
    /// implicitely create the task if it does not exist.
    pub fn retrieve_from_import<R: BufRead>(store: &'a Store, mut r: R) -> Result<Task<'a>> {
        unimplemented!()
    }

    /// Retrieve a task from a String. The String is expected to contain the JSON-representation of
    /// the Task to retrieve from the store (only the UUID really matters in this case)
    pub fn retrieve_from_string(store: &'a Store, s: String) -> Result<Task<'a>> {
        unimplemented!()
    }

    pub fn delete_by_uuid(store: &Store, uuid: Uuid) -> Result<()> {
        store.delete(ModuleEntryPath::new(format!("taskwarrior/{}", uuid)).into_storeid())
            .map_err(|e| TodoError::new(TodoErrorKind::StoreError, Some(Box::new(e))))
    }

    pub fn all_as_ids(store: &Store) -> Result<StoreIdIterator> {
        store.retrieve_for_module("todo/taskwarrior")
            .map_err(|e| TodoError::new(TodoErrorKind::StoreError, Some(Box::new(e))))
    }

    pub fn all(store: &Store) -> Result<TaskIterator> {
        Task::all_as_ids(store)
            .map(|iter| TaskIterator::new(store, iter))
    }

}

impl<'a> Deref for Task<'a> {
    type Target = FileLockEntry<'a>;

    fn deref(&self) -> &FileLockEntry<'a> {
        &self.0
    }

}

impl<'a> DerefMut for Task<'a> {

    fn deref_mut(&mut self) -> &mut FileLockEntry<'a> {
        &mut self.0
    }

}

/// A trait to get a `libimagtodo::task::Task` out of the implementing object.
/// This Task struct is merely a wrapper for a `FileLockEntry`, therefore the function name
/// `into_filelockentry`.
pub trait IntoTask<'a> {

    /// # Usage
    /// ```ignore
    /// use std::io::stdin;
    ///
    /// use task_hookrs::task::Task;
    /// use task_hookrs::import::import;
    /// use libimagstore::store::{Store, FileLockEntry};
    ///
    /// if let Ok(task_hookrs_task) = import(stdin()) {
    ///     // Store is given at runtime
    ///     let task = task_hookrs_task.into_filelockentry(store);
    ///     println!("Task with uuid: {}", task.flentry.get_header().get("todo.uuid"));
    /// }
    /// ```
    fn into_task(self, store : &'a Store) -> Result<Task<'a>>;

}

impl<'a> IntoTask<'a> for TTask {

    fn into_task(self, store : &'a Store) -> Result<Task<'a>> {
        let uuid     = self.uuid();
        let store_id = ModuleEntryPath::new(format!("taskwarrior/{}", uuid)).into_storeid();

        match store.retrieve(store_id) {
            Err(e) => return Err(TodoError::new(TodoErrorKind::StoreError, Some(Box::new(e)))),
            Ok(mut fle) => {
                {
                    let mut header = fle.get_header_mut();
                    match header.read("todo") {
                        Ok(None) => {
                            if let Err(e) = header.set("todo", Value::Table(BTreeMap::new())) {
                                return Err(TodoError::new(TodoErrorKind::StoreError, Some(Box::new(e))))
                            }
                        }
                        Ok(Some(_)) => { }
                        Err(e) => {
                            return Err(TodoError::new(TodoErrorKind::StoreError, Some(Box::new(e))))
                        }
                    }

                    if let Err(e) = header.set("todo.uuid", Value::String(format!("{}",uuid))) {
                        return Err(TodoError::new(TodoErrorKind::StoreError, Some(Box::new(e))))
                    }
                }

                // If none of the errors above have returned the function, everything is fine
                Ok(Task::new(fle))
            }
        }
    }

}

trait FromStoreId {
    fn from_storeid<'a>(&'a Store, StoreId) -> Result<Task<'a>>;
}

impl<'a> FromStoreId for Task<'a> {

    fn from_storeid<'b>(store: &'b Store, id: StoreId) -> Result<Task<'b>> {
        match store.retrieve(id) {
            Err(e) => Err(TodoError::new(TodoErrorKind::StoreError, Some(Box::new(e)))),
            Ok(c)  => Ok(Task::new( c )),
        }
    }
}

pub struct TaskIterator<'a> {
    store: &'a Store,
    iditer: StoreIdIterator,
}

impl<'a> TaskIterator<'a> {

    pub fn new(store: &'a Store, iditer: StoreIdIterator) -> TaskIterator<'a> {
        TaskIterator {
            store: store,
            iditer: iditer,
        }
    }

}

impl<'a> Iterator for TaskIterator<'a> {
    type Item = Result<Task<'a>>;

    fn next(&mut self) -> Option<Result<Task<'a>>> {
        self.iditer.next().map(|id| Task::from_storeid(self.store, id))
    }
}

<|endoftext|>


<commit_before>fn log(a: i32, b: i32) {}

fn main() {
    let error = 42;
    log(error, 0b_usize);
    //~^ ERROR no valid digits found for number
}
<commit_msg>fix test<commit_after>fn log(a: i32, b: i32) {}

fn main() {
    let error = 42;
    log(error, 0b_usize);
    //~^ ERROR no valid digits found for number
    //~| ERROR mismatched types
}
<|endoftext|>