<commit_before>use std::io;
use std::fs::{remove_file, rename, create_dir_all, set_permissions};
use std::fs::{File, Permissions};
use std::os::unix::fs::PermissionsExt;
use std::path::{Path, PathBuf};

use sha2::{Digest, Sha256};
use unshare::{Command, Stdio};

use super::capsule;
use super::context::Context;
use file_util::check_stream_hashsum;

pub fn download_file<S>(ctx: &mut Context, urls: &[S], sha256: Option<String>)
    -> Result<PathBuf, String>
    where S: AsRef<str>
{
    let https = urls.iter().any(|x| x.as_ref().starts_with("https:"));
    if https {
        try!(capsule::ensure_features(ctx, &[capsule::Https]));
    }
    let urlpath = Path::new(urls[0].as_ref());
    let hash = match sha256 {
        Some(ref sha256) => sha256[..8].to_string(),
        None => {
            let mut hash = Sha256::new();
            hash.input_str(urls[0].as_ref());
            hash.result_str()[..8].to_string()
        },
    };
    let name = match urlpath.file_name().and_then(|x| x.to_str()) {
        Some(name) => name,
        None => "file.bin",
    };
    let name = hash[..8].to_string() + "-" + name;
    let dir = Path::new("/vagga/cache/downloads");
    if !dir.exists() {
        try!(create_dir_all(&dir)
            .map_err(|e| format!("Error moving file: {}", e)));
        try!(set_permissions(&dir, Permissions::from_mode(0o755))
            .map_err(|e| format!("Can't chmod file: {}", e)));
    }
    let filename = dir.join(&name);
    if filename.exists() {
        return Ok(filename);
    }
    for url in urls {
        let url = url.as_ref();
        info!("Downloading image {} -> {:?}", url, filename);
        let tmpfilename = filename.with_file_name(name.clone() + ".part");
        let mut cmd = Command::new(
            if https { "/usr/bin/wget" } else { "/vagga/bin/busybox" });
        cmd.stdin(Stdio::null());
        if !https {
            cmd.arg("wget");
        }
        cmd.arg("-O");
        cmd.arg(&tmpfilename);
        cmd.arg(url);

        debug!("Running: {:?}", cmd);
        match cmd.status() {
            Ok(st) if st.success() => {
                if let Some(ref sha256) = sha256 {
                    let mut tmpfile = try_msg!(File::open(&tmpfilename),
                                            "Cannot open archive: {err}");
                    if let Err(e) = check_stream_hashsum(&mut tmpfile, sha256) {
                        remove_file(&filename)
                            .map_err(|e| error!(
                                "Error unlinking cache file: {}", e)).ok();
                        error!("Bad hashsum of {:?}", url);
                        return Err(e);
                    }
                }
                try!(rename(&tmpfilename, &filename)
                    .map_err(|e| format!("Error moving file: {}", e)));
                return Ok(filename);
            }
            Ok(val) => {
                match remove_file(&tmpfilename) {
                    Err(ref e) if e.kind() == io::ErrorKind::NotFound => {
                        // Assume we got 404 and download have not even started
                        continue;
                    }
                    Err(e) => {
                        error!("Error unlinking cache file: {}", e);
                    }
                    Ok(_) => {}
                }
                error!("Error downloading {:?}, wget {:?}", url, val);
                continue;
            }
            Err(x) => {
                return Err(format!("Error starting wget: {}", x));
            }
        }
    }
    return Err(format!("Error downloading file {:?} from {:?}",
        filename, urls.iter().map(|x| x.as_ref()).collect::<Vec<_>>()));
}

fn check_if_local(url: &str, sha256: &Option<String>)
    -> Result<Option<PathBuf>, String>
{
    let path = if url.starts_with(".") {
        PathBuf::from("/work").join(url)
    } else if url.starts_with("/volumes/") {
        PathBuf::from(url)
    } else {
        return Ok(None);
    };
    if let Some(ref sha256) = *sha256 {
        let mut file = try_msg!(File::open(&path),
            "Cannot open file: {err}");
        try!(check_stream_hashsum(&mut file, sha256)
            .map_err(|e| format!(
                "Error when checking hashsum for file {:?}: {}",
                &path, e)));
    }
    Ok(Some(path))
}

pub fn maybe_download_and_check_hashsum(ctx: &mut Context,
    url: &str, sha256: Option<String>)
    -> Result<(PathBuf, bool), String>
{
    Ok(match try!(check_if_local(url, &sha256)) {
        Some(path) => (path, false),
        None => (try!(download_file(ctx, &[url], sha256)), true),
    })
}
<commit_msg>Don't download capsule packages if file to download is in cache<commit_after>use std::io;
use std::fs::{remove_file, rename, create_dir_all, set_permissions};
use std::fs::{File, Permissions};
use std::os::unix::fs::PermissionsExt;
use std::path::{Path, PathBuf};

use sha2::{Digest, Sha256};
use unshare::{Command, Stdio};

use super::capsule;
use super::context::Context;
use file_util::check_stream_hashsum;

pub fn download_file<S>(ctx: &mut Context, urls: &[S], sha256: Option<String>)
    -> Result<PathBuf, String>
    where S: AsRef<str>
{
    let urlpath = Path::new(urls[0].as_ref());
    let hash = match sha256 {
        Some(ref sha256) => sha256[..8].to_string(),
        None => {
            let mut hash = Sha256::new();
            hash.input_str(urls[0].as_ref());
            hash.result_str()[..8].to_string()
        },
    };
    let name = match urlpath.file_name().and_then(|x| x.to_str()) {
        Some(name) => name,
        None => "file.bin",
    };
    let name = hash[..8].to_string() + "-" + name;
    let dir = Path::new("/vagga/cache/downloads");
    if !dir.exists() {
        try!(create_dir_all(&dir)
            .map_err(|e| format!("Error moving file: {}", e)));
        try!(set_permissions(&dir, Permissions::from_mode(0o755))
            .map_err(|e| format!("Can't chmod file: {}", e)));
    }
    let filename = dir.join(&name);
    if filename.exists() {
        return Ok(filename);
    }
    let https = urls.iter().any(|x| x.as_ref().starts_with("https:"));
    if https {
        try!(capsule::ensure_features(ctx, &[capsule::Https]));
    }
    for url in urls {
        let url = url.as_ref();
        info!("Downloading image {} -> {:?}", url, filename);
        let tmpfilename = filename.with_file_name(name.clone() + ".part");
        let mut cmd = Command::new(
            if https { "/usr/bin/wget" } else { "/vagga/bin/busybox" });
        cmd.stdin(Stdio::null());
        if !https {
            cmd.arg("wget");
        }
        cmd.arg("-O");
        cmd.arg(&tmpfilename);
        cmd.arg(url);

        debug!("Running: {:?}", cmd);
        match cmd.status() {
            Ok(st) if st.success() => {
                if let Some(ref sha256) = sha256 {
                    let mut tmpfile = try_msg!(File::open(&tmpfilename),
                                            "Cannot open archive: {err}");
                    if let Err(e) = check_stream_hashsum(&mut tmpfile, sha256) {
                        remove_file(&filename)
                            .map_err(|e| error!(
                                "Error unlinking cache file: {}", e)).ok();
                        error!("Bad hashsum of {:?}", url);
                        return Err(e);
                    }
                }
                try!(rename(&tmpfilename, &filename)
                    .map_err(|e| format!("Error moving file: {}", e)));
                return Ok(filename);
            }
            Ok(val) => {
                match remove_file(&tmpfilename) {
                    Err(ref e) if e.kind() == io::ErrorKind::NotFound => {
                        // Assume we got 404 and download have not even started
                        continue;
                    }
                    Err(e) => {
                        error!("Error unlinking cache file: {}", e);
                    }
                    Ok(_) => {}
                }
                error!("Error downloading {:?}, wget {:?}", url, val);
                continue;
            }
            Err(x) => {
                return Err(format!("Error starting wget: {}", x));
            }
        }
    }
    return Err(format!("Error downloading file {:?} from {:?}",
        filename, urls.iter().map(|x| x.as_ref()).collect::<Vec<_>>()));
}

fn check_if_local(url: &str, sha256: &Option<String>)
    -> Result<Option<PathBuf>, String>
{
    let path = if url.starts_with(".") {
        PathBuf::from("/work").join(url)
    } else if url.starts_with("/volumes/") {
        PathBuf::from(url)
    } else {
        return Ok(None);
    };
    if let Some(ref sha256) = *sha256 {
        let mut file = try_msg!(File::open(&path),
            "Cannot open file: {err}");
        try!(check_stream_hashsum(&mut file, sha256)
            .map_err(|e| format!(
                "Error when checking hashsum for file {:?}: {}",
                &path, e)));
    }
    Ok(Some(path))
}

pub fn maybe_download_and_check_hashsum(ctx: &mut Context,
    url: &str, sha256: Option<String>)
    -> Result<(PathBuf, bool), String>
{
    Ok(match try!(check_if_local(url, &sha256)) {
        Some(path) => (path, false),
        None => (try!(download_file(ctx, &[url], sha256)), true),
    })
}
<|endoftext|>


<commit_before>use num::Float;
use num::traits::FloatConst;
use std::cmp;
use std::marker::PhantomData;

use generate::generate::{IndexedPolygonGenerator, PolygonGenerator, SpatialPolygonGenerator,
                         SpatialVertexGenerator, VertexGenerator};
use generate::geometry::Unit;
use generate::topology::{Polygon, Quad, Triangle};

#[derive(Clone)]
pub struct UVSphere<T = f32>
where
    T: Float + FloatConst + Unit,
{
    nu: usize, // Meridians.
    nv: usize, // Parallels.
    unit: T,
    phantom: PhantomData<T>,
}

impl<T> UVSphere<T>
where
    T: Float + FloatConst + Unit,
{
    fn new(nu: usize, nv: usize, upper: T) -> Self {
        let nu = cmp::max(3, nu);
        let nv = cmp::max(2, nv);
        UVSphere {
            nu: nu,
            nv: nv,
            unit: upper,
            phantom: PhantomData,
        }
    }

    pub fn with_unit_radius(nu: usize, nv: usize) -> Self {
        Self::new(nu, nv, T::unit_radius().1)
    }

    pub fn with_unit_width(nu: usize, nv: usize) -> Self {
        Self::new(nu, nv, T::unit_width().1)
    }

    fn spatial_vertex(&self, u: usize, v: usize) -> (T, T, T) {
        let u = (T::from(u).unwrap() / T::from(self.nu).unwrap()) * T::PI() * (T::one() + T::one());
        let v = (T::from(v).unwrap() / T::from(self.nv).unwrap()) * T::PI();
        (
            self.unit * u.cos() * v.sin(),
            self.unit * u.sin() * v.sin(),
            self.unit * v.cos(),
        )
    }

    fn indexed_vertex(&self, u: usize, v: usize) -> usize {
        if v == 0 {
            0
        }
        else if v == self.nv {
            ((self.nv - 1) * self.nu) + 1
        }
        else {
            ((v - 1) * self.nu) + (u % self.nu) + 1
        }
    }

    fn map_polygon_index(&self, index: usize) -> (usize, usize) {
        (index % self.nu, index / self.nu)
    }
}

impl<T> VertexGenerator for UVSphere<T>
where
    T: Float + FloatConst + Unit,
{
    fn vertex_count(&self) -> usize {
        (self.nv - 1) * self.nu + 2
    }
}

impl<T> SpatialVertexGenerator for UVSphere<T>
where
    T: Float + FloatConst + Unit,
{
    type Output = (T, T, T);

    fn spatial_vertex(&self, index: usize) -> Self::Output {
        if index == 0 {
            self.spatial_vertex(0, 0)
        }
        else if index == self.vertex_count() - 1 {
            self.spatial_vertex(0, self.nv)
        }
        else {
            let index = index - 1;
            self.spatial_vertex(index % self.nu, (index / self.nu) + 1)
        }
    }
}

impl<T> PolygonGenerator for UVSphere<T>
where
    T: Float + FloatConst + Unit,
{
    fn polygon_count(&self) -> usize {
        self.nu * self.nv
    }
}

impl<T> SpatialPolygonGenerator for UVSphere<T>
where
    T: Float + FloatConst + Unit,
{
    type Output = Polygon<(T, T, T)>;

    fn spatial_polygon(&self, index: usize) -> Self::Output {
        // Prevent floating point rounding errors by wrapping the incremented
        // values for `(u, v)` into `(p, q)`. This is important for indexing
        // geometry, because small differences in the computation of spatial
        // vertices will produce redundant output vertices. There should be
        // exactly `(nv - 1) * nu + 2` unique values of `(u, v)` used to
        // generate positions.
        //
        // There are two important observations:
        //
        //   1. `u` must wrap, but `v` need not. There are `nu` meridians of
        //      points and polygons, but there are `nv` parallels of polygons
        //      and `nv + 1` parallels of points.
        //   2. `u`, which represents a meridian, is meaningless at the poles,
        //      and can be normalized to zero.
        let (u, v) = self.map_polygon_index(index);
        let (p, q) = ((u + 1) % self.nu, v + 1);

        // Generate the vertices at the requested meridian and parallel. The
        // lower bound of `(u, v)` is always used, so compute that in advance
        // (`lower`). Emit triangles at the poles, otherwise quads.
        let lower = self.spatial_vertex(u, v);
        if v == 0 {
            Polygon::Triangle(Triangle::new(
                lower,
                self.spatial_vertex(u, q),
                self.spatial_vertex(p, q),
            ))
        }
        else if v == self.nv - 1 {
            Polygon::Triangle(Triangle::new(
                // Normalize `u` at the pole, using `(0, nv)` in place of
                // `(p, q)`.
                self.spatial_vertex(0, self.nv),
                self.spatial_vertex(p, v),
                lower,
            ))
        }
        else {
            Polygon::Quad(Quad::new(
                lower,
                self.spatial_vertex(u, q),
                self.spatial_vertex(p, q),
                self.spatial_vertex(p, v),
            ))
        }
    }
}

impl<T> IndexedPolygonGenerator for UVSphere<T>
where
    T: Float + FloatConst + Unit,
{
    type Output = Polygon<usize>;

    fn indexed_polygon(&self, index: usize) -> <Self as IndexedPolygonGenerator>::Output {
        let (u, v) = self.map_polygon_index(index);
        let (p, q) = (u + 1, v + 1);

        let low = self.indexed_vertex(u, v);
        let high = self.indexed_vertex(p, q);
        if v == 0 {
            Polygon::Triangle(Triangle::new(low, self.indexed_vertex(u, q), high))
        }
        else if v == self.nv - 1 {
            Polygon::Triangle(Triangle::new(high, self.indexed_vertex(p, v), low))
        }
        else {
            Polygon::Quad(Quad::new(
                low,
                self.indexed_vertex(u, q),
                high,
                self.indexed_vertex(p, v),
            ))
        }
    }
}

#[cfg(test)]
mod tests {
    use std::collections::BTreeSet;
    use std::iter::FromIterator;

    use super::super::*;

    #[test]
    fn vertex_count() {
        assert_eq!(
            5,
            sphere::UVSphere::<f32>::with_unit_radius(3, 2)
                .spatial_vertices() // 5 conjoint vertices.
                .count()
        );
    }

    #[test]
    fn polygon_vertex_count() {
        assert_eq!(
            18,
            sphere::UVSphere::<f32>::with_unit_radius(3, 2)
                .spatial_polygons() // 6 triangles, 18 vertices.
                .vertices()
                .count()
        );
    }

    #[test]
    fn index_to_vertex_mapping() {
        assert_eq!(
            5,
            BTreeSet::from_iter(
                sphere::UVSphere::<f32>::with_unit_radius(3, 2)
                    .indexed_polygons() // 18 vertices, 5 indeces.
                    .vertices()
            ).len()
        )
    }
}
<commit_msg>Use consistent imports in test modules.<commit_after>use num::Float;
use num::traits::FloatConst;
use std::cmp;
use std::marker::PhantomData;

use generate::generate::{IndexedPolygonGenerator, PolygonGenerator, SpatialPolygonGenerator,
                         SpatialVertexGenerator, VertexGenerator};
use generate::geometry::Unit;
use generate::topology::{Polygon, Quad, Triangle};

#[derive(Clone)]
pub struct UVSphere<T = f32>
where
    T: Float + FloatConst + Unit,
{
    nu: usize, // Meridians.
    nv: usize, // Parallels.
    unit: T,
    phantom: PhantomData<T>,
}

impl<T> UVSphere<T>
where
    T: Float + FloatConst + Unit,
{
    fn new(nu: usize, nv: usize, upper: T) -> Self {
        let nu = cmp::max(3, nu);
        let nv = cmp::max(2, nv);
        UVSphere {
            nu: nu,
            nv: nv,
            unit: upper,
            phantom: PhantomData,
        }
    }

    pub fn with_unit_radius(nu: usize, nv: usize) -> Self {
        Self::new(nu, nv, T::unit_radius().1)
    }

    pub fn with_unit_width(nu: usize, nv: usize) -> Self {
        Self::new(nu, nv, T::unit_width().1)
    }

    fn spatial_vertex(&self, u: usize, v: usize) -> (T, T, T) {
        let u = (T::from(u).unwrap() / T::from(self.nu).unwrap()) * T::PI() * (T::one() + T::one());
        let v = (T::from(v).unwrap() / T::from(self.nv).unwrap()) * T::PI();
        (
            self.unit * u.cos() * v.sin(),
            self.unit * u.sin() * v.sin(),
            self.unit * v.cos(),
        )
    }

    fn indexed_vertex(&self, u: usize, v: usize) -> usize {
        if v == 0 {
            0
        }
        else if v == self.nv {
            ((self.nv - 1) * self.nu) + 1
        }
        else {
            ((v - 1) * self.nu) + (u % self.nu) + 1
        }
    }

    fn map_polygon_index(&self, index: usize) -> (usize, usize) {
        (index % self.nu, index / self.nu)
    }
}

impl<T> VertexGenerator for UVSphere<T>
where
    T: Float + FloatConst + Unit,
{
    fn vertex_count(&self) -> usize {
        (self.nv - 1) * self.nu + 2
    }
}

impl<T> SpatialVertexGenerator for UVSphere<T>
where
    T: Float + FloatConst + Unit,
{
    type Output = (T, T, T);

    fn spatial_vertex(&self, index: usize) -> Self::Output {
        if index == 0 {
            self.spatial_vertex(0, 0)
        }
        else if index == self.vertex_count() - 1 {
            self.spatial_vertex(0, self.nv)
        }
        else {
            let index = index - 1;
            self.spatial_vertex(index % self.nu, (index / self.nu) + 1)
        }
    }
}

impl<T> PolygonGenerator for UVSphere<T>
where
    T: Float + FloatConst + Unit,
{
    fn polygon_count(&self) -> usize {
        self.nu * self.nv
    }
}

impl<T> SpatialPolygonGenerator for UVSphere<T>
where
    T: Float + FloatConst + Unit,
{
    type Output = Polygon<(T, T, T)>;

    fn spatial_polygon(&self, index: usize) -> Self::Output {
        // Prevent floating point rounding errors by wrapping the incremented
        // values for `(u, v)` into `(p, q)`. This is important for indexing
        // geometry, because small differences in the computation of spatial
        // vertices will produce redundant output vertices. There should be
        // exactly `(nv - 1) * nu + 2` unique values of `(u, v)` used to
        // generate positions.
        //
        // There are two important observations:
        //
        //   1. `u` must wrap, but `v` need not. There are `nu` meridians of
        //      points and polygons, but there are `nv` parallels of polygons
        //      and `nv + 1` parallels of points.
        //   2. `u`, which represents a meridian, is meaningless at the poles,
        //      and can be normalized to zero.
        let (u, v) = self.map_polygon_index(index);
        let (p, q) = ((u + 1) % self.nu, v + 1);

        // Generate the vertices at the requested meridian and parallel. The
        // lower bound of `(u, v)` is always used, so compute that in advance
        // (`lower`). Emit triangles at the poles, otherwise quads.
        let lower = self.spatial_vertex(u, v);
        if v == 0 {
            Polygon::Triangle(Triangle::new(
                lower,
                self.spatial_vertex(u, q),
                self.spatial_vertex(p, q),
            ))
        }
        else if v == self.nv - 1 {
            Polygon::Triangle(Triangle::new(
                // Normalize `u` at the pole, using `(0, nv)` in place of
                // `(p, q)`.
                self.spatial_vertex(0, self.nv),
                self.spatial_vertex(p, v),
                lower,
            ))
        }
        else {
            Polygon::Quad(Quad::new(
                lower,
                self.spatial_vertex(u, q),
                self.spatial_vertex(p, q),
                self.spatial_vertex(p, v),
            ))
        }
    }
}

impl<T> IndexedPolygonGenerator for UVSphere<T>
where
    T: Float + FloatConst + Unit,
{
    type Output = Polygon<usize>;

    fn indexed_polygon(&self, index: usize) -> <Self as IndexedPolygonGenerator>::Output {
        let (u, v) = self.map_polygon_index(index);
        let (p, q) = (u + 1, v + 1);

        let low = self.indexed_vertex(u, v);
        let high = self.indexed_vertex(p, q);
        if v == 0 {
            Polygon::Triangle(Triangle::new(low, self.indexed_vertex(u, q), high))
        }
        else if v == self.nv - 1 {
            Polygon::Triangle(Triangle::new(high, self.indexed_vertex(p, v), low))
        }
        else {
            Polygon::Quad(Quad::new(
                low,
                self.indexed_vertex(u, q),
                high,
                self.indexed_vertex(p, v),
            ))
        }
    }
}

#[cfg(test)]
mod tests {
    use std::collections::BTreeSet;
    use std::iter::FromIterator;

    use generate::*;

    #[test]
    fn vertex_count() {
        assert_eq!(
            5,
            sphere::UVSphere::<f32>::with_unit_radius(3, 2)
                .spatial_vertices() // 5 conjoint vertices.
                .count()
        );
    }

    #[test]
    fn polygon_vertex_count() {
        assert_eq!(
            18,
            sphere::UVSphere::<f32>::with_unit_radius(3, 2)
                .spatial_polygons() // 6 triangles, 18 vertices.
                .vertices()
                .count()
        );
    }

    #[test]
    fn index_to_vertex_mapping() {
        assert_eq!(
            5,
            BTreeSet::from_iter(
                sphere::UVSphere::<f32>::with_unit_radius(3, 2)
                    .indexed_polygons() // 18 vertices, 5 indeces.
                    .vertices()
            ).len()
        )
    }
}
<|endoftext|>


<commit_before>use std::fmt;
use std::io::prelude::*;
use std::io;

use term::color::{Color, BLACK, RED, GREEN, YELLOW};
use term::{self, Terminal, TerminfoTerminal, color, Attr};

use self::AdequateTerminal::{NoColor, Colored};
use self::Verbosity::{Verbose, Quiet};
use self::ColorConfig::{Auto, Always, Never};

use util::errors::CargoResult;

#[derive(Clone, Copy, PartialEq)]
pub enum Verbosity {
    Verbose,
    Normal,
    Quiet
}

#[derive(Clone, Copy, PartialEq)]
pub enum ColorConfig {
    Auto,
    Always,
    Never
}

#[derive(Clone, Copy)]
pub struct ShellConfig {
    pub color_config: ColorConfig,
    pub tty: bool
}

enum AdequateTerminal {
    NoColor(Box<Write + Send>),
    Colored(Box<Terminal<Output=Box<Write + Send>> + Send>)
}

pub struct Shell {
    terminal: AdequateTerminal,
    config: ShellConfig,
}

pub struct MultiShell {
    out: Shell,
    err: Shell,
    verbosity: Verbosity
}

impl MultiShell {
    pub fn new(out: Shell, err: Shell, verbosity: Verbosity) -> MultiShell {
        MultiShell { out: out, err: err, verbosity: verbosity }
    }

    pub fn out(&mut self) -> &mut Shell {
        &mut self.out
    }

    pub fn err(&mut self) -> &mut Shell {
        &mut self.err
    }

    pub fn say<T: ToString>(&mut self, message: T, color: Color)
                            -> CargoResult<()> {
        match self.verbosity {
            Quiet => Ok(()),
            _ => self.out().say(message, color)
        }
    }

    pub fn status<T, U>(&mut self, status: T, message: U) -> CargoResult<()>
        where T: fmt::Display, U: fmt::Display
    {
        match self.verbosity {
            Quiet => Ok(()),
            _ => self.err().say_status(status, message, GREEN, true)
        }
    }

    pub fn verbose<F>(&mut self, mut callback: F) -> CargoResult<()>
        where F: FnMut(&mut MultiShell) -> CargoResult<()>
    {
        match self.verbosity {
            Verbose => callback(self),
            _ => Ok(())
        }
    }

    pub fn concise<F>(&mut self, mut callback: F) -> CargoResult<()>
        where F: FnMut(&mut MultiShell) -> CargoResult<()>
    {
        match self.verbosity {
            Verbose => Ok(()),
            _ => callback(self)
        }
    }

    pub fn error<T: fmt::Display>(&mut self, message: T) -> CargoResult<()> {
        self.err().say_status("error:", message, RED, false)
    }

    pub fn warn<T: fmt::Display>(&mut self, message: T) -> CargoResult<()> {
        match self.verbosity {
            Quiet => Ok(()),
            _ => self.err().say_status("warning:", message, YELLOW, false),
        }
    }

    pub fn set_verbosity(&mut self, verbosity: Verbosity) {
        self.verbosity = verbosity;
    }

    pub fn set_color_config(&mut self, color: Option<&str>) -> CargoResult<()> {
        self.out.set_color_config(match color {
            Some("auto") => Auto,
            Some("always") => Always,
            Some("never") => Never,

            None => Auto,

            Some(arg) => bail!("argument for --color must be auto, always, or \
                                never, but found `{}`", arg),
        });
        Ok(())
    }

    pub fn get_verbose(&self) -> Verbosity {
        self.verbosity
    }
}

impl Shell {
    pub fn create(out: Box<Write + Send>, config: ShellConfig) -> Shell {
        // Use `TermInfo::from_env()` and `TerminfoTerminal::supports_color()`
        // to determine if creation of a TerminfoTerminal is possible regardless
        // of the tty status. --color options are parsed after Shell creation so
        // always try to create a terminal that supports color output. Fall back
        // to a no-color terminal regardless of whether or not a tty is present
        // and if color output is not possible.
        Shell {
            terminal: match ::term::terminfo::TermInfo::from_env() {
                Ok(ti) => {
                    let term = TerminfoTerminal::new_with_terminfo(out, ti);
                    if !term.supports_color() {
                        NoColor(term.into_inner())
                    } else {
                        // Color output is possible.
                        Colored(Box::new(term))
                    }
                },
                Err(_) => NoColor(out),
            },
            config: config,
        }
    }

    pub fn set_color_config(&mut self, color_config: ColorConfig) {
        self.config.color_config = color_config;
    }

    pub fn say<T: ToString>(&mut self, message: T, color: Color) -> CargoResult<()> {
        try!(self.reset());
        if color != BLACK { try!(self.fg(color)); }
        try!(write!(self, "{}\n", message.to_string()));
        try!(self.reset());
        try!(self.flush());
        Ok(())
    }

    pub fn say_status<T, U>(&mut self,
                            status: T,
                            message: U,
                            color: Color,
                            justified: bool)
                            -> CargoResult<()>
        where T: fmt::Display, U: fmt::Display
    {
        try!(self.reset());
        if color != BLACK { try!(self.fg(color)); }
        if self.supports_attr(Attr::Bold) { try!(self.attr(Attr::Bold)); }
        if justified {
            try!(write!(self, "{:>12}", status.to_string()));
        } else {
            try!(write!(self, "{}", status));
        }
        try!(self.reset());
        try!(write!(self, " {}\n", message));
        try!(self.flush());
        Ok(())
    }

    fn fg(&mut self, color: color::Color) -> CargoResult<bool> {
        let colored = self.colored();

        match self.terminal {
            Colored(ref mut c) if colored => try!(c.fg(color)),
            _ => return Ok(false),
        }
        Ok(true)
    }

    fn attr(&mut self, attr: Attr) -> CargoResult<bool> {
        let colored = self.colored();

        match self.terminal {
            Colored(ref mut c) if colored => try!(c.attr(attr)),
            _ => return Ok(false)
        }
        Ok(true)
    }

    fn supports_attr(&self, attr: Attr) -> bool {
        let colored = self.colored();

        match self.terminal {
            Colored(ref c) if colored => c.supports_attr(attr),
            _ => false
        }
    }

    fn reset(&mut self) -> term::Result<()> {
        let colored = self.colored();

        match self.terminal {
            Colored(ref mut c) if colored => try!(c.reset()),
            _ => ()
        }
        Ok(())
    }

    fn colored(&self) -> bool {
        self.config.tty && Auto == self.config.color_config
            || Always == self.config.color_config
    }
}

impl Write for Shell {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        match self.terminal {
            Colored(ref mut c) => c.write(buf),
            NoColor(ref mut n) => n.write(buf)
        }
    }

    fn flush(&mut self) -> io::Result<()> {
        match self.terminal {
            Colored(ref mut c) => c.flush(),
            NoColor(ref mut n) => n.flush()
        }
    }
}
<commit_msg>Auto merge of #2739 - alexcrichton:configure-colors, r=alexcrichton<commit_after>use std::fmt;
use std::io::prelude::*;
use std::io;

use term::color::{Color, BLACK, RED, GREEN, YELLOW};
use term::{self, Terminal, TerminfoTerminal, color, Attr};

use self::AdequateTerminal::{NoColor, Colored};
use self::Verbosity::{Verbose, Quiet};
use self::ColorConfig::{Auto, Always, Never};

use util::errors::CargoResult;

#[derive(Clone, Copy, PartialEq)]
pub enum Verbosity {
    Verbose,
    Normal,
    Quiet
}

#[derive(Clone, Copy, PartialEq)]
pub enum ColorConfig {
    Auto,
    Always,
    Never
}

#[derive(Clone, Copy)]
pub struct ShellConfig {
    pub color_config: ColorConfig,
    pub tty: bool
}

enum AdequateTerminal {
    NoColor(Box<Write + Send>),
    Colored(Box<Terminal<Output=Box<Write + Send>> + Send>)
}

pub struct Shell {
    terminal: AdequateTerminal,
    config: ShellConfig,
}

pub struct MultiShell {
    out: Shell,
    err: Shell,
    verbosity: Verbosity
}

impl MultiShell {
    pub fn new(out: Shell, err: Shell, verbosity: Verbosity) -> MultiShell {
        MultiShell { out: out, err: err, verbosity: verbosity }
    }

    pub fn out(&mut self) -> &mut Shell {
        &mut self.out
    }

    pub fn err(&mut self) -> &mut Shell {
        &mut self.err
    }

    pub fn say<T: ToString>(&mut self, message: T, color: Color)
                            -> CargoResult<()> {
        match self.verbosity {
            Quiet => Ok(()),
            _ => self.out().say(message, color)
        }
    }

    pub fn status<T, U>(&mut self, status: T, message: U) -> CargoResult<()>
        where T: fmt::Display, U: fmt::Display
    {
        match self.verbosity {
            Quiet => Ok(()),
            _ => self.err().say_status(status, message, GREEN, true)
        }
    }

    pub fn verbose<F>(&mut self, mut callback: F) -> CargoResult<()>
        where F: FnMut(&mut MultiShell) -> CargoResult<()>
    {
        match self.verbosity {
            Verbose => callback(self),
            _ => Ok(())
        }
    }

    pub fn concise<F>(&mut self, mut callback: F) -> CargoResult<()>
        where F: FnMut(&mut MultiShell) -> CargoResult<()>
    {
        match self.verbosity {
            Verbose => Ok(()),
            _ => callback(self)
        }
    }

    pub fn error<T: fmt::Display>(&mut self, message: T) -> CargoResult<()> {
        self.err().say_status("error:", message, RED, false)
    }

    pub fn warn<T: fmt::Display>(&mut self, message: T) -> CargoResult<()> {
        match self.verbosity {
            Quiet => Ok(()),
            _ => self.err().say_status("warning:", message, YELLOW, false),
        }
    }

    pub fn set_verbosity(&mut self, verbosity: Verbosity) {
        self.verbosity = verbosity;
    }

    pub fn set_color_config(&mut self, color: Option<&str>) -> CargoResult<()> {
        let cfg = match color {
            Some("auto") => Auto,
            Some("always") => Always,
            Some("never") => Never,

            None => Auto,

            Some(arg) => bail!("argument for --color must be auto, always, or \
                                never, but found `{}`", arg),
        };
        self.out.set_color_config(cfg);
        self.err.set_color_config(cfg);
        Ok(())
    }

    pub fn get_verbose(&self) -> Verbosity {
        self.verbosity
    }
}

impl Shell {
    pub fn create(out: Box<Write + Send>, config: ShellConfig) -> Shell {
        // Use `TermInfo::from_env()` and `TerminfoTerminal::supports_color()`
        // to determine if creation of a TerminfoTerminal is possible regardless
        // of the tty status. --color options are parsed after Shell creation so
        // always try to create a terminal that supports color output. Fall back
        // to a no-color terminal regardless of whether or not a tty is present
        // and if color output is not possible.
        Shell {
            terminal: match ::term::terminfo::TermInfo::from_env() {
                Ok(ti) => {
                    let term = TerminfoTerminal::new_with_terminfo(out, ti);
                    if !term.supports_color() {
                        NoColor(term.into_inner())
                    } else {
                        // Color output is possible.
                        Colored(Box::new(term))
                    }
                },
                Err(_) => NoColor(out),
            },
            config: config,
        }
    }

    pub fn set_color_config(&mut self, color_config: ColorConfig) {
        self.config.color_config = color_config;
    }

    pub fn say<T: ToString>(&mut self, message: T, color: Color) -> CargoResult<()> {
        try!(self.reset());
        if color != BLACK { try!(self.fg(color)); }
        try!(write!(self, "{}\n", message.to_string()));
        try!(self.reset());
        try!(self.flush());
        Ok(())
    }

    pub fn say_status<T, U>(&mut self,
                            status: T,
                            message: U,
                            color: Color,
                            justified: bool)
                            -> CargoResult<()>
        where T: fmt::Display, U: fmt::Display
    {
        try!(self.reset());
        if color != BLACK { try!(self.fg(color)); }
        if self.supports_attr(Attr::Bold) { try!(self.attr(Attr::Bold)); }
        if justified {
            try!(write!(self, "{:>12}", status.to_string()));
        } else {
            try!(write!(self, "{}", status));
        }
        try!(self.reset());
        try!(write!(self, " {}\n", message));
        try!(self.flush());
        Ok(())
    }

    fn fg(&mut self, color: color::Color) -> CargoResult<bool> {
        let colored = self.colored();

        match self.terminal {
            Colored(ref mut c) if colored => try!(c.fg(color)),
            _ => return Ok(false),
        }
        Ok(true)
    }

    fn attr(&mut self, attr: Attr) -> CargoResult<bool> {
        let colored = self.colored();

        match self.terminal {
            Colored(ref mut c) if colored => try!(c.attr(attr)),
            _ => return Ok(false)
        }
        Ok(true)
    }

    fn supports_attr(&self, attr: Attr) -> bool {
        let colored = self.colored();

        match self.terminal {
            Colored(ref c) if colored => c.supports_attr(attr),
            _ => false
        }
    }

    fn reset(&mut self) -> term::Result<()> {
        let colored = self.colored();

        match self.terminal {
            Colored(ref mut c) if colored => try!(c.reset()),
            _ => ()
        }
        Ok(())
    }

    fn colored(&self) -> bool {
        self.config.tty && Auto == self.config.color_config
            || Always == self.config.color_config
    }
}

impl Write for Shell {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        match self.terminal {
            Colored(ref mut c) => c.write(buf),
            NoColor(ref mut n) => n.write(buf)
        }
    }

    fn flush(&mut self) -> io::Result<()> {
        match self.terminal {
            Colored(ref mut c) => c.flush(),
            NoColor(ref mut n) => n.flush()
        }
    }
}
<|endoftext|>


<commit_before>use subframe;
use subframe::{Subframe, MAX_FIXED_ORDER, MAX_LPC_ORDER};

use std::ptr;

pub fn fixed_restore_signal(order: usize,
                            residual: &[i32],
                            output: &mut [i32]) {
  debug_assert!(order <= MAX_FIXED_ORDER);

  let polynomial = [ &[][..]
                   , &[1][..]
                   , &[-1, 2][..]
                   , &[1, -3, 3][..]
                   , &[-1, 4, -6, 4][..]
                   ];

  let coefficients = polynomial[order];

  for i in 0..residual.len() {
    let offset     = i + order;
    let prediction = coefficients.iter()
                      .zip(&output[i..offset])
                      .fold(0, |result, (coefficient, signal)|
                            result + coefficient * signal);

    output[offset] = residual[i] + prediction;
  }
}

pub fn lpc_restore_signal(quantization_level: i8,
                          coefficients: &[i32],
                          residual: &[i32],
                          output: &mut [i32]) {
  let order = coefficients.len();

  debug_assert!(order <= MAX_LPC_ORDER);

  for i in 0..residual.len() {
    let offset     = i + order;
    let prediction = coefficients.iter().rev()
                       .zip(&output[i..offset])
                       .fold(0, |result, (coefficient, signal)|
                             result + coefficient * signal);

    output[offset] = residual[i] + (prediction >> quantization_level);
  }
}

pub fn decode(subframe: &Subframe, output: &mut [i32]) {
  match subframe.data {
    subframe::Data::Constant(constant)     => {
      for i in 0..output.len() {
        output[i] = constant
      }
    }
    subframe::Data::Verbatim(ref verbatim) => {
      let length = verbatim.len();

      unsafe {
        ptr::copy(verbatim.as_ptr(), output.as_mut_ptr(), length)
      }
    }
    subframe::Data::Fixed(ref fixed)       => {
      let order = fixed.order as usize;

      for i in 0..order {
        output[i] = fixed.warmup[i];
      }

      fixed_restore_signal(order, &fixed.residual, output);
    }
    subframe::Data::LPC(ref lpc)           => {
      let order        = lpc.order as usize;
      let coefficients = &lpc.qlp_coefficients[0..order];

      for i in 0..order {
        output[i] = lpc.warmup[i];
      }

      lpc_restore_signal(lpc.quantization_level, coefficients, &lpc.residual,
                         output);
    }
  }

  if subframe.wasted_bits > 0 {
    for value in output {
      *value <<= subframe.wasted_bits;
    }
  }
}

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

  use subframe;
  use subframe::{
    Subframe, Fixed, LPC,
    EntropyCodingMethod, CodingMethod, PartitionedRice,
    PartitionedRiceContents,
  };

  #[test]
  fn test_fixed_restore_signal() {
    let residuals   = [ &[-19, -16, 17, -23, -7, 16, -16, -5, 3
                         , -8, -13, -15, -1][..]
                      , &[-6513][..]
                      ];
    let mut outputs = [ &mut [-729, -722, -667, 0, 0, 0, 0, 0, 0
                             , 0, 0, 0, 0, 0, 0, 0][..]
                      , &mut [21877, 27482, 0][..]
                      ];

    fixed_restore_signal(3, &residuals[0], &mut outputs[0]);
    fixed_restore_signal(2, &residuals[1], &mut outputs[1]);

    assert_eq!(&outputs[0], &[-729, -722, -667, -583, -486, -359, -225, -91
                             , 59, 209, 354, 497, 630, 740, 812, 845]);
    assert_eq!(&outputs[1], &[21877, 27482, 26574]);
  }

  #[test]
  fn test_lpc_restore_signal() {
    let residuals    = [ &[-2, -23, 14, 6, 3, -4, 12, -2, 10][..]
                       , &[3157][..]
                       ];
    let coefficients = [ &[1042, -399, -75, -269, 121, 166, -75][..]
                       , &[1757, -1199, 879, -836, 555, -255, 119][..]
                       ];
    let mut outputs = [ &mut [-796, -547, -285, -32, 199, 443, 670
                             , 0, 0, 0, 0, 0, 0, 0, 0, 0][..]
                      , &mut [-21363, -21951, -22649, -24364, -27297, -26870
                             ,-30017, 0][..]
                      ];

    lpc_restore_signal(9, &coefficients[0], &residuals[0], &mut outputs[0]);
    lpc_restore_signal(10, &coefficients[1], &residuals[1], &mut outputs[1]);

    assert_eq!(&outputs[0], &[-796, -547, -285, -32, 199, 443, 670, 875
                             , 1046, 1208, 1343, 1454, 1541, 1616, 1663
                             , 1701]);
    assert_eq!(&outputs[1], &[-21363, -21951, -22649, -24364, -27297, -26870
                             , -30017, -29718]);
  }

  #[test]
  fn test_decode() {
    let mut output = [0; 16];

    let constant = Subframe {
      data: subframe::Data::Constant(4),
      wasted_bits: 0,
    };

    let verbatim = Subframe {
      data: subframe::Data::Verbatim(vec![16, -3, 55, 49, -32, 6, 40, -90, 1
                                         ,0, 77, -12, 84, 10, -112, 136]),
      wasted_bits: 0,
    };

    let fixed = Subframe {
      data: subframe::Data::Fixed(Fixed {
        entropy_coding_method: EntropyCodingMethod {
          method_type: CodingMethod::PartitionedRice,
          data: PartitionedRice {
            order: 0,
            contents: PartitionedRiceContents {
              parameters: vec![],
              raw_bits: vec![],
            },
          },
        },
        order: 3,
        warmup: [-729, -722, -667, 0],
        residual: vec![-19, -16, 17, -23, -7, 16, -16, -5, 3 , -8, -13, -15
                      ,-1],
      }),
      wasted_bits: 0,
    };

    let lpc = Subframe {
      data: subframe::Data::LPC(LPC {
        entropy_coding_method: EntropyCodingMethod {
          method_type: CodingMethod::PartitionedRice,
          data: PartitionedRice {
            order: 0,
            contents: PartitionedRiceContents {
              parameters: vec![],
              raw_bits: vec![],
            },
          },
        },
        order: 7,
        qlp_coeff_precision: 0,
        quantization_level: 9,
        qlp_coefficients: [1042, -399, -75, -269, 121, 166, -75, 0, 0, 0, 0
                          ,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
                          ,0, 0, 0, 0],
        warmup: [-796, -547, -285, -32, 199, 443, 670, 0, 0, 0, 0, 0, 0, 0, 0
                ,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        residual: vec![-2, -23, 14, 6, 3, -4, 12, -2, 10],
      }),
      wasted_bits: 0,
    };

    decode(&constant, &mut output);
    assert_eq!(&output, &[4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4]);

    decode(&verbatim, &mut output);
    assert_eq!(&output, &[16, -3, 55, 49, -32, 6, 40 , -90, 1, 0, 77, -12, 84
                         ,10 , -112, 136]);

    decode(&fixed, &mut output);
    assert_eq!(&output, &[-729, -722, -667, -583, -486, -359, -225, -91, 59
                         ,209, 354, 497, 630, 740, 812, 845]);

    decode(&lpc, &mut output);
    assert_eq!(&output, &[-796, -547, -285, -32, 199, 443, 670, 875, 1046
                         ,1208, 1343, 1454, 1541, 1616, 1663, 1701]);
  }
}
<commit_msg>Add test for non-zero wasted_bits decoding<commit_after>use subframe;
use subframe::{Subframe, MAX_FIXED_ORDER, MAX_LPC_ORDER};

use std::ptr;

pub fn fixed_restore_signal(order: usize,
                            residual: &[i32],
                            output: &mut [i32]) {
  debug_assert!(order <= MAX_FIXED_ORDER);

  let polynomial = [ &[][..]
                   , &[1][..]
                   , &[-1, 2][..]
                   , &[1, -3, 3][..]
                   , &[-1, 4, -6, 4][..]
                   ];

  let coefficients = polynomial[order];

  for i in 0..residual.len() {
    let offset     = i + order;
    let prediction = coefficients.iter()
                      .zip(&output[i..offset])
                      .fold(0, |result, (coefficient, signal)|
                            result + coefficient * signal);

    output[offset] = residual[i] + prediction;
  }
}

pub fn lpc_restore_signal(quantization_level: i8,
                          coefficients: &[i32],
                          residual: &[i32],
                          output: &mut [i32]) {
  let order = coefficients.len();

  debug_assert!(order <= MAX_LPC_ORDER);

  for i in 0..residual.len() {
    let offset     = i + order;
    let prediction = coefficients.iter().rev()
                       .zip(&output[i..offset])
                       .fold(0, |result, (coefficient, signal)|
                             result + coefficient * signal);

    output[offset] = residual[i] + (prediction >> quantization_level);
  }
}

pub fn decode(subframe: &Subframe, output: &mut [i32]) {
  match subframe.data {
    subframe::Data::Constant(constant)     => {
      for i in 0..output.len() {
        output[i] = constant
      }
    }
    subframe::Data::Verbatim(ref verbatim) => {
      let length = verbatim.len();

      unsafe {
        ptr::copy(verbatim.as_ptr(), output.as_mut_ptr(), length)
      }
    }
    subframe::Data::Fixed(ref fixed)       => {
      let order = fixed.order as usize;

      for i in 0..order {
        output[i] = fixed.warmup[i];
      }

      fixed_restore_signal(order, &fixed.residual, output);
    }
    subframe::Data::LPC(ref lpc)           => {
      let order        = lpc.order as usize;
      let coefficients = &lpc.qlp_coefficients[0..order];

      for i in 0..order {
        output[i] = lpc.warmup[i];
      }

      lpc_restore_signal(lpc.quantization_level, coefficients, &lpc.residual,
                         output);
    }
  }

  if subframe.wasted_bits > 0 {
    for value in output {
      *value <<= subframe.wasted_bits;
    }
  }
}

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

  use subframe;
  use subframe::{
    Subframe, Fixed, LPC,
    EntropyCodingMethod, CodingMethod, PartitionedRice,
    PartitionedRiceContents,
  };

  #[test]
  fn test_fixed_restore_signal() {
    let residuals   = [ &[-19, -16, 17, -23, -7, 16, -16, -5, 3
                         , -8, -13, -15, -1][..]
                      , &[-6513][..]
                      ];
    let mut outputs = [ &mut [-729, -722, -667, 0, 0, 0, 0, 0, 0
                             , 0, 0, 0, 0, 0, 0, 0][..]
                      , &mut [21877, 27482, 0][..]
                      ];

    fixed_restore_signal(3, &residuals[0], &mut outputs[0]);
    fixed_restore_signal(2, &residuals[1], &mut outputs[1]);

    assert_eq!(&outputs[0], &[-729, -722, -667, -583, -486, -359, -225, -91
                             , 59, 209, 354, 497, 630, 740, 812, 845]);
    assert_eq!(&outputs[1], &[21877, 27482, 26574]);
  }

  #[test]
  fn test_lpc_restore_signal() {
    let residuals    = [ &[-2, -23, 14, 6, 3, -4, 12, -2, 10][..]
                       , &[3157][..]
                       ];
    let coefficients = [ &[1042, -399, -75, -269, 121, 166, -75][..]
                       , &[1757, -1199, 879, -836, 555, -255, 119][..]
                       ];
    let mut outputs = [ &mut [-796, -547, -285, -32, 199, 443, 670
                             , 0, 0, 0, 0, 0, 0, 0, 0, 0][..]
                      , &mut [-21363, -21951, -22649, -24364, -27297, -26870
                             ,-30017, 0][..]
                      ];

    lpc_restore_signal(9, &coefficients[0], &residuals[0], &mut outputs[0]);
    lpc_restore_signal(10, &coefficients[1], &residuals[1], &mut outputs[1]);

    assert_eq!(&outputs[0], &[-796, -547, -285, -32, 199, 443, 670, 875
                             , 1046, 1208, 1343, 1454, 1541, 1616, 1663
                             , 1701]);
    assert_eq!(&outputs[1], &[-21363, -21951, -22649, -24364, -27297, -26870
                             , -30017, -29718]);
  }

  #[test]
  fn test_decode() {
    let mut output = [0; 16];

    let constant = Subframe {
      data: subframe::Data::Constant(4),
      wasted_bits: 0,
    };

    let verbatim = Subframe {
      data: subframe::Data::Verbatim(vec![16, -3, 55, 49, -32, 6, 40, -90, 1
                                         ,0, 77, -12, 84, 10, -112, 136]),
      wasted_bits: 0,
    };

    let fixed = Subframe {
      data: subframe::Data::Fixed(Fixed {
        entropy_coding_method: EntropyCodingMethod {
          method_type: CodingMethod::PartitionedRice,
          data: PartitionedRice {
            order: 0,
            contents: PartitionedRiceContents {
              parameters: vec![],
              raw_bits: vec![],
            },
          },
        },
        order: 3,
        warmup: [-729, -722, -667, 0],
        residual: vec![-19, -16, 17, -23, -7, 16, -16, -5, 3 , -8, -13, -15
                      ,-1],
      }),
      wasted_bits: 0,
    };

    let lpc = Subframe {
      data: subframe::Data::LPC(LPC {
        entropy_coding_method: EntropyCodingMethod {
          method_type: CodingMethod::PartitionedRice,
          data: PartitionedRice {
            order: 0,
            contents: PartitionedRiceContents {
              parameters: vec![],
              raw_bits: vec![],
            },
          },
        },
        order: 7,
        qlp_coeff_precision: 0,
        quantization_level: 9,
        qlp_coefficients: [1042, -399, -75, -269, 121, 166, -75, 0, 0, 0, 0
                          ,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
                          ,0, 0, 0, 0],
        warmup: [-796, -547, -285, -32, 199, 443, 670, 0, 0, 0, 0, 0, 0, 0, 0
                ,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        residual: vec![-2, -23, 14, 6, 3, -4, 12, -2, 10],
      }),
      wasted_bits: 0,
    };

    decode(&constant, &mut output);
    assert_eq!(&output, &[4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4]);

    decode(&verbatim, &mut output);
    assert_eq!(&output, &[16, -3, 55, 49, -32, 6, 40 , -90, 1, 0, 77, -12, 84
                         ,10 , -112, 136]);

    decode(&fixed, &mut output);
    assert_eq!(&output, &[-729, -722, -667, -583, -486, -359, -225, -91, 59
                         ,209, 354, 497, 630, 740, 812, 845]);

    decode(&lpc, &mut output);
    assert_eq!(&output, &[-796, -547, -285, -32, 199, 443, 670, 875, 1046
                         ,1208, 1343, 1454, 1541, 1616, 1663, 1701]);
  }

  #[test]
  fn test_wasted_bit_decode() {
    let mut output = [0; 4];

    let constant = Subframe {
      data: subframe::Data::Constant(1),
      wasted_bits: 10,
    };

    decode(&constant, &mut output);
    assert_eq!(&output, &[1024, 1024, 1024, 1024]);
  }
}
<|endoftext|>


<commit_before>// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use ffi::CStr;
use io;
use libc::{c_char, c_ulong};
use mem;
use sys::backtrace::BacktraceContext;
use sys::backtrace::StackWalkVariant;
use sys::c;
use sys::dynamic_lib::DynamicLibrary;
use sys_common::backtrace::Frame;

// Structs holding printing functions and loaders for them
// Two versions depending on whether dbghelp.dll has StackWalkEx or not
// (the former being in newer Windows versions, the older being in Win7 and before)
pub struct PrintingFnsEx {
    resolve_symname: SymFromInlineContextFn,
    sym_get_line: SymGetLineFromInlineContextFn,
}
pub struct PrintingFns64 {
    resolve_symname: SymFromAddrFn,
    sym_get_line: SymGetLineFromAddr64Fn,
}

pub fn load_printing_fns_ex(dbghelp: &DynamicLibrary) -> io::Result<PrintingFnsEx> {
    Ok(PrintingFnsEx{
        resolve_symname: sym!(dbghelp, "SymFromInlineContext",
                              SymFromInlineContextFn)?,
        sym_get_line: sym!(dbghelp, "SymGetLineFromInlineContext",
                           SymGetLineFromInlineContextFn)?,
    })
}
pub fn load_printing_fns_64(dbghelp: &DynamicLibrary) -> io::Result<PrintingFns64> {
    Ok(PrintingFns64{
        resolve_symname: sym!(dbghelp, "SymFromAddr", SymFromAddrFn)?,
        sym_get_line: sym!(dbghelp, "SymGetLineFromAddr64",
                     SymGetLineFromAddr64Fn)?,
    })
}

type SymFromInlineContextFn =
    unsafe extern "system" fn(c::HANDLE, u64, c::ULONG, *mut u64, *mut c::SYMBOL_INFO) -> c::BOOL;
type SymGetLineFromInlineContextFn = unsafe extern "system" fn(
    c::HANDLE,
    u64,
    c::ULONG,
    u64,
    *mut c::DWORD,
    *mut c::IMAGEHLP_LINE64,
) -> c::BOOL;

type SymFromAddrFn =
    unsafe extern "system" fn(c::HANDLE, u64, *mut u64, *mut c::SYMBOL_INFO) -> c::BOOL;
type SymGetLineFromAddr64Fn =
    unsafe extern "system" fn(c::HANDLE, u64, *mut u32, *mut c::IMAGEHLP_LINE64) -> c::BOOL;

/// Converts a pointer to symbol to its string value.
pub fn resolve_symname<F>(frame: Frame, callback: F, context: &BacktraceContext) -> io::Result<()>
where
    F: FnOnce(Option<&str>) -> io::Result<()>,
{
    match context.StackWalkVariant {
        StackWalkVariant::StackWalkEx(_, ref fns) => {
            resolve_symname_from_inline_context(fns.resolve_symname, frame, callback, context)
        },
        StackWalkVariant::StackWalk64(_, ref fns) => {
            resolve_symname_from_addr(fns.resolve_symname, frame, callback, context)
        }
    }
}

fn resolve_symname_from_inline_context<F>(
    SymFromInlineContext: SymFromInlineContextFn,
    frame: Frame, callback: F, context: &BacktraceContext) -> io::Result<()>
where
    F: FnOnce(Option<&str>) -> io::Result<()>,
{
    unsafe {
            let mut info: c::SYMBOL_INFO = mem::zeroed();
            info.MaxNameLen = c::MAX_SYM_NAME as c_ulong;
            // the struct size in C.  the value is different to
            // `size_of::<SYMBOL_INFO>() - MAX_SYM_NAME + 1` (== 81)
            // due to struct alignment.
            info.SizeOfStruct = 88;

            let mut displacement = 0u64;
            let ret = SymFromInlineContext(
                context.handle,
                frame.symbol_addr as u64,
                frame.inline_context,
                &mut displacement,
                &mut info,
            );
            let valid_range =
                if ret == c::TRUE && frame.symbol_addr as usize >= info.Address as usize {
                    if info.Size != 0 {
                        (frame.symbol_addr as usize) < info.Address as usize + info.Size as usize
                    } else {
                        true
                    }
                } else {
                    false
                };
            let symname = if valid_range {
                let ptr = info.Name.as_ptr() as *const c_char;
                CStr::from_ptr(ptr).to_str().ok()
            } else {
                None
            };
            callback(symname)
        }
}

fn resolve_symname_from_addr<F>(
    SymFromAddr: SymFromAddrFn,
    frame: Frame, callback: F, context: &BacktraceContext) -> io::Result<()>
where
    F: FnOnce(Option<&str>) -> io::Result<()>,
{
    unsafe {
        let mut info: c::SYMBOL_INFO = mem::zeroed();
        info.MaxNameLen = c::MAX_SYM_NAME as c_ulong;
        // the struct size in C.  the value is different to
        // `size_of::<SYMBOL_INFO>() - MAX_SYM_NAME + 1` (== 81)
        // due to struct alignment.
        info.SizeOfStruct = 88;

        let mut displacement = 0u64;
        let ret = SymFromAddr(
            context.handle,
            frame.symbol_addr as u64,
            &mut displacement,
            &mut info,
        );

        let symname = if ret == c::TRUE {
            let ptr = info.Name.as_ptr() as *const c_char;
            CStr::from_ptr(ptr).to_str().ok()
        } else {
            None
        };
        callback(symname)
    }
}

pub fn foreach_symbol_fileline<F>(
    frame: Frame,
    f: F,
    context: &BacktraceContext,
) -> io::Result<bool>
where
    F: FnMut(&[u8], u32) -> io::Result<()>,
{
    match context.StackWalkVariant {
        StackWalkVariant::StackWalkEx(_, ref fns) =>
            foreach_symbol_fileline_ex(fns.sym_get_line, frame, f, context),
        StackWalkVariant::StackWalk64(_, ref fns) =>
            foreach_symbol_fileline_64(fns.sym_get_line, frame, f, context),
    }
}

fn foreach_symbol_fileline_ex<F>(
    SymGetLineFromInlineContext: SymGetLineFromInlineContextFn,
    frame: Frame,
    mut f: F,
    context: &BacktraceContext,
) -> io::Result<bool>
where
    F: FnMut(&[u8], u32) -> io::Result<()>,
{
    unsafe {
        let mut line: c::IMAGEHLP_LINE64 = mem::zeroed();
        line.SizeOfStruct = ::mem::size_of::<c::IMAGEHLP_LINE64>() as u32;

        let mut displacement = 0u32;
        let ret = SymGetLineFromInlineContext(
            context.handle,
            frame.exact_position as u64,
            frame.inline_context,
            0,
            &mut displacement,
            &mut line,
        );
        if ret == c::TRUE {
            let name = CStr::from_ptr(line.Filename).to_bytes();
            f(name, line.LineNumber as u32)?;
        }
        Ok(false)
    }
}

fn foreach_symbol_fileline_64<F>(
    SymGetLineFromAddr64: SymGetLineFromAddr64Fn,
    frame: Frame,
    mut f: F,
    context: &BacktraceContext,
) -> io::Result<bool>
where
    F: FnMut(&[u8], u32) -> io::Result<()>,
{
    unsafe {
        let mut line: c::IMAGEHLP_LINE64 = mem::zeroed();
        line.SizeOfStruct = ::mem::size_of::<c::IMAGEHLP_LINE64>() as u32;

        let mut displacement = 0u32;
        let ret = SymGetLineFromAddr64(
            context.handle,
            frame.exact_position as u64,
            &mut displacement,
            &mut line,
        );
        if ret == c::TRUE {
            let name = CStr::from_ptr(line.Filename).to_bytes();
            f(name, line.LineNumber as u32)?;
        }
        Ok(false)
    }
}
<commit_msg>Make msvc symbol extraction/printing functions generic.<commit_after>// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use ffi::CStr;
use io;
use libc::{c_char, c_ulong};
use mem;
use sys::backtrace::BacktraceContext;
use sys::backtrace::StackWalkVariant;
use sys::c;
use sys::dynamic_lib::DynamicLibrary;
use sys_common::backtrace::Frame;

// Structs holding printing functions and loaders for them
// Two versions depending on whether dbghelp.dll has StackWalkEx or not
// (the former being in newer Windows versions, the older being in Win7 and before)
pub struct PrintingFnsEx {
    resolve_symname: SymFromInlineContextFn,
    sym_get_line: SymGetLineFromInlineContextFn,
}
pub struct PrintingFns64 {
    resolve_symname: SymFromAddrFn,
    sym_get_line: SymGetLineFromAddr64Fn,
}

pub fn load_printing_fns_ex(dbghelp: &DynamicLibrary) -> io::Result<PrintingFnsEx> {
    Ok(PrintingFnsEx {
        resolve_symname: sym!(dbghelp, "SymFromInlineContext", SymFromInlineContextFn)?,
        sym_get_line: sym!(
            dbghelp,
            "SymGetLineFromInlineContext",
            SymGetLineFromInlineContextFn
        )?,
    })
}
pub fn load_printing_fns_64(dbghelp: &DynamicLibrary) -> io::Result<PrintingFns64> {
    Ok(PrintingFns64 {
        resolve_symname: sym!(dbghelp, "SymFromAddr", SymFromAddrFn)?,
        sym_get_line: sym!(dbghelp, "SymGetLineFromAddr64", SymGetLineFromAddr64Fn)?,
    })
}

type SymFromAddrFn =
    unsafe extern "system" fn(c::HANDLE, u64, *mut u64, *mut c::SYMBOL_INFO) -> c::BOOL;
type SymFromInlineContextFn =
    unsafe extern "system" fn(c::HANDLE, u64, c::ULONG, *mut u64, *mut c::SYMBOL_INFO) -> c::BOOL;

type SymGetLineFromAddr64Fn =
    unsafe extern "system" fn(c::HANDLE, u64, *mut u32, *mut c::IMAGEHLP_LINE64) -> c::BOOL;
type SymGetLineFromInlineContextFn = unsafe extern "system" fn(
    c::HANDLE,
    u64,
    c::ULONG,
    u64,
    *mut c::DWORD,
    *mut c::IMAGEHLP_LINE64,
) -> c::BOOL;

/// Converts a pointer to symbol to its string value.
pub fn resolve_symname<F>(frame: Frame, callback: F, context: &BacktraceContext) -> io::Result<()>
where
    F: FnOnce(Option<&str>) -> io::Result<()>,
{
    match context.StackWalkVariant {
        StackWalkVariant::StackWalkEx(_, ref fns) => {
            resolve_symname_internal(fns.resolve_symname, frame, callback, context)
        }
        StackWalkVariant::StackWalk64(_, ref fns) => {
            resolve_symname_internal(fns.resolve_symname, frame, callback, context)
        }
    }
}

fn resolve_symname_internal<F, R>(
    symbol_resolver: R,
    frame: Frame,
    callback: F,
    context: &BacktraceContext,
) -> io::Result<()>
where
    F: FnOnce(Option<&str>) -> io::Result<()>,
    R: SymbolResolver,
{
    unsafe {
        let mut info: c::SYMBOL_INFO = mem::zeroed();
        info.MaxNameLen = c::MAX_SYM_NAME as c_ulong;
        // the struct size in C.  the value is different to
        // `size_of::<SYMBOL_INFO>() - MAX_SYM_NAME + 1` (== 81)
        // due to struct alignment.
        info.SizeOfStruct = 88;

        let ret = symbol_resolver.resolve_symbol(
            context.handle,
            frame.symbol_addr as u64,
            frame.inline_context,
            &mut info,
        );
        let valid_range = if ret == c::TRUE && frame.symbol_addr as usize >= info.Address as usize {
            if info.Size != 0 {
                (frame.symbol_addr as usize) < info.Address as usize + info.Size as usize
            } else {
                true
            }
        } else {
            false
        };
        let symname = if valid_range {
            let ptr = info.Name.as_ptr() as *const c_char;
            CStr::from_ptr(ptr).to_str().ok()
        } else {
            None
        };
        callback(symname)
    }
}

trait SymbolResolver {
    fn resolve_symbol(
        &self,
        process: c::HANDLE,
        symbol_address: u64,
        inline_context: c::ULONG,
        info: *mut c::SYMBOL_INFO,
    ) -> c::BOOL;
}

impl SymbolResolver for SymFromAddrFn {
    fn resolve_symbol(
        &self,
        process: c::HANDLE,
        symbol_address: u64,
        _inline_context: c::ULONG,
        info: *mut c::SYMBOL_INFO,
    ) -> c::BOOL {
        unsafe {
            let mut displacement = 0u64;
            self(process, symbol_address, &mut displacement, info)
        }
    }
}

impl SymbolResolver for SymFromInlineContextFn {
    fn resolve_symbol(
        &self,
        process: c::HANDLE,
        symbol_address: u64,
        inline_context: c::ULONG,
        info: *mut c::SYMBOL_INFO,
    ) -> c::BOOL {
        unsafe {
            let mut displacement = 0u64;
            self(
                process,
                symbol_address,
                inline_context,
                &mut displacement,
                info,
            )
        }
    }
}

pub fn foreach_symbol_fileline<F>(
    frame: Frame,
    callback: F,
    context: &BacktraceContext,
) -> io::Result<bool>
where
    F: FnMut(&[u8], u32) -> io::Result<()>,
{
    match context.StackWalkVariant {
        StackWalkVariant::StackWalkEx(_, ref fns) => {
            foreach_symbol_fileline_iternal(fns.sym_get_line, frame, callback, context)
        }
        StackWalkVariant::StackWalk64(_, ref fns) => {
            foreach_symbol_fileline_iternal(fns.sym_get_line, frame, callback, context)
        }
    }
}

fn foreach_symbol_fileline_iternal<F, G>(
    line_getter: G,
    frame: Frame,
    mut callback: F,
    context: &BacktraceContext,
) -> io::Result<bool>
where
    F: FnMut(&[u8], u32) -> io::Result<()>,
    G: LineGetter,
{
    unsafe {
        let mut line: c::IMAGEHLP_LINE64 = mem::zeroed();
        line.SizeOfStruct = ::mem::size_of::<c::IMAGEHLP_LINE64>() as u32;

        let ret = line_getter.get_line(
            context.handle,
            frame.exact_position as u64,
            frame.inline_context,
            &mut line,
        );
        if ret == c::TRUE {
            let name = CStr::from_ptr(line.Filename).to_bytes();
            callback(name, line.LineNumber as u32)?;
        }
        Ok(false)
    }
}

trait LineGetter {
    fn get_line(
        &self,
        process: c::HANDLE,
        frame_address: u64,
        inline_context: c::ULONG,
        line: *mut c::IMAGEHLP_LINE64,
    ) -> c::BOOL;
}

impl LineGetter for SymGetLineFromAddr64Fn {
    fn get_line(
        &self,
        process: c::HANDLE,
        frame_address: u64,
        _inline_context: c::ULONG,
        line: *mut c::IMAGEHLP_LINE64,
    ) -> c::BOOL {
        unsafe {
            let mut displacement = 0u32;
            self(process, frame_address, &mut displacement, line)
        }
    }
}

impl LineGetter for SymGetLineFromInlineContextFn {
    fn get_line(
        &self,
        process: c::HANDLE,
        frame_address: u64,
        inline_context: c::ULONG,
        line: *mut c::IMAGEHLP_LINE64,
    ) -> c::BOOL {
        unsafe {
            let mut displacement = 0u32;
            self(
                process,
                frame_address,
                inline_context,
                0,
                &mut displacement,
                line,
            )
        }
    }
}
<|endoftext|>


<commit_before>extern crate startuppong;
extern crate regex;

use std::io::Write;
use std::str;

use regex::Regex;

pub use startuppong::account_from_env;

use handler::MessageHandler;
use handler::HandlerResult;
use handler::HandlerError;
use message::IncomingMessage;

pub type Account = startuppong::Account;

/// Startuppong.com get_players handler
///
/// Messages requesting to "show ping pong" or "list ping pong" will result in a dump
/// of the ladder, 1 line per player, to the requester.
pub struct PrintLadder {
    account: Account,
    regex: Regex
}

impl PrintLadder {
    /// Create the PrintLadder handler.
    ///
    /// Requires a startuppong::Account. You will need to register on startuppong.com if you wish to
    /// use this handler.
    pub fn new(account: Account) -> PrintLadder {
        PrintLadder {
            account: account,
            regex: regex!(r"(print|list|show).+ping ?pong")
        }
    }
}

impl MessageHandler for PrintLadder {
    fn name(&self) -> &str {
        "PrintLadder"
    }

    fn re(&self) -> &Regex {
        &self.regex
    }

    fn handle(&self, incoming: &IncomingMessage) -> HandlerResult {
        let res = try!(startuppong::get_players(&self.account));
        let players = res.players();
        let mut reply = Vec::new();

        for player in &players {
            try!(write!(&mut reply, "{} - {}\n", player.rank, player.name));
        }

        Ok(try!(incoming.reply(str::from_utf8(&reply).unwrap().to_string())))
    }
}

impl From<startuppong::error::ApiError> for HandlerError {
    fn from(err: startuppong::error::ApiError) -> HandlerError {
        HandlerError::Other(Box::new(err))
    }
}

#[cfg(test)]
mod tests {
    use startuppong::Account;

    use handler::MessageHandler;
    use super::PrintLadder;

    #[test]
    fn check_valid_messages() {
        let acc = Account::new("a".to_string(), "b".to_string());
        let handler = PrintLadder::new(&acc);

        assert!(handler.can_handle("print the pingpong ladder"));
        assert!(handler.can_handle("list the pingpong ladder"));
        assert!(handler.can_handle("show the pingpong ladder"));
        assert!(handler.can_handle("print the ping pong ladder"));
        assert!(handler.can_handle("list the ping pong ladder"));
        assert!(handler.can_handle("show the ping pong ladder"));
    }

    #[test]
    fn check_invalid_messages() {
        let acc = Account::new("a".to_string(), "b".to_string());
        let handler = PrintLadder::new(&acc);

        assert!(!handler.can_handle("who's on top of ping pong ladder?"));
    }
}
<commit_msg>sup ladder printer has line limiting<commit_after>extern crate startuppong;
extern crate regex;

use std::io::Write;
use std::str;

use regex::Regex;

pub use startuppong::account_from_env;

use handler::MessageHandler;
use handler::HandlerResult;
use handler::HandlerError;
use message::IncomingMessage;

pub type Account = startuppong::Account;

/// Startuppong.com get_players handler
///
/// Messages requesting to "show ping pong" or "list ping pong" will result in a dump
/// of the ladder, 1 line per player, to the requester.
pub struct PrintLadder {
    account: Account,
    regex: Regex,
    max_entries: usize
}

impl PrintLadder {
    /// Create the PrintLadder handler.
    ///
    /// Requires a startuppong::Account. You will need to register on startuppong.com if you wish to
    /// use this handler.
    pub fn new(account: Account, max_entries: usize) -> PrintLadder {
        PrintLadder {
            account: account,
            regex: regex!(r"(print|list|show).+ping ?pong"),
            max_entries: max_entries
        }
    }
}

impl MessageHandler for PrintLadder {
    fn name(&self) -> &str {
        "PrintLadder"
    }

    fn re(&self) -> &Regex {
        &self.regex
    }

    fn handle(&self, incoming: &IncomingMessage) -> HandlerResult {
        let res = try!(startuppong::get_players(&self.account));
        let players = res.players();
        let mut reply = Vec::new();

        let len = if players.len() < self.max_entries {
            players.len()
        } else {
            self.max_entries
        };

        for i in 0..len {
            try!(write!(&mut reply, "{} - {}\n", players[i].rank, players[i].name));
        }

        Ok(try!(incoming.reply(str::from_utf8(&reply).unwrap().to_string())))
    }
}

impl From<startuppong::error::ApiError> for HandlerError {
    fn from(err: startuppong::error::ApiError) -> HandlerError {
        HandlerError::Other(Box::new(err))
    }
}

#[cfg(test)]
mod tests {
    use startuppong::Account;

    use handler::MessageHandler;
    use super::PrintLadder;

    #[test]
    fn check_valid_messages() {
        let acc = Account::new("a".to_string(), "b".to_string());
        let handler = PrintLadder::new(&acc);

        assert!(handler.can_handle("print the pingpong ladder"));
        assert!(handler.can_handle("list the pingpong ladder"));
        assert!(handler.can_handle("show the pingpong ladder"));
        assert!(handler.can_handle("print the ping pong ladder"));
        assert!(handler.can_handle("list the ping pong ladder"));
        assert!(handler.can_handle("show the ping pong ladder"));
    }

    #[test]
    fn check_invalid_messages() {
        let acc = Account::new("a".to_string(), "b".to_string());
        let handler = PrintLadder::new(&acc);

        assert!(!handler.can_handle("who's on top of ping pong ladder?"));
    }
}
<|endoftext|>


<commit_before>use crate::utils::agent::{Agent, Context, Handler, Message};
use crate::utils::BoxError;
use crate::web::read_body;
use err_derive::Error;
use headers::{CacheControl, HeaderMapExt};
use http::{Request, StatusCode};
use hyper::client::{Client, HttpConnector};
use hyper::Body;
use hyper_tls::HttpsConnector;
use std::time::Duration;
use url::Url;

#[derive(Debug, Error)]
pub enum FetchError {
    #[error(display = "HTTP request failed: {}", _0)]
    Hyper(#[error(source)] hyper::Error),
    #[error(display = "unexpected HTTP status code: {}", _0)]
    BadStatus(StatusCode),
    #[error(display = "could not read HTTP response body: {}", _0)]
    Read(BoxError),
    #[error(display = "invalid UTF-8 in HTTP response body: {}", _0)]
    Utf8(#[error(source)] std::string::FromUtf8Error),
}

/// The result of fetching a URL.
pub struct FetchUrlResult {
    /// Data from the response.
    pub data: String,
    /// Public caching age from  `Cache-Control` header, or 0.
    pub max_age: Duration,
}

/// Message containing an HTTP request to make.
pub struct FetchUrl {
    /// The request to make.
    pub request: Request<Body>,
}
impl Message for FetchUrl {
    type Reply = Result<FetchUrlResult, FetchError>;
}
impl FetchUrl {
    /// Create a simple GET request message.
    pub fn get(url: &Url) -> Self {
        let hyper_uri: hyper::Uri = url
            .as_str()
            .parse()
            .expect("could not convert Url to Hyper Url");
        let request = Request::get(hyper_uri)
            .body(Body::empty())
            .expect("could not build GET request");
        FetchUrl { request }
    }
}

/// Agent that fetches URLs.
pub struct FetchAgent {
    client: Client<HttpsConnector<HttpConnector>>,
}

impl FetchAgent {
    pub fn new() -> Self {
        let connector = HttpsConnector::new();
        let client = Client::builder().build(connector);
        FetchAgent { client }
    }
}

impl Agent for FetchAgent {}

impl Handler<FetchUrl> for FetchAgent {
    fn handle(&mut self, message: FetchUrl, cx: Context<Self, FetchUrl>) {
        let future = self.client.request(message.request);
        cx.reply_later(async {
            let res = future.await?;
            if !res.status().is_success() {
                return Err(FetchError::BadStatus(res.status()));
            }

            // Grab the max-age directive from the Cache-Control header.
            let max_age = res
                .headers()
                .typed_get()
                .and_then(|header: CacheControl| header.max_age())
                .unwrap_or_else(|| Duration::from_secs(0));

            let chunk = read_body(res.into_body()).await.map_err(FetchError::Read)?;
            let data = String::from_utf8(chunk.to_vec())?;
            Ok(FetchUrlResult { data, max_age })
        });
    }
}
<commit_msg>Set `User-Agent` on all outgoing requests<commit_after>use crate::utils::agent::{Agent, Context, Handler, Message};
use crate::utils::BoxError;
use crate::web::read_body;
use err_derive::Error;
use headers::{CacheControl, HeaderMapExt};
use http::{HeaderValue, Request, StatusCode};
use hyper::client::{Client, HttpConnector};
use hyper::Body;
use hyper_tls::HttpsConnector;
use std::time::Duration;
use url::Url;

#[derive(Debug, Error)]
pub enum FetchError {
    #[error(display = "HTTP request failed: {}", _0)]
    Hyper(#[error(source)] hyper::Error),
    #[error(display = "unexpected HTTP status code: {}", _0)]
    BadStatus(StatusCode),
    #[error(display = "could not read HTTP response body: {}", _0)]
    Read(BoxError),
    #[error(display = "invalid UTF-8 in HTTP response body: {}", _0)]
    Utf8(#[error(source)] std::string::FromUtf8Error),
}

/// The result of fetching a URL.
pub struct FetchUrlResult {
    /// Data from the response.
    pub data: String,
    /// Public caching age from  `Cache-Control` header, or 0.
    pub max_age: Duration,
}

/// Message containing an HTTP request to make.
pub struct FetchUrl {
    /// The request to make.
    pub request: Request<Body>,
}
impl Message for FetchUrl {
    type Reply = Result<FetchUrlResult, FetchError>;
}
impl FetchUrl {
    /// Create a simple GET request message.
    pub fn get(url: &Url) -> Self {
        let hyper_uri: hyper::Uri = url
            .as_str()
            .parse()
            .expect("could not convert Url to Hyper Url");
        let request = Request::get(hyper_uri)
            .body(Body::empty())
            .expect("could not build GET request");
        FetchUrl { request }
    }
}

/// Agent that fetches URLs.
pub struct FetchAgent {
    client: Client<HttpsConnector<HttpConnector>>,
    user_agent: HeaderValue,
}

impl FetchAgent {
    pub fn new() -> Self {
        FetchAgent {
            client: Client::builder().build(HttpsConnector::new()),
            user_agent: HeaderValue::from_str(&format!("portier.io/{}", env!("CARGO_PKG_VERSION")))
                .expect("Could not prepare User-Agent header"),
        }
    }
}

impl Agent for FetchAgent {}

impl Handler<FetchUrl> for FetchAgent {
    fn handle(&mut self, mut message: FetchUrl, cx: Context<Self, FetchUrl>) {
        message
            .request
            .headers_mut()
            .insert("User-Agent", self.user_agent.clone());

        let future = self.client.request(message.request);
        cx.reply_later(async {
            let res = future.await?;
            if !res.status().is_success() {
                return Err(FetchError::BadStatus(res.status()));
            }

            // Grab the max-age directive from the Cache-Control header.
            let max_age = res
                .headers()
                .typed_get()
                .and_then(|header: CacheControl| header.max_age())
                .unwrap_or_else(|| Duration::from_secs(0));

            let chunk = read_body(res.into_body()).await.map_err(FetchError::Read)?;
            let data = String::from_utf8(chunk.to_vec())?;
            Ok(FetchUrlResult { data, max_age })
        });
    }
}
<|endoftext|>