<commit_before>use hyper::header::common::content_type::ContentType;
use hyper::server::request::Request;

use mime::{Mime, TopLevel, SubLevel, Attr, Value};

use super::{MultipartField, MultipartFile};

use std::cmp;

use std::io::{IoError, IoResult, EndOfFile, standard_error, OtherIoError};

fn is_multipart_formdata(req: &Request) -> bool {
    use mime::{Multipart};

    req.headers.get::<ContentType>().map_or(false, |ct| {
        let ContentType(ref mime) = *ct;

        debug!("Content-Type: {}", mime);

        match *mime {
            Mime(TopLevel::Multipart, SubLevel::FormData, _) => true,
            _ => false,
        }
    })
}

fn get_boundary(ct: &ContentType) -> Option<String> {
    let ContentType(ref mime) = *ct;
    let Mime(_, _, ref params) = *mime;

    params.iter().find(|&&(ref name, _)|
        if let Attr::Ext(ref name) = *name {
            name[] == "boundary"
        } else { false }
    ).and_then(|&(_, ref val)|
        if let Value::Ext(ref val) = *val {
            Some(val.clone())
        } else { None }
    )
}

pub struct Multipart<'a> {
    source: BoundaryReader<Request<'a>>,
}

macro_rules! try_find(
    ($haystack:expr, $f:ident, $needle:expr, $err:expr, $line:expr) => (
        try!($haystack.$f($needle).ok_or(line_error($err, $line.clone())))
    )
)

impl<'a> Multipart<'a> {

    /// If the given `Request` is of `Content-Type: multipart/form-data`, return
    /// the wrapped request as `Ok(Multipart)`, otherwise `Err(Request)`.
    pub fn from_request(req: Request<'a>) -> Result<Multipart<'a>, Request<'a>> {
        if !is_multipart_formdata(&req) { return Err(req); }

        debug!("Is multipart!");

        let boundary = if let Some(boundary) = req.headers.get::<ContentType>()
            .and_then(get_boundary) { boundary } else { return Err(req); };

        debug!("Boundary: {}", boundary);

        Ok(Multipart { source: BoundaryReader::from_reader(req, format!("--{}\r\n", boundary)) })
    }

    pub fn read_entry<'a>(&'a mut self) -> IoResult<(String, MultipartField<'a>)> {
        debug!("Read entry!");

        try!(self.source.consume_boundary());
        let (disp_type, field_name, filename) = try!(self.read_content_disposition());

        if &*disp_type != "form-data" {
            return Err(IoError {
                    kind: OtherIoError,
                    desc: "Content-Disposition value was not \"form-data\"",
                    detail: Some(format!("Content-Disposition: {}", disp_type)),
                });
        }

        if let Some(content_type) = try!(self.read_content_type()) {
            let _ = try!(self.source.read_line()); // Consume empty line
            Ok((field_name,
                MultipartField::File(
                    MultipartFile::from_octet(filename, &mut self.source, content_type[])
                )
            ))
        } else {
            // Empty line consumed by read_content_type()
            let text = try!(self.source.read_to_string());
            // The last two characters are "\r\n".
            // We can't do a simple trim because the content might be terminated
            // with line separators we want to preserve.
            Ok((field_name, MultipartField::Text(text[..text.len() - 2].into_string())))
        }
    }

    /// Call `f` for each entry in the multipart request.
    /// This is a substitute for `Multipart` implementing `Iterator`,
    /// since `Iterator::next()` can't use bound lifetimes.
    /// See https://www.reddit.com/r/rust/comments/2lkk4i/concrete_lifetime_vs_bound_lifetime/
    pub fn foreach_entry<F: for<'a> FnMut(String, MultipartField<'a>)>(&mut self, mut f: F) {
        loop {
            debug!("Loop!");

            match self.read_entry() {
                Ok((name, field)) => f(name, field),
                Err(err) => {
                    if err.kind != EndOfFile {
                        error!("Error reading Multipart: {}", err);
                    }

                    break;
                },
            }
        }
    }

    fn read_content_disposition(&mut self) -> IoResult<(String, String, Option<String>)> {
        debug!("Read content disposition!");
        let line = try!(self.source.read_line());

        debug!("Line: {}", line);

        // Find the end of CONT_DISP in the line
        let disp_type = {
            const CONT_DISP: &'static str = "Content-Disposition:";

            let disp_idx = try_find!(line[], find_str, CONT_DISP,
                "Content-Disposition subheader not found!", line) + CONT_DISP.len();

            debug!("Disp idx: {} Line len: {}", disp_idx, line.len());

            let disp_type_end = try_find!(line[disp_idx..], find, ';',
                "Error parsing Content-Disposition value!", line);

            debug!("Disp end: {}", disp_type_end);

            line[disp_idx .. disp_idx + disp_type_end].trim().into_string()
        };

        debug!("Disp-type: {}", disp_type);

        let field_name = {
            const NAME: &'static str = "name=\"";

            let name_idx = try_find!(line[], find_str, NAME,
                "Error parsing field name!", line) + NAME.len();

            debug!("Name idx: {}", name_idx);

            let name_end = try_find!(line[name_idx ..], find, '"',
                "Error parsing field name!", line);

            debug!("Name end: {}", name_end);

            line[name_idx .. name_idx + name_end].into_string() // No trim here since it's in quotes.
        };

        debug!("Field name: {}", field_name);

        let filename = {
            const FILENAME: &'static str = "filename=\"";

            let filename_idx = line[].find_str(FILENAME).map(|idx| idx + FILENAME.len());
            let filename_idxs = with(filename_idx, |&start| line[start ..].find('"'));

            filename_idxs.map(|(start, end)| line[start .. start + end].into_string())
        };

        debug!("Filename: {}", filename);

        Ok((disp_type, field_name, filename))
    }

    fn read_content_type(&mut self) -> IoResult<Option<String>> {
        debug!("Read content type!");
        let line = try!(self.source.read_line());

        const CONTENT_TYPE: &'static str = "Content-Type:";

        let type_idx = (&*line).find_str(CONTENT_TYPE);

        // FIXME Will not properly parse for multiple files!
        // Does not expect boundary=<boundary>
        Ok(type_idx.map(|start| line[start + CONTENT_TYPE.len()..].trim().into_string()))
    }
}

fn with<T, U>(left: Option<T>, right: |&T| -> Option<U>) -> Option<(T, U)> {
    let temp = left.as_ref().and_then(right);
    match (left, temp) {
        (Some(lval), Some(rval)) => Some((lval, rval)),
        _ => None,
    }
}

fn line_error(msg: &'static str, line: String) -> IoError {
    IoError {
        kind: OtherIoError,
        desc: msg,
        detail: Some(line),
    }
}

/* FIXME: Can't have an iterator return a borrowed reference
impl<'a> Iterator<(String, MultipartField<'a>)> for Multipart {
    fn next(&mut self) -> Option<(String, MultipartField<'a>)> {
        match self.read_entry() {
            Ok(ok) => Some(ok),
            Err(err) => {
                if err.kind != EndOfFile {
                    error!("Error reading Multipart: {}", err);
                }

                None
             },
        }
    }
}
*/

/// A `Reader` that will yield bytes until it sees a given sequence.
pub struct BoundaryReader<S> {
    reader: S,
    boundary: Vec<u8>,
    last_search_idx: uint,
    boundary_read: bool,
    buf: Vec<u8>,
    buf_len: uint,
}

fn eof<T>() -> IoResult<T> {
    Err(standard_error(EndOfFile))
}

const BUF_SIZE: uint = 1024 * 64; // 64k buffer

impl<S> BoundaryReader<S> where S: Reader {
    fn from_reader(reader: S, boundary: String) -> BoundaryReader<S> {
        let mut buf = Vec::with_capacity(BUF_SIZE);
        unsafe { buf.set_len(BUF_SIZE); }

        BoundaryReader {
            reader: reader,
            boundary: boundary.into_bytes(),
            last_search_idx: 0,
            boundary_read: false,
            buf: buf,
            buf_len: 0,
        }
    }

    fn read_to_boundary(&mut self) -> IoResult<()> {
        debug!("Read to boundary!");

         if !self.boundary_read {
            try!(self.true_fill_buf());

            debug!("Exited true_fill_buf");

            if self.buf_len == 0 { return eof(); }

            let lookahead = self.buf[self.last_search_idx .. self.buf_len];

            debug!("Buf len: {}", self.buf_len);

            debug!("Lookahead: {}", lookahead.to_ascii().as_str_ascii());

            let search_idx = lookahead.position_elem(&self.boundary[0])
                .unwrap_or(lookahead.len() - 1);

            debug!("Search idx: {}", search_idx);

            self.boundary_read = lookahead[search_idx..]
                .starts_with(self.boundary[]);

            debug!("Boundary read: {} Boundary: {}", self.boundary_read, self.boundary.to_ascii().as_str_ascii());

            self.last_search_idx += search_idx;

            if !self.boundary_read {
                self.last_search_idx += 1;
            }

        } else if self.last_search_idx == 0 {
            return Err(standard_error(EndOfFile))
        }

        Ok(())
    }

    /// Read bytes until the reader is full
    fn true_fill_buf(&mut self) -> IoResult<()> {
        debug!("True fill buf! Buf len: {}", self.buf_len);

        let mut bytes_read = 1u;

        while bytes_read != 0 {
            debug!("Bytes read loop!");

            bytes_read = match self.reader.read(self.buf[mut self.buf_len..]) {
                Ok(read) => read,
                Err(err) => if err.kind == EndOfFile { break; } else { return Err(err); },
            };

            debug!("Bytes read: {}", bytes_read);

            self.buf_len += bytes_read;
        }

        debug!("Exited bytes read loop!");

        Ok(())
    }

    fn _consume(&mut self, amt: uint) {
        use std::ptr::copy_memory;

        debug!("Consume! Amt: {}", amt);

        assert!(amt <= self.buf_len);

        let src = self.buf[amt..].as_ptr();
        let dest = self.buf[mut].as_mut_ptr();

        unsafe { copy_memory(dest, src, self.buf_len - amt); }

        self.buf_len -= amt;
        self.last_search_idx -= amt;
    }

    fn consume_boundary(&mut self) -> IoResult<()> {
        debug!("Consume boundary!");

        while !self.boundary_read {
            debug!("Boundary read loop!");

            match self.read_to_boundary() {
                Ok(_) => (),
                Err(e) => if e.kind == EndOfFile {
                    break;
                } else {
                    return Err(e);
                }
            }
        }

        let consume_amt = cmp::min(self.buf_len, self.last_search_idx + self.boundary.len());

        debug!("Consume amt: {} Buf len: {}", consume_amt, self.buf_len);

        self._consume(consume_amt);
        self.last_search_idx = 0;
        self.boundary_read = false;

        Ok(())
    }

    #[allow(unused)]
    fn set_boundary(&mut self, boundary: String) {
        self.boundary = boundary.into_bytes();
    }
}

impl<S> Reader for BoundaryReader<S> where S: Reader {
    fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
        use std::cmp;
        use std::slice::bytes::copy_memory;

        debug!("Read!");

        try!(self.read_to_boundary());

        let trunc_len = cmp::min(buf.len(), self.last_search_idx);
        copy_memory(buf, self.buf[..trunc_len]);

        self._consume(trunc_len);

        Ok(trunc_len)
    }
}

impl<S> Buffer for BoundaryReader<S> where S: Reader {
    fn fill_buf<'a>(&'a mut self) -> IoResult<&'a [u8]> {
        debug!("Fill buf!");

        try!(self.read_to_boundary());

        let buf = self.buf[..self.last_search_idx];

        debug!("Buf: {}", buf.to_ascii().as_str_ascii());

        Ok(buf)
    }

    fn consume(&mut self, amt: uint) {
        assert!(amt <= self.last_search_idx);
        self._consume(amt);
    }
}

#[test]
fn test_boundary() {
    use std::io::BufReader;

    const BOUNDARY: &'static str = "--boundary\r\n";
    const TEST_VAL: &'static str = "\r
--boundary\r
dashed-value-1\r
--boundary\r
dashed-value-2\r
--boundary\r
";

    let test_reader = BufReader::new(TEST_VAL.as_bytes());
    let mut reader = BoundaryReader::from_reader(test_reader, BOUNDARY.into_string());

    debug!("Read 1");
    let string = reader.read_to_string().unwrap();
    debug!("{}", string);
    assert!(string[].trim().is_empty());

    debug!("Consume 1");
    reader.consume_boundary().unwrap();

    debug!("Read 2");
    assert_eq!(reader.read_to_string().unwrap()[].trim(), "dashed-value-1");

    debug!("Consume 2");
    reader.consume_boundary().unwrap();

    debug!("Read 3");
    assert_eq!(reader.read_to_string().unwrap()[].trim(), "dashed-value-2");

    debug!("Consume 3");
    reader.consume_boundary().unwrap();

}

<commit_msg>Remove excess debug statements<commit_after>use hyper::header::common::content_type::ContentType;
use hyper::server::request::Request;

use mime::{Mime, TopLevel, SubLevel, Attr, Value};

use super::{MultipartField, MultipartFile};

use std::cmp;

use std::io::{IoError, IoResult, EndOfFile, standard_error, OtherIoError};

fn is_multipart_formdata(req: &Request) -> bool {
    use mime::{Multipart};

    req.headers.get::<ContentType>().map_or(false, |ct| {
        let ContentType(ref mime) = *ct;

        debug!("Content-Type: {}", mime);

        match *mime {
            Mime(TopLevel::Multipart, SubLevel::FormData, _) => true,
            _ => false,
        }
    })
}

fn get_boundary(ct: &ContentType) -> Option<String> {
    let ContentType(ref mime) = *ct;
    let Mime(_, _, ref params) = *mime;

    params.iter().find(|&&(ref name, _)|
        if let Attr::Ext(ref name) = *name {
            name[] == "boundary"
        } else { false }
    ).and_then(|&(_, ref val)|
        if let Value::Ext(ref val) = *val {
            Some(val.clone())
        } else { None }
    )
}

pub struct Multipart<'a> {
    source: BoundaryReader<Request<'a>>,
}

macro_rules! try_find(
    ($haystack:expr, $f:ident, $needle:expr, $err:expr, $line:expr) => (
        try!($haystack.$f($needle).ok_or(line_error($err, $line.clone())))
    )
)

impl<'a> Multipart<'a> {

    /// If the given `Request` is of `Content-Type: multipart/form-data`, return
    /// the wrapped request as `Ok(Multipart)`, otherwise `Err(Request)`.
    pub fn from_request(req: Request<'a>) -> Result<Multipart<'a>, Request<'a>> {
        if !is_multipart_formdata(&req) { return Err(req); }

        debug!("Is multipart!");

        let boundary = if let Some(boundary) = req.headers.get::<ContentType>()
            .and_then(get_boundary) { boundary } else { return Err(req); };

        debug!("Boundary: {}", boundary);

        Ok(Multipart { source: BoundaryReader::from_reader(req, format!("--{}\r\n", boundary)) })
    }

    pub fn read_entry<'a>(&'a mut self) -> IoResult<(String, MultipartField<'a>)> {
        debug!("Read entry!");

        try!(self.source.consume_boundary());
        let (disp_type, field_name, filename) = try!(self.read_content_disposition());

        if &*disp_type != "form-data" {
            return Err(IoError {
                    kind: OtherIoError,
                    desc: "Content-Disposition value was not \"form-data\"",
                    detail: Some(format!("Content-Disposition: {}", disp_type)),
                });
        }

        if let Some(content_type) = try!(self.read_content_type()) {
            let _ = try!(self.source.read_line()); // Consume empty line
            Ok((field_name,
                MultipartField::File(
                    MultipartFile::from_octet(filename, &mut self.source, content_type[])
                )
            ))
        } else {
            // Empty line consumed by read_content_type()
            let text = try!(self.source.read_to_string());
            // The last two characters are "\r\n".
            // We can't do a simple trim because the content might be terminated
            // with line separators we want to preserve.
            Ok((field_name, MultipartField::Text(text[..text.len() - 2].into_string())))
        }
    }

    /// Call `f` for each entry in the multipart request.
    /// This is a substitute for `Multipart` implementing `Iterator`,
    /// since `Iterator::next()` can't use bound lifetimes.
    /// See https://www.reddit.com/r/rust/comments/2lkk4i/concrete_lifetime_vs_bound_lifetime/
    pub fn foreach_entry<F: for<'a> FnMut(String, MultipartField<'a>)>(&mut self, mut f: F) {
        loop {
            debug!("Loop!");

            match self.read_entry() {
                Ok((name, field)) => f(name, field),
                Err(err) => {
                    if err.kind != EndOfFile {
                        error!("Error reading Multipart: {}", err);
                    }

                    break;
                },
            }
        }
    }

    fn read_content_disposition(&mut self) -> IoResult<(String, String, Option<String>)> {
        debug!("Read content disposition!");
        let line = try!(self.source.read_line());

        debug!("Line: {}", line);

        // Find the end of CONT_DISP in the line
        let disp_type = {
            const CONT_DISP: &'static str = "Content-Disposition:";

            let disp_idx = try_find!(line[], find_str, CONT_DISP,
                "Content-Disposition subheader not found!", line) + CONT_DISP.len();

            debug!("Disp idx: {} Line len: {}", disp_idx, line.len());

            let disp_type_end = try_find!(line[disp_idx..], find, ';',
                "Error parsing Content-Disposition value!", line);

            debug!("Disp end: {}", disp_type_end);

            line[disp_idx .. disp_idx + disp_type_end].trim().into_string()
        };

        debug!("Disp-type: {}", disp_type);

        let field_name = {
            const NAME: &'static str = "name=\"";

            let name_idx = try_find!(line[], find_str, NAME,
                "Error parsing field name!", line) + NAME.len();

            debug!("Name idx: {}", name_idx);

            let name_end = try_find!(line[name_idx ..], find, '"',
                "Error parsing field name!", line);

            debug!("Name end: {}", name_end);

            line[name_idx .. name_idx + name_end].into_string() // No trim here since it's in quotes.
        };

        debug!("Field name: {}", field_name);

        let filename = {
            const FILENAME: &'static str = "filename=\"";

            let filename_idx = line[].find_str(FILENAME).map(|idx| idx + FILENAME.len());
            let filename_idxs = with(filename_idx, |&start| line[start ..].find('"'));

            filename_idxs.map(|(start, end)| line[start .. start + end].into_string())
        };

        debug!("Filename: {}", filename);

        Ok((disp_type, field_name, filename))
    }

    fn read_content_type(&mut self) -> IoResult<Option<String>> {
        debug!("Read content type!");
        let line = try!(self.source.read_line());

        const CONTENT_TYPE: &'static str = "Content-Type:";

        let type_idx = (&*line).find_str(CONTENT_TYPE);

        // FIXME Will not properly parse for multiple files!
        // Does not expect boundary=<boundary>
        Ok(type_idx.map(|start| line[start + CONTENT_TYPE.len()..].trim().into_string()))
    }
}

fn with<T, U>(left: Option<T>, right: |&T| -> Option<U>) -> Option<(T, U)> {
    let temp = left.as_ref().and_then(right);
    match (left, temp) {
        (Some(lval), Some(rval)) => Some((lval, rval)),
        _ => None,
    }
}

fn line_error(msg: &'static str, line: String) -> IoError {
    IoError {
        kind: OtherIoError,
        desc: msg,
        detail: Some(line),
    }
}

/* FIXME: Can't have an iterator return a borrowed reference
impl<'a> Iterator<(String, MultipartField<'a>)> for Multipart {
    fn next(&mut self) -> Option<(String, MultipartField<'a>)> {
        match self.read_entry() {
            Ok(ok) => Some(ok),
            Err(err) => {
                if err.kind != EndOfFile {
                    error!("Error reading Multipart: {}", err);
                }

                None
             },
        }
    }
}
*/

/// A `Reader` that will yield bytes until it sees a given sequence.
pub struct BoundaryReader<S> {
    reader: S,
    boundary: Vec<u8>,
    last_search_idx: uint,
    boundary_read: bool,
    buf: Vec<u8>,
    buf_len: uint,
}

fn eof<T>() -> IoResult<T> {
    Err(standard_error(EndOfFile))
}

const BUF_SIZE: uint = 1024 * 64; // 64k buffer

impl<S> BoundaryReader<S> where S: Reader {
    fn from_reader(reader: S, boundary: String) -> BoundaryReader<S> {
        let mut buf = Vec::with_capacity(BUF_SIZE);
        unsafe { buf.set_len(BUF_SIZE); }

        BoundaryReader {
            reader: reader,
            boundary: boundary.into_bytes(),
            last_search_idx: 0,
            boundary_read: false,
            buf: buf,
            buf_len: 0,
        }
    }

    fn read_to_boundary(&mut self) -> IoResult<()> {
         if !self.boundary_read {
            try!(self.true_fill_buf());

            if self.buf_len == 0 { return eof(); }

            let lookahead = self.buf[self.last_search_idx .. self.buf_len];

            let search_idx = lookahead.position_elem(&self.boundary[0])
                .unwrap_or(lookahead.len() - 1);

            debug!("Search idx: {}", search_idx);

            self.boundary_read = lookahead[search_idx..]
                .starts_with(self.boundary[]);

            self.last_search_idx += search_idx;

            if !self.boundary_read {
                self.last_search_idx += 1;
            }

        } else if self.last_search_idx == 0 {
            return Err(standard_error(EndOfFile))
        }

        Ok(())
    }

    /// Read bytes until the reader is full
    fn true_fill_buf(&mut self) -> IoResult<()> {
        let mut bytes_read = 1u;

        while bytes_read != 0 {
            bytes_read = match self.reader.read(self.buf[mut self.buf_len..]) {
                Ok(read) => read,
                Err(err) => if err.kind == EndOfFile { break; } else { return Err(err); },
            };

            self.buf_len += bytes_read;
        }

        Ok(())
    }

    fn _consume(&mut self, amt: uint) {
        use std::ptr::copy_memory;

        assert!(amt <= self.buf_len);

        let src = self.buf[amt..].as_ptr();
        let dest = self.buf[mut].as_mut_ptr();

        unsafe { copy_memory(dest, src, self.buf_len - amt); }

        self.buf_len -= amt;
        self.last_search_idx -= amt;
    }

    fn consume_boundary(&mut self) -> IoResult<()> {
        while !self.boundary_read {
            match self.read_to_boundary() {
                Ok(_) => (),
                Err(e) => if e.kind == EndOfFile {
                    break;
                } else {
                    return Err(e);
                }
            }
        }

        let consume_amt = cmp::min(self.buf_len, self.last_search_idx + self.boundary.len());

        debug!("Consume amt: {} Buf len: {}", consume_amt, self.buf_len);

        self._consume(consume_amt);
        self.last_search_idx = 0;
        self.boundary_read = false;

        Ok(())
    }

    #[allow(unused)]
    fn set_boundary(&mut self, boundary: String) {
        self.boundary = boundary.into_bytes();
    }
}

impl<S> Reader for BoundaryReader<S> where S: Reader {
    fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
        use std::cmp;
        use std::slice::bytes::copy_memory;

        try!(self.read_to_boundary());

        let trunc_len = cmp::min(buf.len(), self.last_search_idx);
        copy_memory(buf, self.buf[..trunc_len]);

        self._consume(trunc_len);

        Ok(trunc_len)
    }
}

impl<S> Buffer for BoundaryReader<S> where S: Reader {
    fn fill_buf<'a>(&'a mut self) -> IoResult<&'a [u8]> {
        try!(self.read_to_boundary());

        let buf = self.buf[..self.last_search_idx];

        Ok(buf)
    }

    fn consume(&mut self, amt: uint) {
        assert!(amt <= self.last_search_idx);
        self._consume(amt);
    }
}

#[test]
fn test_boundary() {
    use std::io::BufReader;

    const BOUNDARY: &'static str = "--boundary\r\n";
    const TEST_VAL: &'static str = "\r
--boundary\r
dashed-value-1\r
--boundary\r
dashed-value-2\r
--boundary\r
";

    let test_reader = BufReader::new(TEST_VAL.as_bytes());
    let mut reader = BoundaryReader::from_reader(test_reader, BOUNDARY.into_string());

    debug!("Read 1");
    let string = reader.read_to_string().unwrap();
    debug!("{}", string);
    assert!(string[].trim().is_empty());

    debug!("Consume 1");
    reader.consume_boundary().unwrap();

    debug!("Read 2");
    assert_eq!(reader.read_to_string().unwrap()[].trim(), "dashed-value-1");

    debug!("Consume 2");
    reader.consume_boundary().unwrap();

    debug!("Read 3");
    assert_eq!(reader.read_to_string().unwrap()[].trim(), "dashed-value-2");

    debug!("Consume 3");
    reader.consume_boundary().unwrap();

}

<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::fmt::Debug;
use std::str::FromStr;

use async_trait::async_trait;

use crate::error::*;
use crate::path;
use crate::value::{Value, ValueKind};

/// Describes a generic _source_ of configuration properties.
pub trait Source: Debug {
    fn clone_into_box(&self) -> Box<dyn Source + Send + Sync>;

    /// Collect all configuration properties available from this source and return
    /// a HashMap.
    fn collect(&self) -> Result<HashMap<String, Value>>;

    /// Collects all configuration properties to a provided cache.
    fn collect_to(&self, cache: &mut Value) -> Result<()> {
        self.collect()?
            .iter()
            .for_each(|(key, val)| set_value(cache, key, val));

        Ok(())
    }
}

fn set_value(cache: &mut Value, key: &String, value: &Value) {
    match path::Expression::from_str(key) {
        // Set using the path
        Ok(expr) => expr.set(cache, value.clone()),

        // Set diretly anyway
        _ => path::Expression::Identifier(key.clone()).set(cache, value.clone()),
    }
}

/// Describes a generic _source_ of configuration properties capable of using an async runtime.
///
/// At the moment this library does not implement it, although it allows using its implementations
/// within builders.  Due to the scattered landscape of asynchronous runtimes, it is impossible to
/// cater to all needs with one implementation.  Also, this trait might be most useful with remote
/// configuration sources, reachable via the network, probably using HTTP protocol.  Numerous HTTP
/// libraries exist, making it even harder to find one implementation that rules them all.
///
/// For those reasons, it is left to other crates to implement runtime-specific or proprietary
/// details.
///
/// It is advised to use `async_trait` crate while implementing this trait.
///
/// See examples for sample implementation.
#[async_trait]
pub trait AsyncSource: Debug + Sync {
    // Sync is supertrait due to https://docs.rs/async-trait/0.1.50/async_trait/index.html#dyn-traits

    /// Collects all configuration properties available from this source and return
    /// a HashMap as an async operations.
    async fn collect(&self) -> Result<HashMap<String, Value>>;

    /// Collects all configuration properties to a provided cache.
    async fn collect_to(&self, cache: &mut Value) -> Result<()> {
        self.collect()
            .await?
            .iter()
            .for_each(|(key, val)| set_value(cache, key, val));

        Ok(())
    }
}

impl Clone for Box<dyn AsyncSource + Send + Sync> {
    fn clone(&self) -> Self {
        self.to_owned()
    }
}

impl Clone for Box<dyn Source + Send + Sync> {
    fn clone(&self) -> Box<dyn Source + Send + Sync> {
        self.clone_into_box()
    }
}

impl Source for Vec<Box<dyn Source + Send + Sync>> {
    fn clone_into_box(&self) -> Box<dyn Source + Send + Sync> {
        Box::new((*self).clone())
    }

    fn collect(&self) -> Result<HashMap<String, Value>> {
        let mut cache: Value = HashMap::<String, Value>::new().into();

        for source in self {
            source.collect_to(&mut cache)?;
        }

        if let ValueKind::Table(table) = cache.kind {
            Ok(table)
        } else {
            unreachable!();
        }
    }
}

impl Source for [Box<dyn Source + Send + Sync>] {
    fn clone_into_box(&self) -> Box<dyn Source + Send + Sync> {
        Box::new(self.to_owned())
    }

    fn collect(&self) -> Result<HashMap<String, Value>> {
        let mut cache: Value = HashMap::<String, Value>::new().into();

        for source in self {
            source.collect_to(&mut cache)?;
        }

        if let ValueKind::Table(table) = cache.kind {
            Ok(table)
        } else {
            unreachable!();
        }
    }
}

impl<T> Source for Vec<T>
where
    T: Source + Sync + Send,
    T: Clone,
    T: 'static,
{
    fn clone_into_box(&self) -> Box<dyn Source + Send + Sync> {
        Box::new((*self).clone())
    }

    fn collect(&self) -> Result<HashMap<String, Value>> {
        let mut cache: Value = HashMap::<String, Value>::new().into();

        for source in self {
            source.collect_to(&mut cache)?;
        }

        if let ValueKind::Table(table) = cache.kind {
            Ok(table)
        } else {
            unreachable!();
        }
    }
}
<commit_msg>Fix clippy: Use &str instead of &String as argument<commit_after>use std::collections::HashMap;
use std::fmt::Debug;
use std::str::FromStr;

use async_trait::async_trait;

use crate::error::*;
use crate::path;
use crate::value::{Value, ValueKind};

/// Describes a generic _source_ of configuration properties.
pub trait Source: Debug {
    fn clone_into_box(&self) -> Box<dyn Source + Send + Sync>;

    /// Collect all configuration properties available from this source and return
    /// a HashMap.
    fn collect(&self) -> Result<HashMap<String, Value>>;

    /// Collects all configuration properties to a provided cache.
    fn collect_to(&self, cache: &mut Value) -> Result<()> {
        self.collect()?
            .iter()
            .for_each(|(key, val)| set_value(cache, key, val));

        Ok(())
    }
}

fn set_value(cache: &mut Value, key: &str, value: &Value) {
    match path::Expression::from_str(key) {
        // Set using the path
        Ok(expr) => expr.set(cache, value.clone()),

        // Set diretly anyway
        _ => path::Expression::Identifier(key.clone()).set(cache, value.clone()),
    }
}

/// Describes a generic _source_ of configuration properties capable of using an async runtime.
///
/// At the moment this library does not implement it, although it allows using its implementations
/// within builders.  Due to the scattered landscape of asynchronous runtimes, it is impossible to
/// cater to all needs with one implementation.  Also, this trait might be most useful with remote
/// configuration sources, reachable via the network, probably using HTTP protocol.  Numerous HTTP
/// libraries exist, making it even harder to find one implementation that rules them all.
///
/// For those reasons, it is left to other crates to implement runtime-specific or proprietary
/// details.
///
/// It is advised to use `async_trait` crate while implementing this trait.
///
/// See examples for sample implementation.
#[async_trait]
pub trait AsyncSource: Debug + Sync {
    // Sync is supertrait due to https://docs.rs/async-trait/0.1.50/async_trait/index.html#dyn-traits

    /// Collects all configuration properties available from this source and return
    /// a HashMap as an async operations.
    async fn collect(&self) -> Result<HashMap<String, Value>>;

    /// Collects all configuration properties to a provided cache.
    async fn collect_to(&self, cache: &mut Value) -> Result<()> {
        self.collect()
            .await?
            .iter()
            .for_each(|(key, val)| set_value(cache, key, val));

        Ok(())
    }
}

impl Clone for Box<dyn AsyncSource + Send + Sync> {
    fn clone(&self) -> Self {
        self.to_owned()
    }
}

impl Clone for Box<dyn Source + Send + Sync> {
    fn clone(&self) -> Box<dyn Source + Send + Sync> {
        self.clone_into_box()
    }
}

impl Source for Vec<Box<dyn Source + Send + Sync>> {
    fn clone_into_box(&self) -> Box<dyn Source + Send + Sync> {
        Box::new((*self).clone())
    }

    fn collect(&self) -> Result<HashMap<String, Value>> {
        let mut cache: Value = HashMap::<String, Value>::new().into();

        for source in self {
            source.collect_to(&mut cache)?;
        }

        if let ValueKind::Table(table) = cache.kind {
            Ok(table)
        } else {
            unreachable!();
        }
    }
}

impl Source for [Box<dyn Source + Send + Sync>] {
    fn clone_into_box(&self) -> Box<dyn Source + Send + Sync> {
        Box::new(self.to_owned())
    }

    fn collect(&self) -> Result<HashMap<String, Value>> {
        let mut cache: Value = HashMap::<String, Value>::new().into();

        for source in self {
            source.collect_to(&mut cache)?;
        }

        if let ValueKind::Table(table) = cache.kind {
            Ok(table)
        } else {
            unreachable!();
        }
    }
}

impl<T> Source for Vec<T>
where
    T: Source + Sync + Send,
    T: Clone,
    T: 'static,
{
    fn clone_into_box(&self) -> Box<dyn Source + Send + Sync> {
        Box::new((*self).clone())
    }

    fn collect(&self) -> Result<HashMap<String, Value>> {
        let mut cache: Value = HashMap::<String, Value>::new().into();

        for source in self {
            source.collect_to(&mut cache)?;
        }

        if let ValueKind::Table(table) = cache.kind {
            Ok(table)
        } else {
            unreachable!();
        }
    }
}
<|endoftext|>


<commit_before>// http://rosettacode.org/wiki/Sudoku

#![allow(unstable)]

use std::{fmt, iter};
use std::str::FromStr;
use std::num::Int;

const BOARD_WIDTH: usize = 9;
const BOARD_HEIGHT: usize = 9;
const GROUP_WIDTH: usize = 3;
const GROUP_HEIGHT: usize = 3;
const MAX_NUMBER: usize = 9;

type BITS = u16;
const MASK_ALL: BITS = 0x1ff;
const INVALID_CELL: usize = !0;

#[derive(Copy, Eq, PartialEq)]
struct Sudoku {
    map: [[BITS; BOARD_WIDTH]; BOARD_HEIGHT]
}

impl Sudoku {
    fn new() -> Sudoku {
        Sudoku { map: [[MASK_ALL; BOARD_WIDTH]; BOARD_HEIGHT] }
    }

    fn get(&self, x: usize, y: usize) -> usize {
        match self.map[y][x].count_ones() {
            0 => INVALID_CELL,
            1 => self.map[y][x].trailing_zeros() + 1,
            _ => 0
        }
    }

    fn set(&mut self, x: usize, y: usize, n: usize) {
        self.map[y][x] = 1 << (n - 1);
    }

}

impl FromStr for Sudoku {
    fn from_str(s: &str) -> Option<Sudoku> {
        let mut sudoku = Sudoku::new();

        for (y, line) in s.lines().filter(|l| !l.is_empty()).enumerate() {
            let line = line.trim_matches(CharExt::is_whitespace);
            for (x, c) in line.chars().enumerate() {
                if let Some(d) = c.to_digit(10) {
                    if d != 0 { sudoku.set(x, y, d); }
                } else {
                    return None
                }
            }
        }

        Some(sudoku)
    }
}

impl fmt::String for Sudoku {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let hbar = "+---+---+---+";

        for y in (0 .. BOARD_HEIGHT) {
            if y % GROUP_HEIGHT == 0 {
                try!(writeln!(f, "{}", hbar));
            }

            for x in (0 .. BOARD_WIDTH) {
                if x % GROUP_WIDTH == 0 {
                    try!(write!(f, "|"));
                }

                match self.get(x, y) {
                    INVALID_CELL => try!(write!(f, "!")),
                    0 => try!(write!(f, " ")),
                    d => try!(write!(f, "{}", d))
                }
            }
            try!(writeln!(f, "|"));
        }
        try!(writeln!(f, "{}", hbar));

        Ok(())
    }
}

fn solve_sudoku(mut puzzle: Sudoku) -> Vec<Sudoku> {
    let idx_in_grp = [(0, 0), (0, 1), (0, 2),
                      (1, 0), (1, 1), (1, 2),
                      (2, 0), (2, 1), (2, 2)];

    loop {
        let bkup = puzzle;

        // If the number at cell (x, y) is uniquely determined, that number must
        // not be appeared at the cells in the same row/column/group.
        for y in (0 .. BOARD_HEIGHT) {
            for x in (0 .. BOARD_WIDTH) {
                if puzzle.map[y][x].count_ones() != 1 { continue }

                let (x0, y0) = ((x / GROUP_WIDTH) * GROUP_WIDTH,
                                (y / GROUP_HEIGHT) * GROUP_HEIGHT);

                let row = (0 .. BOARD_WIDTH).map(|x| (x, y));
                let col = (0 .. BOARD_HEIGHT).map(|y| (x, y));
                let grp = idx_in_grp.iter().map(|&(dx, dy)| (x0 + dx, y0 + dy));

                let mut it = row.chain(col).chain(grp)
                    .filter(|&pos: &(usize, usize)| pos != (x, y));

                let mask = !puzzle.map[y][x] & MASK_ALL;
                for (x, y) in it {
                    puzzle.map[y][x] &= mask;
                }
            }
        }

        // If `n` appears only once at the cell in the row/column/group, the
        // number of the cell must be `n`.
        for n in (0 .. MAX_NUMBER) {
            let bit = 1 << n;

            // Check each rows
            for y in (0 .. BOARD_HEIGHT) {
                let next = {
                    let mut it = (0 .. BOARD_WIDTH)
                        .filter(|&x| puzzle.map[y][x] & bit != 0);
                    let next = it.next();
                    if next.is_none() || it.next().is_some() { continue }
                    next
                };
                puzzle.map[y][next.unwrap()] = bit;
            }

            // Check each columns
            for x in (0 .. BOARD_WIDTH) {
                let next = {
                    let mut it = (0 .. BOARD_HEIGHT)
                        .filter(|&y| puzzle.map[y][x] & bit != 0);
                    let next = it.next();
                    if next.is_none() || it.next().is_some() { continue }
                    next
                };
                puzzle.map[next.unwrap()][x] = bit;
            }

            // Check each groups
            for y0 in iter::range_step(0, BOARD_HEIGHT, GROUP_WIDTH) {
                for x0 in iter::range_step(0, BOARD_WIDTH, GROUP_HEIGHT) {
                    let next = {
                        let mut it = idx_in_grp
                            .iter()
                            .map(|&(dx, dy)| (x0 + dx, y0 + dy))
                            .filter(|&(x, y)| puzzle.map[y][x] & bit != 0);
                        let next = it.next();
                        if next.is_none() || it.next().is_some() { continue }
                        next
                    };
                    let (x, y) = next.unwrap();
                    puzzle.map[y][x] = bit;
                }
            }
        }

        // Loop until no cells can be filled.
        if puzzle == bkup { break }
    }

    let it = (0 .. BOARD_HEIGHT * BOARD_WIDTH)
        .map(|i| (i % BOARD_WIDTH, i / BOARD_WIDTH))
        .map(|(x, y)| (x, y, puzzle.map[y][x].count_ones() as BITS))
        .collect::<Vec<_>>();

    // If some cells have no possible number, there is no answer.
    if it.iter().any(|&(_x, _y, cnt)| cnt == 0) { return vec![]; }

    // If all cells have exact one possible number, this is a answer.
    if it.iter().all(|&(_x, _y, cnt)| cnt == 1) { return vec![puzzle]; }

    // Find the first undetermined cell.
    let (x, y, _cnt) = *it.iter()
        .filter(|& &(_x, _y, cnt)| cnt > 1)
        .min_by(|& &(_x, _y, cnt)| cnt)
        .unwrap();

    let mut answers = vec![];
    for n in (0 .. MAX_NUMBER) {
        let bit = 1 << n;
        if puzzle.map[y][x] & bit == 0 { continue }

        // Assuming the number at (x, y) is `n`, try to solve the problem again.
        // If some answers are found, append them to the `answers`.
        let mut p2 = puzzle;
        p2.map[y][x] = bit;
        answers.extend(solve_sudoku(p2).into_iter());
    }
    answers
}

const INPUT: &'static str = "
    850002400
    720000009
    004000000
    000107002
    305000900
    040000000
    000080070
    017000000
    000036040
";

fn main() {
    let puzzle = INPUT.parse::<Sudoku>().unwrap();

    println!("{}", puzzle);

    for answer in solve_sudoku(puzzle).iter() {
        println!("{}", answer);
    }
}
<commit_msg>sudoku: Add test<commit_after>// http://rosettacode.org/wiki/Sudoku

#![allow(unstable)]

use std::{fmt, iter};
use std::str::FromStr;
use std::num::Int;

const BOARD_WIDTH: usize = 9;
const BOARD_HEIGHT: usize = 9;
const GROUP_WIDTH: usize = 3;
const GROUP_HEIGHT: usize = 3;
const MAX_NUMBER: usize = 9;

type BITS = u16;
const MASK_ALL: BITS = 0x1ff;
const INVALID_CELL: usize = !0;

#[derive(Copy, Eq, PartialEq, Show)]
struct Sudoku {
    map: [[BITS; BOARD_WIDTH]; BOARD_HEIGHT]
}

impl Sudoku {
    fn new() -> Sudoku {
        Sudoku { map: [[MASK_ALL; BOARD_WIDTH]; BOARD_HEIGHT] }
    }

    fn get(&self, x: usize, y: usize) -> usize {
        match self.map[y][x].count_ones() {
            0 => INVALID_CELL,
            1 => self.map[y][x].trailing_zeros() + 1,
            _ => 0
        }
    }

    fn set(&mut self, x: usize, y: usize, n: usize) {
        self.map[y][x] = 1 << (n - 1);
    }

}

impl FromStr for Sudoku {
    fn from_str(s: &str) -> Option<Sudoku> {
        let mut sudoku = Sudoku::new();

        for (y, line) in s.lines().filter(|l| !l.is_empty()).enumerate() {
            let line = line.trim_matches(CharExt::is_whitespace);
            for (x, c) in line.chars().enumerate() {
                if let Some(d) = c.to_digit(10) {
                    if d != 0 { sudoku.set(x, y, d); }
                } else {
                    return None
                }
            }
        }

        Some(sudoku)
    }
}

impl fmt::String for Sudoku {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let hbar = "+---+---+---+";

        for y in (0 .. BOARD_HEIGHT) {
            if y % GROUP_HEIGHT == 0 {
                try!(writeln!(f, "{}", hbar));
            }

            for x in (0 .. BOARD_WIDTH) {
                if x % GROUP_WIDTH == 0 {
                    try!(write!(f, "|"));
                }

                match self.get(x, y) {
                    INVALID_CELL => try!(write!(f, "!")),
                    0 => try!(write!(f, " ")),
                    d => try!(write!(f, "{}", d))
                }
            }
            try!(writeln!(f, "|"));
        }
        try!(writeln!(f, "{}", hbar));

        Ok(())
    }
}

fn solve_sudoku(mut puzzle: Sudoku) -> Vec<Sudoku> {
    let idx_in_grp = [(0, 0), (0, 1), (0, 2),
                      (1, 0), (1, 1), (1, 2),
                      (2, 0), (2, 1), (2, 2)];

    loop {
        let bkup = puzzle;

        // If the number at cell (x, y) is uniquely determined, that number must
        // not be appeared at the cells in the same row/column/group.
        for y in (0 .. BOARD_HEIGHT) {
            for x in (0 .. BOARD_WIDTH) {
                if puzzle.map[y][x].count_ones() != 1 { continue }

                let (x0, y0) = ((x / GROUP_WIDTH) * GROUP_WIDTH,
                                (y / GROUP_HEIGHT) * GROUP_HEIGHT);

                let row = (0 .. BOARD_WIDTH).map(|x| (x, y));
                let col = (0 .. BOARD_HEIGHT).map(|y| (x, y));
                let grp = idx_in_grp.iter().map(|&(dx, dy)| (x0 + dx, y0 + dy));

                let mut it = row.chain(col).chain(grp)
                    .filter(|&pos: &(usize, usize)| pos != (x, y));

                let mask = !puzzle.map[y][x] & MASK_ALL;
                for (x, y) in it {
                    puzzle.map[y][x] &= mask;
                }
            }
        }

        // If `n` appears only once at the cell in the row/column/group, the
        // number of the cell must be `n`.
        for n in (0 .. MAX_NUMBER) {
            let bit = 1 << n;

            // Check each rows
            for y in (0 .. BOARD_HEIGHT) {
                let next = {
                    let mut it = (0 .. BOARD_WIDTH)
                        .filter(|&x| puzzle.map[y][x] & bit != 0);
                    let next = it.next();
                    if next.is_none() || it.next().is_some() { continue }
                    next
                };
                puzzle.map[y][next.unwrap()] = bit;
            }

            // Check each columns
            for x in (0 .. BOARD_WIDTH) {
                let next = {
                    let mut it = (0 .. BOARD_HEIGHT)
                        .filter(|&y| puzzle.map[y][x] & bit != 0);
                    let next = it.next();
                    if next.is_none() || it.next().is_some() { continue }
                    next
                };
                puzzle.map[next.unwrap()][x] = bit;
            }

            // Check each groups
            for y0 in iter::range_step(0, BOARD_HEIGHT, GROUP_WIDTH) {
                for x0 in iter::range_step(0, BOARD_WIDTH, GROUP_HEIGHT) {
                    let next = {
                        let mut it = idx_in_grp
                            .iter()
                            .map(|&(dx, dy)| (x0 + dx, y0 + dy))
                            .filter(|&(x, y)| puzzle.map[y][x] & bit != 0);
                        let next = it.next();
                        if next.is_none() || it.next().is_some() { continue }
                        next
                    };
                    let (x, y) = next.unwrap();
                    puzzle.map[y][x] = bit;
                }
            }
        }

        // Loop until no cells can be filled.
        if puzzle == bkup { break }
    }

    let it = (0 .. BOARD_HEIGHT * BOARD_WIDTH)
        .map(|i| (i % BOARD_WIDTH, i / BOARD_WIDTH))
        .map(|(x, y)| (x, y, puzzle.map[y][x].count_ones() as BITS))
        .collect::<Vec<_>>();

    // If some cells have no possible number, there is no answer.
    if it.iter().any(|&(_x, _y, cnt)| cnt == 0) { return vec![]; }

    // If all cells have exact one possible number, this is a answer.
    if it.iter().all(|&(_x, _y, cnt)| cnt == 1) { return vec![puzzle]; }

    // Find the first undetermined cell.
    let (x, y, _cnt) = *it.iter()
        .filter(|& &(_x, _y, cnt)| cnt > 1)
        .min_by(|& &(_x, _y, cnt)| cnt)
        .unwrap();

    let mut answers = vec![];
    for n in (0 .. MAX_NUMBER) {
        let bit = 1 << n;
        if puzzle.map[y][x] & bit == 0 { continue }

        // Assuming the number at (x, y) is `n`, try to solve the problem again.
        // If some answers are found, append them to the `answers`.
        let mut p2 = puzzle;
        p2.map[y][x] = bit;
        answers.extend(solve_sudoku(p2).into_iter());
    }
    answers
}

const INPUT: &'static str = "
    850002400
    720000009
    004000000
    000107002
    305000900
    040000000
    000080070
    017000000
    000036040
";

fn main() {
    let puzzle = INPUT.parse::<Sudoku>().unwrap();

    println!("{}", puzzle);

    for answer in solve_sudoku(puzzle).iter() {
        println!("{}", answer);
    }
}

#[cfg(test)]
const SOLUTION: &'static str = "
    859612437
    723854169
    164379528
    986147352
    375268914
    241593786
    432981675
    617425893
    598736241
";

#[test]
fn solution() {
    let puzzle = INPUT.parse::<Sudoku>().unwrap();
    let answer = SOLUTION.parse::<Sudoku>().unwrap();
    let solution = solve_sudoku(puzzle);
    assert_eq!(solution, [answer]);
}
<|endoftext|>


<commit_before>// Copyright (c) 2017-2018 Rene van der Meer
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

//! Raspberry Pi system-related tools.
//!
//! Use [`DeviceInfo`] to identify what Raspberry Pi model and SoC the software is
//! running on.
//!
//! [`DeviceInfo`]: struct.DeviceInfo.html

use std::fmt;
use std::fs;
use std::fs::File;
use std::io::{BufRead, BufReader};
use std::result;

const PERIPHERAL_BASE_RPI: u32 = 0x2000_0000;
const PERIPHERAL_BASE_RPI2: u32 = 0x3f00_0000;
const GPIO_OFFSET: u32 = 0x20_0000;

quick_error! {
/// Errors that can occur when trying to identify the Raspberry Pi hardware.
    #[derive(Debug)]
    pub enum Error {
/// Unknown model.
///
/// `DeviceInfo` was unable to identify the Raspberry Pi model based on the
/// contents of `/proc/cpuinfo`, `/sys/firmware/devicetree/base/compatible`
/// and `/sys/firmware/devicetree/base/model`.
        UnknownModel { description("unknown Raspberry Pi model") }
    }
}

/// Result type returned from methods that can have `system::Error`s.
pub type Result<T> = result::Result<T, Error>;

/// Identifiable Raspberry Pi models.
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum Model {
    RaspberryPiA,
    RaspberryPiAPlus,
    RaspberryPiBRev1,
    RaspberryPiBRev2,
    RaspberryPiBPlus,
    RaspberryPi2B,
    RaspberryPi3B,
    RaspberryPi3BPlus,
    RaspberryPiComputeModule,
    RaspberryPiComputeModule3,
    RaspberryPiZero,
    RaspberryPiZeroW,
}

impl fmt::Display for Model {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Model::RaspberryPiA => write!(f, "Raspberry Pi A"),
            Model::RaspberryPiAPlus => write!(f, "Raspberry Pi A+"),
            Model::RaspberryPiBRev1 => write!(f, "Raspberry Pi B Rev 1"),
            Model::RaspberryPiBRev2 => write!(f, "Raspberry Pi B Rev 2"),
            Model::RaspberryPiBPlus => write!(f, "Raspberry Pi B+"),
            Model::RaspberryPi2B => write!(f, "Raspberry Pi 2 B"),
            Model::RaspberryPi3B => write!(f, "Raspberry Pi 3 B"),
            Model::RaspberryPi3BPlus => write!(f, "Raspberry Pi 3 B+"),
            Model::RaspberryPiComputeModule => write!(f, "Raspberry Pi Compute Module"),
            Model::RaspberryPiComputeModule3 => write!(f, "Raspberry Pi Compute Module 3"),
            Model::RaspberryPiZero => write!(f, "Raspberry Pi Zero"),
            Model::RaspberryPiZeroW => write!(f, "Raspberry Pi Zero W"),
        }
    }
}

/// Identifiable Raspberry Pi SoCs.
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum SoC {
    Bcm2835,
    Bcm2836,
    Bcm2837A1,
    Bcm2837B0,
}

impl fmt::Display for SoC {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            SoC::Bcm2835 => write!(f, "BCM2835"),
            SoC::Bcm2836 => write!(f, "BCM2836"),
            SoC::Bcm2837A1 => write!(f, "BCM2837A1"),
            SoC::Bcm2837B0 => write!(f, "BCM2837B0"),
        }
    }
}

// Identify Pi model based on /proc/cpuinfo
fn parse_proc_cpuinfo() -> Result<Model> {
    let proc_cpuinfo = BufReader::new(match File::open("/proc/cpuinfo") {
        Err(_) => return Err(Error::UnknownModel),
        Ok(file) => file,
    });

    let mut hardware: String = String::new();
    let mut revision: String = String::new();
    for line_result in proc_cpuinfo.lines() {
        if let Ok(line) = line_result {
            if line.starts_with("Hardware\t: ") {
                hardware = String::from(&line[11..]);
            } else if line.starts_with("Revision\t: ") {
                revision = String::from(&line[11..]).to_lowercase();
            }
        }
    }

    // Return an error if we don't recognize the SoC. This check is
    // done to prevent accidentally identifying a non-Pi SBC as a Pi
    // solely based on the revision field.
    match &hardware[..] {
        "BCM2708" | "BCM2835" | "BCM2709" | "BCM2836" | "BCM2710" | "BCM2837" | "BCM2837A1"
        | "BCM2837B0" => {}
        _ => return Err(Error::UnknownModel),
    }

    let model = if (revision.len() == 4) || (revision.len() == 8) {
        // Older revisions are 4 characters long, or 8 if they've been over-volted
        match &revision[revision.len() - 4..] {
            "0007" | "0008" | "0009" | "0015" => Model::RaspberryPiA,
            "Beta" | "0002" | "0003" => Model::RaspberryPiBRev1,
            "0004" | "0005" | "0006" | "000d" | "000e" | "000f" => Model::RaspberryPiBRev2,
            "0012" => Model::RaspberryPiAPlus,
            "0010" | "0013" => Model::RaspberryPiBPlus,
            "0011" | "0014" => Model::RaspberryPiComputeModule,
            _ => return Err(Error::UnknownModel),
        }
    } else if revision.len() >= 6 {
        // Newer revisions consist of at least 6 characters
        match &revision[revision.len() - 3..revision.len() - 1] {
            "00" => Model::RaspberryPiA,
            "01" => Model::RaspberryPiBRev2,
            "02" => Model::RaspberryPiAPlus,
            "03" => Model::RaspberryPiBPlus,
            "04" => Model::RaspberryPi2B,
            "06" => Model::RaspberryPiComputeModule,
            "08" => Model::RaspberryPi3B,
            "09" => Model::RaspberryPiZero,
            "0a" => Model::RaspberryPiComputeModule3,
            "0c" => Model::RaspberryPiZeroW,
            "0d" => Model::RaspberryPi3BPlus,
            _ => return Err(Error::UnknownModel),
        }
    } else {
        return Err(Error::UnknownModel);
    };

    Ok(model)
}

// Identify Pi model based on /sys/firmware/devicetree/base/compatible
fn parse_firmware_compatible() -> Result<Model> {
    let compatible = match fs::read_to_string("/sys/firmware/devicetree/base/compatible") {
        Err(_) => return Err(Error::UnknownModel),
        Ok(s) => s,
    };

    // Based on /arch/arm/boot/dts/ and /Documentation/devicetree/bindings/arm/bcm/
    for compid in compatible.split('\0') {
        match compid {
            "raspberrypi,model-b-i2c0" => return Ok(Model::RaspberryPiBRev1),
            "raspberrypi,model-b" => return Ok(Model::RaspberryPiBRev1),
            "raspberrypi,model-a" => return Ok(Model::RaspberryPiA),
            "raspberrypi,model-b-rev2" => return Ok(Model::RaspberryPiBRev2),
            "raspberrypi,model-a-plus" => return Ok(Model::RaspberryPiAPlus),
            "raspberrypi,model-b-plus" => return Ok(Model::RaspberryPiBPlus),
            "raspberrypi,2-model-b" => return Ok(Model::RaspberryPi2B),
            "raspberrypi,compute-module" => return Ok(Model::RaspberryPiComputeModule),
            "raspberrypi,3-model-b" => return Ok(Model::RaspberryPi3B),
            "raspberrypi,model-zero" => return Ok(Model::RaspberryPiZero),
            "raspberrypi,3-compute-module" => return Ok(Model::RaspberryPiComputeModule3),
            "raspberrypi,model-zero-w" => return Ok(Model::RaspberryPiZeroW),
            "raspberrypi,3-model-b-plus" => return Ok(Model::RaspberryPi3BPlus),
            _ => (),
        }
    }

    Err(Error::UnknownModel)
}

// Identify Pi model based on /sys/firmware/devicetree/base/model
fn parse_firmware_model() -> Result<Model> {
    let model = match fs::read_to_string("/sys/firmware/devicetree/base/model") {
        Err(_) => return Err(Error::UnknownModel),
        Ok(buffer) => if let Some(rev_idx) = buffer.find(" Rev ") {
            // We don't want to strip rev2 here
            buffer[0..rev_idx].to_owned()
        } else {
            buffer
        },
    };

    // Based on /arch/arm/boot/dts/ and /Documentation/devicetree/bindings/arm/bcm/
    match &model[..] {
        "Raspberry Pi Model B (no P5)" => Ok(Model::RaspberryPiBRev1),
        "Raspberry Pi Model B" => Ok(Model::RaspberryPiBRev1),
        "Raspberry Pi Model A" => Ok(Model::RaspberryPiA),
        "Raspberry Pi Model B rev2" => Ok(Model::RaspberryPiBRev2),
        "Raspberry Pi Model A+" => Ok(Model::RaspberryPiAPlus),
        "Raspberry Pi Model B+" => Ok(Model::RaspberryPiBPlus),
        "Raspberry Pi 2 Model B" => Ok(Model::RaspberryPi2B),
        "Raspberry Pi Compute Module" => Ok(Model::RaspberryPiComputeModule),
        "Raspberry Pi 3 Model B" => Ok(Model::RaspberryPi3B),
        "Raspberry Pi Zero" => Ok(Model::RaspberryPiZero),
        "Raspberry Pi Compute Module 3" => Ok(Model::RaspberryPiComputeModule3),
        "Raspberry Pi Zero W" => Ok(Model::RaspberryPiZeroW),
        "Raspberry Pi 3 Model B+" => Ok(Model::RaspberryPi3BPlus),
        _ => Err(Error::UnknownModel),
    }
}

/// Retrieves Raspberry Pi device information.
#[derive(Debug, PartialEq, Copy, Clone)]
pub struct DeviceInfo {
    model: Model,
    soc: SoC,
    peripheral_base: u32,
    gpio_offset: u32,
}

impl DeviceInfo {
    /// Constructs a new `DeviceInfo`.
    ///
    /// `new` automatically identifies the Raspberry Pi model and SoC based on
    /// the contents of `/proc/cpuinfo`, `/sys/firmware/devicetree/base/compatible`
    /// and `/sys/firmware/devicetree/base/model`.
    pub fn new() -> Result<DeviceInfo> {
        // Parse order from most-detailed to least-detailed info
        let model = parse_proc_cpuinfo()
            .or_else(|_| parse_firmware_compatible().or_else(|_| parse_firmware_model()))?;

        // Set SoC and memory offsets based on model
        match model {
            Model::RaspberryPiA
            | Model::RaspberryPiAPlus
            | Model::RaspberryPiBRev1
            | Model::RaspberryPiBRev2
            | Model::RaspberryPiBPlus
            | Model::RaspberryPiComputeModule
            | Model::RaspberryPiZero
            | Model::RaspberryPiZeroW => Ok(DeviceInfo {
                model,
                soc: SoC::Bcm2835,
                peripheral_base: PERIPHERAL_BASE_RPI,
                gpio_offset: GPIO_OFFSET,
            }),
            Model::RaspberryPi2B => Ok(DeviceInfo {
                model,
                soc: SoC::Bcm2836,
                peripheral_base: PERIPHERAL_BASE_RPI2,
                gpio_offset: GPIO_OFFSET,
            }),
            Model::RaspberryPi3B | Model::RaspberryPiComputeModule3 => Ok(DeviceInfo {
                model,
                soc: SoC::Bcm2837A1,
                peripheral_base: PERIPHERAL_BASE_RPI2,
                gpio_offset: GPIO_OFFSET,
            }),
            Model::RaspberryPi3BPlus => Ok(DeviceInfo {
                model,
                soc: SoC::Bcm2837B0,
                peripheral_base: PERIPHERAL_BASE_RPI2,
                gpio_offset: GPIO_OFFSET,
            }),
        }
    }

    /// Returns the Raspberry Pi's model.
    pub fn model(&self) -> Model {
        self.model
    }

    /// Returns the Raspberry Pi's SoC.
    pub fn soc(&self) -> SoC {
        self.soc
    }

    /// Returns the base memory address for the BCM283x peripherals.
    pub fn peripheral_base(&self) -> u32 {
        self.peripheral_base
    }

    /// Returns the offset from the base memory address for the GPIO section.
    pub fn gpio_offset(&self) -> u32 {
        self.gpio_offset
    }
}
<commit_msg>Add alternative model names<commit_after>// Copyright (c) 2017-2018 Rene van der Meer
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

//! Raspberry Pi system-related tools.
//!
//! Use [`DeviceInfo`] to identify what Raspberry Pi model and SoC the software is
//! running on.
//!
//! [`DeviceInfo`]: struct.DeviceInfo.html

use std::fmt;
use std::fs;
use std::fs::File;
use std::io::{BufRead, BufReader};
use std::result;

const PERIPHERAL_BASE_RPI: u32 = 0x2000_0000;
const PERIPHERAL_BASE_RPI2: u32 = 0x3f00_0000;
const GPIO_OFFSET: u32 = 0x20_0000;

quick_error! {
/// Errors that can occur when trying to identify the Raspberry Pi hardware.
    #[derive(Debug)]
    pub enum Error {
/// Unknown model.
///
/// `DeviceInfo` was unable to identify the Raspberry Pi model based on the
/// contents of `/proc/cpuinfo`, `/sys/firmware/devicetree/base/compatible`
/// and `/sys/firmware/devicetree/base/model`.
        UnknownModel { description("unknown Raspberry Pi model") }
    }
}

/// Result type returned from methods that can have `system::Error`s.
pub type Result<T> = result::Result<T, Error>;

/// Identifiable Raspberry Pi models.
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum Model {
    RaspberryPiA,
    RaspberryPiAPlus,
    RaspberryPiBRev1,
    RaspberryPiBRev2,
    RaspberryPiBPlus,
    RaspberryPi2B,
    RaspberryPi3B,
    RaspberryPi3BPlus,
    RaspberryPiComputeModule,
    RaspberryPiComputeModule3,
    RaspberryPiZero,
    RaspberryPiZeroW,
}

impl fmt::Display for Model {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Model::RaspberryPiA => write!(f, "Raspberry Pi A"),
            Model::RaspberryPiAPlus => write!(f, "Raspberry Pi A+"),
            Model::RaspberryPiBRev1 => write!(f, "Raspberry Pi B Rev 1"),
            Model::RaspberryPiBRev2 => write!(f, "Raspberry Pi B Rev 2"),
            Model::RaspberryPiBPlus => write!(f, "Raspberry Pi B+"),
            Model::RaspberryPi2B => write!(f, "Raspberry Pi 2 B"),
            Model::RaspberryPi3B => write!(f, "Raspberry Pi 3 B"),
            Model::RaspberryPi3BPlus => write!(f, "Raspberry Pi 3 B+"),
            Model::RaspberryPiComputeModule => write!(f, "Raspberry Pi Compute Module"),
            Model::RaspberryPiComputeModule3 => write!(f, "Raspberry Pi Compute Module 3"),
            Model::RaspberryPiZero => write!(f, "Raspberry Pi Zero"),
            Model::RaspberryPiZeroW => write!(f, "Raspberry Pi Zero W"),
        }
    }
}

/// Identifiable Raspberry Pi SoCs.
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum SoC {
    Bcm2835,
    Bcm2836,
    Bcm2837A1,
    Bcm2837B0,
}

impl fmt::Display for SoC {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            SoC::Bcm2835 => write!(f, "BCM2835"),
            SoC::Bcm2836 => write!(f, "BCM2836"),
            SoC::Bcm2837A1 => write!(f, "BCM2837A1"),
            SoC::Bcm2837B0 => write!(f, "BCM2837B0"),
        }
    }
}

// Identify Pi model based on /proc/cpuinfo
fn parse_proc_cpuinfo() -> Result<Model> {
    let proc_cpuinfo = BufReader::new(match File::open("/proc/cpuinfo") {
        Err(_) => return Err(Error::UnknownModel),
        Ok(file) => file,
    });

    let mut hardware: String = String::new();
    let mut revision: String = String::new();
    for line_result in proc_cpuinfo.lines() {
        if let Ok(line) = line_result {
            if line.starts_with("Hardware\t: ") {
                hardware = String::from(&line[11..]);
            } else if line.starts_with("Revision\t: ") {
                revision = String::from(&line[11..]).to_lowercase();
            }
        }
    }

    // Return an error if we don't recognize the SoC. This check is
    // done to prevent accidentally identifying a non-Pi SBC as a Pi
    // solely based on the revision field.
    match &hardware[..] {
        "BCM2708" | "BCM2835" | "BCM2709" | "BCM2836" | "BCM2710" | "BCM2837" | "BCM2837A1"
        | "BCM2837B0" => {}
        _ => return Err(Error::UnknownModel),
    }

    let model = if (revision.len() == 4) || (revision.len() == 8) {
        // Older revisions are 4 characters long, or 8 if they've been over-volted
        match &revision[revision.len() - 4..] {
            "0007" | "0008" | "0009" | "0015" => Model::RaspberryPiA,
            "Beta" | "0002" | "0003" => Model::RaspberryPiBRev1,
            "0004" | "0005" | "0006" | "000d" | "000e" | "000f" => Model::RaspberryPiBRev2,
            "0012" => Model::RaspberryPiAPlus,
            "0010" | "0013" => Model::RaspberryPiBPlus,
            "0011" | "0014" => Model::RaspberryPiComputeModule,
            _ => return Err(Error::UnknownModel),
        }
    } else if revision.len() >= 6 {
        // Newer revisions consist of at least 6 characters
        match &revision[revision.len() - 3..revision.len() - 1] {
            "00" => Model::RaspberryPiA,
            "01" => Model::RaspberryPiBRev2,
            "02" => Model::RaspberryPiAPlus,
            "03" => Model::RaspberryPiBPlus,
            "04" => Model::RaspberryPi2B,
            "06" => Model::RaspberryPiComputeModule,
            "08" => Model::RaspberryPi3B,
            "09" => Model::RaspberryPiZero,
            "0a" => Model::RaspberryPiComputeModule3,
            "0c" => Model::RaspberryPiZeroW,
            "0d" => Model::RaspberryPi3BPlus,
            _ => return Err(Error::UnknownModel),
        }
    } else {
        return Err(Error::UnknownModel);
    };

    Ok(model)
}

// Identify Pi model based on /sys/firmware/devicetree/base/compatible
fn parse_firmware_compatible() -> Result<Model> {
    let compatible = match fs::read_to_string("/sys/firmware/devicetree/base/compatible") {
        Err(_) => return Err(Error::UnknownModel),
        Ok(s) => s,
    };

    // Based on /arch/arm/boot/dts/ and /Documentation/devicetree/bindings/arm/bcm/
    for compid in compatible.split('\0') {
        match compid {
            "raspberrypi,model-b-i2c0" => return Ok(Model::RaspberryPiBRev1),
            "raspberrypi,model-b" => return Ok(Model::RaspberryPiBRev1),
            "raspberrypi,model-a" => return Ok(Model::RaspberryPiA),
            "raspberrypi,model-b-rev2" => return Ok(Model::RaspberryPiBRev2),
            "raspberrypi,model-a-plus" => return Ok(Model::RaspberryPiAPlus),
            "raspberrypi,model-b-plus" => return Ok(Model::RaspberryPiBPlus),
            "raspberrypi,2-model-b" => return Ok(Model::RaspberryPi2B),
            "raspberrypi,compute-module" => return Ok(Model::RaspberryPiComputeModule),
            "raspberrypi,3-model-b" => return Ok(Model::RaspberryPi3B),
            "raspberrypi,model-zero" => return Ok(Model::RaspberryPiZero),
            "raspberrypi,3-compute-module" => return Ok(Model::RaspberryPiComputeModule3),
            "raspberrypi,model-zero-w" => return Ok(Model::RaspberryPiZeroW),
            "raspberrypi,3-model-b-plus" => return Ok(Model::RaspberryPi3BPlus),
            _ => (),
        }
    }

    Err(Error::UnknownModel)
}

// Identify Pi model based on /sys/firmware/devicetree/base/model
fn parse_firmware_model() -> Result<Model> {
    let model = match fs::read_to_string("/sys/firmware/devicetree/base/model") {
        Err(_) => return Err(Error::UnknownModel),
        Ok(buffer) => if let Some(rev_idx) = buffer.find(" Rev ") {
            // We don't want to strip rev2 here
            buffer[0..rev_idx].to_owned()
        } else {
            buffer
        },
    };

    // Based on /arch/arm/boot/dts/ and /Documentation/devicetree/bindings/arm/bcm/
    match &model[..] {
        "Raspberry Pi Model B (no P5)" => Ok(Model::RaspberryPiBRev1),
        "Raspberry Pi Model B" => Ok(Model::RaspberryPiBRev1),
        "Raspberry Pi Model A" => Ok(Model::RaspberryPiA),
        "Raspberry Pi Model B rev2" => Ok(Model::RaspberryPiBRev2),
        "Raspberry Pi Model A+" => Ok(Model::RaspberryPiAPlus),
        "Raspberry Pi Model A Plus" => Ok(Model::RaspberryPiAPlus),
        "Raspberry Pi Model B+" => Ok(Model::RaspberryPiBPlus),
        "Raspberry Pi Model B Plus" => Ok(Model::RaspberryPiBPlus),
        "Raspberry Pi 2 Model B" => Ok(Model::RaspberryPi2B),
        "Raspberry Pi Compute Module" => Ok(Model::RaspberryPiComputeModule),
        "Raspberry Pi 3 Model B" => Ok(Model::RaspberryPi3B),
        "Raspberry Pi Zero" => Ok(Model::RaspberryPiZero),
        "Raspberry Pi Compute Module 3" => Ok(Model::RaspberryPiComputeModule3),
        "Raspberry Pi Zero W" => Ok(Model::RaspberryPiZeroW),
        "Raspberry Pi 3 Model B+" => Ok(Model::RaspberryPi3BPlus),
        "Raspberry Pi 3 Model B Plus" => Ok(Model::RaspberryPi3BPlus),
        _ => Err(Error::UnknownModel),
    }
}

/// Retrieves Raspberry Pi device information.
#[derive(Debug, PartialEq, Copy, Clone)]
pub struct DeviceInfo {
    model: Model,
    soc: SoC,
    peripheral_base: u32,
    gpio_offset: u32,
}

impl DeviceInfo {
    /// Constructs a new `DeviceInfo`.
    ///
    /// `new` automatically identifies the Raspberry Pi model and SoC based on
    /// the contents of `/proc/cpuinfo`, `/sys/firmware/devicetree/base/compatible`
    /// and `/sys/firmware/devicetree/base/model`.
    pub fn new() -> Result<DeviceInfo> {
        // Parse order from most-detailed to least-detailed info
        let model = parse_proc_cpuinfo()
            .or_else(|_| parse_firmware_compatible().or_else(|_| parse_firmware_model()))?;

        // Set SoC and memory offsets based on model
        match model {
            Model::RaspberryPiA
            | Model::RaspberryPiAPlus
            | Model::RaspberryPiBRev1
            | Model::RaspberryPiBRev2
            | Model::RaspberryPiBPlus
            | Model::RaspberryPiComputeModule
            | Model::RaspberryPiZero
            | Model::RaspberryPiZeroW => Ok(DeviceInfo {
                model,
                soc: SoC::Bcm2835,
                peripheral_base: PERIPHERAL_BASE_RPI,
                gpio_offset: GPIO_OFFSET,
            }),
            Model::RaspberryPi2B => Ok(DeviceInfo {
                model,
                soc: SoC::Bcm2836,
                peripheral_base: PERIPHERAL_BASE_RPI2,
                gpio_offset: GPIO_OFFSET,
            }),
            Model::RaspberryPi3B | Model::RaspberryPiComputeModule3 => Ok(DeviceInfo {
                model,
                soc: SoC::Bcm2837A1,
                peripheral_base: PERIPHERAL_BASE_RPI2,
                gpio_offset: GPIO_OFFSET,
            }),
            Model::RaspberryPi3BPlus => Ok(DeviceInfo {
                model,
                soc: SoC::Bcm2837B0,
                peripheral_base: PERIPHERAL_BASE_RPI2,
                gpio_offset: GPIO_OFFSET,
            }),
        }
    }

    /// Returns the Raspberry Pi's model.
    pub fn model(&self) -> Model {
        self.model
    }

    /// Returns the Raspberry Pi's SoC.
    pub fn soc(&self) -> SoC {
        self.soc
    }

    /// Returns the base memory address for the BCM283x peripherals.
    pub fn peripheral_base(&self) -> u32 {
        self.peripheral_base
    }

    /// Returns the offset from the base memory address for the GPIO section.
    pub fn gpio_offset(&self) -> u32 {
        self.gpio_offset
    }
}
<|endoftext|>


<commit_before>use rand;
use std::fmt::Debug;
use std::thread;
use super::{Arbitrary, Gen, StdGen};
use tester::Status::{Discard, Fail, Pass};

/// The main QuickCheck type for setting configuration and running QuickCheck.
pub struct QuickCheck<G> {
    tests: usize,
    max_tests: usize,
    gen: G,
}

impl QuickCheck<StdGen<rand::ThreadRng>> {
    /// Creates a new QuickCheck value.
    ///
    /// This can be used to run QuickCheck on things that implement
    /// `Testable`. You may also adjust the configuration, such as
    /// the number of tests to run.
    ///
    /// By default, the maximum number of passed tests is set to `100`,
    /// the max number of overall tests is set to `10000` and the generator
    /// is set to a `StdGen` with a default size of `100`.
    pub fn new() -> QuickCheck<StdGen<rand::ThreadRng>> {
        QuickCheck {
            tests: 100,
            max_tests: 10000,
            gen: StdGen::new(rand::thread_rng(), 100),
        }
    }
}

impl<G: Gen> QuickCheck<G> {
    /// Set the number of tests to run.
    ///
    /// This actually refers to the maximum number of *passed* tests that
    /// can occur. Namely, if a test causes a failure, future testing on that
    /// property stops. Additionally, if tests are discarded, there may be
    /// fewer than `tests` passed.
    pub fn tests(mut self, tests: usize) -> QuickCheck<G> {
        self.tests = tests;
        self
    }

    /// Set the maximum number of tests to run.
    ///
    /// The number of invocations of a property will never exceed this number.
    /// This is necessary to cap the number of tests because QuickCheck
    /// properties can discard tests.
    pub fn max_tests(mut self, max_tests: usize) -> QuickCheck<G> {
        self.max_tests = max_tests;
        self
    }

    /// Set the random number generator to be used by QuickCheck.
    pub fn gen(mut self, gen: G) -> QuickCheck<G> {
        self.gen = gen;
        self
    }

    /// Tests a property and returns the result.
    ///
    /// The result returned is either the number of tests passed or a witness
    /// of failure.
    ///
    /// (If you're using Rust's unit testing infrastructure, then you'll
    /// want to use the `quickcheck` method, which will `panic!` on failure.)
    pub fn quicktest<A>(&mut self, f: A) -> Result<usize, TestResult>
                    where A: Testable {
        let mut ntests: usize = 0;
        for _ in 0..self.max_tests {
            if ntests >= self.tests {
                break
            }
            match f.result(&mut self.gen) {
                TestResult { status: Pass, .. } => ntests += 1,
                TestResult { status: Discard, .. } => continue,
                r @ TestResult { status: Fail, .. } => return Err(r),
            }
        }
        Ok(ntests)
    }

    /// Tests a property and calls `panic!` on failure.
    ///
    /// The `panic!` message will include a (hopefully) minimal witness of
    /// failure.
    ///
    /// It is appropriate to use this method with Rust's unit testing
    /// infrastructure.
    ///
    /// Note that if the environment variable `RUST_LOG` is set to enable
    /// `info` level log messages for the `quickcheck` crate, then this will
    /// include output on how many QuickCheck tests were passed.
    ///
    /// # Example
    ///
    /// ```rust
    /// use quickcheck::QuickCheck;
    ///
    /// fn prop_reverse_reverse() {
    ///     fn revrev(xs: Vec<usize>) -> bool {
    ///         let rev: Vec<_> = xs.clone().into_iter().rev().collect();
    ///         let revrev: Vec<_> = rev.into_iter().rev().collect();
    ///         xs == revrev
    ///     }
    ///     QuickCheck::new().quickcheck(revrev as fn(Vec<usize>) -> bool);
    /// }
    /// ```
    pub fn quickcheck<A>(&mut self, f: A) where A: Testable {
        // Ignore log init failures, implying it has already been done.
        let _ = ::env_logger::init();

        match self.quicktest(f) {
            Ok(ntests) => info!("(Passed {} QuickCheck tests.)", ntests),
            Err(result) => panic!(result.failed_msg()),
        }
    }
}

/// Convenience function for running QuickCheck.
///
/// This is an alias for `QuickCheck::new().quickcheck(f)`.
pub fn quickcheck<A: Testable>(f: A) { QuickCheck::new().quickcheck(f) }

/// Describes the status of a single instance of a test.
///
/// All testable things must be capable of producing a `TestResult`.
#[derive(Clone, Debug)]
pub struct TestResult {
    status: Status,
    arguments: Vec<String>,
    err: Option<String>,
}

/// Whether a test has passed, failed or been discarded.
#[derive(Clone, Debug)]
enum Status { Pass, Fail, Discard }

impl TestResult {
    /// Produces a test result that indicates the current test has passed.
    pub fn passed() -> TestResult { TestResult::from_bool(true) }

    /// Produces a test result that indicates the current test has failed.
    pub fn failed() -> TestResult { TestResult::from_bool(false) }

    /// Produces a test result that indicates failure from a runtime error.
    pub fn error<S: Into<String>>(msg: S) -> TestResult {
        let mut r = TestResult::from_bool(false);
        r.err = Some(msg.into());
        r
    }

    /// Produces a test result that instructs `quickcheck` to ignore it.
    /// This is useful for restricting the domain of your properties.
    /// When a test is discarded, `quickcheck` will replace it with a
    /// fresh one (up to a certain limit).
    pub fn discard() -> TestResult {
        TestResult {
            status: Discard,
            arguments: vec![],
            err: None,
        }
    }

    /// Converts a `bool` to a `TestResult`. A `true` value indicates that
    /// the test has passed and a `false` value indicates that the test
    /// has failed.
    pub fn from_bool(b: bool) -> TestResult {
        TestResult {
            status: if b { Pass } else { Fail },
            arguments: vec![],
            err: None,
        }
    }

    /// Tests if a "procedure" fails when executed. The test passes only if
    /// `f` generates a task failure during its execution.
    pub fn must_fail<T, F>(f: F) -> TestResult
            where F: FnOnce() -> T, F: Send + 'static, T: Send + 'static {
        TestResult::from_bool(
            thread::Builder::new()
                            .spawn(move || { let _ = f(); })
                            .unwrap()
                            .join()
                            .is_err())
    }

    /// Returns `true` if and only if this test result describes a failing
    /// test.
    pub fn is_failure(&self) -> bool {
        match self.status {
            Fail => true,
            Pass|Discard => false,
        }
    }

    /// Returns `true` if and only if this test result describes a failing
    /// test as a result of a run time error.
    pub fn is_error(&self) -> bool {
        self.is_failure() && self.err.is_some()
    }

    fn failed_msg(&self) -> String {
        match self.err {
            None => {
                format!("[quickcheck] TEST FAILED. Arguments: ({})",
                        self.arguments.connect(", "))
            }
            Some(ref err) => {
                format!("[quickcheck] TEST FAILED (runtime error). \
                         Arguments: ({})\nError: {}",
                        self.arguments.connect(", "), err)
            }
        }
    }
}

/// `Testable` describes types (e.g., a function) whose values can be
/// tested.
///
/// Anything that can be tested must be capable of producing a `TestResult`
/// given a random number generator. This is trivial for types like `bool`,
/// which are just converted to either a passing or failing test result.
///
/// For functions, an implementation must generate random arguments
/// and potentially shrink those arguments if they produce a failure.
///
/// It's unlikely that you'll have to implement this trait yourself.
pub trait Testable : Send + 'static {
    fn result<G: Gen>(&self, &mut G) -> TestResult;
}

impl Testable for bool {
    fn result<G: Gen>(&self, _: &mut G) -> TestResult {
        TestResult::from_bool(*self)
    }
}

impl Testable for () {
    fn result<G: Gen>(&self, _: &mut G) -> TestResult {
        TestResult::passed()
    }
}

impl Testable for TestResult {
    fn result<G: Gen>(&self, _: &mut G) -> TestResult { self.clone() }
}

impl<A, E> Testable for Result<A, E>
        where A: Testable, E: Debug + Send + 'static {
    fn result<G: Gen>(&self, g: &mut G) -> TestResult {
        match *self {
            Ok(ref r) => r.result(g),
            Err(ref err) => TestResult::error(format!("{:?}", err)),
        }
    }
}

macro_rules! testable_fn {
    ($($name: ident),*) => {

impl<T: Testable,
     $($name: Arbitrary + Debug),*> Testable for fn($($name),*) -> T {
    #[allow(non_snake_case)]
    fn result<G_: Gen>(&self, g: &mut G_) -> TestResult {
        let self_ = *self;
        let a: ($($name,)*) = Arbitrary::arbitrary(g);
        let ( $($name,)* ) = a.clone();
        let mut r = safe(move || {self_($($name),*)}).result(g);
        match r.status {
            Pass|Discard => r,
            Fail => {
                let ( $($name,)* ) = a.clone();
                r.arguments = vec![$(format!("{:?}", $name),)*];
                let mut r_new;
                for t in a.shrink() {
                    let ($($name,)*) = t.clone();
                    r_new = safe(move || {self_($($name),*)}).result(g);
                    if r_new.is_failure() {
                        r = r_new;
                        let ($($name,)*) = t;
                        r.arguments = vec![$(format!("{:?}", $name),)*];
                    }
                }
                r
            }
        }
    }
}}}

testable_fn!();
testable_fn!(A);
testable_fn!(A, B);
testable_fn!(A, B, C);
testable_fn!(A, B, C, D);
testable_fn!(A, B, C, D, E);
testable_fn!(A, B, C, D, E, F);
testable_fn!(A, B, C, D, E, F, G);
testable_fn!(A, B, C, D, E, F, G, H);
testable_fn!(A, B, C, D, E, F, G, H, I);
testable_fn!(A, B, C, D, E, F, G, H, I, J);
testable_fn!(A, B, C, D, E, F, G, H, I, J, K);
testable_fn!(A, B, C, D, E, F, G, H, I, J, K, L);

// TODO(burntsushi): Use `std::thread::catch_panic` once it stabilizes.
fn safe<T, F>(fun: F) -> Result<T, String>
        where F: FnOnce() -> T, F: Send + 'static, T: Send + 'static {
    let t = ::std::thread::Builder::new().name("safe".into());
    t.spawn(fun).unwrap().join().map_err(|any_err| {
        // Extract common types of panic payload:
        // panic and assert produce &str or String
        if let Some(&s) = any_err.downcast_ref::<&str>() {
            s.to_owned()
        } else if let Some(s) = any_err.downcast_ref::<String>() {
            s.to_owned()
        } else {
            "UNABLE TO SHOW RESULT OF PANIC.".to_owned()
        }
    })
}

/// Convenient aliases.
trait AShow : Arbitrary + Debug {}
impl<A: Arbitrary + Debug> AShow for A {}
<commit_msg>Port over the shrinking behavior from before f017c01.<commit_after>use rand;
use std::fmt::Debug;
use std::thread;
use super::{Arbitrary, Gen, StdGen};
use tester::Status::{Discard, Fail, Pass};

/// The main QuickCheck type for setting configuration and running QuickCheck.
pub struct QuickCheck<G> {
    tests: usize,
    max_tests: usize,
    gen: G,
}

impl QuickCheck<StdGen<rand::ThreadRng>> {
    /// Creates a new QuickCheck value.
    ///
    /// This can be used to run QuickCheck on things that implement
    /// `Testable`. You may also adjust the configuration, such as
    /// the number of tests to run.
    ///
    /// By default, the maximum number of passed tests is set to `100`,
    /// the max number of overall tests is set to `10000` and the generator
    /// is set to a `StdGen` with a default size of `100`.
    pub fn new() -> QuickCheck<StdGen<rand::ThreadRng>> {
        QuickCheck {
            tests: 100,
            max_tests: 10000,
            gen: StdGen::new(rand::thread_rng(), 100),
        }
    }
}

impl<G: Gen> QuickCheck<G> {
    /// Set the number of tests to run.
    ///
    /// This actually refers to the maximum number of *passed* tests that
    /// can occur. Namely, if a test causes a failure, future testing on that
    /// property stops. Additionally, if tests are discarded, there may be
    /// fewer than `tests` passed.
    pub fn tests(mut self, tests: usize) -> QuickCheck<G> {
        self.tests = tests;
        self
    }

    /// Set the maximum number of tests to run.
    ///
    /// The number of invocations of a property will never exceed this number.
    /// This is necessary to cap the number of tests because QuickCheck
    /// properties can discard tests.
    pub fn max_tests(mut self, max_tests: usize) -> QuickCheck<G> {
        self.max_tests = max_tests;
        self
    }

    /// Set the random number generator to be used by QuickCheck.
    pub fn gen(mut self, gen: G) -> QuickCheck<G> {
        self.gen = gen;
        self
    }

    /// Tests a property and returns the result.
    ///
    /// The result returned is either the number of tests passed or a witness
    /// of failure.
    ///
    /// (If you're using Rust's unit testing infrastructure, then you'll
    /// want to use the `quickcheck` method, which will `panic!` on failure.)
    pub fn quicktest<A>(&mut self, f: A) -> Result<usize, TestResult>
                    where A: Testable {
        let mut ntests: usize = 0;
        for _ in 0..self.max_tests {
            if ntests >= self.tests {
                break
            }
            match f.result(&mut self.gen) {
                TestResult { status: Pass, .. } => ntests += 1,
                TestResult { status: Discard, .. } => continue,
                r @ TestResult { status: Fail, .. } => return Err(r),
            }
        }
        Ok(ntests)
    }

    /// Tests a property and calls `panic!` on failure.
    ///
    /// The `panic!` message will include a (hopefully) minimal witness of
    /// failure.
    ///
    /// It is appropriate to use this method with Rust's unit testing
    /// infrastructure.
    ///
    /// Note that if the environment variable `RUST_LOG` is set to enable
    /// `info` level log messages for the `quickcheck` crate, then this will
    /// include output on how many QuickCheck tests were passed.
    ///
    /// # Example
    ///
    /// ```rust
    /// use quickcheck::QuickCheck;
    ///
    /// fn prop_reverse_reverse() {
    ///     fn revrev(xs: Vec<usize>) -> bool {
    ///         let rev: Vec<_> = xs.clone().into_iter().rev().collect();
    ///         let revrev: Vec<_> = rev.into_iter().rev().collect();
    ///         xs == revrev
    ///     }
    ///     QuickCheck::new().quickcheck(revrev as fn(Vec<usize>) -> bool);
    /// }
    /// ```
    pub fn quickcheck<A>(&mut self, f: A) where A: Testable {
        // Ignore log init failures, implying it has already been done.
        let _ = ::env_logger::init();

        match self.quicktest(f) {
            Ok(ntests) => info!("(Passed {} QuickCheck tests.)", ntests),
            Err(result) => panic!(result.failed_msg()),
        }
    }
}

/// Convenience function for running QuickCheck.
///
/// This is an alias for `QuickCheck::new().quickcheck(f)`.
pub fn quickcheck<A: Testable>(f: A) { QuickCheck::new().quickcheck(f) }

/// Describes the status of a single instance of a test.
///
/// All testable things must be capable of producing a `TestResult`.
#[derive(Clone, Debug)]
pub struct TestResult {
    status: Status,
    arguments: Vec<String>,
    err: Option<String>,
}

/// Whether a test has passed, failed or been discarded.
#[derive(Clone, Debug)]
enum Status { Pass, Fail, Discard }

impl TestResult {
    /// Produces a test result that indicates the current test has passed.
    pub fn passed() -> TestResult { TestResult::from_bool(true) }

    /// Produces a test result that indicates the current test has failed.
    pub fn failed() -> TestResult { TestResult::from_bool(false) }

    /// Produces a test result that indicates failure from a runtime error.
    pub fn error<S: Into<String>>(msg: S) -> TestResult {
        let mut r = TestResult::from_bool(false);
        r.err = Some(msg.into());
        r
    }

    /// Produces a test result that instructs `quickcheck` to ignore it.
    /// This is useful for restricting the domain of your properties.
    /// When a test is discarded, `quickcheck` will replace it with a
    /// fresh one (up to a certain limit).
    pub fn discard() -> TestResult {
        TestResult {
            status: Discard,
            arguments: vec![],
            err: None,
        }
    }

    /// Converts a `bool` to a `TestResult`. A `true` value indicates that
    /// the test has passed and a `false` value indicates that the test
    /// has failed.
    pub fn from_bool(b: bool) -> TestResult {
        TestResult {
            status: if b { Pass } else { Fail },
            arguments: vec![],
            err: None,
        }
    }

    /// Tests if a "procedure" fails when executed. The test passes only if
    /// `f` generates a task failure during its execution.
    pub fn must_fail<T, F>(f: F) -> TestResult
            where F: FnOnce() -> T, F: Send + 'static, T: Send + 'static {
        TestResult::from_bool(
            thread::Builder::new()
                            .spawn(move || { let _ = f(); })
                            .unwrap()
                            .join()
                            .is_err())
    }

    /// Returns `true` if and only if this test result describes a failing
    /// test.
    pub fn is_failure(&self) -> bool {
        match self.status {
            Fail => true,
            Pass|Discard => false,
        }
    }

    /// Returns `true` if and only if this test result describes a failing
    /// test as a result of a run time error.
    pub fn is_error(&self) -> bool {
        self.is_failure() && self.err.is_some()
    }

    fn failed_msg(&self) -> String {
        match self.err {
            None => {
                format!("[quickcheck] TEST FAILED. Arguments: ({})",
                        self.arguments.connect(", "))
            }
            Some(ref err) => {
                format!("[quickcheck] TEST FAILED (runtime error). \
                         Arguments: ({})\nError: {}",
                        self.arguments.connect(", "), err)
            }
        }
    }
}

/// `Testable` describes types (e.g., a function) whose values can be
/// tested.
///
/// Anything that can be tested must be capable of producing a `TestResult`
/// given a random number generator. This is trivial for types like `bool`,
/// which are just converted to either a passing or failing test result.
///
/// For functions, an implementation must generate random arguments
/// and potentially shrink those arguments if they produce a failure.
///
/// It's unlikely that you'll have to implement this trait yourself.
pub trait Testable : Send + 'static {
    fn result<G: Gen>(&self, &mut G) -> TestResult;
}

impl Testable for bool {
    fn result<G: Gen>(&self, _: &mut G) -> TestResult {
        TestResult::from_bool(*self)
    }
}

impl Testable for () {
    fn result<G: Gen>(&self, _: &mut G) -> TestResult {
        TestResult::passed()
    }
}

impl Testable for TestResult {
    fn result<G: Gen>(&self, _: &mut G) -> TestResult { self.clone() }
}

impl<A, E> Testable for Result<A, E>
        where A: Testable, E: Debug + Send + 'static {
    fn result<G: Gen>(&self, g: &mut G) -> TestResult {
        match *self {
            Ok(ref r) => r.result(g),
            Err(ref err) => TestResult::error(format!("{:?}", err)),
        }
    }
}

macro_rules! testable_fn {
    ($($name: ident),*) => {

impl<T: Testable,
     $($name: Arbitrary + Debug),*> Testable for fn($($name),*) -> T {
    #[allow(non_snake_case)]
    fn result<G_: Gen>(&self, g: &mut G_) -> TestResult {
        fn shrink_failure<T: Testable, G_: Gen, $($name: Arbitrary + Debug),*>(
                g: &mut G_, self_: fn($($name),*) -> T, a: ($($name,)*)) -> Option<TestResult> {
            for t in a.shrink() {
                let ($($name,)*) = t.clone();
                let mut r_new = safe(move || {self_($($name),*)}).result(g);
                if r_new.is_failure() {
                    let ($($name,)*) : ($($name,)*) = t.clone();
                    r_new.arguments = vec![$(format!("{:?}", $name),)*];

                    // The shrunk value *does* witness a failure, so keep trying
                    // to shrink it.
                    let shrunk = shrink_failure(g, self_, t);

                    // If we couldn't witness a failure on any shrunk value,
                    // then return the failure we already have.
                    return Some(shrunk.unwrap_or(r_new))
                }
            }
            None
        }

        let self_ = *self;
        let a: ($($name,)*) = Arbitrary::arbitrary(g);
        let ( $($name,)* ) = a.clone();
        let mut r = safe(move || {self_($($name),*)}).result(g);

        let ( $($name,)* ) = a.clone();
        r.arguments = vec![$(format!("{:?}", $name),)*];
        match r.status {
            Pass|Discard => r,
            Fail => {
                shrink_failure(g, self_, a).unwrap_or(r)
            }
        }
    }
}}}

testable_fn!();
testable_fn!(A);
testable_fn!(A, B);
testable_fn!(A, B, C);
testable_fn!(A, B, C, D);
testable_fn!(A, B, C, D, E);
testable_fn!(A, B, C, D, E, F);
testable_fn!(A, B, C, D, E, F, G);
testable_fn!(A, B, C, D, E, F, G, H);
testable_fn!(A, B, C, D, E, F, G, H, I);
testable_fn!(A, B, C, D, E, F, G, H, I, J);
testable_fn!(A, B, C, D, E, F, G, H, I, J, K);
testable_fn!(A, B, C, D, E, F, G, H, I, J, K, L);

// TODO(burntsushi): Use `std::thread::catch_panic` once it stabilizes.
fn safe<T, F>(fun: F) -> Result<T, String>
        where F: FnOnce() -> T, F: Send + 'static, T: Send + 'static {
    let t = ::std::thread::Builder::new().name("safe".into());
    t.spawn(fun).unwrap().join().map_err(|any_err| {
        // Extract common types of panic payload:
        // panic and assert produce &str or String
        if let Some(&s) = any_err.downcast_ref::<&str>() {
            s.to_owned()
        } else if let Some(s) = any_err.downcast_ref::<String>() {
            s.to_owned()
        } else {
            "UNABLE TO SHOW RESULT OF PANIC.".to_owned()
        }
    })
}

/// Convenient aliases.
trait AShow : Arbitrary + Debug {}
impl<A: Arbitrary + Debug> AShow for A {}
<|endoftext|>