<commit_before>//! Handling of `preserveAspectRatio` values
//!
//! This module handles `preserveAspectRatio` values [per the SVG specification][spec].
//! We have an [`AspectRatio`] struct which encapsulates such a value.
//!
//! ```ignore
//! assert_eq!(
//!     AspectRatio::parse("xMidYMid", ()),
//!     Ok(AspectRatio {
//!         defer: false,
//!         align: Some(Align {
//!             x: X(Align1D::Mid),
//!             y: Y(Align1D::Mid),
//!             fit: FitMode::Meet,
//!         }),
//!     })
//! );
//! ```
//!
//! [`AspectRatio`]: struct.AspectRatio.html
//! [spec]: https://www.w3.org/TR/SVG/coords.html#PreserveAspectRatioAttribute

use cssparser::{Parser, ParserInput};
use error::*;
use parsers::Parse;
use parsers::ParseError;
use std::ops::Deref;

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct AspectRatio {
    defer: bool,
    align: Option<Align>,
}

impl Default for AspectRatio {
    fn default() -> AspectRatio {
        AspectRatio {
            defer: false,
            align: Some(Align::default()),
        }
    }
}

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
enum FitMode {
    Meet,
    Slice,
}

impl Default for FitMode {
    fn default() -> FitMode {
        FitMode::Meet
    }
}

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
struct Align {
    x: X,
    y: Y,
    fit: FitMode,
}

impl Default for Align {
    fn default() -> Align {
        Align {
            x: X(Align1D::Mid),
            y: Y(Align1D::Mid),
            fit: FitMode::default(),
        }
    }
}

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
enum Align1D {
    Min,
    Mid,
    Max,
}

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
struct X(Align1D);
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
struct Y(Align1D);

impl Deref for X {
    type Target = Align1D;

    fn deref(&self) -> &Align1D {
        &self.0
    }
}

impl Deref for Y {
    type Target = Align1D;

    fn deref(&self) -> &Align1D {
        &self.0
    }
}

impl Align1D {
    fn compute(self, dest_pos: f64, dest_size: f64, obj_size: f64) -> f64 {
        match self {
            Align1D::Min => dest_pos,
            Align1D::Mid => dest_pos + (dest_size - obj_size) / 2.0,
            Align1D::Max => dest_pos + dest_size - obj_size,
        }
    }
}

impl AspectRatio {
    pub fn is_slice(&self) -> bool {
        match self.align {
            Some(Align {
                fit: FitMode::Slice,
                ..
            }) => true,

            _ => false,
        }
    }

    pub fn compute(
        &self,
        object_width: f64,
        object_height: f64,
        dest_x: f64,
        dest_y: f64,
        dest_width: f64,
        dest_height: f64,
    ) -> (f64, f64, f64, f64) {
        match self.align {
            None => (dest_x, dest_y, dest_width, dest_height),

            Some(Align { x, y, fit }) => {
                let w_factor = dest_width / object_width;
                let h_factor = dest_height / object_height;
                let factor = match fit {
                    FitMode::Meet => w_factor.min(h_factor),
                    FitMode::Slice => w_factor.max(h_factor),
                };

                let w = object_width * factor;
                let h = object_height * factor;

                let xpos = x.compute(dest_x, dest_width, w);
                let ypos = y.compute(dest_y, dest_height, h);

                (xpos, ypos, w, h)
            }
        }
    }

    fn parse_input<'i, 't>(p: &mut Parser<'i, 't>) -> Result<AspectRatio, ()> {
        let defer = p.try(|p| p.expect_ident_matching("defer")).is_ok();

        let align_xy = p.try(|p| {
            p.expect_ident()
                .map_err(|_| ())
                .and_then(|ident| Align::parse_xy(ident))
        })?;

        let fit =
            p.try(|p| {
                p.expect_ident()
                    .map_err(|_| ())
                    .and_then(|ident| FitMode::parse(ident))
            }).unwrap_or(FitMode::default());

        p.expect_exhausted().map_err(|_| ())?;

        let align = align_xy.map(|(x, y)| Align { x, y, fit });

        Ok(AspectRatio { defer, align })
    }
}

impl Align {
    fn parse_xy(s: &str) -> Result<Option<(X, Y)>, ()> {
        use self::Align1D::*;

        match s {
            "none" => Ok(None),

            "xMinYMin" => Ok(Some((X(Min), Y(Min)))),
            "xMidYMin" => Ok(Some((X(Mid), Y(Min)))),
            "xMaxYMin" => Ok(Some((X(Max), Y(Min)))),

            "xMinYMid" => Ok(Some((X(Min), Y(Mid)))),
            "xMidYMid" => Ok(Some((X(Mid), Y(Mid)))),
            "xMaxYMid" => Ok(Some((X(Max), Y(Mid)))),

            "xMinYMax" => Ok(Some((X(Min), Y(Max)))),
            "xMidYMax" => Ok(Some((X(Mid), Y(Max)))),
            "xMaxYMax" => Ok(Some((X(Max), Y(Max)))),

            _ => Err(()),
        }
    }
}

impl FitMode {
    fn parse(s: &str) -> Result<FitMode, ()> {
        match s {
            "meet" => Ok(FitMode::Meet),
            "slice" => Ok(FitMode::Slice),
            _ => Err(()),
        }
    }
}

impl Parse for AspectRatio {
    type Data = ();
    type Err = AttributeError;

    fn parse(s: &str, _: ()) -> Result<AspectRatio, AttributeError> {
        let mut input = ParserInput::new(s);
        AspectRatio::parse_input(&mut Parser::new(&mut input)).map_err(|_| {
            AttributeError::Parse(ParseError::new(
                "expected \"[defer] <align> [meet | slice]\"",
            ))
        })
    }
}

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

    #[test]
    fn parsing_invalid_strings_yields_error() {
        assert!(AspectRatio::parse("", ()).is_err());
        assert!(AspectRatio::parse("defer", ()).is_err());
        assert!(AspectRatio::parse("defer foo", ()).is_err());
        assert!(AspectRatio::parse("defer xmidymid", ()).is_err());
        assert!(AspectRatio::parse("defer xMidYMid foo", ()).is_err());
        assert!(AspectRatio::parse("xmidymid", ()).is_err());
        assert!(AspectRatio::parse("xMidYMid foo", ()).is_err());
        assert!(AspectRatio::parse("defer xMidYMid meet foo", ()).is_err());
    }

    #[test]
    fn parses_valid_strings() {
        assert_eq!(
            AspectRatio::parse("defer none", ()),
            Ok(AspectRatio {
                defer: true,
                align: None,
            },)
        );

        assert_eq!(
            AspectRatio::parse("xMidYMid", ()),
            Ok(AspectRatio {
                defer: false,
                align: Some(Align {
                    x: X(Align1D::Mid),
                    y: Y(Align1D::Mid),
                    fit: FitMode::Meet,
                },),
            },)
        );

        assert_eq!(
            AspectRatio::parse("defer xMidYMid", ()),
            Ok(AspectRatio {
                defer: true,
                align: Some(Align {
                    x: X(Align1D::Mid),
                    y: Y(Align1D::Mid),
                    fit: FitMode::Meet,
                },),
            },)
        );

        assert_eq!(
            AspectRatio::parse("defer xMinYMax", ()),
            Ok(AspectRatio {
                defer: true,
                align: Some(Align {
                    x: X(Align1D::Min),
                    y: Y(Align1D::Max),
                    fit: FitMode::Meet,
                },),
            },)
        );

        assert_eq!(
            AspectRatio::parse("defer xMaxYMid meet", ()),
            Ok(AspectRatio {
                defer: true,
                align: Some(Align {
                    x: X(Align1D::Max),
                    y: Y(Align1D::Mid),
                    fit: FitMode::Meet,
                },),
            },)
        );

        assert_eq!(
            AspectRatio::parse("defer xMinYMax slice", ()),
            Ok(AspectRatio {
                defer: true,
                align: Some(Align {
                    x: X(Align1D::Min),
                    y: Y(Align1D::Max),
                    fit: FitMode::Slice,
                },),
            },)
        );
    }

    #[test]
    fn aligns() {
        let foo = AspectRatio::parse("xMinYMin meet", ()).unwrap();
        let foo = foo.compute(1.0, 10.0, 0.0, 0.0, 10.0, 1.0);
        assert_eq!(foo, (0.0, 0.0, 0.1, 1.0));

        let foo = AspectRatio::parse("xMinYMin slice", ()).unwrap();
        let foo = foo.compute(1.0, 10.0, 0.0, 0.0, 10.0, 1.0);
        assert_eq!(foo, (0.0, 0.0, 10.0, 100.0));

        let foo = AspectRatio::parse("xMinYMid meet", ()).unwrap();
        let foo = foo.compute(1.0, 10.0, 0.0, 0.0, 10.0, 1.0);
        assert_eq!(foo, (0.0, 0.0, 0.1, 1.0));

        let foo = AspectRatio::parse("xMinYMid slice", ()).unwrap();
        let foo = foo.compute(1.0, 10.0, 0.0, 0.0, 10.0, 1.0);

        assert_eq!(foo, (0.0, -49.5, 10.0, 100.0));

        let foo = AspectRatio::parse("xMinYMax meet", ()).unwrap();
        let foo = foo.compute(1.0, 10.0, 0.0, 0.0, 10.0, 1.0);
        assert_eq!(foo, (0.0, 0.0, 0.1, 1.0));

        let foo = AspectRatio::parse("xMinYMax slice", ()).unwrap();
        let foo = foo.compute(1.0, 10.0, 0.0, 0.0, 10.0, 1.0);
        assert_eq!(foo, (0.0, -99.0, 10.0, 100.0));

        let foo = AspectRatio::parse("xMidYMin meet", ()).unwrap();
        let foo = foo.compute(1.0, 10.0, 0.0, 0.0, 10.0, 1.0);
        assert_eq!(foo, (4.95, 0.0, 0.1, 1.0));

        let foo = AspectRatio::parse("xMidYMin slice", ()).unwrap();
        let foo = foo.compute(1.0, 10.0, 0.0, 0.0, 10.0, 1.0);
        assert_eq!(foo, (0.0, 0.0, 10.0, 100.0));

        let foo = AspectRatio::parse("xMidYMid meet", ()).unwrap();
        let foo = foo.compute(1.0, 10.0, 0.0, 0.0, 10.0, 1.0);
        assert_eq!(foo, (4.95, 0.0, 0.1, 1.0));

        let foo = AspectRatio::parse("xMidYMid slice", ()).unwrap();
        let foo = foo.compute(1.0, 10.0, 0.0, 0.0, 10.0, 1.0);
        assert_eq!(foo, (0.0, -49.5, 10.0, 100.0));

        let foo = AspectRatio::parse("xMidYMax meet", ()).unwrap();
        let foo = foo.compute(1.0, 10.0, 0.0, 0.0, 10.0, 1.0);
        assert_eq!(foo, (4.95, 0.0, 0.1, 1.0));

        let foo = AspectRatio::parse("xMidYMax slice", ()).unwrap();
        let foo = foo.compute(1.0, 10.0, 0.0, 0.0, 10.0, 1.0);
        assert_eq!(foo, (0.0, -99.0, 10.0, 100.0));

        let foo = AspectRatio::parse("xMaxYMin meet", ()).unwrap();
        let foo = foo.compute(1.0, 10.0, 0.0, 0.0, 10.0, 1.0);
        assert_eq!(foo, (9.9, 0.0, 0.1, 1.0));

        let foo = AspectRatio::parse("xMaxYMin slice", ()).unwrap();
        let foo = foo.compute(1.0, 10.0, 0.0, 0.0, 10.0, 1.0);
        assert_eq!(foo, (0.0, 0.0, 10.0, 100.0));

        let foo = AspectRatio::parse("xMaxYMid meet", ()).unwrap();
        let foo = foo.compute(1.0, 10.0, 0.0, 0.0, 10.0, 1.0);
        assert_eq!(foo, (9.9, 0.0, 0.1, 1.0));

        let foo = AspectRatio::parse("xMaxYMid slice", ()).unwrap();
        let foo = foo.compute(1.0, 10.0, 0.0, 0.0, 10.0, 1.0);
        assert_eq!(foo, (0.0, -49.5, 10.0, 100.0));

        let foo = AspectRatio::parse("xMaxYMax meet", ()).unwrap();
        let foo = foo.compute(1.0, 10.0, 0.0, 0.0, 10.0, 1.0);
        assert_eq!(foo, (9.9, 0.0, 0.1, 1.0));

        let foo = AspectRatio::parse("xMaxYMax slice", ()).unwrap();
        let foo = foo.compute(1.0, 10.0, 0.0, 0.0, 10.0, 1.0);
        assert_eq!(foo, (0.0, -99.0, 10.0, 100.0));
    }
}
<commit_msg>AspectRatio: don't inline the creation of the Parser<commit_after>//! Handling of `preserveAspectRatio` values
//!
//! This module handles `preserveAspectRatio` values [per the SVG specification][spec].
//! We have an [`AspectRatio`] struct which encapsulates such a value.
//!
//! ```ignore
//! assert_eq!(
//!     AspectRatio::parse("xMidYMid", ()),
//!     Ok(AspectRatio {
//!         defer: false,
//!         align: Some(Align {
//!             x: X(Align1D::Mid),
//!             y: Y(Align1D::Mid),
//!             fit: FitMode::Meet,
//!         }),
//!     })
//! );
//! ```
//!
//! [`AspectRatio`]: struct.AspectRatio.html
//! [spec]: https://www.w3.org/TR/SVG/coords.html#PreserveAspectRatioAttribute

use cssparser::{Parser, ParserInput};
use error::*;
use parsers::Parse;
use parsers::ParseError;
use std::ops::Deref;

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct AspectRatio {
    defer: bool,
    align: Option<Align>,
}

impl Default for AspectRatio {
    fn default() -> AspectRatio {
        AspectRatio {
            defer: false,
            align: Some(Align::default()),
        }
    }
}

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
enum FitMode {
    Meet,
    Slice,
}

impl Default for FitMode {
    fn default() -> FitMode {
        FitMode::Meet
    }
}

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
struct Align {
    x: X,
    y: Y,
    fit: FitMode,
}

impl Default for Align {
    fn default() -> Align {
        Align {
            x: X(Align1D::Mid),
            y: Y(Align1D::Mid),
            fit: FitMode::default(),
        }
    }
}

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
enum Align1D {
    Min,
    Mid,
    Max,
}

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
struct X(Align1D);
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
struct Y(Align1D);

impl Deref for X {
    type Target = Align1D;

    fn deref(&self) -> &Align1D {
        &self.0
    }
}

impl Deref for Y {
    type Target = Align1D;

    fn deref(&self) -> &Align1D {
        &self.0
    }
}

impl Align1D {
    fn compute(self, dest_pos: f64, dest_size: f64, obj_size: f64) -> f64 {
        match self {
            Align1D::Min => dest_pos,
            Align1D::Mid => dest_pos + (dest_size - obj_size) / 2.0,
            Align1D::Max => dest_pos + dest_size - obj_size,
        }
    }
}

impl AspectRatio {
    pub fn is_slice(&self) -> bool {
        match self.align {
            Some(Align {
                fit: FitMode::Slice,
                ..
            }) => true,

            _ => false,
        }
    }

    pub fn compute(
        &self,
        object_width: f64,
        object_height: f64,
        dest_x: f64,
        dest_y: f64,
        dest_width: f64,
        dest_height: f64,
    ) -> (f64, f64, f64, f64) {
        match self.align {
            None => (dest_x, dest_y, dest_width, dest_height),

            Some(Align { x, y, fit }) => {
                let w_factor = dest_width / object_width;
                let h_factor = dest_height / object_height;
                let factor = match fit {
                    FitMode::Meet => w_factor.min(h_factor),
                    FitMode::Slice => w_factor.max(h_factor),
                };

                let w = object_width * factor;
                let h = object_height * factor;

                let xpos = x.compute(dest_x, dest_width, w);
                let ypos = y.compute(dest_y, dest_height, h);

                (xpos, ypos, w, h)
            }
        }
    }

    fn parse_input<'i, 't>(p: &mut Parser<'i, 't>) -> Result<AspectRatio, ()> {
        let defer = p.try(|p| p.expect_ident_matching("defer")).is_ok();

        let align_xy = p.try(|p| {
            p.expect_ident()
                .map_err(|_| ())
                .and_then(|ident| Align::parse_xy(ident))
        })?;

        let fit =
            p.try(|p| {
                p.expect_ident()
                    .map_err(|_| ())
                    .and_then(|ident| FitMode::parse(ident))
            }).unwrap_or(FitMode::default());

        p.expect_exhausted().map_err(|_| ())?;

        let align = align_xy.map(|(x, y)| Align { x, y, fit });

        Ok(AspectRatio { defer, align })
    }
}

impl Align {
    fn parse_xy(s: &str) -> Result<Option<(X, Y)>, ()> {
        use self::Align1D::*;

        match s {
            "none" => Ok(None),

            "xMinYMin" => Ok(Some((X(Min), Y(Min)))),
            "xMidYMin" => Ok(Some((X(Mid), Y(Min)))),
            "xMaxYMin" => Ok(Some((X(Max), Y(Min)))),

            "xMinYMid" => Ok(Some((X(Min), Y(Mid)))),
            "xMidYMid" => Ok(Some((X(Mid), Y(Mid)))),
            "xMaxYMid" => Ok(Some((X(Max), Y(Mid)))),

            "xMinYMax" => Ok(Some((X(Min), Y(Max)))),
            "xMidYMax" => Ok(Some((X(Mid), Y(Max)))),
            "xMaxYMax" => Ok(Some((X(Max), Y(Max)))),

            _ => Err(()),
        }
    }
}

impl FitMode {
    fn parse(s: &str) -> Result<FitMode, ()> {
        match s {
            "meet" => Ok(FitMode::Meet),
            "slice" => Ok(FitMode::Slice),
            _ => Err(()),
        }
    }
}

impl Parse for AspectRatio {
    type Data = ();
    type Err = AttributeError;

    fn parse(s: &str, _: ()) -> Result<AspectRatio, AttributeError> {
        let mut input = ParserInput::new(s);
        let mut parser = Parser::new(&mut input);

        AspectRatio::parse_input(&mut parser).map_err(|_| {
            AttributeError::Parse(ParseError::new(
                "expected \"[defer] <align> [meet | slice]\"",
            ))
        })
    }
}

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

    #[test]
    fn parsing_invalid_strings_yields_error() {
        assert!(AspectRatio::parse("", ()).is_err());
        assert!(AspectRatio::parse("defer", ()).is_err());
        assert!(AspectRatio::parse("defer foo", ()).is_err());
        assert!(AspectRatio::parse("defer xmidymid", ()).is_err());
        assert!(AspectRatio::parse("defer xMidYMid foo", ()).is_err());
        assert!(AspectRatio::parse("xmidymid", ()).is_err());
        assert!(AspectRatio::parse("xMidYMid foo", ()).is_err());
        assert!(AspectRatio::parse("defer xMidYMid meet foo", ()).is_err());
    }

    #[test]
    fn parses_valid_strings() {
        assert_eq!(
            AspectRatio::parse("defer none", ()),
            Ok(AspectRatio {
                defer: true,
                align: None,
            },)
        );

        assert_eq!(
            AspectRatio::parse("xMidYMid", ()),
            Ok(AspectRatio {
                defer: false,
                align: Some(Align {
                    x: X(Align1D::Mid),
                    y: Y(Align1D::Mid),
                    fit: FitMode::Meet,
                },),
            },)
        );

        assert_eq!(
            AspectRatio::parse("defer xMidYMid", ()),
            Ok(AspectRatio {
                defer: true,
                align: Some(Align {
                    x: X(Align1D::Mid),
                    y: Y(Align1D::Mid),
                    fit: FitMode::Meet,
                },),
            },)
        );

        assert_eq!(
            AspectRatio::parse("defer xMinYMax", ()),
            Ok(AspectRatio {
                defer: true,
                align: Some(Align {
                    x: X(Align1D::Min),
                    y: Y(Align1D::Max),
                    fit: FitMode::Meet,
                },),
            },)
        );

        assert_eq!(
            AspectRatio::parse("defer xMaxYMid meet", ()),
            Ok(AspectRatio {
                defer: true,
                align: Some(Align {
                    x: X(Align1D::Max),
                    y: Y(Align1D::Mid),
                    fit: FitMode::Meet,
                },),
            },)
        );

        assert_eq!(
            AspectRatio::parse("defer xMinYMax slice", ()),
            Ok(AspectRatio {
                defer: true,
                align: Some(Align {
                    x: X(Align1D::Min),
                    y: Y(Align1D::Max),
                    fit: FitMode::Slice,
                },),
            },)
        );
    }

    #[test]
    fn aligns() {
        let foo = AspectRatio::parse("xMinYMin meet", ()).unwrap();
        let foo = foo.compute(1.0, 10.0, 0.0, 0.0, 10.0, 1.0);
        assert_eq!(foo, (0.0, 0.0, 0.1, 1.0));

        let foo = AspectRatio::parse("xMinYMin slice", ()).unwrap();
        let foo = foo.compute(1.0, 10.0, 0.0, 0.0, 10.0, 1.0);
        assert_eq!(foo, (0.0, 0.0, 10.0, 100.0));

        let foo = AspectRatio::parse("xMinYMid meet", ()).unwrap();
        let foo = foo.compute(1.0, 10.0, 0.0, 0.0, 10.0, 1.0);
        assert_eq!(foo, (0.0, 0.0, 0.1, 1.0));

        let foo = AspectRatio::parse("xMinYMid slice", ()).unwrap();
        let foo = foo.compute(1.0, 10.0, 0.0, 0.0, 10.0, 1.0);

        assert_eq!(foo, (0.0, -49.5, 10.0, 100.0));

        let foo = AspectRatio::parse("xMinYMax meet", ()).unwrap();
        let foo = foo.compute(1.0, 10.0, 0.0, 0.0, 10.0, 1.0);
        assert_eq!(foo, (0.0, 0.0, 0.1, 1.0));

        let foo = AspectRatio::parse("xMinYMax slice", ()).unwrap();
        let foo = foo.compute(1.0, 10.0, 0.0, 0.0, 10.0, 1.0);
        assert_eq!(foo, (0.0, -99.0, 10.0, 100.0));

        let foo = AspectRatio::parse("xMidYMin meet", ()).unwrap();
        let foo = foo.compute(1.0, 10.0, 0.0, 0.0, 10.0, 1.0);
        assert_eq!(foo, (4.95, 0.0, 0.1, 1.0));

        let foo = AspectRatio::parse("xMidYMin slice", ()).unwrap();
        let foo = foo.compute(1.0, 10.0, 0.0, 0.0, 10.0, 1.0);
        assert_eq!(foo, (0.0, 0.0, 10.0, 100.0));

        let foo = AspectRatio::parse("xMidYMid meet", ()).unwrap();
        let foo = foo.compute(1.0, 10.0, 0.0, 0.0, 10.0, 1.0);
        assert_eq!(foo, (4.95, 0.0, 0.1, 1.0));

        let foo = AspectRatio::parse("xMidYMid slice", ()).unwrap();
        let foo = foo.compute(1.0, 10.0, 0.0, 0.0, 10.0, 1.0);
        assert_eq!(foo, (0.0, -49.5, 10.0, 100.0));

        let foo = AspectRatio::parse("xMidYMax meet", ()).unwrap();
        let foo = foo.compute(1.0, 10.0, 0.0, 0.0, 10.0, 1.0);
        assert_eq!(foo, (4.95, 0.0, 0.1, 1.0));

        let foo = AspectRatio::parse("xMidYMax slice", ()).unwrap();
        let foo = foo.compute(1.0, 10.0, 0.0, 0.0, 10.0, 1.0);
        assert_eq!(foo, (0.0, -99.0, 10.0, 100.0));

        let foo = AspectRatio::parse("xMaxYMin meet", ()).unwrap();
        let foo = foo.compute(1.0, 10.0, 0.0, 0.0, 10.0, 1.0);
        assert_eq!(foo, (9.9, 0.0, 0.1, 1.0));

        let foo = AspectRatio::parse("xMaxYMin slice", ()).unwrap();
        let foo = foo.compute(1.0, 10.0, 0.0, 0.0, 10.0, 1.0);
        assert_eq!(foo, (0.0, 0.0, 10.0, 100.0));

        let foo = AspectRatio::parse("xMaxYMid meet", ()).unwrap();
        let foo = foo.compute(1.0, 10.0, 0.0, 0.0, 10.0, 1.0);
        assert_eq!(foo, (9.9, 0.0, 0.1, 1.0));

        let foo = AspectRatio::parse("xMaxYMid slice", ()).unwrap();
        let foo = foo.compute(1.0, 10.0, 0.0, 0.0, 10.0, 1.0);
        assert_eq!(foo, (0.0, -49.5, 10.0, 100.0));

        let foo = AspectRatio::parse("xMaxYMax meet", ()).unwrap();
        let foo = foo.compute(1.0, 10.0, 0.0, 0.0, 10.0, 1.0);
        assert_eq!(foo, (9.9, 0.0, 0.1, 1.0));

        let foo = AspectRatio::parse("xMaxYMax slice", ()).unwrap();
        let foo = foo.compute(1.0, 10.0, 0.0, 0.0, 10.0, 1.0);
        assert_eq!(foo, (0.0, -99.0, 10.0, 100.0));
    }
}
<|endoftext|>


<commit_before>use std::path::PathBuf;
use std::ptr;

use cairo::{self, ImageSurface};
use gdk_pixbuf::{Colorspace, Pixbuf};
use gdk_pixbuf_sys;
use gio;
use gio::prelude::*;
use glib::translate::*;
use glib_sys;
use libc;
use url::Url;

use crate::dpi::Dpi;
use crate::error::{set_gerror, LoadingError, RenderingError};
use crate::handle::{Handle, LoadOptions, RsvgDimensionData, SizeCallback};
use crate::rect::IRect;
use crate::surface_utils::{
    iterators::Pixels,
    shared_surface::SharedImageSurface,
    shared_surface::SurfaceType,
};

// Pixbuf::new() doesn't return out-of-memory errors properly
// See https://github.com/gtk-rs/gdk-pixbuf/issues/96
fn pixbuf_new(width: i32, height: i32) -> Result<Pixbuf, RenderingError> {
    assert!(width > 0 && height > 0);

    unsafe {
        let raw_pixbuf = gdk_pixbuf_sys::gdk_pixbuf_new(
            Colorspace::Rgb.to_glib(),
            true.to_glib(),
            8,
            width,
            height,
        );

        if raw_pixbuf.is_null() {
            return Err(RenderingError::OutOfMemory);
        }

        Ok(from_glib_full(raw_pixbuf))
    }
}

pub fn empty_pixbuf() -> Result<Pixbuf, RenderingError> {
    // GdkPixbuf does not allow zero-sized pixbufs, but Cairo allows zero-sized
    // surfaces.  In this case, return a 1-pixel transparent pixbuf.

    unsafe {
        let raw_pixbuf =
            gdk_pixbuf_sys::gdk_pixbuf_new(Colorspace::Rgb.to_glib(), true.to_glib(), 8, 1, 1);

        if raw_pixbuf.is_null() {
            return Err(RenderingError::OutOfMemory);
        }

        let pixbuf: Pixbuf = from_glib_full(raw_pixbuf);
        pixbuf.put_pixel(0, 0, 0, 0, 0, 0);

        Ok(pixbuf)
    }
}

pub fn pixbuf_from_surface(surface: &SharedImageSurface) -> Result<Pixbuf, RenderingError> {
    let width = surface.width();
    let height = surface.height();

    let pixbuf = pixbuf_new(width as i32, height as i32)?;

    let bounds = IRect {
        x0: 0,
        y0: 0,
        x1: width,
        y1: height,
    };

    for (x, y, pixel) in Pixels::new(&surface, bounds) {
        let (r, g, b, a) = if pixel.a == 0 {
            (0, 0, 0, 0)
        } else {
            let pixel = pixel.unpremultiply();
            (pixel.r, pixel.g, pixel.b, pixel.a)
        };

        pixbuf.put_pixel(x as i32, y as i32, r, g, b, a);
    }

    Ok(pixbuf)
}

enum SizeKind {
    Zoom,
    WidthHeight,
    WidthHeightMax,
    ZoomMax,
}

struct SizeMode {
    kind: SizeKind,
    x_zoom: f64,
    y_zoom: f64,
    width: i32,
    height: i32,
}

fn get_final_size(dimensions: &RsvgDimensionData, size_mode: &SizeMode) -> (i32, i32) {
    let in_width = dimensions.width;
    let in_height = dimensions.height;

    let mut out_width;
    let mut out_height;

    match size_mode.kind {
        SizeKind::Zoom => {
            out_width = (size_mode.x_zoom * f64::from(in_width) + 0.5).floor() as i32;
            out_height = (size_mode.y_zoom * f64::from(in_height) + 0.5).floor() as i32;
        }

        SizeKind::ZoomMax => {
            out_width = (size_mode.x_zoom * f64::from(in_width) + 0.5).floor() as i32;
            out_height = (size_mode.y_zoom * f64::from(in_height) + 0.5).floor() as i32;

            if out_width > size_mode.width || out_height > size_mode.height {
                let zoom_x = f64::from(size_mode.width) / f64::from(out_width);
                let zoom_y = f64::from(size_mode.height) / f64::from(out_height);
                let zoom = zoom_x.min(zoom_y);

                out_width = (zoom * f64::from(out_width) + 0.5) as i32;
                out_height = (zoom * f64::from(out_height) + 0.5) as i32;
            }
        }

        SizeKind::WidthHeightMax => {
            let zoom_x = f64::from(size_mode.width) / f64::from(in_width);
            let zoom_y = f64::from(size_mode.height) / f64::from(in_height);

            let zoom = zoom_x.min(zoom_y);

            out_width = (zoom * f64::from(in_width) + 0.5) as i32;
            out_height = (zoom * f64::from(in_height) + 0.5) as i32;
        }

        SizeKind::WidthHeight => {
            if size_mode.width != -1 {
                out_width = size_mode.width;
            } else {
                out_width = in_width;
            }

            if size_mode.height != -1 {
                out_height = size_mode.height;
            } else {
                out_height = in_height;
            }
        }
    }

    (out_width, out_height)
}

fn render_to_pixbuf_at_size(
    handle: &Handle,
    dimensions: &RsvgDimensionData,
    width: i32,
    height: i32,
    dpi: Dpi,
) -> Result<Pixbuf, RenderingError> {
    if width == 0 || height == 0 {
        return empty_pixbuf();
    }

    let surface = ImageSurface::create(cairo::Format::ARgb32, width, height)?;

    {
        let cr = cairo::Context::new(&surface);
        cr.scale(
            f64::from(width) / f64::from(dimensions.width),
            f64::from(height) / f64::from(dimensions.height),
        );
        handle.render_cairo_sub(&cr, None, dpi, &SizeCallback::default(), false)?;
    }

    let shared_surface = SharedImageSurface::new(surface, SurfaceType::SRgb)?;

    pixbuf_from_surface(&shared_surface)
}

fn get_default_dpi() -> Dpi {
    // This is ugly, but it preserves the C API semantics of
    //
    //   rsvg_set_default_dpi(...);
    //   pixbuf = rsvg_pixbuf_from_file(...);
    //
    // Passing negative numbers here means that the global default DPI will be used.
    Dpi::new(-1.0, -1.0)
}

fn url_from_file(file: &gio::File) -> Result<Url, LoadingError> {
    Ok(Url::parse(&file.get_uri()).map_err(|_| LoadingError::BadUrl)?)
}

fn pixbuf_from_file_with_size_mode(
    filename: *const libc::c_char,
    size_mode: &SizeMode,
    error: *mut *mut glib_sys::GError,
) -> *mut gdk_pixbuf_sys::GdkPixbuf {
    let dpi = get_default_dpi();

    unsafe {
        let path = PathBuf::from_glib_none(filename);
        let file = gio::File::new_for_path(path);

        let base_url = match url_from_file(&file) {
            Ok(url) => url,
            Err(e) => {
                set_gerror(error, 0, &format!("{}", e));
                return ptr::null_mut();
            }
        };

        let load_options = LoadOptions::new(Some(base_url));

        let cancellable: Option<&gio::Cancellable> = None;
        let handle = match file
            .read(cancellable)
            .map_err(|e| LoadingError::from(e))
            .and_then(|stream| Handle::from_stream(&load_options, &stream, None))
        {
            Ok(handle) => handle,
            Err(e) => {
                set_gerror(error, 0, &format!("{}", e));
                return ptr::null_mut();
            }
        };

        handle
            .get_dimensions(dpi, &SizeCallback::default(), false)
            .and_then(|dimensions| {
                let (width, height) = get_final_size(&dimensions, size_mode);

                render_to_pixbuf_at_size(&handle, &dimensions, width, height, dpi)
            })
            .and_then(|pixbuf| Ok(pixbuf.to_glib_full()))
            .unwrap_or_else(|e| {
                set_gerror(error, 0, &format!("{}", e));
                ptr::null_mut()
            })
    }
}

#[no_mangle]
pub unsafe extern "C" fn rsvg_rust_pixbuf_from_file_at_size(
    filename: *const libc::c_char,
    width: i32,
    height: i32,
    error: *mut *mut glib_sys::GError,
) -> *mut gdk_pixbuf_sys::GdkPixbuf {
    pixbuf_from_file_with_size_mode(
        filename,
        &SizeMode {
            kind: SizeKind::WidthHeight,
            x_zoom: 0.0,
            y_zoom: 0.0,
            width,
            height,
        },
        error,
    )
}

#[no_mangle]
pub unsafe extern "C" fn rsvg_rust_pixbuf_from_file_at_zoom(
    filename: *const libc::c_char,
    x_zoom: f64,
    y_zoom: f64,
    error: *mut *mut glib_sys::GError,
) -> *mut gdk_pixbuf_sys::GdkPixbuf {
    pixbuf_from_file_with_size_mode(
        filename,
        &SizeMode {
            kind: SizeKind::Zoom,
            x_zoom,
            y_zoom,
            width: 0,
            height: 0,
        },
        error,
    )
}

#[no_mangle]
pub unsafe extern "C" fn rsvg_rust_pixbuf_from_file_at_zoom_with_max(
    filename: *const libc::c_char,
    x_zoom: f64,
    y_zoom: f64,
    width: i32,
    height: i32,
    error: *mut *mut glib_sys::GError,
) -> *mut gdk_pixbuf_sys::GdkPixbuf {
    pixbuf_from_file_with_size_mode(
        filename,
        &SizeMode {
            kind: SizeKind::ZoomMax,
            x_zoom,
            y_zoom,
            width,
            height,
        },
        error,
    )
}

#[no_mangle]
pub unsafe extern "C" fn rsvg_rust_pixbuf_from_file_at_max_size(
    filename: *const libc::c_char,
    width: i32,
    height: i32,
    error: *mut *mut glib_sys::GError,
) -> *mut gdk_pixbuf_sys::GdkPixbuf {
    pixbuf_from_file_with_size_mode(
        filename,
        &SizeMode {
            kind: SizeKind::WidthHeightMax,
            x_zoom: 0.0,
            y_zoom: 0.0,
            width,
            height,
        },
        error,
    )
}
<commit_msg>Use plain Pixbuf::new() now that it detects out-of-memory properly<commit_after>use std::path::PathBuf;
use std::ptr;

use cairo::{self, ImageSurface};
use gdk_pixbuf::{Colorspace, Pixbuf};
use gdk_pixbuf_sys;
use gio;
use gio::prelude::*;
use glib::translate::*;
use glib_sys;
use libc;
use url::Url;

use crate::dpi::Dpi;
use crate::error::{set_gerror, LoadingError, RenderingError};
use crate::handle::{Handle, LoadOptions, RsvgDimensionData, SizeCallback};
use crate::rect::IRect;
use crate::surface_utils::{
    iterators::Pixels,
    shared_surface::SharedImageSurface,
    shared_surface::SurfaceType,
};

fn pixbuf_new(width: i32, height: i32) -> Result<Pixbuf, RenderingError> {
    assert!(width > 0 && height > 0);

    Pixbuf::new(Colorspace::Rgb, true, 8, width, height).ok_or(RenderingError::OutOfMemory)
}

pub fn empty_pixbuf() -> Result<Pixbuf, RenderingError> {
    // GdkPixbuf does not allow zero-sized pixbufs, but Cairo allows zero-sized
    // surfaces.  In this case, return a 1-pixel transparent pixbuf.

    let pixbuf = pixbuf_new(1, 1)?;
    pixbuf.put_pixel(0, 0, 0, 0, 0, 0);

    Ok(pixbuf)
}

pub fn pixbuf_from_surface(surface: &SharedImageSurface) -> Result<Pixbuf, RenderingError> {
    let width = surface.width();
    let height = surface.height();

    let pixbuf = pixbuf_new(width as i32, height as i32)?;

    let bounds = IRect {
        x0: 0,
        y0: 0,
        x1: width,
        y1: height,
    };

    for (x, y, pixel) in Pixels::new(&surface, bounds) {
        let (r, g, b, a) = if pixel.a == 0 {
            (0, 0, 0, 0)
        } else {
            let pixel = pixel.unpremultiply();
            (pixel.r, pixel.g, pixel.b, pixel.a)
        };

        pixbuf.put_pixel(x as i32, y as i32, r, g, b, a);
    }

    Ok(pixbuf)
}

enum SizeKind {
    Zoom,
    WidthHeight,
    WidthHeightMax,
    ZoomMax,
}

struct SizeMode {
    kind: SizeKind,
    x_zoom: f64,
    y_zoom: f64,
    width: i32,
    height: i32,
}

fn get_final_size(dimensions: &RsvgDimensionData, size_mode: &SizeMode) -> (i32, i32) {
    let in_width = dimensions.width;
    let in_height = dimensions.height;

    let mut out_width;
    let mut out_height;

    match size_mode.kind {
        SizeKind::Zoom => {
            out_width = (size_mode.x_zoom * f64::from(in_width) + 0.5).floor() as i32;
            out_height = (size_mode.y_zoom * f64::from(in_height) + 0.5).floor() as i32;
        }

        SizeKind::ZoomMax => {
            out_width = (size_mode.x_zoom * f64::from(in_width) + 0.5).floor() as i32;
            out_height = (size_mode.y_zoom * f64::from(in_height) + 0.5).floor() as i32;

            if out_width > size_mode.width || out_height > size_mode.height {
                let zoom_x = f64::from(size_mode.width) / f64::from(out_width);
                let zoom_y = f64::from(size_mode.height) / f64::from(out_height);
                let zoom = zoom_x.min(zoom_y);

                out_width = (zoom * f64::from(out_width) + 0.5) as i32;
                out_height = (zoom * f64::from(out_height) + 0.5) as i32;
            }
        }

        SizeKind::WidthHeightMax => {
            let zoom_x = f64::from(size_mode.width) / f64::from(in_width);
            let zoom_y = f64::from(size_mode.height) / f64::from(in_height);

            let zoom = zoom_x.min(zoom_y);

            out_width = (zoom * f64::from(in_width) + 0.5) as i32;
            out_height = (zoom * f64::from(in_height) + 0.5) as i32;
        }

        SizeKind::WidthHeight => {
            if size_mode.width != -1 {
                out_width = size_mode.width;
            } else {
                out_width = in_width;
            }

            if size_mode.height != -1 {
                out_height = size_mode.height;
            } else {
                out_height = in_height;
            }
        }
    }

    (out_width, out_height)
}

fn render_to_pixbuf_at_size(
    handle: &Handle,
    dimensions: &RsvgDimensionData,
    width: i32,
    height: i32,
    dpi: Dpi,
) -> Result<Pixbuf, RenderingError> {
    if width == 0 || height == 0 {
        return empty_pixbuf();
    }

    let surface = ImageSurface::create(cairo::Format::ARgb32, width, height)?;

    {
        let cr = cairo::Context::new(&surface);
        cr.scale(
            f64::from(width) / f64::from(dimensions.width),
            f64::from(height) / f64::from(dimensions.height),
        );
        handle.render_cairo_sub(&cr, None, dpi, &SizeCallback::default(), false)?;
    }

    let shared_surface = SharedImageSurface::new(surface, SurfaceType::SRgb)?;

    pixbuf_from_surface(&shared_surface)
}

fn get_default_dpi() -> Dpi {
    // This is ugly, but it preserves the C API semantics of
    //
    //   rsvg_set_default_dpi(...);
    //   pixbuf = rsvg_pixbuf_from_file(...);
    //
    // Passing negative numbers here means that the global default DPI will be used.
    Dpi::new(-1.0, -1.0)
}

fn url_from_file(file: &gio::File) -> Result<Url, LoadingError> {
    Ok(Url::parse(&file.get_uri()).map_err(|_| LoadingError::BadUrl)?)
}

fn pixbuf_from_file_with_size_mode(
    filename: *const libc::c_char,
    size_mode: &SizeMode,
    error: *mut *mut glib_sys::GError,
) -> *mut gdk_pixbuf_sys::GdkPixbuf {
    let dpi = get_default_dpi();

    unsafe {
        let path = PathBuf::from_glib_none(filename);
        let file = gio::File::new_for_path(path);

        let base_url = match url_from_file(&file) {
            Ok(url) => url,
            Err(e) => {
                set_gerror(error, 0, &format!("{}", e));
                return ptr::null_mut();
            }
        };

        let load_options = LoadOptions::new(Some(base_url));

        let cancellable: Option<&gio::Cancellable> = None;
        let handle = match file
            .read(cancellable)
            .map_err(|e| LoadingError::from(e))
            .and_then(|stream| Handle::from_stream(&load_options, &stream, None))
        {
            Ok(handle) => handle,
            Err(e) => {
                set_gerror(error, 0, &format!("{}", e));
                return ptr::null_mut();
            }
        };

        handle
            .get_dimensions(dpi, &SizeCallback::default(), false)
            .and_then(|dimensions| {
                let (width, height) = get_final_size(&dimensions, size_mode);

                render_to_pixbuf_at_size(&handle, &dimensions, width, height, dpi)
            })
            .and_then(|pixbuf| Ok(pixbuf.to_glib_full()))
            .unwrap_or_else(|e| {
                set_gerror(error, 0, &format!("{}", e));
                ptr::null_mut()
            })
    }
}

#[no_mangle]
pub unsafe extern "C" fn rsvg_rust_pixbuf_from_file_at_size(
    filename: *const libc::c_char,
    width: i32,
    height: i32,
    error: *mut *mut glib_sys::GError,
) -> *mut gdk_pixbuf_sys::GdkPixbuf {
    pixbuf_from_file_with_size_mode(
        filename,
        &SizeMode {
            kind: SizeKind::WidthHeight,
            x_zoom: 0.0,
            y_zoom: 0.0,
            width,
            height,
        },
        error,
    )
}

#[no_mangle]
pub unsafe extern "C" fn rsvg_rust_pixbuf_from_file_at_zoom(
    filename: *const libc::c_char,
    x_zoom: f64,
    y_zoom: f64,
    error: *mut *mut glib_sys::GError,
) -> *mut gdk_pixbuf_sys::GdkPixbuf {
    pixbuf_from_file_with_size_mode(
        filename,
        &SizeMode {
            kind: SizeKind::Zoom,
            x_zoom,
            y_zoom,
            width: 0,
            height: 0,
        },
        error,
    )
}

#[no_mangle]
pub unsafe extern "C" fn rsvg_rust_pixbuf_from_file_at_zoom_with_max(
    filename: *const libc::c_char,
    x_zoom: f64,
    y_zoom: f64,
    width: i32,
    height: i32,
    error: *mut *mut glib_sys::GError,
) -> *mut gdk_pixbuf_sys::GdkPixbuf {
    pixbuf_from_file_with_size_mode(
        filename,
        &SizeMode {
            kind: SizeKind::ZoomMax,
            x_zoom,
            y_zoom,
            width,
            height,
        },
        error,
    )
}

#[no_mangle]
pub unsafe extern "C" fn rsvg_rust_pixbuf_from_file_at_max_size(
    filename: *const libc::c_char,
    width: i32,
    height: i32,
    error: *mut *mut glib_sys::GError,
) -> *mut gdk_pixbuf_sys::GdkPixbuf {
    pixbuf_from_file_with_size_mode(
        filename,
        &SizeMode {
            kind: SizeKind::WidthHeightMax,
            x_zoom: 0.0,
            y_zoom: 0.0,
            width,
            height,
        },
        error,
    )
}
<|endoftext|>


<commit_before><commit_msg>Use sample instead of ind_sample<commit_after><|endoftext|>


<commit_before><commit_msg>mac again<commit_after><|endoftext|>


<commit_before>use byteorder::{BigEndian, ByteOrder};
use std::io::Cursor;
use super::source_destination::SourceDestination;

pub struct IPv4Header {
    pub version: u8,
    pub header_length: u8,
    pub total_length: u16,
    pub protocol: Protocol,
    pub source: u32,
    pub destination: u32,
}

#[derive(Debug, PartialEq)]
pub enum Protocol {
    TCP,
    UDP,
    OTHER,
}

impl IPv4Header {
    pub fn parse(raw: &[u8]) -> IPv4Header {
        IPv4Header {
            version: raw[0] >> 4,
            header_length: (raw[0] & 0xf) << 2,
            total_length: BigEndian::read_u16(&raw[2..4]),
            protocol: match raw[9] {
                6 => Protocol::TCP,
                17 => Protocol::UDP,
                _ => Protocol::OTHER
            },
            source: BigEndian::read_u32(&raw[12..16]),
            destination: BigEndian::read_u32(&raw[16..20]),
        }
    }

    pub fn set_total_length(&mut self, raw: &mut [u8], total_length: u16) {
        self.total_length = total_length;
        BigEndian::write_u16(&mut raw[2..4], total_length);
    }

    pub fn set_source(&mut self, raw: &mut [u8], source: u32) {
        self.source = source;
        BigEndian::write_u32(&mut raw[12..16], source);
    }

    pub fn set_destination(&mut self, raw: &mut [u8], destination: u32) {
        self.destination = destination;
        BigEndian::write_u32(&mut raw[16..20], destination);
    }

    pub fn compute_checksum(&mut self, raw: &mut [u8]) {
        // reset checksum field
        self.set_checksum(raw, 0);

        let j = self.header_length as usize / 2;
        let mut sum = (0..j).map(|i| {
            let range = 2*i..2*(i+1);
            BigEndian::read_u16(&raw[range]) as u32
        }).sum::<u32>();
        while (sum & !0xffff) != 0 {
            sum = (sum & 0xffff) + (sum >> 16);
        }

        self.set_checksum(raw, !sum as u16);
    }

    fn get_checksum(&mut self, raw: &[u8]) -> u16 {
        BigEndian::read_u16(&raw[10..12])
    }

    fn set_checksum(&mut self, raw: &mut [u8], checksum: u16) {
        BigEndian::write_u16(&mut raw[10..12], checksum);
    }
}

impl SourceDestination<u32> for IPv4Header {
    fn get_source(&self, _: &[u8]) -> u32 {
        self.source
    }

    fn get_destination(&self, _: &[u8]) -> u32 {
        self.destination
    }

    fn set_source(&mut self, raw: &mut [u8], source: u32) {
        (self as &mut IPv4Header).set_source(raw, source);
    }

    fn set_destination(&mut self, raw: &mut [u8], destination: u32) {
        (self as &mut IPv4Header).set_destination(raw, destination);
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::io::Cursor;
    use byteorder::{BigEndian, WriteBytesExt};

    fn create_header() -> Vec<u8> {
        let mut raw: Vec<u8> = vec![];
        raw.reserve(20);
        raw.write_u8(4u8 << 4 | 5).unwrap(); // version_and_ihl
        raw.write_u8(0).unwrap(); //ToS
        raw.write_u16::<BigEndian>(28).unwrap(); // total length
        raw.write_u32::<BigEndian>(0).unwrap(); // id_flags_fragment_offset
        raw.write_u8(0).unwrap(); // TTL
        raw.write_u8(17).unwrap(); // protocol (UDP)
        raw.write_u16::<BigEndian>(0).unwrap(); // checksum
        raw.write_u32::<BigEndian>(0x12345678).unwrap(); // source address
        raw.write_u32::<BigEndian>(0x42424242).unwrap(); // destination address
        raw
    }

    #[test]
    fn parse_header() {
        let raw = &create_header()[..];
        let data = IPv4Header::parse(raw);
        assert_eq!(4, data.version);
        assert_eq!(20, data.header_length);
        assert_eq!(28, data.total_length);
        assert_eq!(Protocol::UDP, data.protocol);
        assert_eq!(0x12345678, data.source);
        assert_eq!(0x42424242, data.destination);
    }

    #[test]
    fn edit_header() {
        let raw = &mut create_header()[..];
        let mut header = IPv4Header::parse(raw);

        header.set_source(raw, 0x87654321);
        header.set_destination(raw, 0x24242424);
        header.set_total_length(raw, 42);
        assert_eq!(0x87654321, header.source);
        assert_eq!(0x24242424, header.destination);
        assert_eq!(42, header.total_length);

        // assert that the buffer has been modified
        let raw_source = BigEndian::read_u32(&raw[12..16]);
        let raw_destination = BigEndian::read_u32(&raw[16..20]);
        let raw_total_length = BigEndian::read_u16(&raw[2..4]);
        assert_eq!(0x87654321, raw_source);
        assert_eq!(0x24242424, raw_destination);
        assert_eq!(42, raw_total_length);

        header.switch_source_and_destination(raw);

        assert_eq!(0x24242424, header.source);
        assert_eq!(0x87654321, header.destination);

        let raw_source = BigEndian::read_u32(&raw[12..16]);
        let raw_destination = BigEndian::read_u32(&raw[16..20]);
        assert_eq!(0x24242424, raw_source);
        assert_eq!(0x87654321, raw_destination);
    }

    #[test]
    fn compute_checksum() {
        let mut raw = &mut create_header()[..];
        let mut header = IPv4Header::parse(raw);

        // set a fake checksum value to assert that it is correctly computed
        header.set_checksum(raw, 0x79);

        header.compute_checksum(raw);

        let mut sum = 0x4500u32 + 0x001Cu32 + 0x0000u32 + 0x0000u32 + 0x0011u32
                    + 0x0000u32 + 0x1234u32 + 0x5678u32 + 0x4242u32 + 0x4242u32;
        while (sum & !0xffff) != 0 {
            sum = (sum & 0xffff) + (sum >> 16);
        }
        let sum = !sum as u16;
        assert_eq!(sum, header.get_checksum(raw));
    }
}
<commit_msg>Implement read_length() and read_version()<commit_after>use byteorder::{BigEndian, ByteOrder};
use std::io::Cursor;
use super::source_destination::SourceDestination;

pub struct IPv4Header {
    pub version: u8,
    pub header_length: u8,
    pub total_length: u16,
    pub protocol: Protocol,
    pub source: u32,
    pub destination: u32,
}

#[derive(Debug, PartialEq)]
pub enum Protocol {
    TCP,
    UDP,
    OTHER,
}

impl IPv4Header {
    pub fn parse(raw: &[u8]) -> IPv4Header {
        IPv4Header {
            version: raw[0] >> 4,
            header_length: (raw[0] & 0xf) << 2,
            total_length: BigEndian::read_u16(&raw[2..4]),
            protocol: match raw[9] {
                6 => Protocol::TCP,
                17 => Protocol::UDP,
                _ => Protocol::OTHER
            },
            source: BigEndian::read_u32(&raw[12..16]),
            destination: BigEndian::read_u32(&raw[16..20]),
        }
    }

    pub fn set_total_length(&mut self, raw: &mut [u8], total_length: u16) {
        self.total_length = total_length;
        BigEndian::write_u16(&mut raw[2..4], total_length);
    }

    pub fn set_source(&mut self, raw: &mut [u8], source: u32) {
        self.source = source;
        BigEndian::write_u32(&mut raw[12..16], source);
    }

    pub fn set_destination(&mut self, raw: &mut [u8], destination: u32) {
        self.destination = destination;
        BigEndian::write_u32(&mut raw[16..20], destination);
    }

    pub fn compute_checksum(&mut self, raw: &mut [u8]) {
        // reset checksum field
        self.set_checksum(raw, 0);

        let j = self.header_length as usize / 2;
        let mut sum = (0..j).map(|i| {
            let range = 2*i..2*(i+1);
            BigEndian::read_u16(&raw[range]) as u32
        }).sum::<u32>();
        while (sum & !0xffff) != 0 {
            sum = (sum & 0xffff) + (sum >> 16);
        }

        self.set_checksum(raw, !sum as u16);
    }

    fn get_checksum(&mut self, raw: &[u8]) -> u16 {
        BigEndian::read_u16(&raw[10..12])
    }

    fn set_checksum(&mut self, raw: &mut [u8], checksum: u16) {
        BigEndian::write_u16(&mut raw[10..12], checksum);
    }

    fn read_version(raw: &[u8]) -> Option<u8> {
        if raw.is_empty() {
            None
        } else {
            // version is stored in the 4 first bits
            Some(raw[0] >> 4)
        }
    }

    fn read_length(raw: &[u8]) -> Option<u16> {
        if raw.len() < 4 {
            None
        } else {
            // packet length is 16 bits starting at offset 2
            let length = BigEndian::read_u16(&raw[2..4]);
            Some(length)
        }
    }
}

impl SourceDestination<u32> for IPv4Header {
    fn get_source(&self, _: &[u8]) -> u32 {
        self.source
    }

    fn get_destination(&self, _: &[u8]) -> u32 {
        self.destination
    }

    fn set_source(&mut self, raw: &mut [u8], source: u32) {
        (self as &mut IPv4Header).set_source(raw, source);
    }

    fn set_destination(&mut self, raw: &mut [u8], destination: u32) {
        (self as &mut IPv4Header).set_destination(raw, destination);
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::io::Cursor;
    use byteorder::{BigEndian, WriteBytesExt};

    fn create_header() -> Vec<u8> {
        let mut raw: Vec<u8> = vec![];
        raw.reserve(20);
        raw.write_u8(4u8 << 4 | 5).unwrap(); // version_and_ihl
        raw.write_u8(0).unwrap(); //ToS
        raw.write_u16::<BigEndian>(28).unwrap(); // total length
        raw.write_u32::<BigEndian>(0).unwrap(); // id_flags_fragment_offset
        raw.write_u8(0).unwrap(); // TTL
        raw.write_u8(17).unwrap(); // protocol (UDP)
        raw.write_u16::<BigEndian>(0).unwrap(); // checksum
        raw.write_u32::<BigEndian>(0x12345678).unwrap(); // source address
        raw.write_u32::<BigEndian>(0x42424242).unwrap(); // destination address
        raw
    }

    #[test]
    fn parse_header() {
        let raw = &create_header()[..];
        let data = IPv4Header::parse(raw);
        assert_eq!(4, data.version);
        assert_eq!(20, data.header_length);
        assert_eq!(28, data.total_length);
        assert_eq!(Protocol::UDP, data.protocol);
        assert_eq!(0x12345678, data.source);
        assert_eq!(0x42424242, data.destination);
    }

    #[test]
    fn edit_header() {
        let raw = &mut create_header()[..];
        let mut header = IPv4Header::parse(raw);

        header.set_source(raw, 0x87654321);
        header.set_destination(raw, 0x24242424);
        header.set_total_length(raw, 42);
        assert_eq!(0x87654321, header.source);
        assert_eq!(0x24242424, header.destination);
        assert_eq!(42, header.total_length);

        // assert that the buffer has been modified
        let raw_source = BigEndian::read_u32(&raw[12..16]);
        let raw_destination = BigEndian::read_u32(&raw[16..20]);
        let raw_total_length = BigEndian::read_u16(&raw[2..4]);
        assert_eq!(0x87654321, raw_source);
        assert_eq!(0x24242424, raw_destination);
        assert_eq!(42, raw_total_length);

        header.switch_source_and_destination(raw);

        assert_eq!(0x24242424, header.source);
        assert_eq!(0x87654321, header.destination);

        let raw_source = BigEndian::read_u32(&raw[12..16]);
        let raw_destination = BigEndian::read_u32(&raw[16..20]);
        assert_eq!(0x24242424, raw_source);
        assert_eq!(0x87654321, raw_destination);
    }

    #[test]
    fn compute_checksum() {
        let mut raw = &mut create_header()[..];
        let mut header = IPv4Header::parse(raw);

        // set a fake checksum value to assert that it is correctly computed
        header.set_checksum(raw, 0x79);

        header.compute_checksum(raw);

        let mut sum = 0x4500u32 + 0x001Cu32 + 0x0000u32 + 0x0000u32 + 0x0011u32
                    + 0x0000u32 + 0x1234u32 + 0x5678u32 + 0x4242u32 + 0x4242u32;
        while (sum & !0xffff) != 0 {
            sum = (sum & 0xffff) + (sum >> 16);
        }
        let sum = !sum as u16;
        assert_eq!(sum, header.get_checksum(raw));
    }

    #[test]
    fn read_ip_version_unavailable() {
        let empty_slice = &[][0..0];
        let version = IPv4Header::read_version(empty_slice);
        assert!(version.is_none());
    }

    #[test]
    fn read_ip_version_available() {
        let version_and_ihl: u8 = (4 << 4) | 5;
        let raw = [ version_and_ihl, 0, 0, 0, 0, 0, 0, 0 ];
        let version = IPv4Header::read_version(&raw);
        assert_eq!(4, version.unwrap());
    }
}
<|endoftext|>


<commit_before><commit_msg>improved tests<commit_after><|endoftext|>


<commit_before><commit_msg>Refactored unify to avoid as many clones as possible<commit_after><|endoftext|>


<commit_before>// chameneos

import io::reader_util;

use std;
import std::map;
import std::map::hashmap;
import std::sort;

fn print_complements() {
    let all = ~[Blue, Red, Yellow];
    for vec::each(all) |aa| {
        for vec::each(all) |bb| {
            io::println(show_color(aa) + " + " + show_color(bb) +
                " -> " + show_color(transform(aa,bb)));
        }
    }
}

enum color { Red, Yellow, Blue }

type creature_info = { name: uint, color: color };

fn show_color(cc: color) -> str {
    alt (cc) {
        Red    {"red"}
        Yellow {"yellow"}
        Blue   {"blue"}
    }
}

fn show_color_list(set: ~[color]) -> str {
    let mut out = "";
    for vec::eachi(set) |_ii, col| {
        out += " ";
        out += show_color(col);
    }
    ret out;
}

fn show_digit(nn: uint) -> str {
    alt (nn) {
        0 {"zero"}
        1 {"one"}
        2 {"two"}
        3 {"three"}
        4 {"four"}
        5 {"five"}
        6 {"six"}
        7 {"seven"}
        8 {"eight"}
        9 {"nine"}
        _ {fail "expected digits from 0 to 9..."}
    }
}

fn show_number(nn: uint) -> str {
    let mut out = "";
    let mut num = nn;
    let mut dig;

    if num == 0 { out = show_digit(0) };

    while num != 0 {
        dig = num % 10;
        num = num / 10;
        out = show_digit(dig) + " " + out;
    }

    ret out;
}

fn transform(aa: color, bb: color) -> color {
    alt (aa, bb) {
        (Red,    Red   ) { Red    }
        (Red,    Yellow) { Blue   }
        (Red,    Blue  ) { Yellow }
        (Yellow, Red   ) { Blue   }
        (Yellow, Yellow) { Yellow }
        (Yellow, Blue  ) { Red    }
        (Blue,   Red   ) { Yellow }
        (Blue,   Yellow) { Red    }
        (Blue,   Blue  ) { Blue   }
    }
}

fn creature(
    name: uint,
    color: color,
    from_rendezvous: comm::port<option<creature_info>>,
    to_rendezvous: comm::chan<creature_info>,
    to_rendezvous_log: comm::chan<str>
) {
    let mut color = color;
    let mut creatures_met = 0;
    let mut evil_clones_met = 0;

    loop {
        // ask for a pairing
        comm::send(to_rendezvous, {name: name, color: color});
        let resp = comm::recv(from_rendezvous);

        // log and change, or print and quit
        alt resp {
            option::some(other_creature) {
                color = transform(color, other_creature.color);

                // track some statistics
                creatures_met += 1;
                if other_creature.name == name {
                   evil_clones_met += 1;
                }
            }
            option::none {
                // log creatures met and evil clones of self
                let report = #fmt("%u", creatures_met) + " " +
                             show_number(evil_clones_met);
                comm::send(to_rendezvous_log, report);
                break;
            }
        }
    }
}

fn rendezvous(nn: uint, set: ~[color]) {
    // these ports will allow us to hear from the creatures
    let from_creatures:     comm::port<creature_info> = comm::port();
    let from_creatures_log: comm::port<str> = comm::port();

    // these channels will be passed to the creatures so they can talk to us
    let to_rendezvous     = comm::chan(from_creatures);
    let to_rendezvous_log = comm::chan(from_creatures_log);

    // these channels will allow us to talk to each creature by 'name'/index
    let to_creature: ~[comm::chan<option<creature_info>>] =
        vec::mapi(set,
            fn@(ii: uint, col: color) -> comm::chan<option<creature_info>> {
                // create each creature as a listener with a port, and
                // give us a channel to talk to each
                ret do task::spawn_listener |from_rendezvous| {
                    creature(ii, col, from_rendezvous, to_rendezvous,
                             to_rendezvous_log);
                };
            }
        );

    let mut creatures_met = 0;

    // set up meetings...
    for nn.times {
        let fst_creature: creature_info = comm::recv(from_creatures);
        let snd_creature: creature_info = comm::recv(from_creatures);

        creatures_met += 2;

        comm::send(to_creature[fst_creature.name], some(snd_creature));
        comm::send(to_creature[snd_creature.name], some(fst_creature));
    }

    // tell each creature to stop
    for vec::eachi(to_creature) |_ii, to_one| {
        comm::send(to_one, none);
    }

    // save each creature's meeting stats
    let mut report = ~[];
    for vec::each(to_creature) |_to_one| {
        vec::push(report, comm::recv(from_creatures_log));
    }

    // print each color in the set
    io::println(show_color_list(set));

    // print each creature's stats
    for vec::each(report) |rep| {
        io::println(rep);
    }

    // print the total number of creatures met
    io::println(show_number(creatures_met));
}

fn main(args: ~[str]) {
    let args = if os::getenv("RUST_BENCH").is_some() || args.len() <= 1u {
        ~["", "600"]
    } else {
        args
    };

    let nn = uint::from_str(args[1]).get();

    print_complements();
    io::println("");

    rendezvous(nn, ~[Blue, Red, Yellow]);
    io::println("");

    rendezvous(nn,
        ~[Blue, Red, Yellow, Red, Yellow, Blue, Red, Yellow, Red, Blue]);
}

<commit_msg>Increase the difficulty of chameneos-redux under 'make perf'<commit_after>// chameneos

import io::reader_util;

use std;
import std::map;
import std::map::hashmap;
import std::sort;

fn print_complements() {
    let all = ~[Blue, Red, Yellow];
    for vec::each(all) |aa| {
        for vec::each(all) |bb| {
            io::println(show_color(aa) + " + " + show_color(bb) +
                " -> " + show_color(transform(aa,bb)));
        }
    }
}

enum color { Red, Yellow, Blue }

type creature_info = { name: uint, color: color };

fn show_color(cc: color) -> str {
    alt (cc) {
        Red    {"red"}
        Yellow {"yellow"}
        Blue   {"blue"}
    }
}

fn show_color_list(set: ~[color]) -> str {
    let mut out = "";
    for vec::eachi(set) |_ii, col| {
        out += " ";
        out += show_color(col);
    }
    ret out;
}

fn show_digit(nn: uint) -> str {
    alt (nn) {
        0 {"zero"}
        1 {"one"}
        2 {"two"}
        3 {"three"}
        4 {"four"}
        5 {"five"}
        6 {"six"}
        7 {"seven"}
        8 {"eight"}
        9 {"nine"}
        _ {fail "expected digits from 0 to 9..."}
    }
}

fn show_number(nn: uint) -> str {
    let mut out = "";
    let mut num = nn;
    let mut dig;

    if num == 0 { out = show_digit(0) };

    while num != 0 {
        dig = num % 10;
        num = num / 10;
        out = show_digit(dig) + " " + out;
    }

    ret out;
}

fn transform(aa: color, bb: color) -> color {
    alt (aa, bb) {
        (Red,    Red   ) { Red    }
        (Red,    Yellow) { Blue   }
        (Red,    Blue  ) { Yellow }
        (Yellow, Red   ) { Blue   }
        (Yellow, Yellow) { Yellow }
        (Yellow, Blue  ) { Red    }
        (Blue,   Red   ) { Yellow }
        (Blue,   Yellow) { Red    }
        (Blue,   Blue  ) { Blue   }
    }
}

fn creature(
    name: uint,
    color: color,
    from_rendezvous: comm::port<option<creature_info>>,
    to_rendezvous: comm::chan<creature_info>,
    to_rendezvous_log: comm::chan<str>
) {
    let mut color = color;
    let mut creatures_met = 0;
    let mut evil_clones_met = 0;

    loop {
        // ask for a pairing
        comm::send(to_rendezvous, {name: name, color: color});
        let resp = comm::recv(from_rendezvous);

        // log and change, or print and quit
        alt resp {
            option::some(other_creature) {
                color = transform(color, other_creature.color);

                // track some statistics
                creatures_met += 1;
                if other_creature.name == name {
                   evil_clones_met += 1;
                }
            }
            option::none {
                // log creatures met and evil clones of self
                let report = #fmt("%u", creatures_met) + " " +
                             show_number(evil_clones_met);
                comm::send(to_rendezvous_log, report);
                break;
            }
        }
    }
}

fn rendezvous(nn: uint, set: ~[color]) {
    // these ports will allow us to hear from the creatures
    let from_creatures:     comm::port<creature_info> = comm::port();
    let from_creatures_log: comm::port<str> = comm::port();

    // these channels will be passed to the creatures so they can talk to us
    let to_rendezvous     = comm::chan(from_creatures);
    let to_rendezvous_log = comm::chan(from_creatures_log);

    // these channels will allow us to talk to each creature by 'name'/index
    let to_creature: ~[comm::chan<option<creature_info>>] =
        vec::mapi(set,
            fn@(ii: uint, col: color) -> comm::chan<option<creature_info>> {
                // create each creature as a listener with a port, and
                // give us a channel to talk to each
                ret do task::spawn_listener |from_rendezvous| {
                    creature(ii, col, from_rendezvous, to_rendezvous,
                             to_rendezvous_log);
                };
            }
        );

    let mut creatures_met = 0;

    // set up meetings...
    for nn.times {
        let fst_creature: creature_info = comm::recv(from_creatures);
        let snd_creature: creature_info = comm::recv(from_creatures);

        creatures_met += 2;

        comm::send(to_creature[fst_creature.name], some(snd_creature));
        comm::send(to_creature[snd_creature.name], some(fst_creature));
    }

    // tell each creature to stop
    for vec::eachi(to_creature) |_ii, to_one| {
        comm::send(to_one, none);
    }

    // save each creature's meeting stats
    let mut report = ~[];
    for vec::each(to_creature) |_to_one| {
        vec::push(report, comm::recv(from_creatures_log));
    }

    // print each color in the set
    io::println(show_color_list(set));

    // print each creature's stats
    for vec::each(report) |rep| {
        io::println(rep);
    }

    // print the total number of creatures met
    io::println(show_number(creatures_met));
}

fn main(args: ~[str]) {
    let args = if os::getenv("RUST_BENCH").is_some() {
        ~["", "200000"]
    } else if args.len() <= 1u {
        ~["", "600"]
    } else {
        args
    };

    let nn = uint::from_str(args[1]).get();

    print_complements();
    io::println("");

    rendezvous(nn, ~[Blue, Red, Yellow]);
    io::println("");

    rendezvous(nn,
        ~[Blue, Red, Yellow, Red, Yellow, Blue, Red, Yellow, Red, Blue]);
}

<|endoftext|>


<commit_before>/*!

This crate provides the default theme resources of OrbTks default theme dark and light.
It provides fonts, icons and colors.

 */

use orbtk_theming::{config::ThemeConfig, prelude::*};

/// provides `constants` to reference colors.
pub mod colors;
/// provides `constants` associated to fonts.
pub mod fonts;
pub mod prelude;
/// provides information processed by the `graphic render` (e.g. glyphs, icons).
pub mod vector_graphics;

/// The dark theme resource file.
pub const DARK_THEME_RON: &str = include_str!("../assets/dark/dark.ron");

/// The default theme colors resource file.
pub const COLORS_RON: &str = include_str!("../assets/common/colors.ron");

/// The common fonts resource file.
pub const FONTS_RON: &str = include_str!("../assets/common/fonts.ron");

/// The light theme resource file.
pub const LIGHT_THEME_RON: &str = include_str!("../assets/light/light.ron");

/// The redox theme colors resource file.
pub const REDOX_COLORS_RON: &str = include_str!("../assets/redox/colors.ron");

/// The redox theme resource file.
pub const REDOX_THEME_RON: &str = include_str!("../assets/redox/redox.ron");

#[cfg(all(not(feature = "light"), not(feature = "redox")))]
pub fn default_theme() -> Theme {
    dark_theme()
}

#[cfg(feature = "light")]
pub fn default_theme() -> Theme {
    light_theme()
}

#[cfg(any(feature = "redox", target_os = "redox"))]
pub fn default_theme() -> Theme {
    redox_theme()
}

/// Creates OrbTks default dark theme.
pub fn dark_theme() -> Theme {
    Theme::from_config(
        ThemeConfig::from(DARK_THEME_RON)
            .extend(ThemeConfig::from(COLORS_RON))
            .extend(ThemeConfig::from(FONTS_RON)),
    )
}

/// Creates OrbTks default light theme.
pub fn light_theme() -> Theme {
    Theme::from_config(
        ThemeConfig::from(LIGHT_THEME_RON)
            .extend(ThemeConfig::from(COLORS_RON))
            .extend(ThemeConfig::from(FONTS_RON)),
    )
}

/// Creates OrbTks redox theme.
pub fn redox_theme() -> Theme {
    Theme::from_config(
        ThemeConfig::from(REDOX_THEME_RON)
            .extend(ThemeConfig::from(COLORS_RON))
            .extend(ThemeConfig::from(REDOX_COLORS_RON))
            .extend(ThemeConfig::from(FONTS_RON)),
    )
}
<commit_msg>redox ci fix<commit_after>/*!

This crate provides the default theme resources of OrbTks default theme dark and light.
It provides fonts, icons and colors.

 */

use orbtk_theming::{config::ThemeConfig, prelude::*};

/// provides `constants` to reference colors.
pub mod colors;
/// provides `constants` associated to fonts.
pub mod fonts;
pub mod prelude;
/// provides information processed by the `graphic render` (e.g. glyphs, icons).
pub mod vector_graphics;

/// The dark theme resource file.
pub const DARK_THEME_RON: &str = include_str!("../assets/dark/dark.ron");

/// The default theme colors resource file.
pub const COLORS_RON: &str = include_str!("../assets/common/colors.ron");

/// The common fonts resource file.
pub const FONTS_RON: &str = include_str!("../assets/common/fonts.ron");

/// The light theme resource file.
pub const LIGHT_THEME_RON: &str = include_str!("../assets/light/light.ron");

/// The redox theme colors resource file.
pub const REDOX_COLORS_RON: &str = include_str!("../assets/redox/colors.ron");

/// The redox theme resource file.
pub const REDOX_THEME_RON: &str = include_str!("../assets/redox/redox.ron");

#[cfg(all(
    not(feature = "light"),
    not(feature = "redox"),
    not(target_os = "redox")
))]
pub fn default_theme() -> Theme {
    dark_theme()
}

#[cfg(feature = "light")]
pub fn default_theme() -> Theme {
    light_theme()
}

#[cfg(any(feature = "redox", target_os = "redox"))]
pub fn default_theme() -> Theme {
    redox_theme()
}

/// Creates OrbTks default dark theme.
pub fn dark_theme() -> Theme {
    Theme::from_config(
        ThemeConfig::from(DARK_THEME_RON)
            .extend(ThemeConfig::from(COLORS_RON))
            .extend(ThemeConfig::from(FONTS_RON)),
    )
}

/// Creates OrbTks default light theme.
pub fn light_theme() -> Theme {
    Theme::from_config(
        ThemeConfig::from(LIGHT_THEME_RON)
            .extend(ThemeConfig::from(COLORS_RON))
            .extend(ThemeConfig::from(FONTS_RON)),
    )
}

/// Creates OrbTks redox theme.
pub fn redox_theme() -> Theme {
    Theme::from_config(
        ThemeConfig::from(REDOX_THEME_RON)
            .extend(ThemeConfig::from(COLORS_RON))
            .extend(ThemeConfig::from(REDOX_COLORS_RON))
            .extend(ThemeConfig::from(FONTS_RON)),
    )
}
<|endoftext|>


<commit_before>use if_chain::if_chain;
use rustc_hir::{Local, PatKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::lint::in_external_macro;
use rustc_middle::ty::subst::GenericArgKind;
use rustc_session::{declare_lint_pass, declare_tool_lint};

use crate::utils::{implements_trait, is_must_use_func_call, is_must_use_ty, match_type, paths, span_lint_and_help};

declare_clippy_lint! {
    /// **What it does:** Checks for `let _ = <expr>`
    /// where expr is #[must_use]
    ///
    /// **Why is this bad?** It's better to explicitly
    /// handle the value of a #[must_use] expr
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust
    /// fn f() -> Result<u32, u32> {
    ///     Ok(0)
    /// }
    ///
    /// let _ = f();
    /// // is_ok() is marked #[must_use]
    /// let _ = f().is_ok();
    /// ```
    pub LET_UNDERSCORE_MUST_USE,
    restriction,
    "non-binding let on a `#[must_use]` expression"
}

declare_clippy_lint! {
    /// **What it does:** Checks for `let _ = sync_lock`
    ///
    /// **Why is this bad?** This statement immediately drops the lock instead of
    /// extending its lifetime to the end of the scope, which is often not intended.
    /// To extend lock lifetime to the end of the scope, use an underscore-prefixed
    /// name instead (i.e. _lock). If you want to explicitly drop the lock,
    /// `std::mem::drop` conveys your intention better and is less error-prone.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    ///
    /// Bad:
    /// ```rust,ignore
    /// let _ = mutex.lock();
    /// ```
    ///
    /// Good:
    /// ```rust,ignore
    /// let _lock = mutex.lock();
    /// ```
    pub LET_UNDERSCORE_LOCK,
    correctness,
    "non-binding let on a synchronization lock"
}

declare_clippy_lint! {
    /// **What it does:** Checks for `let _ = <expr>`
    /// where expr has a type that implements `Drop`
    ///
    /// **Why is this bad?** This statement immediately drops the initializer
    /// expression instead of extending its lifetime to the end of the scope, which
    /// is often not intended. To extend the expression's lifetime to the end of the
    /// scope, use an underscore-prefixed name instead (i.e. _var). If you want to
    /// explicitly drop the expression, `std::mem::drop` conveys your intention
    /// better and is less error-prone.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    ///
    /// Bad:
    /// ```rust,ignore
    /// struct Droppable;
    /// impl Drop for Droppable {
    ///     fn drop(&mut self) {}
    /// }
    /// let _ = Droppable;
    /// ```
    ///
    /// Good:
    /// ```rust,ignore
    /// let _droppable = Droppable;
    /// ```
    pub LET_UNDERSCORE_DROP,
    correctness,
    "non-binding let on a type that implements `Drop`"
}

declare_lint_pass!(LetUnderscore => [LET_UNDERSCORE_MUST_USE, LET_UNDERSCORE_LOCK, LET_UNDERSCORE_DROP]);

const SYNC_GUARD_PATHS: [&[&str]; 3] = [
    &paths::MUTEX_GUARD,
    &paths::RWLOCK_READ_GUARD,
    &paths::RWLOCK_WRITE_GUARD,
];

impl<'tcx> LateLintPass<'tcx> for LetUnderscore {
    fn check_local(&mut self, cx: &LateContext<'_>, local: &Local<'_>) {
        if in_external_macro(cx.tcx.sess, local.span) {
            return;
        }

        if_chain! {
            if let PatKind::Wild = local.pat.kind;
            if let Some(ref init) = local.init;
            then {
                let init_ty = cx.typeck_results().expr_ty(init);
                let contains_sync_guard = init_ty.walk().any(|inner| match inner.unpack() {
                    GenericArgKind::Type(inner_ty) => {
                        SYNC_GUARD_PATHS.iter().any(|path| match_type(cx, inner_ty, path))
                    },

                    GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false,
                });
                let implements_drop = cx.tcx.lang_items().drop_trait().map_or(false, |drop_trait|
                    init_ty.walk().any(|inner| match inner.unpack() {
                        GenericArgKind::Type(inner_ty) => {
                            implements_trait(cx, inner_ty, drop_trait, &[])
                        },

                        GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false,
                    })
                );
                if contains_sync_guard {
                    span_lint_and_help(
                        cx,
                        LET_UNDERSCORE_LOCK,
                        local.span,
                        "non-binding let on a synchronization lock",
                        None,
                        "consider using an underscore-prefixed named \
                            binding or dropping explicitly with `std::mem::drop`"
                    )
                } else if implements_drop {
                    span_lint_and_help(
                        cx,
                        LET_UNDERSCORE_DROP,
                        local.span,
                        "non-binding let on a type that implements `Drop`",
                        None,
                        "consider using an underscore-prefixed named \
                            binding or dropping explicitly with `std::mem::drop`"
                    )
                } else if is_must_use_ty(cx, cx.typeck_results().expr_ty(init)) {
                    span_lint_and_help(
                        cx,
                        LET_UNDERSCORE_MUST_USE,
                        local.span,
                        "non-binding let on an expression with `#[must_use]` type",
                        None,
                        "consider explicitly using expression value"
                    )
                } else if is_must_use_func_call(cx, init) {
                    span_lint_and_help(
                        cx,
                        LET_UNDERSCORE_MUST_USE,
                        local.span,
                        "non-binding let on a result of a `#[must_use]` function",
                        None,
                        "consider explicitly using function result"
                    )
                }
            }
        }
    }
}
<commit_msg>Update clippy_lints/src/let_underscore.rs<commit_after>use if_chain::if_chain;
use rustc_hir::{Local, PatKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::lint::in_external_macro;
use rustc_middle::ty::subst::GenericArgKind;
use rustc_session::{declare_lint_pass, declare_tool_lint};

use crate::utils::{implements_trait, is_must_use_func_call, is_must_use_ty, match_type, paths, span_lint_and_help};

declare_clippy_lint! {
    /// **What it does:** Checks for `let _ = <expr>`
    /// where expr is #[must_use]
    ///
    /// **Why is this bad?** It's better to explicitly
    /// handle the value of a #[must_use] expr
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust
    /// fn f() -> Result<u32, u32> {
    ///     Ok(0)
    /// }
    ///
    /// let _ = f();
    /// // is_ok() is marked #[must_use]
    /// let _ = f().is_ok();
    /// ```
    pub LET_UNDERSCORE_MUST_USE,
    restriction,
    "non-binding let on a `#[must_use]` expression"
}

declare_clippy_lint! {
    /// **What it does:** Checks for `let _ = sync_lock`
    ///
    /// **Why is this bad?** This statement immediately drops the lock instead of
    /// extending its lifetime to the end of the scope, which is often not intended.
    /// To extend lock lifetime to the end of the scope, use an underscore-prefixed
    /// name instead (i.e. _lock). If you want to explicitly drop the lock,
    /// `std::mem::drop` conveys your intention better and is less error-prone.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    ///
    /// Bad:
    /// ```rust,ignore
    /// let _ = mutex.lock();
    /// ```
    ///
    /// Good:
    /// ```rust,ignore
    /// let _lock = mutex.lock();
    /// ```
    pub LET_UNDERSCORE_LOCK,
    correctness,
    "non-binding let on a synchronization lock"
}

declare_clippy_lint! {
    /// **What it does:** Checks for `let _ = <expr>`
    /// where expr has a type that implements `Drop`
    ///
    /// **Why is this bad?** This statement immediately drops the initializer
    /// expression instead of extending its lifetime to the end of the scope, which
    /// is often not intended. To extend the expression's lifetime to the end of the
    /// scope, use an underscore-prefixed name instead (i.e. _var). If you want to
    /// explicitly drop the expression, `std::mem::drop` conveys your intention
    /// better and is less error-prone.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    ///
    /// Bad:
    /// ```rust,ignore
    /// struct Droppable;
    /// impl Drop for Droppable {
    ///     fn drop(&mut self) {}
    /// }
    /// let _ = Droppable;
    /// ```
    ///
    /// Good:
    /// ```rust,ignore
    /// let _droppable = Droppable;
    /// ```
    pub LET_UNDERSCORE_DROP,
    correctness,
    "non-binding let on a type that implements `Drop`"
}

declare_lint_pass!(LetUnderscore => [LET_UNDERSCORE_MUST_USE, LET_UNDERSCORE_LOCK, LET_UNDERSCORE_DROP]);

const SYNC_GUARD_PATHS: [&[&str]; 3] = [
    &paths::MUTEX_GUARD,
    &paths::RWLOCK_READ_GUARD,
    &paths::RWLOCK_WRITE_GUARD,
];

impl<'tcx> LateLintPass<'tcx> for LetUnderscore {
    fn check_local(&mut self, cx: &LateContext<'_>, local: &Local<'_>) {
        if in_external_macro(cx.tcx.sess, local.span) {
            return;
        }

        if_chain! {
            if let PatKind::Wild = local.pat.kind;
            if let Some(ref init) = local.init;
            then {
                let init_ty = cx.typeck_results().expr_ty(init);
                let contains_sync_guard = init_ty.walk().any(|inner| match inner.unpack() {
                    GenericArgKind::Type(inner_ty) => {
                        SYNC_GUARD_PATHS.iter().any(|path| match_type(cx, inner_ty, path))
                    },

                    GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false,
                });
                let implements_drop = cx.tcx.lang_items().drop_trait().map_or(false, |drop_trait|
                    init_ty.walk().any(|inner| match inner.unpack() {
                        GenericArgKind::Type(inner_ty) => {
                            implements_trait(cx, inner_ty, drop_trait, &[])
                        },

                        GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false,
                    })
                );
                if contains_sync_guard {
                    span_lint_and_help(
                        cx,
                        LET_UNDERSCORE_LOCK,
                        local.span,
                        "non-binding let on a synchronization lock",
                        None,
                        "consider using an underscore-prefixed named \
                            binding or dropping explicitly with `std::mem::drop`"
                    )
                } else if implements_drop {
                    span_lint_and_help(
                        cx,
                        LET_UNDERSCORE_DROP,
                        local.span,
                        "non-binding `let` on a type that implements `Drop`",
                        None,
                        "consider using an underscore-prefixed named \
                            binding or dropping explicitly with `std::mem::drop`"
                    )
                } else if is_must_use_ty(cx, cx.typeck_results().expr_ty(init)) {
                    span_lint_and_help(
                        cx,
                        LET_UNDERSCORE_MUST_USE,
                        local.span,
                        "non-binding let on an expression with `#[must_use]` type",
                        None,
                        "consider explicitly using expression value"
                    )
                } else if is_must_use_func_call(cx, init) {
                    span_lint_and_help(
                        cx,
                        LET_UNDERSCORE_MUST_USE,
                        local.span,
                        "non-binding let on a result of a `#[must_use]` function",
                        None,
                        "consider explicitly using function result"
                    )
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>// Copyright:: Copyright (c) 2015-2016 The Habitat Maintainers
//
// The terms of the Evaluation Agreement (Habitat) between Chef Software Inc.
// and the party accessing this file ("Licensee") apply to Licensee's use of
// the Software until such time that the Software is made available under an
// open source license such as the Apache 2.0 License.

extern crate habitat_core as hcore;
extern crate habitat_depot_core as depot_core;
#[macro_use]
extern crate hyper;
#[macro_use]
extern crate log;
extern crate rustc_serialize;

pub mod error;

pub use error::{Error, Result};

use std::fs::{self, File};
use std::io::{Read, Write, BufWriter, Seek, SeekFrom};
use std::path::{Path, PathBuf};

use hcore::package::{PackageArchive, PackageIdent};
use depot_core::{XFileName, data_object};
use hyper::client::{Client, Body};
use hyper::status::StatusCode;
use hyper::Url;
use rustc_serialize::json;

/// Download a public key from a remote Depot to the given filepath.
///
/// # Failures
///
/// * Key cannot be found
/// * Remote Depot is not available
/// * File cannot be created and written to
pub fn fetch_key(depot: &str, key: &str, path: &str) -> Result<String> {
    let url = Url::parse(&format!("{}/keys/{}", depot, key)).unwrap();
    download(key, url, path)
}

/// Download the latest release of a package.
///
/// An optional version and release can be specified
/// which, when provided, will increase specificity of the release retrieved. Specifying a version
/// and no release will retrieve the latest release of a given version. Specifying both a version
/// and a release will retrieve that exact package.
///
/// # Failures
///
/// * Package cannot be found
/// * Remote Depot is not available
/// * File cannot be created and written to
pub fn fetch_package(depot: &str, package: &PackageIdent, store: &str) -> Result<PackageArchive> {
    let url = Url::parse(&format!("{}/pkgs/{}/download", depot, package)).unwrap();
    match download(&package.name, url, store) {
        Ok(file) => {
            let path = PathBuf::from(file);
            Ok(PackageArchive::new(path))
        }
        Err(Error::HTTP(StatusCode::NotFound)) => {
            Err(Error::RemotePackageNotFound(package.clone()))
        }
        Err(e) => Err(e),
    }
}

/// Returns a package struct for the latest package.
///
/// An optional version can be specified which will scope the release returned to the latest
/// release of that package.
///
/// # Failures
///
/// * Package cannot be found
/// * Remote Depot is not available
pub fn show_package(depot: &str, ident: &PackageIdent) -> Result<data_object::Package> {
    let url = url_show_package(depot, ident);
    let client = Client::new();
    let request = client.get(&url);
    let mut res = try!(request.send());

    if res.status != hyper::status::StatusCode::Ok {
        return Err(Error::RemotePackageNotFound(ident.clone()));
    }

    let mut encoded = String::new();
    try!(res.read_to_string(&mut encoded));
    debug!("Body: {:?}", encoded);
    let package: data_object::Package = json::decode(&encoded).unwrap();
    Ok(package)
}

/// Upload a public key to a remote Depot.
///
/// # Failures
///
/// * Remote Depot is not available
/// * File cannot be read
pub fn put_key(depot: &str, path: &Path) -> Result<()> {
    let mut file = try!(File::open(path));
    let file_name = try!(path.file_name().ok_or(Error::NoFilePart));
    let url = Url::parse(&format!("{}/keys/{}", depot, file_name.to_string_lossy())).unwrap();
    upload(url, &mut file)
}

/// Upload a package to a remote Depot.
///
/// # Failures
///
/// * Remote Depot is not available
/// * File cannot be read
pub fn put_package(depot: &str, pa: &mut PackageArchive) -> Result<()> {
    let checksum = try!(pa.checksum());
    let ident = try!(pa.ident());
    let url = Url::parse(&format!("{}/pkgs/{}?checksum={}", depot, ident, checksum)).unwrap();
    let mut file = try!(File::open(&pa.path));
    upload(url, &mut file)
}

fn url_show_package(depot: &str, package: &PackageIdent) -> String {
    if package.fully_qualified() {
        format!("{}/pkgs/{}", depot, package)
    } else {
        format!("{}/pkgs/{}/latest", depot, package)
    }
}

fn download(status: &str, url: Url, path: &str) -> Result<String> {
    debug!("Making request to url {}", url);
    let client = Client::new();
    let mut res = try!(client.get(url).send());
    debug!("Response: {:?}", res);

    if res.status != hyper::status::StatusCode::Ok {
        return Err(Error::HTTP(res.status));
    }

    let file_name = match res.headers.get::<XFileName>() {
        Some(filename) => format!("{}", filename),
        None => return Err(Error::NoXFilename),
    };
    let length = res.headers
                    .get::<hyper::header::ContentLength>()
                    .map_or("Unknown".to_string(), |v| format!("{}", v));
    // Here is a moment where you can really like Rust. We create
    // a file, wrap it in a BufWriter - which understands how to
    // safely batch writes into large buffer sizes on the heap,
    // saving us the tax of frequent system calls. We then do
    // what we would do in C - create a buffer of bytes, then
    // read into that buffer, and write out to the other side.
    //
    // Under the hood, Hyper uses the "BufReader" to implement
    // reading the request body - so on both ends, we are getting
    // free buffering on the heap, using our stack buffer just to
    // shuttle back and forth. This is essentially with the "tee"
    // function does in the stdlib, but with error handling that
    // a generic "tee" can't really provide easily.
    //
    // What you can't see is this - the compiler helped with
    // making sure all the edge cases of the pattern were covered,
    // and even though its a trivial case, it was pretty great.
    let tempfile = format!("{}/{}.tmp", path, file_name);
    let finalfile = format!("{}/{}", path, file_name);
    let f = try!(File::create(&tempfile));
    let mut writer = BufWriter::new(&f);
    let mut written: i64 = 0;
    let mut buf = [0u8; 100000]; // Our byte buffer
    loop {
        let len = try!(res.read(&mut buf)); // Raise IO errors
        match len {
            0 => {
                // 0 == EOF, so stop writing and finish progress
                progress(status, written, &length, true);
                break;
            }
            _ => {
                // Write the buffer to the BufWriter on the Heap
                let bytes_written = try!(writer.write(&buf[0..len]));
                if bytes_written == 0 {
                    return Err(Error::WriteSyncFailed);
                }
                written = written + (bytes_written as i64);
                progress(status, written, &length, false);
            }
        };
    }
    try!(fs::rename(&tempfile, &finalfile));
    Ok(finalfile)
}

fn upload(url: Url, file: &mut File) -> Result<()> {
    debug!("Uploading to {}", url);
    try!(file.seek(SeekFrom::Start(0)));
    let client = Client::new();
    let metadata = try!(file.metadata());
    let response = try!(client.post(url).body(Body::SizedBody(file, metadata.len())).send());
    if response.status.is_success() {
        Ok(())
    } else {
        debug!("Response {:?}", response);
        Err(Error::HTTP(response.status))
    }
}

fn progress(status: &str, written: i64, length: &str, finished: bool) {
    let progress = format!("{} {}/{}", status, written, length);
    print!("{}", from_char(progress.len(), '\x08'));
    if finished {
        println!("{}", progress);
    } else {
        print!("{}", progress);
    }
}

fn from_char(length: usize, ch: char) -> String {
    if length == 0 {
        return String::new();
    }

    let mut buf = String::new();
    buf.push(ch);
    let size = buf.len() * length;
    buf.reserve(size);
    for _ in 1..length {
        buf.push(ch)
    }
    buf
}

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

    #[test]
    fn from_char_returns_the_correct_string() {
        assert_eq!("xxxx", from_char(4, 'x'));
    }
}
<commit_msg>[depot-client] URL-encode checksum query param on upload.<commit_after>// Copyright:: Copyright (c) 2015-2016 The Habitat Maintainers
//
// The terms of the Evaluation Agreement (Habitat) between Chef Software Inc.
// and the party accessing this file ("Licensee") apply to Licensee's use of
// the Software until such time that the Software is made available under an
// open source license such as the Apache 2.0 License.

extern crate habitat_core as hcore;
extern crate habitat_depot_core as depot_core;
#[macro_use]
extern crate hyper;
#[macro_use]
extern crate log;
extern crate rustc_serialize;

pub mod error;

pub use error::{Error, Result};

use std::fs::{self, File};
use std::io::{Read, Write, BufWriter, Seek, SeekFrom};
use std::path::{Path, PathBuf};

use hcore::package::{PackageArchive, PackageIdent};
use depot_core::{XFileName, data_object};
use hyper::client::{Client, Body};
use hyper::status::StatusCode;
use hyper::Url;
use rustc_serialize::json;

/// Download a public key from a remote Depot to the given filepath.
///
/// # Failures
///
/// * Key cannot be found
/// * Remote Depot is not available
/// * File cannot be created and written to
pub fn fetch_key(depot: &str, key: &str, path: &str) -> Result<String> {
    let url = Url::parse(&format!("{}/keys/{}", depot, key)).unwrap();
    download(key, url, path)
}

/// Download the latest release of a package.
///
/// An optional version and release can be specified
/// which, when provided, will increase specificity of the release retrieved. Specifying a version
/// and no release will retrieve the latest release of a given version. Specifying both a version
/// and a release will retrieve that exact package.
///
/// # Failures
///
/// * Package cannot be found
/// * Remote Depot is not available
/// * File cannot be created and written to
pub fn fetch_package(depot: &str, package: &PackageIdent, store: &str) -> Result<PackageArchive> {
    let url = Url::parse(&format!("{}/pkgs/{}/download", depot, package)).unwrap();
    match download(&package.name, url, store) {
        Ok(file) => {
            let path = PathBuf::from(file);
            Ok(PackageArchive::new(path))
        }
        Err(Error::HTTP(StatusCode::NotFound)) => {
            Err(Error::RemotePackageNotFound(package.clone()))
        }
        Err(e) => Err(e),
    }
}

/// Returns a package struct for the latest package.
///
/// An optional version can be specified which will scope the release returned to the latest
/// release of that package.
///
/// # Failures
///
/// * Package cannot be found
/// * Remote Depot is not available
pub fn show_package(depot: &str, ident: &PackageIdent) -> Result<data_object::Package> {
    let url = url_show_package(depot, ident);
    let client = Client::new();
    let request = client.get(&url);
    let mut res = try!(request.send());

    if res.status != hyper::status::StatusCode::Ok {
        return Err(Error::RemotePackageNotFound(ident.clone()));
    }

    let mut encoded = String::new();
    try!(res.read_to_string(&mut encoded));
    debug!("Body: {:?}", encoded);
    let package: data_object::Package = json::decode(&encoded).unwrap();
    Ok(package)
}

/// Upload a public key to a remote Depot.
///
/// # Failures
///
/// * Remote Depot is not available
/// * File cannot be read
pub fn put_key(depot: &str, path: &Path) -> Result<()> {
    let mut file = try!(File::open(path));
    let file_name = try!(path.file_name().ok_or(Error::NoFilePart));
    let url = Url::parse(&format!("{}/keys/{}", depot, file_name.to_string_lossy())).unwrap();
    upload(url, &mut file)
}

/// Upload a package to a remote Depot.
///
/// # Failures
///
/// * Remote Depot is not available
/// * File cannot be read
pub fn put_package(depot: &str, pa: &mut PackageArchive) -> Result<()> {
    let checksum = try!(pa.checksum());
    let params = [("checksum", checksum)];
    let ident = try!(pa.ident());
    let mut url = Url::parse(&format!("{}/pkgs/{}", depot, ident)).unwrap();
    url.set_query_from_pairs(&params);

    let mut file = try!(File::open(&pa.path));
    upload(url, &mut file)
}

fn url_show_package(depot: &str, package: &PackageIdent) -> String {
    if package.fully_qualified() {
        format!("{}/pkgs/{}", depot, package)
    } else {
        format!("{}/pkgs/{}/latest", depot, package)
    }
}

fn download(status: &str, url: Url, path: &str) -> Result<String> {
    debug!("Making request to url {}", url);
    let client = Client::new();
    let mut res = try!(client.get(url).send());
    debug!("Response: {:?}", res);

    if res.status != hyper::status::StatusCode::Ok {
        return Err(Error::HTTP(res.status));
    }

    let file_name = match res.headers.get::<XFileName>() {
        Some(filename) => format!("{}", filename),
        None => return Err(Error::NoXFilename),
    };
    let length = res.headers
                    .get::<hyper::header::ContentLength>()
                    .map_or("Unknown".to_string(), |v| format!("{}", v));
    // Here is a moment where you can really like Rust. We create
    // a file, wrap it in a BufWriter - which understands how to
    // safely batch writes into large buffer sizes on the heap,
    // saving us the tax of frequent system calls. We then do
    // what we would do in C - create a buffer of bytes, then
    // read into that buffer, and write out to the other side.
    //
    // Under the hood, Hyper uses the "BufReader" to implement
    // reading the request body - so on both ends, we are getting
    // free buffering on the heap, using our stack buffer just to
    // shuttle back and forth. This is essentially with the "tee"
    // function does in the stdlib, but with error handling that
    // a generic "tee" can't really provide easily.
    //
    // What you can't see is this - the compiler helped with
    // making sure all the edge cases of the pattern were covered,
    // and even though its a trivial case, it was pretty great.
    let tempfile = format!("{}/{}.tmp", path, file_name);
    let finalfile = format!("{}/{}", path, file_name);
    let f = try!(File::create(&tempfile));
    let mut writer = BufWriter::new(&f);
    let mut written: i64 = 0;
    let mut buf = [0u8; 100000]; // Our byte buffer
    loop {
        let len = try!(res.read(&mut buf)); // Raise IO errors
        match len {
            0 => {
                // 0 == EOF, so stop writing and finish progress
                progress(status, written, &length, true);
                break;
            }
            _ => {
                // Write the buffer to the BufWriter on the Heap
                let bytes_written = try!(writer.write(&buf[0..len]));
                if bytes_written == 0 {
                    return Err(Error::WriteSyncFailed);
                }
                written = written + (bytes_written as i64);
                progress(status, written, &length, false);
            }
        };
    }
    try!(fs::rename(&tempfile, &finalfile));
    Ok(finalfile)
}

fn upload(url: Url, file: &mut File) -> Result<()> {
    debug!("Uploading to {}", url);
    try!(file.seek(SeekFrom::Start(0)));
    let client = Client::new();
    let metadata = try!(file.metadata());
    let response = try!(client.post(url).body(Body::SizedBody(file, metadata.len())).send());
    if response.status.is_success() {
        Ok(())
    } else {
        debug!("Response {:?}", response);
        Err(Error::HTTP(response.status))
    }
}

fn progress(status: &str, written: i64, length: &str, finished: bool) {
    let progress = format!("{} {}/{}", status, written, length);
    print!("{}", from_char(progress.len(), '\x08'));
    if finished {
        println!("{}", progress);
    } else {
        print!("{}", progress);
    }
}

fn from_char(length: usize, ch: char) -> String {
    if length == 0 {
        return String::new();
    }

    let mut buf = String::new();
    buf.push(ch);
    let size = buf.len() * length;
    buf.reserve(size);
    for _ in 1..length {
        buf.push(ch)
    }
    buf
}

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

    #[test]
    fn from_char_returns_the_correct_string() {
        assert_eq!("xxxx", from_char(4, 'x'));
    }
}
<|endoftext|>