<commit_before>//! Simple module to store files in database.
//!
//! cratesfyi is generating more than 5 million files, they are small and mostly html files.
//! They are using so many inodes and it is better to store them in database instead of
//! filesystem. This module is adding files into database and retrieving them.

use std::path::Path;
use postgres::Connection;
use rustc_serialize::json::{Json, ToJson};
use std::fs::File;
use std::io::Read;
use errors::*;

fn file_path(prefix: &str, name: &str) -> String {
    match prefix.is_empty() {
        true => name.to_owned(),
        false => format!("{}/{}", prefix, name),
    }
}

fn get_file_list_from_dir<P: AsRef<Path>>(path: P,
                                          prefix: &str,
                                          files: &mut Vec<String>)
                                          -> Result<()> {
    let path = path.as_ref();

    for file in try!(path.read_dir()) {
        let file = try!(file);

        if try!(file.file_type()).is_file() {
            file.file_name().to_str().map(|name| files.push(file_path(prefix, name)));
        } else if try!(file.file_type()).is_dir() {
            file.file_name()
                .to_str()
                .map(|name| get_file_list_from_dir(file.path(), &file_path(prefix, name), files));
        }
    }

    Ok(())
}

pub fn get_file_list<P: AsRef<Path>>(path: P) -> Result<Vec<String>> {
    let path = path.as_ref();
    let mut files: Vec<String> = Vec::new();

    if !path.exists() {
        return Err("File not found".into());
    } else if path.is_file() {
        path.file_name()
            .and_then(|name| name.to_str())
            .map(|name| files.push(format!("{}", name)));
    } else if path.is_dir() {
        try!(get_file_list_from_dir(path, "", &mut files));
    }

    Ok(files)
}

/// Adds files into database and returns list of files with their mime type in Json
pub fn add_path_into_database<P: AsRef<Path>>(conn: &Connection,
                                              prefix: &str,
                                              path: P)
                                              -> Result<Json> {
    use magic::{Cookie, flags};
    let cookie = try!(Cookie::open(flags::MIME_TYPE));
    // FIXME: This is linux specific but idk any alternative
    try!(cookie.load(&vec!["/usr/share/misc/magic.mgc"]));

    let trans = try!(conn.transaction());

    let mut file_list_with_mimes: Vec<(String, String)> = Vec::new();

    for file_path_str in try!(get_file_list(&path)) {
        let (path, content, mime) = {
            let path = Path::new(path.as_ref()).join(&file_path_str);
            // Some files have insufficient permissions (like .lock file created by cargo in
            // documentation directory). We are skipping this files.
            let mut file = match File::open(path) {
                Ok(f) => f,
                Err(_) => continue,
            };
            let mut content: Vec<u8> = Vec::new();
            try!(file.read_to_end(&mut content));
            let mime = {
                let mime = try!(cookie.buffer(&content));
                // css's are causing some problem in browsers
                // magic will return text/plain for css file types
                // convert them to text/css
                // do the same for javascript files
                if mime == "text/plain" {
                    if file_path_str.ends_with(".css") {
                        "text/css".to_owned()
                    } else if file_path_str.ends_with(".js") {
                        "application/javascript".to_owned()
                    } else {
                        mime.to_owned()
                    }
                } else {
                    mime.to_owned()
                }
            };

            file_list_with_mimes.push((mime.clone(), file_path_str.clone()));

            (file_path(prefix, &file_path_str), content, mime)
        };

        // check if file already exists in database
        let rows = try!(conn.query("SELECT COUNT(*) FROM files WHERE path = $1", &[&path]));

        if rows.get(0).get::<usize, i64>(0) == 0 {
            try!(trans.query("INSERT INTO files (path, mime, content) VALUES ($1, $2, $3)",
                             &[&path, &mime, &content]));
        } else {
            try!(trans.query("UPDATE files SET mime = $2, content = $3, date_updated = NOW() \
                              WHERE path = $1",
                             &[&path, &mime, &content]));
        }
    }

    try!(trans.commit());

    file_list_to_json(file_list_with_mimes)
}

fn file_list_to_json(file_list: Vec<(String, String)>) -> Result<Json> {

    let mut file_list_json: Vec<Json> = Vec::new();

    for file in file_list {
        let mut v: Vec<String> = Vec::new();
        v.push(file.0.clone());
        v.push(file.1.clone());
        file_list_json.push(v.to_json());
    }

    Ok(file_list_json.to_json())
}

#[cfg(test)]
mod test {
    extern crate env_logger;
    use std::env;
    use super::{get_file_list, add_path_into_database};
    use super::super::connect_db;

    #[test]
    fn test_get_file_list() {
        let _ = env_logger::init();

        let files = get_file_list(env::current_dir().unwrap());
        assert!(files.is_ok());
        assert!(files.unwrap().len() > 0);

        let files = get_file_list(env::current_dir().unwrap().join("Cargo.toml")).unwrap();
        assert_eq!(files[0], "Cargo.toml");
    }

    #[test]
    #[ignore]
    fn test_add_path_into_database() {
        let _ = env_logger::init();

        let conn = connect_db().unwrap();
        let res = add_path_into_database(&conn, "example", env::current_dir().unwrap().join("src"));
        assert!(res.is_ok());
    }
}
<commit_msg>Load default magic cookie file<commit_after>//! Simple module to store files in database.
//!
//! cratesfyi is generating more than 5 million files, they are small and mostly html files.
//! They are using so many inodes and it is better to store them in database instead of
//! filesystem. This module is adding files into database and retrieving them.

use std::path::Path;
use postgres::Connection;
use rustc_serialize::json::{Json, ToJson};
use std::fs::File;
use std::io::Read;
use errors::*;

fn file_path(prefix: &str, name: &str) -> String {
    match prefix.is_empty() {
        true => name.to_owned(),
        false => format!("{}/{}", prefix, name),
    }
}

fn get_file_list_from_dir<P: AsRef<Path>>(path: P,
                                          prefix: &str,
                                          files: &mut Vec<String>)
                                          -> Result<()> {
    let path = path.as_ref();

    for file in try!(path.read_dir()) {
        let file = try!(file);

        if try!(file.file_type()).is_file() {
            file.file_name().to_str().map(|name| files.push(file_path(prefix, name)));
        } else if try!(file.file_type()).is_dir() {
            file.file_name()
                .to_str()
                .map(|name| get_file_list_from_dir(file.path(), &file_path(prefix, name), files));
        }
    }

    Ok(())
}

pub fn get_file_list<P: AsRef<Path>>(path: P) -> Result<Vec<String>> {
    let path = path.as_ref();
    let mut files: Vec<String> = Vec::new();

    if !path.exists() {
        return Err("File not found".into());
    } else if path.is_file() {
        path.file_name()
            .and_then(|name| name.to_str())
            .map(|name| files.push(format!("{}", name)));
    } else if path.is_dir() {
        try!(get_file_list_from_dir(path, "", &mut files));
    }

    Ok(files)
}

/// Adds files into database and returns list of files with their mime type in Json
pub fn add_path_into_database<P: AsRef<Path>>(conn: &Connection,
                                              prefix: &str,
                                              path: P)
                                              -> Result<Json> {
    use magic::{Cookie, flags};
    let cookie = try!(Cookie::open(flags::MIME_TYPE));
    try!(cookie.load::<&str>(&[]));

    let trans = try!(conn.transaction());

    let mut file_list_with_mimes: Vec<(String, String)> = Vec::new();

    for file_path_str in try!(get_file_list(&path)) {
        let (path, content, mime) = {
            let path = Path::new(path.as_ref()).join(&file_path_str);
            // Some files have insufficient permissions (like .lock file created by cargo in
            // documentation directory). We are skipping this files.
            let mut file = match File::open(path) {
                Ok(f) => f,
                Err(_) => continue,
            };
            let mut content: Vec<u8> = Vec::new();
            try!(file.read_to_end(&mut content));
            let mime = {
                let mime = try!(cookie.buffer(&content));
                // css's are causing some problem in browsers
                // magic will return text/plain for css file types
                // convert them to text/css
                // do the same for javascript files
                if mime == "text/plain" {
                    if file_path_str.ends_with(".css") {
                        "text/css".to_owned()
                    } else if file_path_str.ends_with(".js") {
                        "application/javascript".to_owned()
                    } else {
                        mime.to_owned()
                    }
                } else {
                    mime.to_owned()
                }
            };

            file_list_with_mimes.push((mime.clone(), file_path_str.clone()));

            (file_path(prefix, &file_path_str), content, mime)
        };

        // check if file already exists in database
        let rows = try!(conn.query("SELECT COUNT(*) FROM files WHERE path = $1", &[&path]));

        if rows.get(0).get::<usize, i64>(0) == 0 {
            try!(trans.query("INSERT INTO files (path, mime, content) VALUES ($1, $2, $3)",
                             &[&path, &mime, &content]));
        } else {
            try!(trans.query("UPDATE files SET mime = $2, content = $3, date_updated = NOW() \
                              WHERE path = $1",
                             &[&path, &mime, &content]));
        }
    }

    try!(trans.commit());

    file_list_to_json(file_list_with_mimes)
}

fn file_list_to_json(file_list: Vec<(String, String)>) -> Result<Json> {

    let mut file_list_json: Vec<Json> = Vec::new();

    for file in file_list {
        let mut v: Vec<String> = Vec::new();
        v.push(file.0.clone());
        v.push(file.1.clone());
        file_list_json.push(v.to_json());
    }

    Ok(file_list_json.to_json())
}

#[cfg(test)]
mod test {
    extern crate env_logger;
    use std::env;
    use super::{get_file_list, add_path_into_database};
    use super::super::connect_db;

    #[test]
    fn test_get_file_list() {
        let _ = env_logger::init();

        let files = get_file_list(env::current_dir().unwrap());
        assert!(files.is_ok());
        assert!(files.unwrap().len() > 0);

        let files = get_file_list(env::current_dir().unwrap().join("Cargo.toml")).unwrap();
        assert_eq!(files[0], "Cargo.toml");
    }

    #[test]
    #[ignore]
    fn test_add_path_into_database() {
        let _ = env_logger::init();

        let conn = connect_db().unwrap();
        let res = add_path_into_database(&conn, "example", env::current_dir().unwrap().join("src"));
        assert!(res.is_ok());
    }
}
<|endoftext|>


<commit_before>use std::io::{Read, Seek, SeekFrom};
use byteorder::{BigEndian, ByteOrder};
use {HEADER_LEN, Error, Result};

/// A farbfeld decoder
#[derive(Debug)]
pub struct Decoder<R> {
    r: R,

    width: u32,
    height: u32,
}

impl<R: Read + Seek> Decoder<R> {
    /// Create a new decoder from `r` and parse the header.
    /// # Failures
    /// Returns FarbfeldError::FormatError if the magic number does not match `farbfeld`
    pub fn new(mut r: R) -> Result<Decoder<R>> {
        let head = &mut [0; HEADER_LEN as usize];
        r.seek(SeekFrom::Start(0))?;
        r.read_exact(head)?;
        if &head[..8] != b"farbfeld" {
                return Err(Error::FormatError("unexpected magic number".to_string()))
        }

        Ok(Decoder {
            r: r,
            width: BigEndian::read_u32(&head[8..]),
            height: BigEndian::read_u32(&head[12..]),
        })
    }

    /// Returns the `(width, height)` of the image.
    pub fn dimensions(&self) -> (u32, u32) {
        (self.width, self.height)
    }

    /// Returns the length in bytes for a row.
    pub fn row_len(&self) -> usize {
        self.width as usize * 8
    }

    /// Read a single row from the image and return the bytes read.
    /// # Failures
    /// Returns a `FarbfeldError::ImageEnd` if the `row` is greater as the `height`
    pub fn read_row(&mut self, row: u32,  buf: &mut [u8]) -> Result<usize> {
        if row > self.height { return Err(Error::ImageEnd) }

        let row_len = self.row_len();
        let offset = HEADER_LEN + row as u64 * row_len as u64;
        self.r.seek(SeekFrom::Start(offset))?;
        self.r.read_exact(&mut buf[..row_len])?;
        Ok(row_len)
    }

    /// Read whole image into a `Vec<u8>`.
    pub fn read_image(&mut self) -> Result<Vec<u8>> {
        self.r.seek(SeekFrom::Start(HEADER_LEN))?;
        let num_raw_bytes = self.height as usize * self.row_len();
        let mut buf = vec![0; num_raw_bytes];
        self.r.read_exact(&mut buf)?;
        Ok(buf)
    }
}

#[cfg(test)]
mod tests {
    use std::io::{Cursor, ErrorKind, Write};
    use Decoder;
    use tests::IMAGE_DATA;
    use Error;

    macro_rules! try_panic {
        ($e:expr) => {
            match $e {
                Err(e) => panic!(e),
                Ok(res) => res,
            }
        }
    }

    #[test]
    fn invalid_magic() {
        let mut img_data = Vec::new();
        try_panic!(img_data.write(b"test fail"));
        try_panic!(img_data.write(&IMAGE_DATA[8..]));
        let buf = Cursor::new(img_data);

        match Decoder::new(buf) {
            Err(e) => match e {
                Error::FormatError(_) => return,
                e => panic!("{:?}", e),
            },
            Ok(_) => panic!("Got Ok expected FarbfeldError::FormatError"),
        }
    }

    #[test]
    fn truncate_header() {
        let buf = Cursor::new(&IMAGE_DATA[0..8]);

        match Decoder::new(buf) {
            Err(Error::IoError(e)) => {
                if e.kind() == ErrorKind::UnexpectedEof {
                    return
                } else { panic!("{:?}", e) }
            },
            Err(e) => panic!("{:?}", e),
            Ok(_) => panic!("Got Ok expected FarbfeldError::FormatError"),
        }

    }

    #[test]
    fn truncate_data() {
        let mut img_data = Vec::with_capacity(IMAGE_DATA.len()-1);
        try_panic!(img_data.write_all(&IMAGE_DATA[..IMAGE_DATA.len()-1]));
        let buf = Cursor::new(img_data);
        let mut img = Decoder::new(buf).unwrap();
        match img.read_image() {
            Err(Error::IoError(e)) => {
                if e.kind() == ErrorKind::UnexpectedEof {
                    return
                } else { panic!("{:?}", e) }
            },
            Err(e) => panic!("{:?}", e),
            Ok(_) => panic!("Got Ok expected FarbfeldError::FormatError"),
        }
    }
}
<commit_msg>call unwrap() instead of using a custom macro<commit_after>use std::io::{Read, Seek, SeekFrom};
use byteorder::{BigEndian, ByteOrder};
use {HEADER_LEN, Error, Result};

/// A farbfeld decoder
#[derive(Debug)]
pub struct Decoder<R> {
    r: R,

    width: u32,
    height: u32,
}

impl<R: Read + Seek> Decoder<R> {
    /// Create a new decoder from `r` and parse the header.
    /// # Failures
    /// Returns FarbfeldError::FormatError if the magic number does not match `farbfeld`
    pub fn new(mut r: R) -> Result<Decoder<R>> {
        let head = &mut [0; HEADER_LEN as usize];
        r.seek(SeekFrom::Start(0))?;
        r.read_exact(head)?;
        if &head[..8] != b"farbfeld" {
                return Err(Error::FormatError("unexpected magic number".to_string()))
        }

        Ok(Decoder {
            r: r,
            width: BigEndian::read_u32(&head[8..]),
            height: BigEndian::read_u32(&head[12..]),
        })
    }

    /// Returns the `(width, height)` of the image.
    pub fn dimensions(&self) -> (u32, u32) {
        (self.width, self.height)
    }

    /// Returns the length in bytes for a row.
    pub fn row_len(&self) -> usize {
        self.width as usize * 8
    }

    /// Read a single row from the image and return the bytes read.
    /// # Failures
    /// Returns a `FarbfeldError::ImageEnd` if the `row` is greater as the `height`
    pub fn read_row(&mut self, row: u32,  buf: &mut [u8]) -> Result<usize> {
        if row > self.height { return Err(Error::ImageEnd) }

        let row_len = self.row_len();
        let offset = HEADER_LEN + row as u64 * row_len as u64;
        self.r.seek(SeekFrom::Start(offset))?;
        self.r.read_exact(&mut buf[..row_len])?;
        Ok(row_len)
    }

    /// Read whole image into a `Vec<u8>`.
    pub fn read_image(&mut self) -> Result<Vec<u8>> {
        self.r.seek(SeekFrom::Start(HEADER_LEN))?;
        let num_raw_bytes = self.height as usize * self.row_len();
        let mut buf = vec![0; num_raw_bytes];
        self.r.read_exact(&mut buf)?;
        Ok(buf)
    }
}

#[cfg(test)]
mod tests {
    use std::io::{Cursor, ErrorKind, Write};
    use Decoder;
    use tests::IMAGE_DATA;
    use Error;

    #[test]
    fn invalid_magic() {
        let mut img_data = Vec::new();
        img_data.write(b"test fail").unwrap();
        img_data.write(&IMAGE_DATA[8..]).unwrap();
        let buf = Cursor::new(img_data);

        match Decoder::new(buf) {
            Err(e) => match e {
                Error::FormatError(_) => return,
                e => panic!("{:?}", e),
            },
            Ok(_) => panic!("Got Ok expected FarbfeldError::FormatError"),
        }
    }

    #[test]
    fn truncate_header() {
        let buf = Cursor::new(&IMAGE_DATA[0..8]);

        match Decoder::new(buf) {
            Err(Error::IoError(e)) => {
                if e.kind() == ErrorKind::UnexpectedEof {
                    return
                } else { panic!("{:?}", e) }
            },
            Err(e) => panic!("{:?}", e),
            Ok(_) => panic!("Got Ok expected FarbfeldError::FormatError"),
        }

    }

    #[test]
    fn truncate_data() {
        let mut img_data = Vec::with_capacity(IMAGE_DATA.len()-1);
        img_data.write_all(&IMAGE_DATA[..IMAGE_DATA.len()-1]).unwrap();
        let buf = Cursor::new(img_data);
        let mut img = Decoder::new(buf).unwrap();
        match img.read_image() {
            Err(Error::IoError(e)) => {
                if e.kind() == ErrorKind::UnexpectedEof {
                    return
                } else { panic!("{:?}", e) }
            },
            Err(e) => panic!("{:?}", e),
            Ok(_) => panic!("Got Ok expected FarbfeldError::FormatError"),
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2015 The tiny-http Contributors
// Copyright 2015 The rust-chunked-transfer Contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//	http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::error::Error;
use std::fmt;
use std::io::Error as IoError;
use std::io::ErrorKind;
use std::io::Read;
use std::io::Result as IoResult;

/// Reads HTTP chunks and sends back real data.
///
/// # Example
///
/// ```
/// use chunked_transfer::Decoder;
/// use std::io::Read;
///
/// let encoded = b"3\r\nhel\r\nb\r\nlo world!!!\r\n0\r\n\r\n";
/// let mut decoded = String::new();
///
/// let mut decoder = Decoder::new(encoded as &[u8]);
/// decoder.read_to_string(&mut decoded);
///
/// assert_eq!(decoded, "hello world!!!");
/// ```
pub struct Decoder<R> {
    // where the chunks come from
    source: R,

    // remaining size of the chunk being read
    // none if we are not in a chunk
    remaining_chunks_size: Option<usize>,
}

impl<R> Decoder<R>
where
    R: Read,
{
    pub fn new(source: R) -> Decoder<R> {
        Decoder {
            source,
            remaining_chunks_size: None,
        }
    }

	/// Returns the remaining bytes left in the chunk being read.
	pub fn remaining_chunks_size(&self) -> Option<usize> {
		self.remaining_chunks_size
	}

    /// Unwraps the Decoder into its inner `Read` source.
    pub fn into_inner(self) -> R {
        self.source
    }

    fn read_chunk_size(&mut self) -> IoResult<usize> {
        let mut chunk_size_bytes = Vec::new();
        let mut has_ext = false;

        loop {
            let byte = match self.source.by_ref().bytes().next() {
                Some(b) => b?,
                None => return Err(IoError::new(ErrorKind::InvalidInput, DecoderError)),
            };

            if byte == b'\r' {
                break;
            }

            if byte == b';' {
                has_ext = true;
                break;
            }

            chunk_size_bytes.push(byte);
        }

        // Ignore extensions for now
        if has_ext {
            loop {
                let byte = match self.source.by_ref().bytes().next() {
                    Some(b) => b?,
                    None => return Err(IoError::new(ErrorKind::InvalidInput, DecoderError)),
                };
                if byte == b'\r' {
                    break;
                }
            }
        }

        self.read_line_feed()?;

        let chunk_size = String::from_utf8(chunk_size_bytes)
            .ok()
            .and_then(|c| usize::from_str_radix(c.trim(), 16).ok())
            .ok_or(IoError::new(ErrorKind::InvalidInput, DecoderError))?;

        Ok(chunk_size)
    }

    fn read_carriage_return(&mut self) -> IoResult<()> {
        match self.source.by_ref().bytes().next() {
            Some(Ok(b'\r')) => Ok(()),
            _ => Err(IoError::new(ErrorKind::InvalidInput, DecoderError)),
        }
    }

    fn read_line_feed(&mut self) -> IoResult<()> {
        match self.source.by_ref().bytes().next() {
            Some(Ok(b'\n')) => Ok(()),
            _ => Err(IoError::new(ErrorKind::InvalidInput, DecoderError)),
        }
    }
}

impl<R> Read for Decoder<R>
where
    R: Read,
{
    fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> {
        let remaining_chunks_size = match self.remaining_chunks_size {
            Some(c) => c,
            None => {
                // first possibility: we are not in a chunk, so we'll attempt to determine
                // the chunks size
                let chunk_size = self.read_chunk_size()?;

                // if the chunk size is 0, we are at EOF
                if chunk_size == 0 {
                    self.read_carriage_return()?;
                    self.read_line_feed()?;
                    return Ok(0);
                }

                chunk_size
            }
        };

        // second possibility: we continue reading from a chunk
        if buf.len() < remaining_chunks_size {
            let read = self.source.read(buf)?;
            self.remaining_chunks_size = Some(remaining_chunks_size - read);
            return Ok(read);
        }

        // third possibility: the read request goes further than the current chunk
        // we simply read until the end of the chunk and return
        assert!(buf.len() >= remaining_chunks_size);

        let buf = &mut buf[..remaining_chunks_size];
        let read = self.source.read(buf)?;

        self.remaining_chunks_size = if read == remaining_chunks_size {
            self.read_carriage_return()?;
            self.read_line_feed()?;
            None
        } else {
            Some(remaining_chunks_size - read)
        };

        Ok(read)
    }
}

#[derive(Debug, Copy, Clone)]
struct DecoderError;

impl fmt::Display for DecoderError {
    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
        write!(fmt, "Error while decoding chunks")
    }
}

impl Error for DecoderError {
    fn description(&self) -> &str {
        "Error while decoding chunks"
    }
}

#[cfg(test)]
mod test {
    use super::Decoder;
    use std::io;
    use std::io::Read;

    /// This unit test is taken from from Hyper
    /// https://github.com/hyperium/hyper
    /// Copyright (c) 2014 Sean McArthur
    #[test]
    fn test_read_chunk_size() {
        fn read(s: &str, expected: usize) {
            let mut decoded = Decoder::new(s.as_bytes());
            let actual = decoded.read_chunk_size().unwrap();
            assert_eq!(expected, actual);
        }

        fn read_err(s: &str) {
            let mut decoded = Decoder::new(s.as_bytes());
            let err_kind = decoded.read_chunk_size().unwrap_err().kind();
            assert_eq!(err_kind, io::ErrorKind::InvalidInput);
        }

        read("1\r\n", 1);
        read("01\r\n", 1);
        read("0\r\n", 0);
        read("00\r\n", 0);
        read("A\r\n", 10);
        read("a\r\n", 10);
        read("Ff\r\n", 255);
        read("Ff   \r\n", 255);
        // Missing LF or CRLF
        read_err("F\rF");
        read_err("F");
        // Invalid hex digit
        read_err("X\r\n");
        read_err("1X\r\n");
        read_err("-\r\n");
        read_err("-1\r\n");
        // Acceptable (if not fully valid) extensions do not influence the size
        read("1;extension\r\n", 1);
        read("a;ext name=value\r\n", 10);
        read("1;extension;extension2\r\n", 1);
        read("1;;;  ;\r\n", 1);
        read("2; extension...\r\n", 2);
        read("3   ; extension=123\r\n", 3);
        read("3   ;\r\n", 3);
        read("3   ;   \r\n", 3);
        // Invalid extensions cause an error
        read_err("1 invalid extension\r\n");
        read_err("1 A\r\n");
        read_err("1;no CRLF");
    }

    #[test]
    fn test_valid_chunk_decode() {
        let source = io::Cursor::new(
            "3\r\nhel\r\nb\r\nlo world!!!\r\n0\r\n\r\n"
                .to_string()
                .into_bytes(),
        );
        let mut decoded = Decoder::new(source);

        let mut string = String::new();
        decoded.read_to_string(&mut string).unwrap();

        assert_eq!(string, "hello world!!!");
    }

    #[test]
    fn test_decode_zero_length() {
        let mut decoder = Decoder::new(b"0\r\n\r\n" as &[u8]);

        let mut decoded = String::new();
        decoder.read_to_string(&mut decoded).unwrap();

        assert_eq!(decoded, "");
    }

    #[test]
    fn test_decode_invalid_chunk_length() {
        let mut decoder = Decoder::new(b"m\r\n\r\n" as &[u8]);

        let mut decoded = String::new();
        assert!(decoder.read_to_string(&mut decoded).is_err());
    }

    #[test]
    fn invalid_input1() {
        let source = io::Cursor::new(
            "2\r\nhel\r\nb\r\nlo world!!!\r\n0\r\n"
                .to_string()
                .into_bytes(),
        );
        let mut decoded = Decoder::new(source);

        let mut string = String::new();
        assert!(decoded.read_to_string(&mut string).is_err());
    }

    #[test]
    fn invalid_input2() {
        let source = io::Cursor::new(
            "3\rhel\r\nb\r\nlo world!!!\r\n0\r\n"
                .to_string()
                .into_bytes(),
        );
        let mut decoded = Decoder::new(source);

        let mut string = String::new();
        assert!(decoded.read_to_string(&mut string).is_err());
    }
}
<commit_msg>Tabs to spaces<commit_after>// Copyright 2015 The tiny-http Contributors
// Copyright 2015 The rust-chunked-transfer Contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//	http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::error::Error;
use std::fmt;
use std::io::Error as IoError;
use std::io::ErrorKind;
use std::io::Read;
use std::io::Result as IoResult;

/// Reads HTTP chunks and sends back real data.
///
/// # Example
///
/// ```
/// use chunked_transfer::Decoder;
/// use std::io::Read;
///
/// let encoded = b"3\r\nhel\r\nb\r\nlo world!!!\r\n0\r\n\r\n";
/// let mut decoded = String::new();
///
/// let mut decoder = Decoder::new(encoded as &[u8]);
/// decoder.read_to_string(&mut decoded);
///
/// assert_eq!(decoded, "hello world!!!");
/// ```
pub struct Decoder<R> {
    // where the chunks come from
    source: R,

    // remaining size of the chunk being read
    // none if we are not in a chunk
    remaining_chunks_size: Option<usize>,
}

impl<R> Decoder<R>
where
    R: Read,
{
    pub fn new(source: R) -> Decoder<R> {
        Decoder {
            source,
            remaining_chunks_size: None,
        }
    }

    /// Returns the remaining bytes left in the chunk being read.
    pub fn remaining_chunks_size(&self) -> Option<usize> {
        self.remaining_chunks_size
    }

    /// Unwraps the Decoder into its inner `Read` source.
    pub fn into_inner(self) -> R {
        self.source
    }

    fn read_chunk_size(&mut self) -> IoResult<usize> {
        let mut chunk_size_bytes = Vec::new();
        let mut has_ext = false;

        loop {
            let byte = match self.source.by_ref().bytes().next() {
                Some(b) => b?,
                None => return Err(IoError::new(ErrorKind::InvalidInput, DecoderError)),
            };

            if byte == b'\r' {
                break;
            }

            if byte == b';' {
                has_ext = true;
                break;
            }

            chunk_size_bytes.push(byte);
        }

        // Ignore extensions for now
        if has_ext {
            loop {
                let byte = match self.source.by_ref().bytes().next() {
                    Some(b) => b?,
                    None => return Err(IoError::new(ErrorKind::InvalidInput, DecoderError)),
                };
                if byte == b'\r' {
                    break;
                }
            }
        }

        self.read_line_feed()?;

        let chunk_size = String::from_utf8(chunk_size_bytes)
            .ok()
            .and_then(|c| usize::from_str_radix(c.trim(), 16).ok())
            .ok_or(IoError::new(ErrorKind::InvalidInput, DecoderError))?;

        Ok(chunk_size)
    }

    fn read_carriage_return(&mut self) -> IoResult<()> {
        match self.source.by_ref().bytes().next() {
            Some(Ok(b'\r')) => Ok(()),
            _ => Err(IoError::new(ErrorKind::InvalidInput, DecoderError)),
        }
    }

    fn read_line_feed(&mut self) -> IoResult<()> {
        match self.source.by_ref().bytes().next() {
            Some(Ok(b'\n')) => Ok(()),
            _ => Err(IoError::new(ErrorKind::InvalidInput, DecoderError)),
        }
    }
}

impl<R> Read for Decoder<R>
where
    R: Read,
{
    fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> {
        let remaining_chunks_size = match self.remaining_chunks_size {
            Some(c) => c,
            None => {
                // first possibility: we are not in a chunk, so we'll attempt to determine
                // the chunks size
                let chunk_size = self.read_chunk_size()?;

                // if the chunk size is 0, we are at EOF
                if chunk_size == 0 {
                    self.read_carriage_return()?;
                    self.read_line_feed()?;
                    return Ok(0);
                }

                chunk_size
            }
        };

        // second possibility: we continue reading from a chunk
        if buf.len() < remaining_chunks_size {
            let read = self.source.read(buf)?;
            self.remaining_chunks_size = Some(remaining_chunks_size - read);
            return Ok(read);
        }

        // third possibility: the read request goes further than the current chunk
        // we simply read until the end of the chunk and return
        assert!(buf.len() >= remaining_chunks_size);

        let buf = &mut buf[..remaining_chunks_size];
        let read = self.source.read(buf)?;

        self.remaining_chunks_size = if read == remaining_chunks_size {
            self.read_carriage_return()?;
            self.read_line_feed()?;
            None
        } else {
            Some(remaining_chunks_size - read)
        };

        Ok(read)
    }
}

#[derive(Debug, Copy, Clone)]
struct DecoderError;

impl fmt::Display for DecoderError {
    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
        write!(fmt, "Error while decoding chunks")
    }
}

impl Error for DecoderError {
    fn description(&self) -> &str {
        "Error while decoding chunks"
    }
}

#[cfg(test)]
mod test {
    use super::Decoder;
    use std::io;
    use std::io::Read;

    /// This unit test is taken from from Hyper
    /// https://github.com/hyperium/hyper
    /// Copyright (c) 2014 Sean McArthur
    #[test]
    fn test_read_chunk_size() {
        fn read(s: &str, expected: usize) {
            let mut decoded = Decoder::new(s.as_bytes());
            let actual = decoded.read_chunk_size().unwrap();
            assert_eq!(expected, actual);
        }

        fn read_err(s: &str) {
            let mut decoded = Decoder::new(s.as_bytes());
            let err_kind = decoded.read_chunk_size().unwrap_err().kind();
            assert_eq!(err_kind, io::ErrorKind::InvalidInput);
        }

        read("1\r\n", 1);
        read("01\r\n", 1);
        read("0\r\n", 0);
        read("00\r\n", 0);
        read("A\r\n", 10);
        read("a\r\n", 10);
        read("Ff\r\n", 255);
        read("Ff   \r\n", 255);
        // Missing LF or CRLF
        read_err("F\rF");
        read_err("F");
        // Invalid hex digit
        read_err("X\r\n");
        read_err("1X\r\n");
        read_err("-\r\n");
        read_err("-1\r\n");
        // Acceptable (if not fully valid) extensions do not influence the size
        read("1;extension\r\n", 1);
        read("a;ext name=value\r\n", 10);
        read("1;extension;extension2\r\n", 1);
        read("1;;;  ;\r\n", 1);
        read("2; extension...\r\n", 2);
        read("3   ; extension=123\r\n", 3);
        read("3   ;\r\n", 3);
        read("3   ;   \r\n", 3);
        // Invalid extensions cause an error
        read_err("1 invalid extension\r\n");
        read_err("1 A\r\n");
        read_err("1;no CRLF");
    }

    #[test]
    fn test_valid_chunk_decode() {
        let source = io::Cursor::new(
            "3\r\nhel\r\nb\r\nlo world!!!\r\n0\r\n\r\n"
                .to_string()
                .into_bytes(),
        );
        let mut decoded = Decoder::new(source);

        let mut string = String::new();
        decoded.read_to_string(&mut string).unwrap();

        assert_eq!(string, "hello world!!!");
    }

    #[test]
    fn test_decode_zero_length() {
        let mut decoder = Decoder::new(b"0\r\n\r\n" as &[u8]);

        let mut decoded = String::new();
        decoder.read_to_string(&mut decoded).unwrap();

        assert_eq!(decoded, "");
    }

    #[test]
    fn test_decode_invalid_chunk_length() {
        let mut decoder = Decoder::new(b"m\r\n\r\n" as &[u8]);

        let mut decoded = String::new();
        assert!(decoder.read_to_string(&mut decoded).is_err());
    }

    #[test]
    fn invalid_input1() {
        let source = io::Cursor::new(
            "2\r\nhel\r\nb\r\nlo world!!!\r\n0\r\n"
                .to_string()
                .into_bytes(),
        );
        let mut decoded = Decoder::new(source);

        let mut string = String::new();
        assert!(decoded.read_to_string(&mut string).is_err());
    }

    #[test]
    fn invalid_input2() {
        let source = io::Cursor::new(
            "3\rhel\r\nb\r\nlo world!!!\r\n0\r\n"
                .to_string()
                .into_bytes(),
        );
        let mut decoded = Decoder::new(source);

        let mut string = String::new();
        assert!(decoded.read_to_string(&mut string).is_err());
    }
}
<|endoftext|>


<commit_before>use proc_macro2::{Ident, Span, TokenStream};
use std::fmt::Display;
use syn::{
    parse::{Error, Result},
    spanned::Spanned,
    Attribute, Data, DeriveInput, Fields, Lit, Meta, MetaNameValue, NestedMeta,
};

/// Provides the hook to expand `#[derive(Display)]` into an implementation of `From`
pub fn expand(input: &DeriveInput, trait_name: &str) -> Result<TokenStream> {
    let trait_ident = Ident::new(trait_name, Span::call_site());
    let trait_path = &quote!(::std::fmt::#trait_ident);
    let trait_attr = match trait_name {
        "Display" => "display",
        "Binary" => "binary",
        "Octal" => "octal",
        "LowerHex" => "lower_hex",
        "UpperHex" => "upper_hex",
        "LowerExp" => "lower_exp",
        "UpperExp" => "upper_exp",
        "Pointer" => "pointer",
        _ => unimplemented!(),
    };

    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
    let name = &input.ident;

    let arms = State {
        trait_path,
        trait_attr,
        input,
    }
    .get_match_arms()?;

    Ok(quote! {
        impl #impl_generics #trait_path for #name #ty_generics #where_clause
        {
            #[allow(unused_variables)]
            #[inline]
            fn fmt(&self, _derive_more_Display_formatter: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
                match self {
                    #arms
                    _ => Ok(()) // This is needed for empty enums
                }
            }
        }
    })
}

struct State<'a, 'b> {
    trait_path: &'b TokenStream,
    trait_attr: &'static str,
    input: &'a DeriveInput,
}

impl<'a, 'b> State<'a, 'b> {
    fn get_proper_syntax(&self) -> impl Display {
        format!(
            r#"Proper syntax: #[{}(fmt = "My format", "arg1", "arg2")]"#,
            self.trait_attr
        )
    }
    fn get_matcher(&self, fields: &Fields) -> TokenStream {
        match fields {
            Fields::Unit => TokenStream::new(),
            Fields::Unnamed(fields) => {
                let fields: TokenStream = (0..fields.unnamed.len())
                    .map(|n| {
                        let i = Ident::new(&format!("_{}", n), Span::call_site());
                        quote!(#i,)
                    })
                    .collect();
                quote!((#fields))
            }
            Fields::Named(fields) => {
                let fields: TokenStream = fields
                    .named
                    .iter()
                    .map(|f| {
                        let i = f.ident.as_ref().unwrap();
                        quote!(#i,)
                    })
                    .collect();
                quote!({#fields})
            }
        }
    }
    fn find_meta(&self, attrs: &[Attribute]) -> Result<Option<Meta>> {
        let mut it = attrs
            .iter()
            .filter_map(|a| a.interpret_meta())
            .filter(|m| m.name() == self.trait_attr);

        let meta = it.next();
        if it.next().is_some() {
            Err(Error::new(meta.span(), "Too many formats given"))
        } else {
            Ok(meta)
        }
    }
    fn get_meta_fmt(&self, meta: Meta) -> Result<TokenStream> {
        let list = match &meta {
            Meta::List(list) => list,
            _ => return Err(Error::new(meta.span(), self.get_proper_syntax())),
        };

        let fmt = match &list.nested[0] {
            NestedMeta::Meta(Meta::NameValue(MetaNameValue {
                ident,
                lit: Lit::Str(s),
                ..
            }))
                if ident == "fmt" =>
            {
                s
            }
            _ => return Err(Error::new(list.nested[0].span(), self.get_proper_syntax())),
        };

        let args = list
            .nested
            .iter()
            .skip(1) // skip fmt = "..."
            .try_fold(TokenStream::new(), |args, arg| {
                let arg = match arg {
                    NestedMeta::Literal(Lit::Str(s)) => s,
                    NestedMeta::Meta(Meta::Word(i)) => {
                        return Ok(quote_spanned!(list.span()=> #args #i,))
                    }
                    _ => return Err(Error::new(arg.span(), self.get_proper_syntax())),
                };
                let arg: TokenStream = match arg.parse() {
                    Ok(arg) => arg,
                    Err(e) => return Err(Error::new(arg.span(), e)),
                };
                Ok(quote_spanned!(list.span()=> #args #arg,))
            })?;

        Ok(quote_spanned!(meta.span()=> write!(_derive_more_Display_formatter, #fmt, #args)))
    }
    fn infer_fmt(&self, fields: &Fields, name: &Ident) -> Result<TokenStream> {
        let fields = match fields {
            Fields::Unit => {
                return Ok(quote!(write!(
                    _derive_more_Display_formatter,
                    stringify!(#name)
                )));
            }
            Fields::Named(fields) => &fields.named,
            Fields::Unnamed(fields) => &fields.unnamed,
        };
        if fields.len() == 0 {
            return Ok(quote!(write!(
                _derive_more_Display_formatter,
                stringify!(#name)
            )));
        } else if fields.len() > 1 {
            return Err(Error::new(
                fields.span(),
                "Can not automatically infer format for types with more than 1 field",
            ));
        }

        let trait_path = self.trait_path;
        if let Some(ident) = &fields.iter().next().as_ref().unwrap().ident {
            Ok(quote!(#trait_path::fmt(#ident, _derive_more_Display_formatter)))
        } else {
            Ok(quote!(#trait_path::fmt(_0, _derive_more_Display_formatter)))
        }
    }
    fn get_match_arms(&self) -> Result<TokenStream> {
        match &self.input.data {
            Data::Enum(e) => {
                if let Some(meta) = self.find_meta(&self.input.attrs)? {
                    let fmt = self.get_meta_fmt(meta)?;
                    e.variants.iter().try_for_each(|v| {
                        if let Some(meta) = self.find_meta(&v.attrs)? {
                            Err(Error::new(
                                meta.span(),
                                "Can not have a format on the variant when the whole enum has one",
                            ))
                        } else {
                            Ok(())
                        }
                    })?;
                    Ok(quote_spanned!(self.input.span()=> _ => #fmt,))
                } else {
                    e.variants.iter().try_fold(TokenStream::new(), |arms, v| {
                        let matcher = self.get_matcher(&v.fields);
                        let fmt = if let Some(meta) = self.find_meta(&v.attrs)? {
                            self.get_meta_fmt(meta)?
                        } else {
                            self.infer_fmt(&v.fields, &v.ident)?
                        };
                        let name = &self.input.ident;
                        let v_name = &v.ident;
                        Ok(quote_spanned!(self.input.span()=> #arms #name::#v_name #matcher => #fmt,))
                    })
                }
            }
            Data::Struct(s) => {
                let matcher = self.get_matcher(&s.fields);
                let fmt = if let Some(meta) = self.find_meta(&self.input.attrs)? {
                    self.get_meta_fmt(meta)?
                } else {
                    self.infer_fmt(&s.fields, &self.input.ident)?
                };
                let name = &self.input.ident;
                Ok(quote_spanned!(self.input.span()=> #name #matcher => #fmt,))
            }
            Data::Union(_) => {
                let meta = self.find_meta(&self.input.attrs)?.ok_or(Error::new(
                    self.input.span(),
                    "Can not automatically infer format for unions",
                ))?;
                let fmt = self.get_meta_fmt(meta)?;
                Ok(quote_spanned!(self.input.span()=> _ => #fmt,))
            }
        }
    }
}
<commit_msg>Run clippy<commit_after>use proc_macro2::{Ident, Span, TokenStream};
use std::fmt::Display;
use syn::{
    parse::{Error, Result},
    spanned::Spanned,
    Attribute, Data, DeriveInput, Fields, Lit, Meta, MetaNameValue, NestedMeta,
};

/// Provides the hook to expand `#[derive(Display)]` into an implementation of `From`
pub fn expand(input: &DeriveInput, trait_name: &str) -> Result<TokenStream> {
    let trait_ident = Ident::new(trait_name, Span::call_site());
    let trait_path = &quote!(::std::fmt::#trait_ident);
    let trait_attr = match trait_name {
        "Display" => "display",
        "Binary" => "binary",
        "Octal" => "octal",
        "LowerHex" => "lower_hex",
        "UpperHex" => "upper_hex",
        "LowerExp" => "lower_exp",
        "UpperExp" => "upper_exp",
        "Pointer" => "pointer",
        _ => unimplemented!(),
    };

    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
    let name = &input.ident;

    let arms = State {
        trait_path,
        trait_attr,
        input,
    }
    .get_match_arms()?;

    Ok(quote! {
        impl #impl_generics #trait_path for #name #ty_generics #where_clause
        {
            #[allow(unused_variables)]
            #[inline]
            fn fmt(&self, _derive_more_Display_formatter: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
                match self {
                    #arms
                    _ => Ok(()) // This is needed for empty enums
                }
            }
        }
    })
}

struct State<'a, 'b> {
    trait_path: &'b TokenStream,
    trait_attr: &'static str,
    input: &'a DeriveInput,
}

impl<'a, 'b> State<'a, 'b> {
    fn get_proper_syntax(&self) -> impl Display {
        format!(
            r#"Proper syntax: #[{}(fmt = "My format", "arg1", "arg2")]"#,
            self.trait_attr
        )
    }
    fn get_matcher(&self, fields: &Fields) -> TokenStream {
        match fields {
            Fields::Unit => TokenStream::new(),
            Fields::Unnamed(fields) => {
                let fields: TokenStream = (0..fields.unnamed.len())
                    .map(|n| {
                        let i = Ident::new(&format!("_{}", n), Span::call_site());
                        quote!(#i,)
                    })
                    .collect();
                quote!((#fields))
            }
            Fields::Named(fields) => {
                let fields: TokenStream = fields
                    .named
                    .iter()
                    .map(|f| {
                        let i = f.ident.as_ref().unwrap();
                        quote!(#i,)
                    })
                    .collect();
                quote!({#fields})
            }
        }
    }
    fn find_meta(&self, attrs: &[Attribute]) -> Result<Option<Meta>> {
        let mut it = attrs
            .iter()
            .filter_map(|a| a.interpret_meta())
            .filter(|m| m.name() == self.trait_attr);

        let meta = it.next();
        if it.next().is_some() {
            Err(Error::new(meta.span(), "Too many formats given"))
        } else {
            Ok(meta)
        }
    }
    fn get_meta_fmt(&self, meta: Meta) -> Result<TokenStream> {
        let list = match &meta {
            Meta::List(list) => list,
            _ => return Err(Error::new(meta.span(), self.get_proper_syntax())),
        };

        let fmt = match &list.nested[0] {
            NestedMeta::Meta(Meta::NameValue(MetaNameValue {
                ident,
                lit: Lit::Str(s),
                ..
            }))
                if ident == "fmt" =>
            {
                s
            }
            _ => return Err(Error::new(list.nested[0].span(), self.get_proper_syntax())),
        };

        let args = list
            .nested
            .iter()
            .skip(1) // skip fmt = "..."
            .try_fold(TokenStream::new(), |args, arg| {
                let arg = match arg {
                    NestedMeta::Literal(Lit::Str(s)) => s,
                    NestedMeta::Meta(Meta::Word(i)) => {
                        return Ok(quote_spanned!(list.span()=> #args #i,))
                    }
                    _ => return Err(Error::new(arg.span(), self.get_proper_syntax())),
                };
                let arg: TokenStream = match arg.parse() {
                    Ok(arg) => arg,
                    Err(e) => return Err(Error::new(arg.span(), e)),
                };
                Ok(quote_spanned!(list.span()=> #args #arg,))
            })?;

        Ok(quote_spanned!(meta.span()=> write!(_derive_more_Display_formatter, #fmt, #args)))
    }
    fn infer_fmt(&self, fields: &Fields, name: &Ident) -> Result<TokenStream> {
        let fields = match fields {
            Fields::Unit => {
                return Ok(quote!(write!(
                    _derive_more_Display_formatter,
                    stringify!(#name)
                )));
            }
            Fields::Named(fields) => &fields.named,
            Fields::Unnamed(fields) => &fields.unnamed,
        };
        if fields.is_empty() {
            return Ok(quote!(write!(
                _derive_more_Display_formatter,
                stringify!(#name)
            )));
        } else if fields.len() > 1 {
            return Err(Error::new(
                fields.span(),
                "Can not automatically infer format for types with more than 1 field",
            ));
        }

        let trait_path = self.trait_path;
        if let Some(ident) = &fields.iter().next().as_ref().unwrap().ident {
            Ok(quote!(#trait_path::fmt(#ident, _derive_more_Display_formatter)))
        } else {
            Ok(quote!(#trait_path::fmt(_0, _derive_more_Display_formatter)))
        }
    }
    fn get_match_arms(&self) -> Result<TokenStream> {
        match &self.input.data {
            Data::Enum(e) => {
                if let Some(meta) = self.find_meta(&self.input.attrs)? {
                    let fmt = self.get_meta_fmt(meta)?;
                    e.variants.iter().try_for_each(|v| {
                        if let Some(meta) = self.find_meta(&v.attrs)? {
                            Err(Error::new(
                                meta.span(),
                                "Can not have a format on the variant when the whole enum has one",
                            ))
                        } else {
                            Ok(())
                        }
                    })?;
                    Ok(quote_spanned!(self.input.span()=> _ => #fmt,))
                } else {
                    e.variants.iter().try_fold(TokenStream::new(), |arms, v| {
                        let matcher = self.get_matcher(&v.fields);
                        let fmt = if let Some(meta) = self.find_meta(&v.attrs)? {
                            self.get_meta_fmt(meta)?
                        } else {
                            self.infer_fmt(&v.fields, &v.ident)?
                        };
                        let name = &self.input.ident;
                        let v_name = &v.ident;
                        Ok(quote_spanned!(self.input.span()=> #arms #name::#v_name #matcher => #fmt,))
                    })
                }
            }
            Data::Struct(s) => {
                let matcher = self.get_matcher(&s.fields);
                let fmt = if let Some(meta) = self.find_meta(&self.input.attrs)? {
                    self.get_meta_fmt(meta)?
                } else {
                    self.infer_fmt(&s.fields, &self.input.ident)?
                };
                let name = &self.input.ident;
                Ok(quote_spanned!(self.input.span()=> #name #matcher => #fmt,))
            }
            Data::Union(_) => {
                let meta = self.find_meta(&self.input.attrs)?.ok_or_else(|| {
                    Error::new(
                        self.input.span(),
                        "Can not automatically infer format for unions",
                    )
                })?;
                let fmt = self.get_meta_fmt(meta)?;
                Ok(quote_spanned!(self.input.span()=> _ => #fmt,))
            }
        }
    }
}
<|endoftext|>


<commit_before>use core::marker::PhantomData;

use super::address::Address;
use super::{DisplayControlBuilder, EntryModeBuilder, FunctionSetBuilder, Home};
use hal::{Init, ReadMode, Recieve, Send, WriteMode};

const LCD_WIDTH: usize = 16;

bitflags! {
    struct Instructions: u8 {
        const CLEAR_DISPLAY     = 0b0000_0001;
        const RETURN_HOME       = 0b0000_0010;
        const SHIFT             = 0b0001_0000;
    }
}

bitflags! {
    struct ShiftTarget: u8 {
        const CURSOR  = 0b0000_0000;
        const DISPLAY = 0b0000_1000;
    }
}

bitflags! {
    struct ShiftDirection: u8 {
        const RIGHT = 0b0000_0100;
        const LEFT  = 0b0000_0000;
    }
}

enum RamType {
    DisplayData,
    CharacterGenerator,
}

impl From<RamType> for u8 {
    fn from(ram_type: RamType) -> Self {
        match ram_type {
            RamType::DisplayData => 0b1000_0000,
            RamType::CharacterGenerator => 0b0100_0000,
        }
    }
}

/// Enumeration of possible methods to shift a cursor or display.
pub enum ShiftTo {
    /// Shifts to the right by the given offset.
    Right(u8),
    /// Shifts to the left by the given offset.
    Left(u8),
}

impl ShiftTo {
    fn as_offset_and_raw_direction(&self) -> (u8, ShiftDirection) {
        match *self {
            ShiftTo::Right(offset) => (offset, ShiftDirection::RIGHT),
            ShiftTo::Left(offset) => (offset, ShiftDirection::LEFT),
        }
    }
}

/// Enumeration of possible methods to seek within a `Display` object.
pub enum SeekFrom<T: Into<Address>> {
    /// Sets the cursor position to `Home` plus the provided number of bytes.
    Home(u8),
    /// Sets the cursor to the current position plus the specified number of bytes.
    Current(u8),
    /// Sets the cursor position to the given line plus the specified number of bytes.
    Line { line: T, bytes: u8 },
}

/// A HD44780 compliant display.
///
/// It provides a high-level and hardware agnostic interface to controll a HD44780 compliant
/// liquid crystal display (LCD).
pub struct Display<P, U>
where
    U: Into<Address> + Home,
{
    connection: P,
    cursor_address: Address,
    _line_marker: PhantomData<U>,
}

impl<P, U> Display<P, U>
where
    P: Init + Send + Recieve,
    U: Into<Address> + Home,
{
    const FIRST_4BIT_INIT_INSTRUCTION: WriteMode = WriteMode::Command(0x33);
    const SECOND_4BIT_INIT_INSTRUCTION: WriteMode = WriteMode::Command(0x32);

    /// Create a new `Display` using the given connection.
    pub fn new(connection: P) -> Self {
        Display {
            connection: connection,
            cursor_address: Address::from(0),
            _line_marker: PhantomData,
        }
    }

    pub fn init(&self, builder: &FunctionSetBuilder) {
        self.connection.init();

        let cmd = builder.build_command();
        let cmd = WriteMode::Command(cmd);

        self.init_by_instruction(cmd);
    }

    fn init_by_instruction(&self, function_set: WriteMode) {
        self.connection.send(Self::FIRST_4BIT_INIT_INSTRUCTION);
        self.connection.send(Self::SECOND_4BIT_INIT_INSTRUCTION);

        self.connection.send(function_set);

        self.clear();
    }

    /// Sets the entry mode of the display.
    pub fn set_entry_mode(&self, builder: &EntryModeBuilder) {
        let cmd = WriteMode::Command(builder.build_command());
        self.connection.send(cmd);
    }

    /// Sets the display control settings.
    pub fn set_display_control(&self, builder: &DisplayControlBuilder) {
        let cmd = WriteMode::Command(builder.build_command());
        self.connection.send(cmd);
    }

    /// Shifts the cursor to the left or the right by the given offset.
    ///
    /// **Note:** Consider to use [seek()](struct.Display.html#method.seek) for longer distances.
    pub fn shift_cursor(&mut self, direction: ShiftTo) {
        let (offset, raw_direction) = direction.as_offset_and_raw_direction();

        if offset == 0 {
            return;
        }

        match direction {
            ShiftTo::Right(offset) => self.cursor_address += offset.into(),
            ShiftTo::Left(offset) => self.cursor_address -= offset.into()
        }

        self.raw_shift(ShiftTarget::CURSOR, offset, raw_direction);
    }

    /// Shifts the display to the right or the left by the given offset.
    ///
    /// Note that the first and second line will shift at the same time.
    ///
    /// When the displayed data is shifted repeatedly each line moves only horizontally.
    /// The second line display does not shift into the first line position.
    pub fn shift(&self, direction: ShiftTo) {
        let (offset, raw_direction) = direction.as_offset_and_raw_direction();

        self.raw_shift(ShiftTarget::DISPLAY, offset, raw_direction);
    }

    fn raw_shift(&self, shift_type: ShiftTarget, offset: u8, raw_direction: ShiftDirection) {
        let mut cmd = Instructions::SHIFT.bits();

        cmd |= shift_type.bits();
        cmd |= raw_direction.bits();

        for _ in 0..offset {
            self.connection.send(WriteMode::Command(cmd));
        }
    }

    /// Clears the entire display, sets the cursor to the home position and undo all display
    /// shifts.
    ///
    /// It also sets the cursor's move direction to `Increment`.
    pub fn clear(&self) {
        let cmd = Instructions::CLEAR_DISPLAY.bits();
        self.connection.send(WriteMode::Command(cmd));
    }

    fn generic_seek(&mut self, ram_type: RamType, pos: SeekFrom<U>) {
        let mut cmd = ram_type.into();

        let (start, addr) = match pos {
            SeekFrom::Home(bytes) => (U::FIRST_LINE_ADDRESS.into(), bytes.into()),
            SeekFrom::Current(bytes) => (self.cursor_address, bytes.into()),
            SeekFrom::Line { line, bytes } => (line.into(), bytes.into()),
        };

        self.cursor_address = start + addr;

        cmd |= u8::from(self.cursor_address);

        self.connection.send(WriteMode::Command(cmd));
    }

    /// Seeks to an offset in display data RAM.
    pub fn seek(&mut self, pos: SeekFrom<U>) {
        self.generic_seek(RamType::DisplayData, pos);
    }

    /// Seeks to an offset in display character generator RAM.
    pub fn seek_cgram(&mut self, pos: SeekFrom<U>) {
        self.generic_seek(RamType::CharacterGenerator, pos);
    }

    /// Writes the given byte to data or character generator RAM, depending on the previous
    /// seek operation.
    pub fn write(&mut self, c: u8) {
        self.cursor_address += Address::from(1);
        self.connection.send(WriteMode::Data(c));
    }

    /// Reads a single byte from data RAM.
    pub fn read_byte(&mut self) -> u8 {
        self.cursor_address += Address::from(1);
        self.connection.recieve(ReadMode::Data)
    }

    /// Reads busy flag and the cursor's current address.
    pub fn read_busy_flag(&self) -> (bool, u8) {
        let byte = self.connection.recieve(ReadMode::BusyFlag);

        let busy_flag = (byte & 0b1000_0000) != 0;

        let address = byte & 0b0111_1111;

        (busy_flag, address)
    }

    /// Writes the given message to data or character generator RAM, depending on the previous
    /// seek operation.
    pub fn write_message(&mut self, msg: &str) {
        for c in msg.as_bytes().iter().take(LCD_WIDTH) {
            self.write(*c);
        }
    }

    pub fn get_connection(self) -> P {
        self.connection
    }
}
<commit_msg>Fix formating<commit_after>use core::marker::PhantomData;

use super::address::Address;
use super::{DisplayControlBuilder, EntryModeBuilder, FunctionSetBuilder, Home};
use hal::{Init, ReadMode, Recieve, Send, WriteMode};

const LCD_WIDTH: usize = 16;

bitflags! {
    struct Instructions: u8 {
        const CLEAR_DISPLAY     = 0b0000_0001;
        const RETURN_HOME       = 0b0000_0010;
        const SHIFT             = 0b0001_0000;
    }
}

bitflags! {
    struct ShiftTarget: u8 {
        const CURSOR  = 0b0000_0000;
        const DISPLAY = 0b0000_1000;
    }
}

bitflags! {
    struct ShiftDirection: u8 {
        const RIGHT = 0b0000_0100;
        const LEFT  = 0b0000_0000;
    }
}

enum RamType {
    DisplayData,
    CharacterGenerator,
}

impl From<RamType> for u8 {
    fn from(ram_type: RamType) -> Self {
        match ram_type {
            RamType::DisplayData => 0b1000_0000,
            RamType::CharacterGenerator => 0b0100_0000,
        }
    }
}

/// Enumeration of possible methods to shift a cursor or display.
pub enum ShiftTo {
    /// Shifts to the right by the given offset.
    Right(u8),
    /// Shifts to the left by the given offset.
    Left(u8),
}

impl ShiftTo {
    fn as_offset_and_raw_direction(&self) -> (u8, ShiftDirection) {
        match *self {
            ShiftTo::Right(offset) => (offset, ShiftDirection::RIGHT),
            ShiftTo::Left(offset) => (offset, ShiftDirection::LEFT),
        }
    }
}

/// Enumeration of possible methods to seek within a `Display` object.
pub enum SeekFrom<T: Into<Address>> {
    /// Sets the cursor position to `Home` plus the provided number of bytes.
    Home(u8),
    /// Sets the cursor to the current position plus the specified number of bytes.
    Current(u8),
    /// Sets the cursor position to the given line plus the specified number of bytes.
    Line { line: T, bytes: u8 },
}

/// A HD44780 compliant display.
///
/// It provides a high-level and hardware agnostic interface to controll a HD44780 compliant
/// liquid crystal display (LCD).
pub struct Display<P, U>
where
    U: Into<Address> + Home,
{
    connection: P,
    cursor_address: Address,
    _line_marker: PhantomData<U>,
}

impl<P, U> Display<P, U>
where
    P: Init + Send + Recieve,
    U: Into<Address> + Home,
{
    const FIRST_4BIT_INIT_INSTRUCTION: WriteMode = WriteMode::Command(0x33);
    const SECOND_4BIT_INIT_INSTRUCTION: WriteMode = WriteMode::Command(0x32);

    /// Create a new `Display` using the given connection.
    pub fn new(connection: P) -> Self {
        Display {
            connection: connection,
            cursor_address: Address::from(0),
            _line_marker: PhantomData,
        }
    }

    pub fn init(&self, builder: &FunctionSetBuilder) {
        self.connection.init();

        let cmd = builder.build_command();
        let cmd = WriteMode::Command(cmd);

        self.init_by_instruction(cmd);
    }

    fn init_by_instruction(&self, function_set: WriteMode) {
        self.connection.send(Self::FIRST_4BIT_INIT_INSTRUCTION);
        self.connection.send(Self::SECOND_4BIT_INIT_INSTRUCTION);

        self.connection.send(function_set);

        self.clear();
    }

    /// Sets the entry mode of the display.
    pub fn set_entry_mode(&self, builder: &EntryModeBuilder) {
        let cmd = WriteMode::Command(builder.build_command());
        self.connection.send(cmd);
    }

    /// Sets the display control settings.
    pub fn set_display_control(&self, builder: &DisplayControlBuilder) {
        let cmd = WriteMode::Command(builder.build_command());
        self.connection.send(cmd);
    }

    /// Shifts the cursor to the left or the right by the given offset.
    ///
    /// **Note:** Consider to use [seek()](struct.Display.html#method.seek) for longer distances.
    pub fn shift_cursor(&mut self, direction: ShiftTo) {
        let (offset, raw_direction) = direction.as_offset_and_raw_direction();

        if offset == 0 {
            return;
        }

        match direction {
            ShiftTo::Right(offset) => self.cursor_address += offset.into(),
            ShiftTo::Left(offset) => self.cursor_address -= offset.into(),
        }

        self.raw_shift(ShiftTarget::CURSOR, offset, raw_direction);
    }

    /// Shifts the display to the right or the left by the given offset.
    ///
    /// Note that the first and second line will shift at the same time.
    ///
    /// When the displayed data is shifted repeatedly each line moves only horizontally.
    /// The second line display does not shift into the first line position.
    pub fn shift(&self, direction: ShiftTo) {
        let (offset, raw_direction) = direction.as_offset_and_raw_direction();

        self.raw_shift(ShiftTarget::DISPLAY, offset, raw_direction);
    }

    fn raw_shift(&self, shift_type: ShiftTarget, offset: u8, raw_direction: ShiftDirection) {
        let mut cmd = Instructions::SHIFT.bits();

        cmd |= shift_type.bits();
        cmd |= raw_direction.bits();

        for _ in 0..offset {
            self.connection.send(WriteMode::Command(cmd));
        }
    }

    /// Clears the entire display, sets the cursor to the home position and undo all display
    /// shifts.
    ///
    /// It also sets the cursor's move direction to `Increment`.
    pub fn clear(&self) {
        let cmd = Instructions::CLEAR_DISPLAY.bits();
        self.connection.send(WriteMode::Command(cmd));
    }

    fn generic_seek(&mut self, ram_type: RamType, pos: SeekFrom<U>) {
        let mut cmd = ram_type.into();

        let (start, addr) = match pos {
            SeekFrom::Home(bytes) => (U::FIRST_LINE_ADDRESS.into(), bytes.into()),
            SeekFrom::Current(bytes) => (self.cursor_address, bytes.into()),
            SeekFrom::Line { line, bytes } => (line.into(), bytes.into()),
        };

        self.cursor_address = start + addr;

        cmd |= u8::from(self.cursor_address);

        self.connection.send(WriteMode::Command(cmd));
    }

    /// Seeks to an offset in display data RAM.
    pub fn seek(&mut self, pos: SeekFrom<U>) {
        self.generic_seek(RamType::DisplayData, pos);
    }

    /// Seeks to an offset in display character generator RAM.
    pub fn seek_cgram(&mut self, pos: SeekFrom<U>) {
        self.generic_seek(RamType::CharacterGenerator, pos);
    }

    /// Writes the given byte to data or character generator RAM, depending on the previous
    /// seek operation.
    pub fn write(&mut self, c: u8) {
        self.cursor_address += Address::from(1);
        self.connection.send(WriteMode::Data(c));
    }

    /// Reads a single byte from data RAM.
    pub fn read_byte(&mut self) -> u8 {
        self.cursor_address += Address::from(1);
        self.connection.recieve(ReadMode::Data)
    }

    /// Reads busy flag and the cursor's current address.
    pub fn read_busy_flag(&self) -> (bool, u8) {
        let byte = self.connection.recieve(ReadMode::BusyFlag);

        let busy_flag = (byte & 0b1000_0000) != 0;

        let address = byte & 0b0111_1111;

        (busy_flag, address)
    }

    /// Writes the given message to data or character generator RAM, depending on the previous
    /// seek operation.
    pub fn write_message(&mut self, msg: &str) {
        for c in msg.as_bytes().iter().take(LCD_WIDTH) {
            self.write(*c);
        }
    }

    pub fn get_connection(self) -> P {
        self.connection
    }
}
<|endoftext|>


<commit_before>/*
Simple, random dungeon generator written in Rust. Originally described by Mike Anderson at
http://www.roguebasin.com/index.php?title=Dungeon-Building_Algorithm

Sample usage:

let mut d = Dungeon::new(50, 50);
let max_features = 35;
d.generate(max_features);

To see the output, call d._print_dungeon()
*/

use std::slice::Iter;
use rng;

#[derive(PartialEq, Debug, Copy, Clone)]
enum Tile {
    Unused,
    Floor,
    Corridor,
    Wall,
    ClosedDoor,
    OpenDoor,
    Exit,
    Entrance,
}

#[derive(Debug, PartialEq)]
enum Dir {
    North,
    South,
    East,
    West,
}

impl Dir {
    // iterator over direction variants
    pub fn iterator() -> Iter<'static, Dir> {
        static DIR: [Dir;  4] = [Dir::North, Dir::South, Dir::East, Dir::West];
        DIR.into_iter()
    }

    pub fn get_random_dir<'a>()-> &'a Dir {
        Dir::iterator().nth(rng::inclusive_random(0, 3) as usize).unwrap()
    }
}

#[derive(Debug, Copy, Clone)]
struct Rect {
    x: isize,
    y: isize,
    width: isize,
    height: isize,
}

impl Rect {
    fn new(x: isize, y: isize, width: isize, height: isize) -> Rect {
        Rect { x: x, y: y, width: width, height: height }
    }
}

pub struct Dungeon {
    width: isize,
    height: isize,
    tiles: Vec<Tile>,
    rooms: Vec<Rect>,
    exits: Vec<Rect>,
}

impl Dungeon {
    pub fn new(width: isize, height: isize) -> Dungeon {
        let mut tiles = Vec::new();
        for _x in 1..width*height+1 {
            tiles.push(Tile::Unused);
        }

        Dungeon { width: width, height: height, tiles: tiles, rooms: Vec::new(),
                    exits: Vec::new() }
    }

    fn _print_dungeon(&self) {
        for y in 1..self.height {
            for x in 1..self.width {
                print!("{}", self._get_tile_icon(self.get_tile(x, y)));
            }
            println!("");
        }
    }

    fn _get_tile_icon(&self, tile: Tile) -> char {
        match tile {
            Tile::Floor =>      '.',
            Tile::Corridor =>   ',',
            Tile::Wall =>       '#',
            Tile::ClosedDoor => '+',
            Tile::OpenDoor =>   '-',
            Tile::Exit =>       '>',
            Tile::Entrance =>   '<',
            Tile::Unused =>     ' ',
        }
    }

    pub fn generate(&mut self, maxfeatures: isize) {
        let x = self.width;
        let y = self.height;
        if !self.make_room(x / 2, y / 2, Dir::get_random_dir(), true) {
            println!("unable to place first room!");
        }

        for x in 1..maxfeatures {
            if !self.has_exits() {
                println!("unable to place more features, placed {}.", x);
                break;
            }
        }

        if !self.place_object(Tile::Exit) {
            println!("unable to place exit");
        }

        if !self.place_object(Tile::Entrance) {
            println!("unable to place entrance");
        }
    }

    fn get_tile(&self, x: isize, y: isize) -> Tile {
        if (x < 0) || (y < 0) || (x >= self.width as isize) || (y >= self.height as isize) {
            return Tile::Unused
        }

        self.tiles[x as usize + y as usize * self.width as usize]
    }

    fn set_tile(&mut self, x: isize, y: isize, tile: Tile) {
        self.tiles[x as usize + y as usize * self.width as usize] = tile;
    }

    fn has_exits(&mut self) -> bool {
        for _i in 0..1000 {
            if self.exits.is_empty() {
                break;
            }

            // pick a random side of a room/corridor
            let r: isize = rng::exclusive_random(self.exits.len() as isize);
            let x: isize = rng::inclusive_random(self.exits[r as usize].x, self.exits[r as usize].x +
                self.exits[r as usize].width - 1);
            let y: isize = rng::inclusive_random(self.exits[r as usize].y, self.exits[r as usize].y +
                self.exits[r as usize].height - 1);

            for dir in Dir::iterator() {
                if self.create_feature(x, y, dir) {
                    self.exits.remove(r as usize);
                    return true
                }
            }
        }
        false
    }

    fn create_feature(&mut self, x: isize, y: isize, dir: &Dir) -> bool {
        let room_chance: isize = 50;
        let mut dx: isize = 0;
        let mut dy: isize = 0;

        if *dir == Dir::North {
            dy = 1;
        }

        else if *dir == Dir::South {
            dy = -1;
        }

        else if *dir == Dir::East {
            dx = -1;
        }

        else if *dir == Dir::West {
            dx = 1;
        }

        if self.get_tile(x as isize + dx, y as isize + dy) != Tile::Floor && self.get_tile(x as isize + dx, y as isize + dy) != Tile::Corridor {
            return false
        }

        if rng::exclusive_random(100) < room_chance {
            if self.make_room(x, y, &dir, false) {
                self.set_tile(x, y, Tile::ClosedDoor);

                return true
            }
        }

        else {
            if self.make_corridor(x, y, dir) {
                if self.get_tile(x as isize + dx, y as isize + dy) == Tile::Floor {
                    self.set_tile(x, y, Tile::ClosedDoor);
                }

                else {
                    self.set_tile(x, y, Tile::Corridor);

                    return true
                }
            }
        }
        return false
    }

    fn make_room(&mut self, x: isize, y: isize, dir: &Dir, firstroom: bool) -> bool {
        let minsize: isize = 3;
        let maxsize: isize = 16;

        let mut room: Rect = Rect::new(0, 0, rng::inclusive_random(minsize, maxsize), rng::inclusive_random(minsize, maxsize));

        if *dir == Dir::North {
            room.x = x - room.width / 2;
            room.y = y - room.height;
        }

        else if *dir == Dir::South {
            room.x = x - room.width /2;
            room.y = y + 1;
        }

        else if *dir == Dir::East {
            room.x = x + 1;
            room.y = y - room.height / 2;
        }

        else if *dir == Dir::West {
            room.x = x - room.width;
            room.y = y - room.height / 2;
        }

        if self.place_rect(&room, Tile::Floor) {
            self.rooms.push(room);

            if *dir != Dir::South || firstroom {
                self.exits.push(Rect::new(room.x, room.y - 1, room.width, 1));
            }

            if *dir != Dir::North || firstroom {
                self.exits.push(Rect::new(room.x, room.y + room.height, room.width, 1));
            }

            if *dir != Dir::East || firstroom {
                self.exits.push(Rect::new(room.x - 1, room.y, 1, room.height));
            }

            if *dir != Dir::West || firstroom {
                self.exits.push(Rect::new(room.x + room.width, room.y, 1, room.height));
            }

            return true
        }

        return false
    }

    fn make_corridor(&mut self, x: isize, y: isize, dir: &Dir) -> bool {
        use rand::{thread_rng, Rng};
        let mut rng = thread_rng();

        let minlength = 3;
        let maxlength = 10;

        let mut corridor = Rect::new(x, y, 0, 0);

        if rng.gen() { // horizontal
            corridor.width = rng::inclusive_random(minlength, maxlength);
            corridor.height = 1;

            if *dir == Dir::North {
                corridor.y = y - 1;
                if rng.gen() { // west
                    corridor.x = x - corridor.width + 1;
                }
            }

            else if *dir == Dir::South {
                corridor.y = y + 1;

                if rng.gen() { // west
                    corridor.x = x - corridor.width + 1;
                }
            }

            else if *dir == Dir::East {
                corridor.x = x + 1;
            }

            else if *dir == Dir::West {
                corridor.x = x - corridor.width + 1;
            }
        }

        else { // vertical
            corridor.width = 1;
            corridor.height = rng::inclusive_random(minlength, maxlength);

            if *dir == Dir::North {
                corridor.y = y - corridor.height;
            }

            else if *dir == Dir::South {
                corridor.y = y + 1;
            }

            else if *dir == Dir::East {
                corridor.x = x + 1;

                if rng.gen() { // north
                    corridor.y = y - corridor.height + 1;
                }
            }

            else if *dir == Dir::West {
                corridor.x = x - 1;

                if rng.gen() { // north
                    corridor.y = y - corridor.height + 1;
                }
            }
        }

        if self.place_rect(&corridor, Tile::Corridor) {
            if *dir != Dir::South && corridor.width != 1 { // north side
                self.exits.push(Rect::new(corridor.x, corridor.y - 1, corridor.width, 1));
            }

            if *dir != Dir::North && corridor.width != 1 { // south side
                self.exits.push(Rect::new(corridor.x, corridor.y + corridor.height, corridor.width, 1));
            }

            if *dir != Dir::East && corridor.height != 1 { // west side
                self.exits.push(Rect::new(corridor.x - 1, corridor.y, 1, corridor.height));
            }

            if *dir != Dir::West && corridor.height != 1 { // east side
                self.exits.push(Rect::new(corridor.x + corridor.width, corridor.y, 1, corridor.height));
            }

            return true
        }

        return false
    }

    fn place_rect(&mut self, rect: &Rect, tile: Tile) -> bool {
        // ensure rect is placed within the boundaries of the dungeon
        if (rect.x <= 1) || (rect.y <= 1) || (rect.x + rect.width > self.width - 1) || (rect.y + rect.height > self.height - 1) {
            return false
        }

        for y in rect.y..rect.y+rect.height {
            for x in rect.x..rect.x + rect.width {
                if self.get_tile(x, y) != Tile::Unused {
                    return false // this area is already in use
                }
            }
        }

        // checks have passed, we can place a rect here
        for y in rect.y-1..rect.y+rect.height+1 {
            for x in rect.x-1..rect.x+rect.width+1 {
                // fill boundaries of rect with walls
                if (x == rect.x - 1) || (y == rect.y - 1) || (x == rect.x + rect.width) || (y == rect.y + rect.height) {
                    self.set_tile(x, y, Tile::Wall);
                }

                // fill rect with appropriate tiles
                else {
                    self.set_tile(x, y, tile);
                }
            }
        }

        return true
    }

    fn place_object(&mut self, tile: Tile) -> bool {
        if self.rooms.is_empty() {
            return false
        }

        let r: isize = rng::exclusive_random(self.rooms.len() as isize);
        let x: isize = rng::inclusive_random(self.rooms[r as usize].x + 1, self.rooms[r as usize].x + self.rooms[r as usize].width - 2);
        let y: isize = rng::inclusive_random(self.rooms[r as usize].y + 1, self.rooms[r as usize].y + self.rooms[r as usize].height - 2);

        if self.get_tile(x, y) == Tile::Floor {
            self.set_tile(x, y, tile);

            self.rooms.remove(r as usize);

            return true
        }
        return false;
    }
}

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

    #[test]
    fn test_dungeon() {
        let mut d: Dungeon = Dungeon::new(100, 100);
        let max_features: isize = 78;

        d.generate(max_features);
        // must use cargo test -- --nocapture to see this output
        d._print_dungeon();
    }
}<commit_msg>Use `slice::iter` instead of `into_iter` to avoid future breakage<commit_after>/*
Simple, random dungeon generator written in Rust. Originally described by Mike Anderson at
http://www.roguebasin.com/index.php?title=Dungeon-Building_Algorithm

Sample usage:

let mut d = Dungeon::new(50, 50);
let max_features = 35;
d.generate(max_features);

To see the output, call d._print_dungeon()
*/

use std::slice::Iter;
use rng;

#[derive(PartialEq, Debug, Copy, Clone)]
enum Tile {
    Unused,
    Floor,
    Corridor,
    Wall,
    ClosedDoor,
    OpenDoor,
    Exit,
    Entrance,
}

#[derive(Debug, PartialEq)]
enum Dir {
    North,
    South,
    East,
    West,
}

impl Dir {
    // iterator over direction variants
    pub fn iterator() -> Iter<'static, Dir> {
        static DIR: [Dir;  4] = [Dir::North, Dir::South, Dir::East, Dir::West];
        DIR.iter()
    }

    pub fn get_random_dir<'a>()-> &'a Dir {
        Dir::iterator().nth(rng::inclusive_random(0, 3) as usize).unwrap()
    }
}

#[derive(Debug, Copy, Clone)]
struct Rect {
    x: isize,
    y: isize,
    width: isize,
    height: isize,
}

impl Rect {
    fn new(x: isize, y: isize, width: isize, height: isize) -> Rect {
        Rect { x: x, y: y, width: width, height: height }
    }
}

pub struct Dungeon {
    width: isize,
    height: isize,
    tiles: Vec<Tile>,
    rooms: Vec<Rect>,
    exits: Vec<Rect>,
}

impl Dungeon {
    pub fn new(width: isize, height: isize) -> Dungeon {
        let mut tiles = Vec::new();
        for _x in 1..width*height+1 {
            tiles.push(Tile::Unused);
        }

        Dungeon { width: width, height: height, tiles: tiles, rooms: Vec::new(),
                    exits: Vec::new() }
    }

    fn _print_dungeon(&self) {
        for y in 1..self.height {
            for x in 1..self.width {
                print!("{}", self._get_tile_icon(self.get_tile(x, y)));
            }
            println!("");
        }
    }

    fn _get_tile_icon(&self, tile: Tile) -> char {
        match tile {
            Tile::Floor =>      '.',
            Tile::Corridor =>   ',',
            Tile::Wall =>       '#',
            Tile::ClosedDoor => '+',
            Tile::OpenDoor =>   '-',
            Tile::Exit =>       '>',
            Tile::Entrance =>   '<',
            Tile::Unused =>     ' ',
        }
    }

    pub fn generate(&mut self, maxfeatures: isize) {
        let x = self.width;
        let y = self.height;
        if !self.make_room(x / 2, y / 2, Dir::get_random_dir(), true) {
            println!("unable to place first room!");
        }

        for x in 1..maxfeatures {
            if !self.has_exits() {
                println!("unable to place more features, placed {}.", x);
                break;
            }
        }

        if !self.place_object(Tile::Exit) {
            println!("unable to place exit");
        }

        if !self.place_object(Tile::Entrance) {
            println!("unable to place entrance");
        }
    }

    fn get_tile(&self, x: isize, y: isize) -> Tile {
        if (x < 0) || (y < 0) || (x >= self.width as isize) || (y >= self.height as isize) {
            return Tile::Unused
        }

        self.tiles[x as usize + y as usize * self.width as usize]
    }

    fn set_tile(&mut self, x: isize, y: isize, tile: Tile) {
        self.tiles[x as usize + y as usize * self.width as usize] = tile;
    }

    fn has_exits(&mut self) -> bool {
        for _i in 0..1000 {
            if self.exits.is_empty() {
                break;
            }

            // pick a random side of a room/corridor
            let r: isize = rng::exclusive_random(self.exits.len() as isize);
            let x: isize = rng::inclusive_random(self.exits[r as usize].x, self.exits[r as usize].x +
                self.exits[r as usize].width - 1);
            let y: isize = rng::inclusive_random(self.exits[r as usize].y, self.exits[r as usize].y +
                self.exits[r as usize].height - 1);

            for dir in Dir::iterator() {
                if self.create_feature(x, y, dir) {
                    self.exits.remove(r as usize);
                    return true
                }
            }
        }
        false
    }

    fn create_feature(&mut self, x: isize, y: isize, dir: &Dir) -> bool {
        let room_chance: isize = 50;
        let mut dx: isize = 0;
        let mut dy: isize = 0;

        if *dir == Dir::North {
            dy = 1;
        }

        else if *dir == Dir::South {
            dy = -1;
        }

        else if *dir == Dir::East {
            dx = -1;
        }

        else if *dir == Dir::West {
            dx = 1;
        }

        if self.get_tile(x as isize + dx, y as isize + dy) != Tile::Floor && self.get_tile(x as isize + dx, y as isize + dy) != Tile::Corridor {
            return false
        }

        if rng::exclusive_random(100) < room_chance {
            if self.make_room(x, y, &dir, false) {
                self.set_tile(x, y, Tile::ClosedDoor);

                return true
            }
        }

        else {
            if self.make_corridor(x, y, dir) {
                if self.get_tile(x as isize + dx, y as isize + dy) == Tile::Floor {
                    self.set_tile(x, y, Tile::ClosedDoor);
                }

                else {
                    self.set_tile(x, y, Tile::Corridor);

                    return true
                }
            }
        }
        return false
    }

    fn make_room(&mut self, x: isize, y: isize, dir: &Dir, firstroom: bool) -> bool {
        let minsize: isize = 3;
        let maxsize: isize = 16;

        let mut room: Rect = Rect::new(0, 0, rng::inclusive_random(minsize, maxsize), rng::inclusive_random(minsize, maxsize));

        if *dir == Dir::North {
            room.x = x - room.width / 2;
            room.y = y - room.height;
        }

        else if *dir == Dir::South {
            room.x = x - room.width /2;
            room.y = y + 1;
        }

        else if *dir == Dir::East {
            room.x = x + 1;
            room.y = y - room.height / 2;
        }

        else if *dir == Dir::West {
            room.x = x - room.width;
            room.y = y - room.height / 2;
        }

        if self.place_rect(&room, Tile::Floor) {
            self.rooms.push(room);

            if *dir != Dir::South || firstroom {
                self.exits.push(Rect::new(room.x, room.y - 1, room.width, 1));
            }

            if *dir != Dir::North || firstroom {
                self.exits.push(Rect::new(room.x, room.y + room.height, room.width, 1));
            }

            if *dir != Dir::East || firstroom {
                self.exits.push(Rect::new(room.x - 1, room.y, 1, room.height));
            }

            if *dir != Dir::West || firstroom {
                self.exits.push(Rect::new(room.x + room.width, room.y, 1, room.height));
            }

            return true
        }

        return false
    }

    fn make_corridor(&mut self, x: isize, y: isize, dir: &Dir) -> bool {
        use rand::{thread_rng, Rng};
        let mut rng = thread_rng();

        let minlength = 3;
        let maxlength = 10;

        let mut corridor = Rect::new(x, y, 0, 0);

        if rng.gen() { // horizontal
            corridor.width = rng::inclusive_random(minlength, maxlength);
            corridor.height = 1;

            if *dir == Dir::North {
                corridor.y = y - 1;
                if rng.gen() { // west
                    corridor.x = x - corridor.width + 1;
                }
            }

            else if *dir == Dir::South {
                corridor.y = y + 1;

                if rng.gen() { // west
                    corridor.x = x - corridor.width + 1;
                }
            }

            else if *dir == Dir::East {
                corridor.x = x + 1;
            }

            else if *dir == Dir::West {
                corridor.x = x - corridor.width + 1;
            }
        }

        else { // vertical
            corridor.width = 1;
            corridor.height = rng::inclusive_random(minlength, maxlength);

            if *dir == Dir::North {
                corridor.y = y - corridor.height;
            }

            else if *dir == Dir::South {
                corridor.y = y + 1;
            }

            else if *dir == Dir::East {
                corridor.x = x + 1;

                if rng.gen() { // north
                    corridor.y = y - corridor.height + 1;
                }
            }

            else if *dir == Dir::West {
                corridor.x = x - 1;

                if rng.gen() { // north
                    corridor.y = y - corridor.height + 1;
                }
            }
        }

        if self.place_rect(&corridor, Tile::Corridor) {
            if *dir != Dir::South && corridor.width != 1 { // north side
                self.exits.push(Rect::new(corridor.x, corridor.y - 1, corridor.width, 1));
            }

            if *dir != Dir::North && corridor.width != 1 { // south side
                self.exits.push(Rect::new(corridor.x, corridor.y + corridor.height, corridor.width, 1));
            }

            if *dir != Dir::East && corridor.height != 1 { // west side
                self.exits.push(Rect::new(corridor.x - 1, corridor.y, 1, corridor.height));
            }

            if *dir != Dir::West && corridor.height != 1 { // east side
                self.exits.push(Rect::new(corridor.x + corridor.width, corridor.y, 1, corridor.height));
            }

            return true
        }

        return false
    }

    fn place_rect(&mut self, rect: &Rect, tile: Tile) -> bool {
        // ensure rect is placed within the boundaries of the dungeon
        if (rect.x <= 1) || (rect.y <= 1) || (rect.x + rect.width > self.width - 1) || (rect.y + rect.height > self.height - 1) {
            return false
        }

        for y in rect.y..rect.y+rect.height {
            for x in rect.x..rect.x + rect.width {
                if self.get_tile(x, y) != Tile::Unused {
                    return false // this area is already in use
                }
            }
        }

        // checks have passed, we can place a rect here
        for y in rect.y-1..rect.y+rect.height+1 {
            for x in rect.x-1..rect.x+rect.width+1 {
                // fill boundaries of rect with walls
                if (x == rect.x - 1) || (y == rect.y - 1) || (x == rect.x + rect.width) || (y == rect.y + rect.height) {
                    self.set_tile(x, y, Tile::Wall);
                }

                // fill rect with appropriate tiles
                else {
                    self.set_tile(x, y, tile);
                }
            }
        }

        return true
    }

    fn place_object(&mut self, tile: Tile) -> bool {
        if self.rooms.is_empty() {
            return false
        }

        let r: isize = rng::exclusive_random(self.rooms.len() as isize);
        let x: isize = rng::inclusive_random(self.rooms[r as usize].x + 1, self.rooms[r as usize].x + self.rooms[r as usize].width - 2);
        let y: isize = rng::inclusive_random(self.rooms[r as usize].y + 1, self.rooms[r as usize].y + self.rooms[r as usize].height - 2);

        if self.get_tile(x, y) == Tile::Floor {
            self.set_tile(x, y, tile);

            self.rooms.remove(r as usize);

            return true
        }
        return false;
    }
}

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

    #[test]
    fn test_dungeon() {
        let mut d: Dungeon = Dungeon::new(100, 100);
        let max_features: isize = 78;

        d.generate(max_features);
        // must use cargo test -- --nocapture to see this output
        d._print_dungeon();
    }
}<|endoftext|>