<commit_before>//! Operations on manifest files.

use std::error::Error;
use std::fmt::{self, Display, Formatter};
use std::io::{self, BufRead};
use std::path::Path;
use std::str::{self, FromStr};
use std::string::FromUtf8Error;
use std::usize;

use rawpath::RawPath;

/// Manifest file info.
pub struct Manifest {
    hostname: String,
    local_dir: RawPath,
    volumes: Vec<Option<Volume>>,
}

/// Volume info.
pub struct Volume {
    start_path: RawPath,
    end_path: RawPath,
    start_block: Option<usize>,
    end_block: Option<usize>,
    hash_type: String,
    hash: Vec<u8>,
}

/// wip
#[derive(Debug)]
pub enum ParseError {
    /// wip
    Io(io::Error),
    /// wip
    MissingKeyword(String),
    /// wip
    Utf8Error(FromUtf8Error),
}

struct ManifestParser<R> {
    input: R,
    buf: Vec<u8>,
}

impl Manifest {
    /// Parses a stream to get a manifest.
    pub fn parse<R: BufRead>(m: &mut R) -> Result<Self, ParseError> {
        let parser = ManifestParser::new(m);
        parser.parse()
    }

    /// wip
    pub fn hostname(&self) -> Option<&str> {
        Some(&self.hostname)
    }

    /// wip
    pub fn local_dir(&self) -> Option<&Path> {
        self.local_dir.as_path()
    }

    /// wip
    pub fn max_vol_num(&self) -> usize {
        self.volumes.len()
    }

    /// wip
    pub fn volume(&self, num: usize) -> Option<&Volume> {
        self.volumes.get(num).and_then(|v| v.as_ref())
    }
}

impl Volume {
    /// wip
    pub fn start_path(&self) -> Option<&Path> {
        self.start_path.as_path()
    }

    /// wip
    pub fn end_path(&self) -> Option<&Path> {
        self.end_path.as_path()
    }

    /// wip
    pub fn start_path_bytes(&self) -> &[u8] {
        self.start_path.as_bytes()
    }

    /// wip
    pub fn end_path_bytes(&self) -> &[u8] {
        self.end_path.as_bytes()
    }

    /// wip
    pub fn start_block(&self) -> Option<usize> {
        self.start_block
    }

    /// wip
    pub fn end_block(&self) -> Option<usize> {
        self.end_block
    }

    /// wip
    pub fn hash_type(&self) -> &str {
        &self.hash_type
    }

    /// wip
    pub fn hash(&self) -> &[u8] {
        &self.hash
    }
}

impl Error for ParseError {
    fn description(&self) -> &str {
        match *self {
            ParseError::Io(ref err) => err.description(),
            ParseError::MissingKeyword(_) => "missing keyword in manifest",
            ParseError::Utf8Error(ref err) => err.description(),
        }
    }
}

impl Display for ParseError {
    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
        match *self {
            ParseError::Io(ref e) => write!(fmt, "{}", e),
            ParseError::MissingKeyword(ref e) => write!(fmt, "missing keyword '{}' in manifest", e),
            ParseError::Utf8Error(ref e) => write!(fmt, "{}", e),
        }
    }
}

impl From<io::Error> for ParseError {
    fn from(err: io::Error) -> ParseError {
        ParseError::Io(err)
    }
}

impl From<FromUtf8Error> for ParseError {
    fn from(err: FromUtf8Error) -> ParseError {
        ParseError::Utf8Error(err)
    }
}

impl<R: BufRead> ManifestParser<R> {
    pub fn new(input: R) -> Self {
        ManifestParser {
            input: input,
            buf: vec![],
        }
    }

    pub fn parse(mut self) -> Result<Manifest, ParseError> {
        let hostname = try!(self.read_param_str("Hostname"));
        let local_dir = RawPath::with_bytes(try!(self.read_param_bytes("Localdir")));

        let mut volumes = Vec::new();
        while let Some((vol, i)) = try!(self.read_volume()) {
            // resize volumes if necessary
            if i >= volumes.len() {
                volumes.reserve(i + 1);
                for _ in volumes.len()..i + 1 {
                    volumes.push(None);
                }
            }
            volumes[i] = Some(vol);
        }

        Ok(Manifest {
            hostname: hostname,
            local_dir: local_dir,
            volumes: volumes,
        })
    }

    fn read_volume(&mut self) -> Result<Option<(Volume, usize)>, ParseError> {
        // volume number
        let mut param = try!(self.read_param_str("Volume"));
        if param.ends_with(":") {
            param.pop();
        }
        let num = usize::from_str(&param);

        unimplemented!()
    }

    fn read_param_bytes(&mut self, key: &str) -> Result<Vec<u8>, ParseError> {
        try!(self.consume_whitespace());
        if !try!(self.consume_keyword(key)) {
            return Err(ParseError::MissingKeyword(key.to_owned()));
        }
        try!(self.consume_whitespace());
        match self.read_param_value() {
            Ok(res) => {
                try!(self.consume_newline());
                Ok(res)
            }
            Err(e) => Err(From::from(e)),
        }
    }

    fn read_param_str(&mut self, key: &str) -> Result<String, ParseError> {
        let bytes = try!(self.read_param_bytes(key));
        String::from_utf8(bytes).map_err(From::from)
    }

    fn consume_keyword(&mut self, key: &str) -> io::Result<bool> {
        try!(self.input.read_until(b' ', &mut self.buf));
        Ok(match_keyword(&self.buf, key))
    }

    fn consume_newline(&mut self) -> io::Result<()> {
        self.consume_until(|b| {
            match b {
                b' ' | b'\t' | b'\r' | b'\n' => true,
                _ => false,
            }
        })
    }

    fn consume_whitespace(&mut self) -> io::Result<()> {
        self.consume_until(|b| {
            match b {
                b' ' | b'\t' => true,
                _ => false,
            }
        })
    }

    fn consume_until<F>(&mut self, mut f: F) -> io::Result<()>
        where F: FnMut(u8) -> bool
    {
        loop {
            let (pos, end) = {
                let buf = try!(self.input.fill_buf());
                match buf.iter().cloned().position(&mut f) {
                    Some(p) => (p, true),
                    None => (buf.len(), buf.is_empty()),
                }
            };
            self.input.consume(pos);
            if end {
                return Ok(());
            }
        }
    }

    fn read_param_value(&mut self) -> io::Result<Vec<u8>> {
        if try!(self.consume_byte(b'"')) {
            try!(self.input.read_until(b'"', &mut self.buf));
        } else {
            try!(self.input.read_until(b'\n', &mut self.buf));
        }
        let mut result = Vec::with_capacity(self.buf.len());
        // unescape
        for (i, b) in self.buf.iter().cloned().enumerate() {
            if b != b'\\' {
                result.push(b);
            } else {
                // expects a \xNN where NN is a number string representing the escaped char in hex
                // e.g. \x20 is the space ' '
                if self.buf.len() - i >= 4 && self.buf[i + 1] == b'x' {
                    let num = ((self.buf[i + 2] - b'0') << 4) + self.buf[i + 3] - b'0';
                    result.push(num);
                }
            }
        }
        Ok(result)
    }

    fn consume_byte(&mut self, expected: u8) -> io::Result<bool> {
        let found = {
            let buf = try!(self.input.fill_buf());
            buf.first().map_or(false, |b| *b == expected)
        };
        if found {
            self.input.consume(1);
        }
        Ok(found)
    }
}

#[inline]
fn match_keyword(buf: &[u8], key: &str) -> bool {
    str::from_utf8(&buf).ok().map_or(false, |s| s == key)
}
<commit_msg>Basic parsing of volumes<commit_after>//! Operations on manifest files.

use std::error::Error;
use std::fmt::{self, Display, Formatter};
use std::io::{self, BufRead};
use std::num::ParseIntError;
use std::path::Path;
use std::str::{self, FromStr};
use std::string::FromUtf8Error;
use std::usize;

use rawpath::RawPath;

/// Manifest file info.
pub struct Manifest {
    hostname: String,
    local_dir: RawPath,
    volumes: Vec<Option<Volume>>,
}

/// Volume info.
pub struct Volume {
    start_path: PathBlock,
    end_path: PathBlock,
    hash_type: String,
    hash: Vec<u8>,
}

/// wip
#[derive(Debug)]
pub enum ParseError {
    /// wip
    Io(io::Error),
    /// wip
    MissingKeyword(String),
    /// wip
    ParseInt(ParseIntError),
    /// wip
    Utf8(FromUtf8Error),
}

struct PathBlock {
    path: RawPath,
    block: Option<usize>,
}

struct ManifestParser<R> {
    input: R,
    buf: Vec<u8>,
}

impl Manifest {
    /// Parses a stream to get a manifest.
    pub fn parse<R: BufRead>(m: &mut R) -> Result<Self, ParseError> {
        let parser = ManifestParser::new(m);
        parser.parse()
    }

    /// wip
    pub fn hostname(&self) -> Option<&str> {
        Some(&self.hostname)
    }

    /// wip
    pub fn local_dir(&self) -> Option<&Path> {
        self.local_dir.as_path()
    }

    /// wip
    pub fn max_vol_num(&self) -> usize {
        self.volumes.len()
    }

    /// wip
    pub fn volume(&self, num: usize) -> Option<&Volume> {
        self.volumes.get(num).and_then(|v| v.as_ref())
    }
}

impl Volume {
    /// wip
    pub fn start_path(&self) -> Option<&Path> {
        self.start_path.path.as_path()
    }

    /// wip
    pub fn end_path(&self) -> Option<&Path> {
        self.end_path.path.as_path()
    }

    /// wip
    pub fn start_path_bytes(&self) -> &[u8] {
        self.start_path.path.as_bytes()
    }

    /// wip
    pub fn end_path_bytes(&self) -> &[u8] {
        self.end_path.path.as_bytes()
    }

    /// wip
    pub fn start_block(&self) -> Option<usize> {
        self.start_path.block
    }

    /// wip
    pub fn end_block(&self) -> Option<usize> {
        self.end_path.block
    }

    /// wip
    pub fn hash_type(&self) -> &str {
        &self.hash_type
    }

    /// wip
    pub fn hash(&self) -> &[u8] {
        &self.hash
    }
}

impl Error for ParseError {
    fn description(&self) -> &str {
        match *self {
            ParseError::Io(ref err) => err.description(),
            ParseError::MissingKeyword(_) => "missing keyword in manifest",
            ParseError::ParseInt(ref err) => err.description(),
            ParseError::Utf8(ref err) => err.description(),
        }
    }
}

impl Display for ParseError {
    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
        match *self {
            ParseError::Io(ref e) => write!(fmt, "{}", e),
            ParseError::MissingKeyword(ref e) => write!(fmt, "missing keyword '{}' in manifest", e),
            ParseError::ParseInt(ref e) => write!(fmt, "{}", e),
            ParseError::Utf8(ref e) => write!(fmt, "{}", e),
        }
    }
}

impl From<io::Error> for ParseError {
    fn from(err: io::Error) -> ParseError {
        ParseError::Io(err)
    }
}

impl From<ParseIntError> for ParseError {
    fn from(err: ParseIntError) -> ParseError {
        ParseError::ParseInt(err)
    }
}

impl From<FromUtf8Error> for ParseError {
    fn from(err: FromUtf8Error) -> ParseError {
        ParseError::Utf8(err)
    }
}

impl<R: BufRead> ManifestParser<R> {
    pub fn new(input: R) -> Self {
        ManifestParser {
            input: input,
            buf: vec![],
        }
    }

    pub fn parse(mut self) -> Result<Manifest, ParseError> {
        let hostname = try!(self.read_param_str("Hostname"));
        let local_dir = RawPath::with_bytes(try!(self.read_param_bytes("Localdir")));

        let mut volumes = Vec::new();
        while let Some((vol, i)) = try!(self.read_volume()) {
            // resize volumes if necessary
            if i >= volumes.len() {
                volumes.reserve(i + 1);
                for _ in volumes.len()..i + 1 {
                    volumes.push(None);
                }
            }
            volumes[i] = Some(vol);
        }

        Ok(Manifest {
            hostname: hostname,
            local_dir: local_dir,
            volumes: volumes,
        })
    }

    fn read_volume(&mut self) -> Result<Option<(Volume, usize)>, ParseError> {
        // volume number
        let mut param = try!(self.read_param_str("Volume"));
        if param.ends_with(":") {
            param.pop();
        }
        let num = try!(usize::from_str(&param));
        let start_path = try!(self.read_path_block("StartingPath"));
        let end_path = try!(self.read_path_block("EndingPath"));

        // TODO: missing fields
        let vol = Volume {
            start_path: start_path,
            end_path: end_path,
            hash_type: String::new(),
            hash: vec![],
        };
        Ok(Some((vol, num)))
    }

    fn read_path_block(&mut self, key: &str) -> Result<PathBlock, ParseError> {
        try!(self.consume_whitespace());
        if !try!(self.consume_keyword(key)) {
            return Err(ParseError::MissingKeyword(key.to_owned()));
        }
        try!(self.consume_whitespace());
        let path = try!(self.read_param_value());
        try!(self.consume_whitespace());
        let block = if !try!(self.consume_byte(b'\n')) {
            let bytes = try!(self.read_param_value());
            let s = try!(String::from_utf8(bytes));
            let num = try!(usize::from_str(&s));
            Some(num)
        } else {
            None
        };
        Ok(PathBlock {
            path: RawPath::with_bytes(path),
            block: block,
        })
    }

    fn read_param_bytes(&mut self, key: &str) -> Result<Vec<u8>, ParseError> {
        try!(self.consume_whitespace());
        if !try!(self.consume_keyword(key)) {
            return Err(ParseError::MissingKeyword(key.to_owned()));
        }
        try!(self.consume_whitespace());
        match self.read_param_value() {
            Ok(res) => {
                try!(self.consume_newline());
                Ok(res)
            }
            Err(e) => Err(From::from(e)),
        }
    }

    fn read_param_str(&mut self, key: &str) -> Result<String, ParseError> {
        let bytes = try!(self.read_param_bytes(key));
        String::from_utf8(bytes).map_err(From::from)
    }

    fn consume_keyword(&mut self, key: &str) -> io::Result<bool> {
        try!(self.input.read_until(b' ', &mut self.buf));
        Ok(match_keyword(&self.buf, key))
    }

    fn consume_newline(&mut self) -> io::Result<()> {
        self.consume_until(|b| {
            match b {
                b' ' | b'\t' | b'\r' | b'\n' => true,
                _ => false,
            }
        })
    }

    fn consume_whitespace(&mut self) -> io::Result<()> {
        self.consume_until(|b| {
            match b {
                b' ' | b'\t' => true,
                _ => false,
            }
        })
    }

    fn consume_until<F>(&mut self, mut f: F) -> io::Result<()>
        where F: FnMut(u8) -> bool
    {
        loop {
            let (pos, end) = {
                let buf = try!(self.input.fill_buf());
                match buf.iter().cloned().position(&mut f) {
                    Some(p) => (p, true),
                    None => (buf.len(), buf.is_empty()),
                }
            };
            self.input.consume(pos);
            if end {
                return Ok(());
            }
        }
    }

    fn read_param_value(&mut self) -> io::Result<Vec<u8>> {
        if try!(self.consume_byte(b'"')) {
            try!(self.input.read_until(b'"', &mut self.buf));
        } else {
            try!(self.input.read_until(b'\n', &mut self.buf));
        }
        let mut result = Vec::with_capacity(self.buf.len());
        // unescape
        for (i, b) in self.buf.iter().cloned().enumerate() {
            if b != b'\\' {
                result.push(b);
            } else {
                // expects a \xNN where NN is a number string representing the escaped char in hex
                // e.g. \x20 is the space ' '
                if self.buf.len() - i >= 4 && self.buf[i + 1] == b'x' {
                    let num = ((self.buf[i + 2] - b'0') << 4) + self.buf[i + 3] - b'0';
                    result.push(num);
                }
            }
        }
        Ok(result)
    }

    fn consume_byte(&mut self, expected: u8) -> io::Result<bool> {
        let found = {
            let buf = try!(self.input.fill_buf());
            buf.first().map_or(false, |b| *b == expected)
        };
        if found {
            self.input.consume(1);
        }
        Ok(found)
    }
}

#[inline]
fn match_keyword(buf: &[u8], key: &str) -> bool {
    str::from_utf8(&buf).ok().map_or(false, |s| s == key)
}
<|endoftext|>


<commit_before>use std::collections::BTreeMap;
use std::env;
use std::error::Error;
use std::fmt;
use std::fs::{self, File, OpenOptions};
use std::io::{Read, Write, Seek, SeekFrom};
use std::path::{Path, PathBuf};
use toml;

pub type Dependency = (String, toml::Value);
pub type TomlMap = BTreeMap<String, toml::Value>;

#[derive(Debug)]
/// Catch-all error for misconfigured crates.
pub struct ManifestError;

impl Error for ManifestError {
    fn description(&self) -> &str {
        "Your Cargo.toml is either missing or incorrectly structured."
    }
}

impl fmt::Display for ManifestError {
    fn fmt(&self, format: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        format.write_str(self.description())
    }
}

#[derive(Debug, PartialEq)]
pub struct Manifest {
    data: TomlMap
}

impl Manifest {
    pub fn from_str(input: &str) -> Result<Manifest, Box<Error>> {
        let mut parser = toml::Parser::new(&input);

        parser.parse()
        .ok_or(parser.errors.pop())
        .map_err(Option::unwrap).map_err(From::from)
        .map(|data| {
            Manifest { data: data }
        })
    }

    pub fn find_file(path: &Option<&String>) -> Result<File, Box<Error>> {
        /// If a manifest is specified, return that one, otherise perform a manifest search starting from
        /// the current directory.
        fn find(specified: &Option<&String>) -> Result<PathBuf, Box<Error>> {
            specified.map(PathBuf::from).ok_or(())
            .or_else(|_| env::current_dir().map_err(From::from)
                         .and_then(|ref dir| search(dir).map_err(From::from)))
        }

        /// Search for Cargo.toml in this directory and recursively up the tree until one is found.
        #[allow(unconditional_recursion)] //Incorrect lint; recursion is conditional.
        fn search(dir: &Path) -> Result<PathBuf, ManifestError> {
            let manifest = dir.join("Cargo.toml");
            fs::metadata(&manifest).map(|_| manifest)
            .or(dir.parent().ok_or(ManifestError).and_then(search))
        }

        find(path)
        .and_then(|path| {
            OpenOptions::new()
            .read(true).write(true).open(path)
            .map_err(From::from)
        })
    }

    pub fn open(path: &Option<&String>) -> Result<Manifest, Box<Error>> {
        let mut file = try!(Manifest::find_file(path));
        let mut data = String::new();
        try!(file.read_to_string(&mut data));

        Manifest::from_str(&data)
    }

    /// Overwrite a file with TOML data.
    pub fn write_to_file<T: Seek + Write>(&self, file: &mut T)
            -> Result<(), Box<Error>> {
        try!(file.seek(SeekFrom::Start(0)));
        let mut toml = self.data.clone();

        let (proj_header, proj_data) =
            try!(toml.remove("package").map(|data| ("package", data))
                 .or_else(|| toml.remove("project").map(|data| ("project", data)))
                 .ok_or(ManifestError));
        write!(file, "[{}]\n{}{}", proj_header, proj_data,
               toml::Value::Table(toml)).map_err(From::from)
    }

    /// Add entry to a Cargo.toml.
    fn insert_into_table(&mut self, table: &str, &(ref name, ref data): &Dependency)
            -> Result<(), ManifestError> {
        let ref mut manifest = self.data;
        let entry = manifest.entry(String::from(table))
            .or_insert(toml::Value::Table(BTreeMap::new()));
        match entry {
            &mut toml::Value::Table(ref mut deps) => {
                deps.insert(name.clone(), data.clone());
                Ok(())
            }
            _ => Err(ManifestError)
        }
    }

    /// Add entry to manifest
    pub fn add_deps(&mut self, table: &str, deps: &[Dependency])
            -> Result<(), Box<Error>> {
        deps.iter()
        .map(|dep| self.insert_into_table(table, &dep))
        .collect::<Result<Vec<_>, _>>()
        .map_err(From::from)
        .map(|_| ())
    }
}
<commit_msg>Add Manifest Test<commit_after>use std::collections::BTreeMap;
use std::env;
use std::error::Error;
use std::fmt;
use std::fs::{self, File, OpenOptions};
use std::io::{Read, Write, Seek, SeekFrom};
use std::path::{Path, PathBuf};
use toml;

pub type Dependency = (String, toml::Value);
pub type TomlMap = BTreeMap<String, toml::Value>;

#[derive(Debug)]
/// Catch-all error for misconfigured crates.
pub struct ManifestError;

impl Error for ManifestError {
    fn description(&self) -> &str {
        "Your Cargo.toml is either missing or incorrectly structured."
    }
}

impl fmt::Display for ManifestError {
    fn fmt(&self, format: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        format.write_str(self.description())
    }
}

#[derive(Debug, PartialEq)]
pub struct Manifest {
    data: TomlMap
}

impl Manifest {
    pub fn from_str(input: &str) -> Result<Manifest, Box<Error>> {
        let mut parser = toml::Parser::new(&input);

        parser.parse()
        .ok_or(parser.errors.pop())
        .map_err(Option::unwrap).map_err(From::from)
        .map(|data| {
            Manifest { data: data }
        })
    }

    pub fn find_file(path: &Option<&String>) -> Result<File, Box<Error>> {
        /// If a manifest is specified, return that one, otherise perform a manifest search starting from
        /// the current directory.
        fn find(specified: &Option<&String>) -> Result<PathBuf, Box<Error>> {
            specified.map(PathBuf::from).ok_or(())
            .or_else(|_| env::current_dir().map_err(From::from)
                         .and_then(|ref dir| search(dir).map_err(From::from)))
        }

        /// Search for Cargo.toml in this directory and recursively up the tree until one is found.
        #[allow(unconditional_recursion)] //Incorrect lint; recursion is conditional.
        fn search(dir: &Path) -> Result<PathBuf, ManifestError> {
            let manifest = dir.join("Cargo.toml");
            fs::metadata(&manifest).map(|_| manifest)
            .or(dir.parent().ok_or(ManifestError).and_then(search))
        }

        find(path)
        .and_then(|path| {
            OpenOptions::new()
            .read(true).write(true).open(path)
            .map_err(From::from)
        })
    }

    pub fn open(path: &Option<&String>) -> Result<Manifest, Box<Error>> {
        let mut file = try!(Manifest::find_file(path));
        let mut data = String::new();
        try!(file.read_to_string(&mut data));

        Manifest::from_str(&data)
    }

    /// Overwrite a file with TOML data.
    pub fn write_to_file<T: Seek + Write>(&self, file: &mut T)
            -> Result<(), Box<Error>> {
        try!(file.seek(SeekFrom::Start(0)));
        let mut toml = self.data.clone();

        let (proj_header, proj_data) =
            try!(toml.remove("package").map(|data| ("package", data))
                 .or_else(|| toml.remove("project").map(|data| ("project", data)))
                 .ok_or(ManifestError));
        write!(file, "[{}]\n{}{}", proj_header, proj_data,
               toml::Value::Table(toml)).map_err(From::from)
    }

    /// Add entry to a Cargo.toml.
    fn insert_into_table(&mut self, table: &str, &(ref name, ref data): &Dependency)
            -> Result<(), ManifestError> {
        let ref mut manifest = self.data;
        let entry = manifest.entry(String::from(table))
            .or_insert(toml::Value::Table(BTreeMap::new()));
        match entry {
            &mut toml::Value::Table(ref mut deps) => {
                deps.insert(name.clone(), data.clone());
                Ok(())
            }
            _ => Err(ManifestError)
        }
    }

    /// Add entry to manifest
    pub fn add_deps(&mut self, table: &str, deps: &[Dependency])
            -> Result<(), Box<Error>> {
        deps.iter()
        .map(|dep| self.insert_into_table(table, &dep))
        .collect::<Result<Vec<_>, _>>()
        .map_err(From::from)
        .map(|_| ())
    }
}

#[cfg(test)]
mod test {
    use args::Args;
    use super::Manifest;

    static default_cargo_toml: &'static str = r#"[package]
authors = ["Some Guy"]
name = "lorem-ipsum"
version = "0.1.0"

[dependencies]
foo-bar = "0.1""#;

    #[test]
    fn add_dependency() {
        let opts = Args {
            arg_section: String::from("dependencies"),
            arg_dep: vec![String::from("lorem-ipsum")],
            ..Default::default()
        };

        let mut manifile = Manifest::from_str(default_cargo_toml).unwrap();

        manifile.add_deps(
            &opts.get_section(),
            &opts.get_dependencies()
        );

        println!("{:#?}", manifile);

        let lorem = manifile.data.get(&opts.get_section()).expect("no section")
            .lookup("lorem-ipsum").expect("no lorem")
            .as_str().expect("not a str");

        assert_eq!(
            lorem,
            "*"
        );
    }
}
<|endoftext|>


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

use net_traits::{ControlMsg, CookieSource, LoadData, Metadata, LoadConsumer};
use net_traits::ProgressMsg::{Payload, Done};
use mime_classifier::MIMEClassifier;
use resource_task::{start_sending_opt, start_sending_sniffed_opt};

use log;
use std::collections::HashSet;
use file_loader;
use flate2::read::{DeflateDecoder, GzDecoder};
use hyper::client::Request;
use hyper::header::{AcceptEncoding, ContentLength, ContentType, Host, Location};
use hyper::HttpError;
use hyper::method::Method;
use hyper::mime::{Mime, TopLevel, SubLevel};
use hyper::net::HttpConnector;
use hyper::status::{StatusCode, StatusClass};
use std::error::Error;
use openssl::ssl::{SslContext, SslVerifyMode};
use std::io::{self, Read, Write};
use std::sync::Arc;
use std::sync::mpsc::{Sender, channel};
use std::thunk::Invoke;
use util::task::spawn_named;
use util::resource_files::resources_dir_path;
use util::opts;
use url::{Url, UrlParser};

use std::borrow::ToOwned;

pub fn factory(cookies_chan: Sender<ControlMsg>)
               -> Box<Invoke<(LoadData, LoadConsumer, Arc<MIMEClassifier>)> + Send> {
    box move |(load_data, senders, classifier)| {
        spawn_named("http_loader".to_owned(), move || load(load_data, senders, classifier, cookies_chan))
    }
}

fn send_error(url: Url, err: String, start_chan: LoadConsumer) {
    let mut metadata: Metadata = Metadata::default(url);
    metadata.status = None;

    match start_sending_opt(start_chan, metadata) {
        Ok(p) => p.send(Done(Err(err))).unwrap(),
        _ => {}
    };
}

enum ReadResult {
    Payload(Vec<u8>),
    EOF,
}

fn read_block<R: Read>(reader: &mut R) -> Result<ReadResult, ()> {
    let mut buf = vec![0; 1024];

    match reader.read(buf.as_mut_slice()) {
        Ok(len) if len > 0 => {
            unsafe { buf.set_len(len); }
            Ok(ReadResult::Payload(buf))
        }
        Ok(_) => Ok(ReadResult::EOF),
        Err(_) => Err(()),
    }
}

fn load(mut load_data: LoadData, start_chan: LoadConsumer, classifier: Arc<MIMEClassifier>, cookies_chan: Sender<ControlMsg>) {
    // FIXME: At the time of writing this FIXME, servo didn't have any central
    //        location for configuration. If you're reading this and such a
    //        repository DOES exist, please update this constant to use it.
    let max_redirects = 50;
    let mut iters = 0;
    let mut url = load_data.url.clone();
    let mut redirected_to = HashSet::new();

    // If the URL is a view-source scheme then the scheme data contains the
    // real URL that should be used for which the source is to be viewed.
    // Change our existing URL to that and keep note that we are viewing
    // the source rather than rendering the contents of the URL.
    let viewing_source = url.scheme == "view-source";
    if viewing_source {
        let inner_url = load_data.url.non_relative_scheme_data().unwrap();
        url = Url::parse(inner_url).unwrap();
        match &*url.scheme {
            "http" | "https" => {}
            _ => {
                let s = format!("The {} scheme with view-source is not supported", url.scheme);
                send_error(url, s, start_chan);
                return;
            }
        };
    }

    // Loop to handle redirects.
    loop {
        iters = iters + 1;

        if iters > max_redirects {
            send_error(url, "too many redirects".to_string(), start_chan);
            return;
        }

        match &*url.scheme {
            "http" | "https" => {}
            _ => {
                let s = format!("{} request, but we don't support that scheme", url.scheme);
                send_error(url, s, start_chan);
                return;
            }
        }

        info!("requesting {}", url.serialize());

        fn verifier(ssl: &mut SslContext) {
            ssl.set_verify(SslVerifyMode::SslVerifyPeer, None);
            let mut certs = resources_dir_path();
            certs.push("certs");
            ssl.set_CA_file(&certs);
        };

        let ssl_err_string = "[UnknownError { library: \"SSL routines\", \
function: \"SSL3_GET_SERVER_CERTIFICATE\", \
reason: \"certificate verify failed\" }]";

        let mut connector = if opts::get().nossl {
            HttpConnector(None)
        } else {
            HttpConnector(Some(box verifier as Box<FnMut(&mut SslContext)>))
        };

        let mut req = match Request::with_connector(load_data.method.clone(), url.clone(), &mut connector) {
            Ok(req) => req,
            Err(HttpError::HttpIoError(ref io_error)) if (
                io_error.kind() == io::ErrorKind::Other &&
                io_error.description() == "Error in OpenSSL" &&
                io_error.detail() == Some(ssl_err_string.to_owned())
            ) => {
                let mut image = resources_dir_path();
                image.push("badcert.html");
                let load_data = LoadData::new(Url::from_file_path(&*image).unwrap());
                file_loader::factory(load_data, start_chan, classifier);
                return;
            },
            Err(e) => {
                println!("{:?}", e);
                send_error(url, e.description().to_string(), start_chan);
                return;
            }
        };

        // Preserve the `host` header set automatically by Request.
        let host = req.headers().get::<Host>().unwrap().clone();

        // Avoid automatically preserving request headers when redirects occur.
        // See https://bugzilla.mozilla.org/show_bug.cgi?id=401564 and
        // https://bugzilla.mozilla.org/show_bug.cgi?id=216828 .
        // Only preserve ones which have been explicitly marked as such.
        if iters == 1 {
            let mut combined_headers = load_data.headers.clone();
            combined_headers.extend(load_data.preserved_headers.iter());
            *req.headers_mut() = combined_headers;
        } else {
            *req.headers_mut() = load_data.preserved_headers.clone();
        }

        req.headers_mut().set(host);

        let (tx, rx) = channel();
        cookies_chan.send(ControlMsg::GetCookiesForUrl(url.clone(), tx, CookieSource::HTTP)).unwrap();
        if let Some(cookie_list) = rx.recv().unwrap() {
            let mut v = Vec::new();
            v.push(cookie_list.into_bytes());
            req.headers_mut().set_raw("Cookie".to_owned(), v);
        }

        if !req.headers().has::<AcceptEncoding>() {
            req.headers_mut().set_raw("Accept-Encoding".to_owned(), vec![b"gzip, deflate".to_vec()]);
        }
        if log_enabled!(log::INFO) {
            info!("{}", load_data.method);
            for header in req.headers().iter() {
                info!(" - {}", header);
            }
            info!("{:?}", load_data.data);
        }

        // Avoid automatically sending request body if a redirect has occurred.
        let writer = match load_data.data {
            Some(ref data) if iters == 1 => {
                req.headers_mut().set(ContentLength(data.len() as u64));
                let mut writer = match req.start() {
                    Ok(w) => w,
                    Err(e) => {
                        send_error(url, e.description().to_string(), start_chan);
                        return;
                    }
                };
                match writer.write_all(&*data) {
                    Err(e) => {
                        send_error(url, e.description().to_string(), start_chan);
                        return;
                    }
                    _ => {}
                };
                writer
            },
            _ => {
                match load_data.method {
                    Method::Get | Method::Head => (),
                    _ => req.headers_mut().set(ContentLength(0))
                }
                match req.start() {
                    Ok(w) => w,
                    Err(e) => {
                        send_error(url, e.description().to_string(), start_chan);
                        return;
                    }
                }
            }
        };
        let mut response = match writer.send() {
            Ok(r) => r,
            Err(e) => {
                send_error(url, e.description().to_string(), start_chan);
                return;
            }
        };

        // Dump headers, but only do the iteration if info!() is enabled.
        info!("got HTTP response {}, headers:", response.status);
        if log_enabled!(log::INFO) {
            for header in response.headers.iter() {
                info!(" - {}", header);
            }
        }

        if let Some(cookies) = response.headers.get_raw("set-cookie") {
            for cookie in cookies.iter() {
                if let Ok(cookies) = String::from_utf8(cookie.clone()) {
                    cookies_chan.send(ControlMsg::SetCookiesForUrl(url.clone(),
                                                                   cookies,
                                                                   CookieSource::HTTP)).unwrap();
                }
            }
        }

        if response.status.class() == StatusClass::Redirection {
            match response.headers.get::<Location>() {
                Some(&Location(ref new_url)) => {
                    // CORS (https://fetch.spec.whatwg.org/#http-fetch, status section, point 9, 10)
                    match load_data.cors {
                        Some(ref c) => {
                            if c.preflight {
                                // The preflight lied
                                send_error(url, "Preflight fetch inconsistent with main fetch".to_string(), start_chan);
                                return;
                            } else {
                                // XXXManishearth There are some CORS-related steps here,
                                // but they don't seem necessary until credentials are implemented
                            }
                        }
                        _ => {}
                    }
                    let new_url = match UrlParser::new().base_url(&url).parse(&new_url) {
                        Ok(u) => u,
                        Err(e) => {
                            send_error(url, e.to_string(), start_chan);
                            return;
                        }
                    };
                    info!("redirecting to {}", new_url);
                    url = new_url;

                    // According to https://tools.ietf.org/html/rfc7231#section-6.4.2,
                    // historically UAs have rewritten POST->GET on 301 and 302 responses.
                    if load_data.method == Method::Post &&
                        (response.status == StatusCode::MovedPermanently ||
                         response.status == StatusCode::Found) {
                        load_data.method = Method::Get;
                    }

                    if redirected_to.contains(&url) {
                        send_error(url, "redirect loop".to_string(), start_chan);
                        return;
                    }

                    redirected_to.insert(url.clone());
                    continue;
                }
                None => ()
            }
        }

        let mut adjusted_headers = response.headers.clone();
        if viewing_source {
            adjusted_headers.set(ContentType(Mime(TopLevel::Text, SubLevel::Plain, vec![])));
        }
        let mut metadata: Metadata = Metadata::default(url);
        metadata.set_content_type(match adjusted_headers.get() {
            Some(&ContentType(ref mime)) => Some(mime),
            None => None
        });
        metadata.headers = Some(adjusted_headers);
        metadata.status = Some(response.status_raw().clone());

        let mut encoding_str: Option<String> = None;
        //FIXME: Implement Content-Encoding Header https://github.com/hyperium/hyper/issues/391
        if let Some(encodings) = response.headers.get_raw("content-encoding") {
            for encoding in encodings.iter() {
                if let Ok(encodings) = String::from_utf8(encoding.clone()) {
                    if encodings == "gzip" || encodings == "deflate" {
                        encoding_str = Some(encodings);
                        break;
                    }
                }
            }
        }

        match encoding_str {
            Some(encoding) => {
                if encoding == "gzip" {
                    let mut response_decoding = GzDecoder::new(response).unwrap();
                    send_data(&mut response_decoding, start_chan, metadata, classifier);
                } else if encoding == "deflate" {
                    let mut response_decoding = DeflateDecoder::new(response);
                    send_data(&mut response_decoding, start_chan, metadata, classifier);
                }
            },
            None => {
                send_data(&mut response, start_chan, metadata, classifier);
            }
        }

        // We didn't get redirected.
        break;
    }
}

fn send_data<R: Read>(reader: &mut R,
                      start_chan: LoadConsumer,
                      metadata: Metadata,
                      classifier: Arc<MIMEClassifier>) {
    let (progress_chan, mut chunk) = {
        let buf = match read_block(reader) {
            Ok(ReadResult::Payload(buf)) => buf,
            _ => vec!(),
        };
        let p = match start_sending_sniffed_opt(start_chan, metadata, classifier, &buf) {
            Ok(p) => p,
            _ => return
        };
        (p, buf)
    };

    loop {
        if progress_chan.send(Payload(chunk)).is_err() {
            // The send errors when the receiver is out of scope,
            // which will happen if the fetch has timed out (or has been aborted)
            // so we don't need to continue with the loading of the file here.
            return;
        }

        chunk = match read_block(reader) {
            Ok(ReadResult::Payload(buf)) => buf,
            Ok(ReadResult::EOF) | Err(_) => break,
        };
    }

    let _ = progress_chan.send(Done(Ok(())));
}
<commit_msg>Add Accept header to HTTP loader<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use net_traits::{ControlMsg, CookieSource, LoadData, Metadata, LoadConsumer};
use net_traits::ProgressMsg::{Payload, Done};
use mime_classifier::MIMEClassifier;
use resource_task::{start_sending_opt, start_sending_sniffed_opt};

use log;
use std::collections::HashSet;
use file_loader;
use flate2::read::{DeflateDecoder, GzDecoder};
use hyper::client::Request;
use hyper::header::{AcceptEncoding, Accept, ContentLength, ContentType, Host, Location, qitem, Quality, QualityItem};
use hyper::HttpError;
use hyper::method::Method;
use hyper::mime::{Mime, TopLevel, SubLevel};
use hyper::net::HttpConnector;
use hyper::status::{StatusCode, StatusClass};
use std::error::Error;
use openssl::ssl::{SslContext, SslVerifyMode};
use std::io::{self, Read, Write};
use std::sync::Arc;
use std::sync::mpsc::{Sender, channel};
use std::thunk::Invoke;
use util::task::spawn_named;
use util::resource_files::resources_dir_path;
use util::opts;
use url::{Url, UrlParser};

use std::borrow::ToOwned;

pub fn factory(cookies_chan: Sender<ControlMsg>)
               -> Box<Invoke<(LoadData, LoadConsumer, Arc<MIMEClassifier>)> + Send> {
    box move |(load_data, senders, classifier)| {
        spawn_named("http_loader".to_owned(), move || load(load_data, senders, classifier, cookies_chan))
    }
}

fn send_error(url: Url, err: String, start_chan: LoadConsumer) {
    let mut metadata: Metadata = Metadata::default(url);
    metadata.status = None;

    match start_sending_opt(start_chan, metadata) {
        Ok(p) => p.send(Done(Err(err))).unwrap(),
        _ => {}
    };
}

enum ReadResult {
    Payload(Vec<u8>),
    EOF,
}

fn read_block<R: Read>(reader: &mut R) -> Result<ReadResult, ()> {
    let mut buf = vec![0; 1024];

    match reader.read(buf.as_mut_slice()) {
        Ok(len) if len > 0 => {
            unsafe { buf.set_len(len); }
            Ok(ReadResult::Payload(buf))
        }
        Ok(_) => Ok(ReadResult::EOF),
        Err(_) => Err(()),
    }
}

fn load(mut load_data: LoadData, start_chan: LoadConsumer, classifier: Arc<MIMEClassifier>, cookies_chan: Sender<ControlMsg>) {
    // FIXME: At the time of writing this FIXME, servo didn't have any central
    //        location for configuration. If you're reading this and such a
    //        repository DOES exist, please update this constant to use it.
    let max_redirects = 50;
    let mut iters = 0;
    let mut url = load_data.url.clone();
    let mut redirected_to = HashSet::new();

    // If the URL is a view-source scheme then the scheme data contains the
    // real URL that should be used for which the source is to be viewed.
    // Change our existing URL to that and keep note that we are viewing
    // the source rather than rendering the contents of the URL.
    let viewing_source = url.scheme == "view-source";
    if viewing_source {
        let inner_url = load_data.url.non_relative_scheme_data().unwrap();
        url = Url::parse(inner_url).unwrap();
        match &*url.scheme {
            "http" | "https" => {}
            _ => {
                let s = format!("The {} scheme with view-source is not supported", url.scheme);
                send_error(url, s, start_chan);
                return;
            }
        };
    }

    // Loop to handle redirects.
    loop {
        iters = iters + 1;

        if iters > max_redirects {
            send_error(url, "too many redirects".to_string(), start_chan);
            return;
        }

        match &*url.scheme {
            "http" | "https" => {}
            _ => {
                let s = format!("{} request, but we don't support that scheme", url.scheme);
                send_error(url, s, start_chan);
                return;
            }
        }

        info!("requesting {}", url.serialize());

        fn verifier(ssl: &mut SslContext) {
            ssl.set_verify(SslVerifyMode::SslVerifyPeer, None);
            let mut certs = resources_dir_path();
            certs.push("certs");
            ssl.set_CA_file(&certs);
        };

        let ssl_err_string = "[UnknownError { library: \"SSL routines\", \
function: \"SSL3_GET_SERVER_CERTIFICATE\", \
reason: \"certificate verify failed\" }]";

        let mut connector = if opts::get().nossl {
            HttpConnector(None)
        } else {
            HttpConnector(Some(box verifier as Box<FnMut(&mut SslContext)>))
        };

        let mut req = match Request::with_connector(load_data.method.clone(), url.clone(), &mut connector) {
            Ok(req) => req,
            Err(HttpError::HttpIoError(ref io_error)) if (
                io_error.kind() == io::ErrorKind::Other &&
                io_error.description() == "Error in OpenSSL" &&
                io_error.detail() == Some(ssl_err_string.to_owned())
            ) => {
                let mut image = resources_dir_path();
                image.push("badcert.html");
                let load_data = LoadData::new(Url::from_file_path(&*image).unwrap());
                file_loader::factory(load_data, start_chan, classifier);
                return;
            },
            Err(e) => {
                println!("{:?}", e);
                send_error(url, e.description().to_string(), start_chan);
                return;
            }
        };

        // Preserve the `host` header set automatically by Request.
        let host = req.headers().get::<Host>().unwrap().clone();

        // Avoid automatically preserving request headers when redirects occur.
        // See https://bugzilla.mozilla.org/show_bug.cgi?id=401564 and
        // https://bugzilla.mozilla.org/show_bug.cgi?id=216828 .
        // Only preserve ones which have been explicitly marked as such.
        if iters == 1 {
            let mut combined_headers = load_data.headers.clone();
            combined_headers.extend(load_data.preserved_headers.iter());
            *req.headers_mut() = combined_headers;
        } else {
            *req.headers_mut() = load_data.preserved_headers.clone();
        }

        req.headers_mut().set(host);

        if !req.headers().has::<Accept>() {
            let accept = Accept(vec![
                qitem(Mime(TopLevel::Text, SubLevel::Html, vec![])),
                qitem(Mime(TopLevel::Application, SubLevel::Ext("xhtml+xml".to_string()), vec![])),
                QualityItem::new(Mime(TopLevel::Application, SubLevel::Xml, vec![]), Quality(900u16)),
                QualityItem::new(Mime(TopLevel::Star, SubLevel::Star, vec![]), Quality(800u16)),
            ]);
            req.headers_mut().set(accept);
        }

        let (tx, rx) = channel();
        cookies_chan.send(ControlMsg::GetCookiesForUrl(url.clone(), tx, CookieSource::HTTP)).unwrap();
        if let Some(cookie_list) = rx.recv().unwrap() {
            let mut v = Vec::new();
            v.push(cookie_list.into_bytes());
            req.headers_mut().set_raw("Cookie".to_owned(), v);
        }

        if !req.headers().has::<AcceptEncoding>() {
            req.headers_mut().set_raw("Accept-Encoding".to_owned(), vec![b"gzip, deflate".to_vec()]);
        }
        if log_enabled!(log::INFO) {
            info!("{}", load_data.method);
            for header in req.headers().iter() {
                info!(" - {}", header);
            }
            info!("{:?}", load_data.data);
        }

        // Avoid automatically sending request body if a redirect has occurred.
        let writer = match load_data.data {
            Some(ref data) if iters == 1 => {
                req.headers_mut().set(ContentLength(data.len() as u64));
                let mut writer = match req.start() {
                    Ok(w) => w,
                    Err(e) => {
                        send_error(url, e.description().to_string(), start_chan);
                        return;
                    }
                };
                match writer.write_all(&*data) {
                    Err(e) => {
                        send_error(url, e.description().to_string(), start_chan);
                        return;
                    }
                    _ => {}
                };
                writer
            },
            _ => {
                match load_data.method {
                    Method::Get | Method::Head => (),
                    _ => req.headers_mut().set(ContentLength(0))
                }
                match req.start() {
                    Ok(w) => w,
                    Err(e) => {
                        send_error(url, e.description().to_string(), start_chan);
                        return;
                    }
                }
            }
        };
        let mut response = match writer.send() {
            Ok(r) => r,
            Err(e) => {
                send_error(url, e.description().to_string(), start_chan);
                return;
            }
        };

        // Dump headers, but only do the iteration if info!() is enabled.
        info!("got HTTP response {}, headers:", response.status);
        if log_enabled!(log::INFO) {
            for header in response.headers.iter() {
                info!(" - {}", header);
            }
        }

        if let Some(cookies) = response.headers.get_raw("set-cookie") {
            for cookie in cookies.iter() {
                if let Ok(cookies) = String::from_utf8(cookie.clone()) {
                    cookies_chan.send(ControlMsg::SetCookiesForUrl(url.clone(),
                                                                   cookies,
                                                                   CookieSource::HTTP)).unwrap();
                }
            }
        }

        if response.status.class() == StatusClass::Redirection {
            match response.headers.get::<Location>() {
                Some(&Location(ref new_url)) => {
                    // CORS (https://fetch.spec.whatwg.org/#http-fetch, status section, point 9, 10)
                    match load_data.cors {
                        Some(ref c) => {
                            if c.preflight {
                                // The preflight lied
                                send_error(url, "Preflight fetch inconsistent with main fetch".to_string(), start_chan);
                                return;
                            } else {
                                // XXXManishearth There are some CORS-related steps here,
                                // but they don't seem necessary until credentials are implemented
                            }
                        }
                        _ => {}
                    }
                    let new_url = match UrlParser::new().base_url(&url).parse(&new_url) {
                        Ok(u) => u,
                        Err(e) => {
                            send_error(url, e.to_string(), start_chan);
                            return;
                        }
                    };
                    info!("redirecting to {}", new_url);
                    url = new_url;

                    // According to https://tools.ietf.org/html/rfc7231#section-6.4.2,
                    // historically UAs have rewritten POST->GET on 301 and 302 responses.
                    if load_data.method == Method::Post &&
                        (response.status == StatusCode::MovedPermanently ||
                         response.status == StatusCode::Found) {
                        load_data.method = Method::Get;
                    }

                    if redirected_to.contains(&url) {
                        send_error(url, "redirect loop".to_string(), start_chan);
                        return;
                    }

                    redirected_to.insert(url.clone());
                    continue;
                }
                None => ()
            }
        }

        let mut adjusted_headers = response.headers.clone();
        if viewing_source {
            adjusted_headers.set(ContentType(Mime(TopLevel::Text, SubLevel::Plain, vec![])));
        }
        let mut metadata: Metadata = Metadata::default(url);
        metadata.set_content_type(match adjusted_headers.get() {
            Some(&ContentType(ref mime)) => Some(mime),
            None => None
        });
        metadata.headers = Some(adjusted_headers);
        metadata.status = Some(response.status_raw().clone());

        let mut encoding_str: Option<String> = None;
        //FIXME: Implement Content-Encoding Header https://github.com/hyperium/hyper/issues/391
        if let Some(encodings) = response.headers.get_raw("content-encoding") {
            for encoding in encodings.iter() {
                if let Ok(encodings) = String::from_utf8(encoding.clone()) {
                    if encodings == "gzip" || encodings == "deflate" {
                        encoding_str = Some(encodings);
                        break;
                    }
                }
            }
        }

        match encoding_str {
            Some(encoding) => {
                if encoding == "gzip" {
                    let mut response_decoding = GzDecoder::new(response).unwrap();
                    send_data(&mut response_decoding, start_chan, metadata, classifier);
                } else if encoding == "deflate" {
                    let mut response_decoding = DeflateDecoder::new(response);
                    send_data(&mut response_decoding, start_chan, metadata, classifier);
                }
            },
            None => {
                send_data(&mut response, start_chan, metadata, classifier);
            }
        }

        // We didn't get redirected.
        break;
    }
}

fn send_data<R: Read>(reader: &mut R,
                      start_chan: LoadConsumer,
                      metadata: Metadata,
                      classifier: Arc<MIMEClassifier>) {
    let (progress_chan, mut chunk) = {
        let buf = match read_block(reader) {
            Ok(ReadResult::Payload(buf)) => buf,
            _ => vec!(),
        };
        let p = match start_sending_sniffed_opt(start_chan, metadata, classifier, &buf) {
            Ok(p) => p,
            _ => return
        };
        (p, buf)
    };

    loop {
        if progress_chan.send(Payload(chunk)).is_err() {
            // The send errors when the receiver is out of scope,
            // which will happen if the fetch has timed out (or has been aborted)
            // so we don't need to continue with the loading of the file here.
            return;
        }

        chunk = match read_block(reader) {
            Ok(ReadResult::Payload(buf)) => buf,
            Ok(ReadResult::EOF) | Err(_) => break,
        };
    }

    let _ = progress_chan.send(Done(Ok(())));
}
<|endoftext|>


<commit_before>use std::ffi::CStr;
use std::fmt;
use std::os::raw::{
    c_char,
    c_int,
    c_void,
};
use std::str;

use c_ares_sys;

use error::AresError;
use utils::ares_error;

/// The result of a successful name-info lookup.
#[derive(Clone, Copy, Debug)]
pub struct NameInfoResult<'a> {
    node: Option<&'a c_char>,
    service: Option<&'a c_char>,
}

impl<'a> NameInfoResult<'a> {
    fn new(
        node: Option<&'a c_char>,
        service: Option<&'a c_char>) -> NameInfoResult<'a> {
        NameInfoResult {
            node: node,
            service: service,
        }
    }

    /// Returns the node from this `NameInfoResult`.
    pub fn node(&self) -> Option<&str> {
        self.node.map(|string| {
            unsafe {
                let c_str = CStr::from_ptr(string);
                str::from_utf8_unchecked(c_str.to_bytes())
            }
        })
    }

    /// Returns the service from this `NameInfoResult`.
    pub fn service(&self) -> Option<&str> {
        self.service.map(|string| {
            unsafe {
                let c_str = CStr::from_ptr(string);
                str::from_utf8_unchecked(c_str.to_bytes())
            }
        })
    }
}

impl<'a> fmt::Display for NameInfoResult<'a> {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        let node = self.node().unwrap_or("<None>");
        try!(write!(fmt, "Node: {}, ", node));
        let service = self.service().unwrap_or("<None>");
        try!(write!(fmt, "Service: {}", service));
        Ok(())
    }
}

unsafe impl<'a> Send for NameInfoResult<'a> { }
unsafe impl<'a> Sync for NameInfoResult<'a> { }

pub unsafe extern "C" fn get_name_info_callback<F>(
    arg: *mut c_void,
    status: c_int,
    _timeouts: c_int,
    node: *mut c_char,
    service: *mut c_char)
    where F: FnOnce(Result<NameInfoResult, AresError>) + 'static {
    let result = if status == c_ares_sys::ARES_SUCCESS {
        let name_info_result = NameInfoResult::new(node.as_ref(), service.as_ref());
        Ok(name_info_result)
    } else {
        Err(ares_error(status))
    };
    let handler = Box::from_raw(arg as *mut F);
    handler(result);
}
<commit_msg>Long line<commit_after>use std::ffi::CStr;
use std::fmt;
use std::os::raw::{
    c_char,
    c_int,
    c_void,
};
use std::str;

use c_ares_sys;

use error::AresError;
use utils::ares_error;

/// The result of a successful name-info lookup.
#[derive(Clone, Copy, Debug)]
pub struct NameInfoResult<'a> {
    node: Option<&'a c_char>,
    service: Option<&'a c_char>,
}

impl<'a> NameInfoResult<'a> {
    fn new(
        node: Option<&'a c_char>,
        service: Option<&'a c_char>) -> NameInfoResult<'a> {
        NameInfoResult {
            node: node,
            service: service,
        }
    }

    /// Returns the node from this `NameInfoResult`.
    pub fn node(&self) -> Option<&str> {
        self.node.map(|string| {
            unsafe {
                let c_str = CStr::from_ptr(string);
                str::from_utf8_unchecked(c_str.to_bytes())
            }
        })
    }

    /// Returns the service from this `NameInfoResult`.
    pub fn service(&self) -> Option<&str> {
        self.service.map(|string| {
            unsafe {
                let c_str = CStr::from_ptr(string);
                str::from_utf8_unchecked(c_str.to_bytes())
            }
        })
    }
}

impl<'a> fmt::Display for NameInfoResult<'a> {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        let node = self.node().unwrap_or("<None>");
        try!(write!(fmt, "Node: {}, ", node));
        let service = self.service().unwrap_or("<None>");
        try!(write!(fmt, "Service: {}", service));
        Ok(())
    }
}

unsafe impl<'a> Send for NameInfoResult<'a> { }
unsafe impl<'a> Sync for NameInfoResult<'a> { }

pub unsafe extern "C" fn get_name_info_callback<F>(
    arg: *mut c_void,
    status: c_int,
    _timeouts: c_int,
    node: *mut c_char,
    service: *mut c_char)
    where F: FnOnce(Result<NameInfoResult, AresError>) + 'static {
    let result = if status == c_ares_sys::ARES_SUCCESS {
        let name_info_result = NameInfoResult::new(
            node.as_ref(),
            service.as_ref());
        Ok(name_info_result)
    } else {
        Err(ares_error(status))
    };
    let handler = Box::from_raw(arg as *mut F);
    handler(result);
}
<|endoftext|>


<commit_before>use std::borrow::Cow;
use std::collections::HashMap;
use std::path::*;
use std::string::String;

use case::*;

pub fn split_namespace_name(name: &str) -> (Option<&str>, &str) {
    let mut parts = name.split('.');
    let name = parts.next_back().unwrap();
    let ns = parts.next_back();
    assert!(ns.is_none() || parts.next().is_none());
    (ns, name)
}

/* unused :(
pub fn strip_prefix<'a>(prefix: &str, name: &'a str) -> &'a str {
    let mut skip = 0;
    let mut prefix_upper = prefix.to_ascii_uppercase();
    prefix_upper.push('_');
    if name.starts_with(&prefix_upper) {
        skip = prefix_upper.len();
    }
    else if name.starts_with(prefix) {
        skip = prefix.len();
    }
    &name[skip..]
}

pub fn strip_suffix<'a>(name: &'a str, suffix: &str) -> Option<&'a str> {
    if name.ends_with(suffix) {
        Some(&name[..name.len() - suffix.len()])
    }
    else {
        None
    }
}
*/

pub fn file_name(full_name: &str) -> String {
    let (_, class_name) = split_namespace_name(full_name);
    let mut name = PathBuf::from(module_name(class_name));
    let added = name.set_extension("rs");
    assert!(added);
    name.to_str().unwrap().into()
}

pub fn file_name_sys(name: &str) -> String {
    let mut path = PathBuf::from("src").join(name);
    let added = path.set_extension("rs");
    assert!(added);
    path.to_str().unwrap().into()
}

pub fn crate_name(name: &str) -> String {
    let name = name.to_snake();
    if name.starts_with("g_") {
        format!("g{}", &name[2..])
    }
    else {
        name
    }
}

pub fn module_name(name: &str) -> String {
    name.to_snake()
}

pub fn mangle_keywords<'a, S: Into<Cow<'a, str>>>(name: S) -> Cow<'a, str> {
    let name = name.into();
    if let Some(s) = KEYWORDS.get(&*name) {
        s[..].into()
    }
    else {
        name
    }
}

lazy_static! {
    static ref KEYWORDS: HashMap<&'static str, String> = {
        let mut map = HashMap::new();
        [
            "abstract", "alignof", "as", "become", "box", "break", "const",
            "continue", "crate", "do", "else", "enum", "extern", "false", "final",
            "fn", "for", "if", "impl", "in", "let", "loop", "macro", "match", "mod",
            "move", "mut", "offsetof", "override", "priv", "proc", "pub", "pure",
            "ref", "return", "Self", "self", "sizeof", "static", "struct", "super",
            "trait", "true", "type", "typeof", "unsafe", "unsized", "use", "virtual",
            "where", "while", "yield",
        ].iter().map(|k| map.insert(*k, format!("{}_", k))).count();
        map
    };
}

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

    #[test]
    fn split_no_namespace() {
        let (ns, name) = split_namespace_name("GObject");
        assert_eq!(ns, None);
        assert_eq!(name, "GObject");
    }

    #[test]
    fn split_full_name() {
        let (ns, name) = split_namespace_name("Gtk.StatusIcon");
        assert_eq!(ns, Some("Gtk"));
        assert_eq!(name, "StatusIcon");
    }

    /*
    #[test]
    fn strip_prefix_g() {
        assert_eq!(strip_prefix("G", "GBusType"), "BusType");
        assert_eq!(strip_prefix("G", "G_BUS_TYPE_NONE"), "BUS_TYPE_NONE");
    }

    #[test]
    fn strip_prefix_gtk() {
        assert_eq!(strip_prefix("Gtk", "GtkAlign"), "Align");
        assert_eq!(strip_prefix("Gtk", "GTK_ALIGN_FILL"), "ALIGN_FILL");
    }
    */

    #[test]
    fn crate_name_works() {
        assert_eq!(crate_name("GdkPixbuf"), "gdk_pixbuf");
        assert_eq!(crate_name("GLib"), "glib");
        assert_eq!(crate_name("Gtk"), "gtk");
    }

    #[test]
    fn file_name_works() {
        assert_eq!(file_name("Gtk.StatusIcon"), "status_icon.rs");
    }

    #[test]
    fn file_name_sys_works() {
        let expected: String = PathBuf::from("src").join("funcs.rs")
            .to_str().unwrap().into();
        assert_eq!(file_name_sys("funcs"), expected);
    }
}
<commit_msg>Make module_name mangle reserved names<commit_after>use std::borrow::Cow;
use std::collections::HashMap;
use std::path::*;
use std::string::String;

use case::*;

pub fn split_namespace_name(name: &str) -> (Option<&str>, &str) {
    let mut parts = name.split('.');
    let name = parts.next_back().unwrap();
    let ns = parts.next_back();
    assert!(ns.is_none() || parts.next().is_none());
    (ns, name)
}

/* unused :(
pub fn strip_prefix<'a>(prefix: &str, name: &'a str) -> &'a str {
    let mut skip = 0;
    let mut prefix_upper = prefix.to_ascii_uppercase();
    prefix_upper.push('_');
    if name.starts_with(&prefix_upper) {
        skip = prefix_upper.len();
    }
    else if name.starts_with(prefix) {
        skip = prefix.len();
    }
    &name[skip..]
}

pub fn strip_suffix<'a>(name: &'a str, suffix: &str) -> Option<&'a str> {
    if name.ends_with(suffix) {
        Some(&name[..name.len() - suffix.len()])
    }
    else {
        None
    }
}
*/

pub fn file_name(full_name: &str) -> String {
    let (_, class_name) = split_namespace_name(full_name);
    let mut name = PathBuf::from(module_name(class_name));
    let added = name.set_extension("rs");
    assert!(added);
    name.to_str().unwrap().into()
}

pub fn file_name_sys(name: &str) -> String {
    let mut path = PathBuf::from("src").join(name);
    let added = path.set_extension("rs");
    assert!(added);
    path.to_str().unwrap().into()
}

pub fn crate_name(name: &str) -> String {
    let name = name.to_snake();
    if name.starts_with("g_") {
        format!("g{}", &name[2..])
    }
    else {
        name
    }
}

pub fn module_name(name: &str) -> String {
    mangle_keywords(name.to_snake()).into_owned()
}

pub fn mangle_keywords<'a, S: Into<Cow<'a, str>>>(name: S) -> Cow<'a, str> {
    let name = name.into();
    if let Some(s) = KEYWORDS.get(&*name) {
        s[..].into()
    }
    else {
        name
    }
}

lazy_static! {
    static ref KEYWORDS: HashMap<&'static str, String> = {
        let mut map = HashMap::new();
        [
            "abstract", "alignof", "as", "become", "box", "break", "const",
            "continue", "crate", "do", "else", "enum", "extern", "false", "final",
            "fn", "for", "if", "impl", "in", "let", "loop", "macro", "match", "mod",
            "move", "mut", "offsetof", "override", "priv", "proc", "pub", "pure",
            "ref", "return", "Self", "self", "sizeof", "static", "struct", "super",
            "trait", "true", "type", "typeof", "unsafe", "unsized", "use", "virtual",
            "where", "while", "yield",
        ].iter().map(|k| map.insert(*k, format!("{}_", k))).count();
        map
    };
}

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

    #[test]
    fn split_no_namespace() {
        let (ns, name) = split_namespace_name("GObject");
        assert_eq!(ns, None);
        assert_eq!(name, "GObject");
    }

    #[test]
    fn split_full_name() {
        let (ns, name) = split_namespace_name("Gtk.StatusIcon");
        assert_eq!(ns, Some("Gtk"));
        assert_eq!(name, "StatusIcon");
    }

    /*
    #[test]
    fn strip_prefix_g() {
        assert_eq!(strip_prefix("G", "GBusType"), "BusType");
        assert_eq!(strip_prefix("G", "G_BUS_TYPE_NONE"), "BUS_TYPE_NONE");
    }

    #[test]
    fn strip_prefix_gtk() {
        assert_eq!(strip_prefix("Gtk", "GtkAlign"), "Align");
        assert_eq!(strip_prefix("Gtk", "GTK_ALIGN_FILL"), "ALIGN_FILL");
    }
    */

    #[test]
    fn crate_name_works() {
        assert_eq!(crate_name("GdkPixbuf"), "gdk_pixbuf");
        assert_eq!(crate_name("GLib"), "glib");
        assert_eq!(crate_name("Gtk"), "gtk");
    }

    #[test]
    fn file_name_works() {
        assert_eq!(file_name("Gtk.StatusIcon"), "status_icon.rs");
    }

    #[test]
    fn file_name_sys_works() {
        let expected: String = PathBuf::from("src").join("funcs.rs")
            .to_str().unwrap().into();
        assert_eq!(file_name_sys("funcs"), expected);
    }
}
<|endoftext|>


<commit_before>extern crate bencode;

use self::bencode::{FromBencode, Bencode, StringFromBencodeError};
use self::bencode::util::ByteString;
use std::fs::File;
use std::io::Read;
use std::{convert, io};

#[derive(PartialEq, Debug)]
struct Metainfo {
    announce: String,
    created_by: String,
}

#[derive(Debug)]
enum MetainfoError {
    NotADict,
    DoesntContain(&'static str),
    NotAString(StringFromBencodeError),
}

impl convert::From<StringFromBencodeError> for MetainfoError {
    fn from(err: StringFromBencodeError) -> MetainfoError {
        MetainfoError::NotAString(err)
    }
}

impl FromBencode for Metainfo {
    type Err = MetainfoError;

    fn from_bencode(bencode: &bencode::Bencode) -> Result<Metainfo, MetainfoError> {
        match bencode {
            &Bencode::Dict(ref m) => {
                let announce = match m.get(&ByteString::from_str("announce")) {
                    Some(a) => try!(FromBencode::from_bencode(a)),
                    None => return Err(MetainfoError::DoesntContain("announce"))
                };

                let created_by = match m.get(&ByteString::from_str("created by")) {
                    Some(a) => try!(FromBencode::from_bencode(a)),
                    None => "".to_string()
                };

                Ok(Metainfo{ announce: announce, created_by: created_by })
            }
            _ => Err(MetainfoError::NotADict)
        }
    }
}

pub fn run() {
    let result = parse_torrent_file("test_data/flagfromserver.torrent");
    match result {
        Ok(s)  => println!("Yay, it worked: {:?}", s),
        Err(e) => println!("Oops, it failed: {}", e)
    }
}

fn parse_torrent_file(filename: &str) -> Result<Metainfo, io::Error> {
    // read the torrent file into a byte vector
    let mut f = try!(File::open(filename));
    let mut v = Vec::new();
    try!(f.read_to_end(&mut v));

    // decode the byte vector into a struct
    let bencode = bencode::from_vec(v).unwrap();
    let result = FromBencode::from_bencode(&bencode).unwrap();

    Ok(result)
}
<commit_msg>Refactored calls to unwrap into try with Metainfo error wrappers.<commit_after>extern crate bencode;

use self::bencode::{FromBencode, Bencode, StringFromBencodeError};
use self::bencode::util::ByteString;
use std::fs::File;
use std::io::Read;
use std::{convert, io};

#[derive(PartialEq, Debug)]
struct Metainfo {
    announce: String,
    created_by: String,
}

#[derive(Debug)]
enum MetainfoError {
    IoError(io::Error),
    DecodingError(bencode::streaming::Error),
    NotADict,
    DoesntContain(&'static str),
    NotAString(StringFromBencodeError),
}

impl convert::From<io::Error> for MetainfoError {
    fn from(err: io::Error) -> MetainfoError {
        MetainfoError::IoError(err)
    }
}

impl convert::From<bencode::streaming::Error> for MetainfoError {
    fn from(err: bencode::streaming::Error) -> MetainfoError {
        MetainfoError::DecodingError(err)
    }
}

impl convert::From<StringFromBencodeError> for MetainfoError {
    fn from(err: StringFromBencodeError) -> MetainfoError {
        MetainfoError::NotAString(err)
    }
}

impl FromBencode for Metainfo {
    type Err = MetainfoError;

    fn from_bencode(bencode: &bencode::Bencode) -> Result<Metainfo, MetainfoError> {
        match bencode {
            &Bencode::Dict(ref m) => {
                let announce = match m.get(&ByteString::from_str("announce")) {
                    Some(a) => try!(FromBencode::from_bencode(a)),
                    None => return Err(MetainfoError::DoesntContain("announce"))
                };

                let created_by = match m.get(&ByteString::from_str("created by")) {
                    Some(a) => try!(FromBencode::from_bencode(a)),
                    None => "".to_string()
                };

                Ok(Metainfo{ announce: announce, created_by: created_by })
            }
            _ => Err(MetainfoError::NotADict)
        }
    }
}

pub fn run() {
    let result = parse_torrent_file("test_data/flagfromserver.torrent");
    match result {
        Ok(s)  => println!("Yay, it worked: {:?}", s),
        Err(e) => println!("Oops, it failed: {:?}", e)
    }
}

fn parse_torrent_file(filename: &str) -> Result<Metainfo, MetainfoError> {
    // read the torrent file into a byte vector
    let mut f = try!(File::open(filename));
    let mut v = Vec::new();
    try!(f.read_to_end(&mut v));

    // decode the byte vector into a struct
    let bencode = try!(bencode::from_vec(v));
    let result = try!(FromBencode::from_bencode(&bencode));

    Ok(result)
}
<|endoftext|>


<commit_before>// Copyright (c) 2016 The vulkano developers
// 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. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

use std::error;
use std::fmt;
use std::mem;
use std::ptr;
use std::sync::Arc;

use descriptor::PipelineLayout;
use descriptor::pipeline_layout::PipelineLayoutDesc;
use descriptor::pipeline_layout::PipelineLayoutSuperset;
use pipeline::shader::ComputeShaderEntryPoint;
use pipeline::shader::SpecializationConstants;

use device::Device;
use Error;
use OomError;
use VulkanObject;
use VulkanPointers;
use check_errors;
use vk;

///
///
/// The template parameter contains the descriptor set to use with this pipeline.
pub struct ComputePipeline<Pl> {
    pipeline: vk::Pipeline,
    device: Arc<Device>,
    pipeline_layout: Arc<Pl>,
}

impl<Pl> ComputePipeline<Pl> {
    ///
    ///
    /// # Panic
    ///
    /// Panicks if the pipeline layout and/or shader don't belong to the device.
    pub fn new<Css, Csl>(device: &Arc<Device>, pipeline_layout: &Arc<Pl>,
                         shader: &ComputeShaderEntryPoint<Css, Csl>, specialization: &Css)
                         -> Result<Arc<ComputePipeline<Pl>>, ComputePipelineCreationError>
        where Pl: PipelineLayout + PipelineLayoutSuperset<Csl>, Csl: PipelineLayoutDesc,
              Css: SpecializationConstants
    {
        let vk = device.pointers();

        if !PipelineLayoutSuperset::is_superset_of(&**pipeline_layout, shader.layout()) {
            return Err(ComputePipelineCreationError::IncompatiblePipelineLayout);
        }

        let pipeline = unsafe {
            let spec_descriptors = <Css as SpecializationConstants>::descriptors();
            let specialization = vk::SpecializationInfo {
                mapEntryCount: spec_descriptors.len() as u32,
                pMapEntries: spec_descriptors.as_ptr() as *const _,
                dataSize: mem::size_of_val(specialization),
                pData: specialization as *const Css as *const _,
            };

            let stage = vk::PipelineShaderStageCreateInfo {
                sType: vk::STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO,
                pNext: ptr::null(),
                flags: 0,
                stage: vk::SHADER_STAGE_COMPUTE_BIT,
                module: shader.module().internal_object(),
                pName: shader.name().as_ptr(),
                pSpecializationInfo: if specialization.dataSize == 0 {
                    ptr::null()
                } else {
                    &specialization
                },
            };

            let infos = vk::ComputePipelineCreateInfo {
                sType: vk::STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO,
                pNext: ptr::null(),
                flags: 0,
                stage: stage,
                layout: PipelineLayout::inner_pipeline_layout(&**pipeline_layout).internal_object(),
                basePipelineHandle: 0,
                basePipelineIndex: 0,
            };

            let mut output = mem::uninitialized();
            try!(check_errors(vk.CreateComputePipelines(device.internal_object(), 0,
                                                        1, &infos, ptr::null(), &mut output)));
            output
        };

        Ok(Arc::new(ComputePipeline {
            device: device.clone(),
            pipeline: pipeline,
            pipeline_layout: pipeline_layout.clone(),
        }))
    }

    /// Returns the `Device` this compute pipeline was created with.
    #[inline]
    pub fn device(&self) -> &Arc<Device> {
        &self.device
    }

    /// Returns the pipeline layout used in this compute pipeline.
    #[inline]
    pub fn layout(&self) -> &Arc<Pl> {
        &self.pipeline_layout
    }
}

unsafe impl<Pl> VulkanObject for ComputePipeline<Pl> {
    type Object = vk::Pipeline;

    #[inline]
    fn internal_object(&self) -> vk::Pipeline {
        self.pipeline
    }
}

impl<Pl> Drop for ComputePipeline<Pl> {
    #[inline]
    fn drop(&mut self) {
        unsafe {
            let vk = self.device.pointers();
            vk.DestroyPipeline(self.device.internal_object(), self.pipeline, ptr::null());
        }
    }
}

/// Error that can happen when creating an instance.
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum ComputePipelineCreationError {
    /// Not enough memory.
    OomError(OomError),
    /// The pipeline layout is not compatible with what the shader expects.
    IncompatiblePipelineLayout,
}

impl error::Error for ComputePipelineCreationError {
    #[inline]
    fn description(&self) -> &str {
        match *self {
            ComputePipelineCreationError::OomError(_) => "not enough memory available",
            ComputePipelineCreationError::IncompatiblePipelineLayout => "the pipeline layout is \
                                                                         not compatible with what \
                                                                         the shader expects",
        }
    }

    #[inline]
    fn cause(&self) -> Option<&error::Error> {
        match *self {
            ComputePipelineCreationError::OomError(ref err) => Some(err),
            _ => None
        }
    }
}

impl fmt::Display for ComputePipelineCreationError {
    #[inline]
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(fmt, "{}", error::Error::description(self))
    }
}

impl From<OomError> for ComputePipelineCreationError {
    #[inline]
    fn from(err: OomError) -> ComputePipelineCreationError {
        ComputePipelineCreationError::OomError(err)
    }
}

impl From<Error> for ComputePipelineCreationError {
    #[inline]
    fn from(err: Error) -> ComputePipelineCreationError {
        match err {
            err @ Error::OutOfHostMemory => {
                ComputePipelineCreationError::OomError(OomError::from(err))
            },
            err @ Error::OutOfDeviceMemory => {
                ComputePipelineCreationError::OomError(OomError::from(err))
            },
            _ => panic!("unexpected error: {:?}", err)
        }
    }
}

#[cfg(test)]
mod tests {
    // TODO: test for basic creation
    // TODO: test for pipeline layout error
}
<commit_msg>Fix documentation of ComputePipeline<commit_after>// Copyright (c) 2016 The vulkano developers
// 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. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

use std::error;
use std::fmt;
use std::mem;
use std::ptr;
use std::sync::Arc;

use descriptor::PipelineLayout;
use descriptor::pipeline_layout::PipelineLayoutDesc;
use descriptor::pipeline_layout::PipelineLayoutSuperset;
use pipeline::shader::ComputeShaderEntryPoint;
use pipeline::shader::SpecializationConstants;

use device::Device;
use Error;
use OomError;
use VulkanObject;
use VulkanPointers;
use check_errors;
use vk;

/// A pipeline object that describes to the Vulkan implementation how it should perform compute
/// operations.
///
/// The template parameter contains the descriptor set to use with this pipeline.
pub struct ComputePipeline<Pl> {
    pipeline: vk::Pipeline,
    device: Arc<Device>,
    pipeline_layout: Arc<Pl>,
}

impl<Pl> ComputePipeline<Pl> {
    /// Builds a new `ComputePipeline`.
    pub fn new<Css, Csl>(device: &Arc<Device>, pipeline_layout: &Arc<Pl>,
                         shader: &ComputeShaderEntryPoint<Css, Csl>, specialization: &Css)
                         -> Result<Arc<ComputePipeline<Pl>>, ComputePipelineCreationError>
        where Pl: PipelineLayout + PipelineLayoutSuperset<Csl>, Csl: PipelineLayoutDesc,
              Css: SpecializationConstants
    {
        let vk = device.pointers();

        if !PipelineLayoutSuperset::is_superset_of(&**pipeline_layout, shader.layout()) {
            return Err(ComputePipelineCreationError::IncompatiblePipelineLayout);
        }

        let pipeline = unsafe {
            let spec_descriptors = <Css as SpecializationConstants>::descriptors();
            let specialization = vk::SpecializationInfo {
                mapEntryCount: spec_descriptors.len() as u32,
                pMapEntries: spec_descriptors.as_ptr() as *const _,
                dataSize: mem::size_of_val(specialization),
                pData: specialization as *const Css as *const _,
            };

            let stage = vk::PipelineShaderStageCreateInfo {
                sType: vk::STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO,
                pNext: ptr::null(),
                flags: 0,
                stage: vk::SHADER_STAGE_COMPUTE_BIT,
                module: shader.module().internal_object(),
                pName: shader.name().as_ptr(),
                pSpecializationInfo: if specialization.dataSize == 0 {
                    ptr::null()
                } else {
                    &specialization
                },
            };

            let infos = vk::ComputePipelineCreateInfo {
                sType: vk::STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO,
                pNext: ptr::null(),
                flags: 0,
                stage: stage,
                layout: PipelineLayout::inner_pipeline_layout(&**pipeline_layout).internal_object(),
                basePipelineHandle: 0,
                basePipelineIndex: 0,
            };

            let mut output = mem::uninitialized();
            try!(check_errors(vk.CreateComputePipelines(device.internal_object(), 0,
                                                        1, &infos, ptr::null(), &mut output)));
            output
        };

        Ok(Arc::new(ComputePipeline {
            device: device.clone(),
            pipeline: pipeline,
            pipeline_layout: pipeline_layout.clone(),
        }))
    }

    /// Returns the `Device` this compute pipeline was created with.
    #[inline]
    pub fn device(&self) -> &Arc<Device> {
        &self.device
    }

    /// Returns the pipeline layout used in this compute pipeline.
    #[inline]
    pub fn layout(&self) -> &Arc<Pl> {
        &self.pipeline_layout
    }
}

unsafe impl<Pl> VulkanObject for ComputePipeline<Pl> {
    type Object = vk::Pipeline;

    #[inline]
    fn internal_object(&self) -> vk::Pipeline {
        self.pipeline
    }
}

impl<Pl> Drop for ComputePipeline<Pl> {
    #[inline]
    fn drop(&mut self) {
        unsafe {
            let vk = self.device.pointers();
            vk.DestroyPipeline(self.device.internal_object(), self.pipeline, ptr::null());
        }
    }
}

/// Error that can happen when creating a compute pipeline.
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum ComputePipelineCreationError {
    /// Not enough memory.
    OomError(OomError),
    /// The pipeline layout is not compatible with what the shader expects.
    IncompatiblePipelineLayout,
}

impl error::Error for ComputePipelineCreationError {
    #[inline]
    fn description(&self) -> &str {
        match *self {
            ComputePipelineCreationError::OomError(_) => "not enough memory available",
            ComputePipelineCreationError::IncompatiblePipelineLayout => "the pipeline layout is \
                                                                         not compatible with what \
                                                                         the shader expects",
        }
    }

    #[inline]
    fn cause(&self) -> Option<&error::Error> {
        match *self {
            ComputePipelineCreationError::OomError(ref err) => Some(err),
            _ => None
        }
    }
}

impl fmt::Display for ComputePipelineCreationError {
    #[inline]
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(fmt, "{}", error::Error::description(self))
    }
}

impl From<OomError> for ComputePipelineCreationError {
    #[inline]
    fn from(err: OomError) -> ComputePipelineCreationError {
        ComputePipelineCreationError::OomError(err)
    }
}

impl From<Error> for ComputePipelineCreationError {
    #[inline]
    fn from(err: Error) -> ComputePipelineCreationError {
        match err {
            err @ Error::OutOfHostMemory => {
                ComputePipelineCreationError::OomError(OomError::from(err))
            },
            err @ Error::OutOfDeviceMemory => {
                ComputePipelineCreationError::OomError(OomError::from(err))
            },
            _ => panic!("unexpected error: {:?}", err)
        }
    }
}

#[cfg(test)]
mod tests {
    // TODO: test for basic creation
    // TODO: test for pipeline layout error
}
<|endoftext|>


<commit_before>use std::thread;
use std::sync::mpsc::{TryRecvError, channel};
use std::time::Duration;
use std::io;
use std::net::{SocketAddr, SocketAddrV4, Ipv4Addr};

use byteorder::{ByteOrder, LittleEndian};
use mio::tcp::TcpListener;

use net::EventLoop;

#[test]
fn get_packet_length_correct() {
    let mut m_number: [u8; 4] = [0; 4];
    LittleEndian::write_u32(&mut m_number, super::NET_MAGIC_NUMBER);
    let mut intended_length: [u8; 2] = [0; 2];
    LittleEndian::write_u16(&mut intended_length, 10);
    let length = super::get_packet_length([m_number[0],
                                           m_number[1],
                                           m_number[2],
                                           m_number[3],
                                           intended_length[0],
                                           intended_length[1]])
                     .expect("get_packet_length wrongly checks NET_MAGIC_NUMBER");
    assert_eq!(length, 10);
}

#[test]
fn get_packet_length_bad_magic_number() {
    let length: Option<u16> = super::get_packet_length([0xFE, 0xF0, 0xF6, 0xFD, 0, 10]);
    assert_eq!(length, None);
}

#[test]
fn handler_new() {
    super::Handler::new(super::MAX_CONNECTIONS);
}

#[test]
fn event_loop_impl_new() {
    super::EventLoopImpl::new(super::MAX_CONNECTIONS).unwrap();
}

#[test]
fn event_loop_impl_shutdown() {
    let (tx, rx) = channel::<Result<(), io::Error>>();
    let mut event_loop = super::EventLoopImpl::new(super::MAX_CONNECTIONS).unwrap();
    let event_loop_ref: super::EventLoopImplRef = (&mut event_loop).into();
    let thread = thread::spawn(move || tx.send(event_loop.run()).unwrap());
    event_loop_ref.shutdown();
    thread::sleep(Duration::from_millis(250));
    // This may, once in a very long time, fail. It really shouldn't, but it is possible.
    // Just raise the number or try again if it fails.
    match rx.try_recv() {
        Err(TryRecvError::Empty) => {
            panic!("EventLoopImpl did not stop after calling shutdown()! This fact is depended on \
                    by other unit tests, so ctrl+c here")
        }
        Err(TryRecvError::Disconnected) => {
            panic!("EventLoop somehow disconnected it's channel without stopping! This fact is \
                    depended on by other unit tests, so ctrl+c here")
        }
        Ok(res) => res,
    }
    .unwrap();
    thread.join().unwrap();
}

#[test]
fn event_loop_impl_add_listener() {
    let mut event_loop = super::EventLoopImpl::new(super::MAX_CONNECTIONS).unwrap();
    let event_loop_ref: super::EventLoopImplRef = (&mut event_loop).into();
    let listener = TcpListener::bind(&SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(0, 0, 0, 0),
                                                                       25567)))
                       .unwrap();
    let thread = thread::spawn(move || {
        event_loop.run().unwrap();
        event_loop
    });
    event_loop_ref.add_listener(listener);
    event_loop_ref.shutdown();
    let event_loop = thread.join().unwrap();
    assert_eq!(event_loop.handler.listeners.len(), 1);
}
<commit_msg>Unit test: event_loop_impl_add_socket<commit_after>use std::thread;
use std::thread::JoinHandle;
use std::sync::mpsc::{TryRecvError, channel};
use std::time::Duration;
use std::io;
use std::net::{SocketAddr, SocketAddrV4, Ipv4Addr};

use byteorder::{ByteOrder, LittleEndian};
use mio::tcp::{TcpListener, TcpStream};

use net::EventLoop;

#[test]
fn get_packet_length_correct() {
    let mut m_number: [u8; 4] = [0; 4];
    LittleEndian::write_u32(&mut m_number, super::NET_MAGIC_NUMBER);
    let mut intended_length: [u8; 2] = [0; 2];
    LittleEndian::write_u16(&mut intended_length, 10);
    let length = super::get_packet_length([m_number[0],
                                           m_number[1],
                                           m_number[2],
                                           m_number[3],
                                           intended_length[0],
                                           intended_length[1]])
                     .expect("get_packet_length wrongly checks NET_MAGIC_NUMBER");
    assert_eq!(length, 10);
}

#[test]
fn get_packet_length_bad_magic_number() {
    let length: Option<u16> = super::get_packet_length([0xFE, 0xF0, 0xF6, 0xFD, 0, 10]);
    assert_eq!(length, None);
}

#[test]
fn handler_new() {
    super::Handler::new(super::MAX_CONNECTIONS);
}

#[test]
fn event_loop_impl_new() {
    super::EventLoopImpl::new(super::MAX_CONNECTIONS).unwrap();
}

#[test]
fn event_loop_impl_shutdown() {
    let (tx, rx) = channel::<Result<(), io::Error>>();
    let mut event_loop = super::EventLoopImpl::new(super::MAX_CONNECTIONS).unwrap();
    let event_loop_ref: super::EventLoopImplRef = (&mut event_loop).into();
    let thread = thread::spawn(move || tx.send(event_loop.run()).unwrap());
    event_loop_ref.shutdown();
    thread::sleep(Duration::from_millis(250));
    // This may, once in a very long time, fail. It really shouldn't, but it is possible.
    // Just raise the number or try again if it fails.
    match rx.try_recv() {
        Err(TryRecvError::Empty) => {
            panic!("EventLoopImpl did not stop after calling shutdown()! This fact is depended on \
                    by other unit tests, so ctrl+c here")
        }
        Err(TryRecvError::Disconnected) => {
            panic!("EventLoop somehow disconnected it's channel without stopping! This fact is \
                    depended on by other unit tests, so ctrl+c here")
        }
        Ok(res) => res,
    }
    .unwrap();
    thread.join().unwrap();
}

fn event_loop_helper() -> (super::EventLoopImplRef, JoinHandle<super::EventLoopImpl>) {
    let mut event_loop = super::EventLoopImpl::new(super::MAX_CONNECTIONS).unwrap();
    let event_loop_ref: super::EventLoopImplRef = (&mut event_loop).into();
    (event_loop_ref,
     thread::spawn(move || {
        event_loop.run().unwrap();
        event_loop
    }))
}

#[test]
fn event_loop_impl_add_listener() {
    let (event_loop_ref, thread) = event_loop_helper();
    let listener = TcpListener::bind(&SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127,
                                                                                0,
                                                                                0,
                                                                                1),
                                                                       0)))
                       .unwrap();
    event_loop_ref.add_listener(listener);
    event_loop_ref.shutdown();
    let event_loop = thread.join().unwrap();
    assert_eq!(event_loop.handler.listeners.len(), 1);
}

#[test]
fn event_loop_impl_add_socket() {
    let (event_loop_ref, thread) = event_loop_helper();
    let listener = TcpListener::bind(&SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127,
                                                                                0,
                                                                                0,
                                                                                1),
                                                                       25567)))
                       .unwrap();
    let _stream_local = TcpStream::connect(&SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127,
                                                                                0,
                                                                                0,
                                                                                1),
                                                                             25567)));
    let stream_remote;
    loop {
        match listener.accept().unwrap() {
            None => {}  // I think this is that there is no socket avalable to accept.
            Some((stream, _addr)) => {
                stream_remote = stream;
                break;
            }
        }
    }
    event_loop_ref.add_socket(stream_remote);
    event_loop_ref.shutdown();
    let event_loop = thread.join().unwrap();
    assert_eq!(event_loop.handler.connections.count(), 1);
}

#[test]
fn event_loop_impl_kill() {
    unimplemented!()
}

#[test]
fn event_loop_impl_send() {
    unimplemented!()
}
<|endoftext|>