<commit_before>//! Implementation of the `build` subcommand, used to compile a book.

use std::os;
use std::io;
use std::io::{fs, Command, File, BufferedWriter, TempDir, IoResult};

use subcommand::Subcommand;
use term::Term;
use error::{Error, CliResult};
use book;
use book::{Book, BookItem};

use regex::Regex;

struct Build;

pub fn parse_cmd(name: &str) -> Option<Box<Subcommand>> {
    if name == "build" {
        Some(box Build as Box<Subcommand>)
    } else {
        None
    }
}

fn write_toc(book: &Book, path_to_root: &Path, out: &mut Writer) -> IoResult<()> {
    fn walk_items(items: &[BookItem], section: &str, path_to_root: &Path, out: &mut Writer) -> IoResult<()> {
        for (i, item) in items.iter().enumerate() {
            try!(walk_item(item, format!("{}{}.", section, i + 1).as_slice(), path_to_root, out));
        }
        Ok(())
    }
    fn walk_item(item: &BookItem, section: &str, path_to_root: &Path, out: &mut Writer) -> IoResult<()> {
        try!(writeln!(out, "
{} {}",
                 path_to_root.join(item.path.with_extension("html")).display(),
                 section,
                 item.title));
        if !item.children.is_empty() {
            try!(writeln!(out, "
"));
            walk_items(item.children.as_slice(), section, path_to_root, out);
            try!(writeln!(out, "
"));
        }
        try!(writeln!(out, "
"));

        Ok(())
    }

    try!(writeln!(out, "
"));
    try!(writeln!(out, "
"));
    try!(walk_items(book.chapters.as_slice(), "", path_to_root, out));
    try!(writeln!(out, "
"));
    try!(writeln!(out, "
"));

    Ok(())
}

fn render(book: &Book, tgt: &Path) -> CliResult<()> {
    let tmp = TempDir::new("rust-book")
                      .expect("could not create temporary directory"); // FIXME: lift to Result instead

    for (section, item) in book.iter() {
        println!("{} {}", section, item.title);

        let md_urls = regex!(r"\[(?P<title>[^]]*)\]\((?P<url_stem>[^)]*)\.(?P<ext>md|markdown)\)");

        // preprocess the markdown, rerouting markdown references to html references
        let markdown_data = try!(File::open(&item.path).read_to_string());
        let processed_path = tmp.path().join(item.path.filename().unwrap());
        {
            let mut processed_file = File::create(&processed_path);
            processed_file.write_str(md_urls.replace_all(markdown_data.as_slice(),
                                                         "[$title]($url_stem.html)").as_slice());
        }

        // write the prelude to a temporary HTML file for rustdoc inclusion
        let prelude = tmp.path().join("prelude.html");
        {
            let mut toc = BufferedWriter::new(try!(File::create(&prelude)));
            write_toc(book, &item.path_to_root, &mut toc);
            try!(writeln!(toc, "
"));
            try!(writeln!(toc, "
"));
        }

        // write the postlude to a temporary HTML file for rustdoc inclusion
        let postlude = tmp.path().join("postlude.html");
        {
            let mut toc = BufferedWriter::new(try!(File::create(&postlude)));
            try!(writeln!(toc, "

"));
        }

        let output_result = Command::new("rustdoc")
            .arg(&processed_path)
            //.arg(&item.path)
            .arg("-o").arg(&tgt.join(item.path.dirname()))
            .arg(format!("--html-before-content={}", prelude.display()))
            .arg(format!("--html-after-content={}", postlude.display()))
            .arg("--markdown-css").arg(item.path_to_root.join("rust-book.css"))
            .arg("--markdown-no-toc")
            .output();
        match output_result {
            Ok(output) => {
                if !output.output.is_empty() || !output.error.is_empty() {
                    return Err(box format!("{}\n{}",
                                           String::from_utf8_lossy(output.output.as_slice()),
                                           String::from_utf8_lossy(output.error.as_slice()))
                               as Box<Error>);
                }
            }
            Err(e) => {
                return Err(box format!("Could not execute `rustdoc`: {}", e) as Box<Error>);
            }
        }
    }

    Ok(())
}

impl Subcommand for Build {
    fn parse_args(&mut self, args: &[String]) -> CliResult<()> {
        Ok(())
    }
    fn usage(&self) {}
    fn execute(self, term: &mut Term) {
        let cwd = os::getcwd();
        let src = cwd.clone();
        let tgt = cwd.join("_book");

        fs::mkdir(&tgt, io::UserDir); // FIXME: handle errors

        let summary = File::open(&src.join("SUMMARY.md"));
        match book::parse_summary(summary, &src) {
            Ok(book) => {
                // execute rustdoc on the whole book
                render(&book, &tgt).map_err(|err| {
                    term.err(format!("error: {}", err.description()).as_slice());
                    err.detail().map(|detail| {
                        term.err(format!("detail: {}", detail).as_slice());
                    })
                });
            }
            Err(errors) => {
                for err in errors.move_iter() {
                    term.err(err.as_slice());
                }
            }
        }
    }
}
<commit_msg>Bugfix: make nested target location using mkdir_recursive<commit_after>//! Implementation of the `build` subcommand, used to compile a book.

use std::os;
use std::io;
use std::io::{fs, Command, File, BufferedWriter, TempDir, IoResult};

use subcommand::Subcommand;
use term::Term;
use error::{Error, CliResult};
use book;
use book::{Book, BookItem};

use regex::Regex;

struct Build;

pub fn parse_cmd(name: &str) -> Option<Box<Subcommand>> {
    if name == "build" {
        Some(box Build as Box<Subcommand>)
    } else {
        None
    }
}

fn write_toc(book: &Book, path_to_root: &Path, out: &mut Writer) -> IoResult<()> {
    fn walk_items(items: &[BookItem], section: &str, path_to_root: &Path, out: &mut Writer) -> IoResult<()> {
        for (i, item) in items.iter().enumerate() {
            try!(walk_item(item, format!("{}{}.", section, i + 1).as_slice(), path_to_root, out));
        }
        Ok(())
    }
    fn walk_item(item: &BookItem, section: &str, path_to_root: &Path, out: &mut Writer) -> IoResult<()> {
        try!(writeln!(out, "
{} {}",
                 path_to_root.join(item.path.with_extension("html")).display(),
                 section,
                 item.title));
        if !item.children.is_empty() {
            try!(writeln!(out, "
"));
            walk_items(item.children.as_slice(), section, path_to_root, out);
            try!(writeln!(out, "
"));
        }
        try!(writeln!(out, "
"));

        Ok(())
    }

    try!(writeln!(out, "
"));
    try!(writeln!(out, "
"));
    try!(walk_items(book.chapters.as_slice(), "", path_to_root, out));
    try!(writeln!(out, "
"));
    try!(writeln!(out, "
"));

    Ok(())
}

fn render(book: &Book, tgt: &Path) -> CliResult<()> {
    let tmp = TempDir::new("rust-book")
                      .expect("could not create temporary directory"); // FIXME: lift to Result instead

    for (section, item) in book.iter() {
        println!("{} {}", section, item.title);

        let md_urls = regex!(r"\[(?P<title>[^]]*)\]\((?P<url_stem>[^)]*)\.(?P<ext>md|markdown)\)");

        // preprocess the markdown, rerouting markdown references to html references
        let markdown_data = try!(File::open(&item.path).read_to_string());
        let preprocessed_path = tmp.path().join(item.path.filename().unwrap());
        {
            try!(File::create(&preprocessed_path)
                      .write_str(md_urls.replace_all(markdown_data.as_slice(),
                                                     "[$title]($url_stem.html)").as_slice()));
        }

        // write the prelude to a temporary HTML file for rustdoc inclusion
        let prelude = tmp.path().join("prelude.html");
        {
            let mut toc = BufferedWriter::new(try!(File::create(&prelude)));
            write_toc(book, &item.path_to_root, &mut toc);
            try!(writeln!(toc, "
"));
            try!(writeln!(toc, "
"));
        }

        // write the postlude to a temporary HTML file for rustdoc inclusion
        let postlude = tmp.path().join("postlude.html");
        {
            let mut toc = BufferedWriter::new(try!(File::create(&postlude)));
            try!(writeln!(toc, "

"));
        }

        let out_path = tgt.join(item.path.dirname());
        try!(fs::mkdir_recursive(&out_path, io::UserDir));

        let output_result = Command::new("rustdoc")
            .arg(&preprocessed_path)
            .arg("-o").arg(&out_path)
            .arg(format!("--html-before-content={}", prelude.display()))
            .arg(format!("--html-after-content={}", postlude.display()))
            .arg("--markdown-css").arg(item.path_to_root.join("rust-book.css"))
            .arg("--markdown-no-toc")
            .output();
        match output_result {
            Ok(output) => {
                if !output.output.is_empty() || !output.error.is_empty() {
                    return Err(box format!("{}\n{}",
                                           String::from_utf8_lossy(output.output.as_slice()),
                                           String::from_utf8_lossy(output.error.as_slice()))
                               as Box<Error>);
                }
            }
            Err(e) => {
                return Err(box format!("Could not execute `rustdoc`: {}", e) as Box<Error>);
            }
        }
    }

    Ok(())
}

impl Subcommand for Build {
    fn parse_args(&mut self, args: &[String]) -> CliResult<()> {
        Ok(())
    }
    fn usage(&self) {}
    fn execute(self, term: &mut Term) {
        let cwd = os::getcwd();
        let src = cwd.clone();
        let tgt = cwd.join("_book");

        fs::mkdir(&tgt, io::UserDir); // FIXME: handle errors

        let summary = File::open(&src.join("SUMMARY.md"));
        match book::parse_summary(summary, &src) {
            Ok(book) => {
                // execute rustdoc on the whole book
                render(&book, &tgt).map_err(|err| {
                    term.err(format!("error: {}", err.description()).as_slice());
                    err.detail().map(|detail| {
                        term.err(format!("detail: {}", detail).as_slice());
                    })
                });
            }
            Err(errors) => {
                for err in errors.move_iter() {
                    term.err(err.as_slice());
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>use cassandra::CassError_;

error_chain! {
errors {
    LIB_BAD_PARAMS(t:CassError_){}
    LIB_NO_STREAMS (t:CassError_){}
    LIB_UNABLE_TO_INIT(t:CassError_){}
    LIB_MESSAGE_ENCODE(t:CassError_){}
    LIB_HOST_RESOLUTION(t:CassError_){}
    LIB_UNEXPECTED_RESPONSE(t:CassError_){}
    LIB_REQUEST_QUEUE_FULL(t:CassError_){}
    LIB_NO_AVAILABLE_IO_THREAD(t:CassError_){}
    LIB_WRITE_ERROR(t:CassError_){}
    LIB_NO_HOSTS_AVAILABLE(t:CassError_){}
    LIB_INDEX_OUT_OF_BOUNDS(t:CassError_){}
    LIB_INVALID_ITEM_COUNT(t:CassError_){}
    LIB_INVALID_VALUE_TYPE(t:CassError_){}
    LIB_REQUEST_TIMED_OUT(t:CassError_){}
    LIB_UNABLE_TO_SET_KEYSPACE(t:CassError_){}
    LIB_CALLBACK_ALREADY_SET(t:CassError_){}
    LIB_INVALID_STATEMENT_TYPE(t:CassError_){}
    LIB_NAME_DOES_NOT_EXIST(t:CassError_){}
    LIB_UNABLE_TO_DETERMINE_PROTOCOL(t:CassError_){}
    LIB_NULL_VALUE(t:CassError_){}
    LIB_NOT_IMPLEMENTED(t:CassError_){}
    LIB_UNABLE_TO_CONNECT(t:CassError_){}
    LIB_UNABLE_TO_CLOSE(t:CassError_){}
    LIB_NO_PAGING_STATE(t:CassError_){}
    LIB_PARAMETER_UNSET(t:CassError_){}
    LIB_INVALID_ERROR_RESULT_TYPE(t:CassError_){}
    LIB_INVALID_FUTURE_TYPE(t:CassError_){}
    LIB_INTERNAL_ERROR(t:CassError_){}
    LIB_INVALID_CUSTOM_TYPE(t:CassError_){}
    LIB_INVALID_DATA(t:CassError_){}
    LIB_NOT_ENOUGH_DATA(t:CassError_){}
    LIB_INVALID_STATE(t:CassError_){}
    LIB_NO_CUSTOM_PAYLOAD(t:CassError_){}
    SERVER_SERVER_ERROR(t:CassError_){}
    SERVER_PROTOCOL_ERROR(t:CassError_){}
    SERVER_BAD_CREDENTIALS(t:CassError_){}
    SERVER_UNAVAILABLE(t:CassError_){}
    SERVER_OVERLOADED(t:CassError_){}
    SERVER_IS_BOOTSTRAPPING(t:CassError_){}
    SERVER_TRUNCATE_ERROR(t:CassError_){}
    SERVER_WRITE_TIMEOUT(t:CassError_){}
    SERVER_READ_TIMEOUT(t:CassError_){}
    SERVER_READ_FAILURE(t:CassError_){}
    SERVER_FUNCTION_FAILURE(t:CassError_){}
    SERVER_WRITE_FAILURE(t:CassError_){}
    SERVER_SYNTAX_ERROR(t:CassError_){}
    SERVER_UNAUTHORIZED(t:CassError_){}
    SERVER_INVALID_QUERY(t:CassError_){}
    SERVER_CONFIG_ERROR(t:CassError_){}
    SERVER_ALREADY_EXISTS(t:CassError_){}
    SERVER_UNPREPARED(t:CassError_){}
    SSL_INVALID_CERT(t:CassError_){}
    SSL_INVALID_PRIVATE_KEY(t:CassError_){}
    SSL_NO_PEER_CERT(t:CassError_){}
    SSL_INVALID_PEER_CERT(t:CassError_){}
    SSL_IDENTITY_MISMATCH(t:CassError_){}
    SSL_PROTOCOL_ERROR(t:CassError_){}
    LAST_ENTRY(t:CassError_){}
    }
}

impl CassError_ {
    fn to_result<T>(self,T:T) -> Result<T> {
        use self::CassError_::*;
        match self {
            CASS_OK => Ok(T),
            CASS_ERROR_LIB_BAD_PARAMS => Err(ErrorKind::LIB_BAD_PARAMS(CASS_ERROR_LIB_BAD_PARAMS).into()),
            CASS_ERROR_LIB_NO_STREAMS => Err(ErrorKind::LIB_NO_STREAMS(CASS_ERROR_LIB_NO_STREAMS).into()),
            CASS_ERROR_LIB_UNABLE_TO_INIT => Err(ErrorKind::LIB_UNABLE_TO_INIT(CASS_ERROR_LIB_UNABLE_TO_INIT).into()),
            CASS_ERROR_LIB_MESSAGE_ENCODE => Err(ErrorKind::LIB_MESSAGE_ENCODE(CASS_ERROR_LIB_MESSAGE_ENCODE).into()),
            CASS_ERROR_LIB_HOST_RESOLUTION => {
                Err(ErrorKind::LIB_HOST_RESOLUTION(CASS_ERROR_LIB_HOST_RESOLUTION).into())
            }
            CASS_ERROR_LIB_UNEXPECTED_RESPONSE => {
                Err(ErrorKind::LIB_UNEXPECTED_RESPONSE(CASS_ERROR_LIB_UNEXPECTED_RESPONSE).into())
            }
            CASS_ERROR_LIB_REQUEST_QUEUE_FULL => {
                Err(ErrorKind::LIB_REQUEST_QUEUE_FULL(CASS_ERROR_LIB_UNEXPECTED_RESPONSE).into())
            }
            CASS_ERROR_LIB_NO_AVAILABLE_IO_THREAD => {
                Err(ErrorKind::LIB_NO_AVAILABLE_IO_THREAD(CASS_ERROR_LIB_NO_AVAILABLE_IO_THREAD).into())
            }
            CASS_ERROR_LIB_WRITE_ERROR => Err(ErrorKind::LIB_WRITE_ERROR(CASS_ERROR_LIB_WRITE_ERROR).into()),
            CASS_ERROR_LIB_NO_HOSTS_AVAILABLE => {
                Err(ErrorKind::LIB_NO_HOSTS_AVAILABLE(CASS_ERROR_LIB_NO_HOSTS_AVAILABLE).into())
            }
            CASS_ERROR_LIB_INDEX_OUT_OF_BOUNDS => {
                Err(ErrorKind::LIB_INDEX_OUT_OF_BOUNDS(CASS_ERROR_LIB_INDEX_OUT_OF_BOUNDS).into())
            }
            CASS_ERROR_LIB_INVALID_ITEM_COUNT => {
                Err(ErrorKind::LIB_INVALID_ITEM_COUNT(CASS_ERROR_LIB_INVALID_ITEM_COUNT).into())
            }
            CASS_ERROR_LIB_INVALID_VALUE_TYPE => {
                Err(ErrorKind::LIB_INVALID_VALUE_TYPE(CASS_ERROR_LIB_INVALID_VALUE_TYPE).into())
            }
            CASS_ERROR_LIB_REQUEST_TIMED_OUT => {
                Err(ErrorKind::LIB_REQUEST_TIMED_OUT(CASS_ERROR_LIB_REQUEST_TIMED_OUT).into())
            }
            CASS_ERROR_LIB_UNABLE_TO_SET_KEYSPACE => {
                Err(ErrorKind::LIB_UNABLE_TO_SET_KEYSPACE(CASS_ERROR_LIB_UNABLE_TO_SET_KEYSPACE).into())
            }
            CASS_ERROR_LIB_CALLBACK_ALREADY_SET => {
                Err(ErrorKind::LIB_CALLBACK_ALREADY_SET(CASS_ERROR_LIB_CALLBACK_ALREADY_SET).into())
            }
            CASS_ERROR_LIB_INVALID_STATEMENT_TYPE => {
                Err(ErrorKind::LIB_INVALID_STATEMENT_TYPE(CASS_ERROR_LIB_INVALID_STATEMENT_TYPE).into())
            }
            CASS_ERROR_LIB_NAME_DOES_NOT_EXIST => {
                Err(ErrorKind::LIB_NAME_DOES_NOT_EXIST(CASS_ERROR_LIB_NAME_DOES_NOT_EXIST).into())
            }
            CASS_ERROR_LIB_UNABLE_TO_DETERMINE_PROTOCOL => {
                Err(ErrorKind::LIB_UNABLE_TO_DETERMINE_PROTOCOL(CASS_ERROR_LIB_UNABLE_TO_DETERMINE_PROTOCOL).into())
            }
            CASS_ERROR_LIB_NULL_VALUE => Err(ErrorKind::LIB_NULL_VALUE(CASS_ERROR_LIB_NULL_VALUE).into()),
            CASS_ERROR_LIB_NOT_IMPLEMENTED => {
                Err(ErrorKind::LIB_NOT_IMPLEMENTED(CASS_ERROR_LIB_NOT_IMPLEMENTED).into())
            }
            CASS_ERROR_LIB_UNABLE_TO_CONNECT => {
                Err(ErrorKind::LIB_UNABLE_TO_CONNECT(CASS_ERROR_LIB_UNABLE_TO_CONNECT).into())
            }
            CASS_ERROR_LIB_UNABLE_TO_CLOSE => {
                Err(ErrorKind::LIB_UNABLE_TO_CLOSE(CASS_ERROR_LIB_UNABLE_TO_CLOSE).into())
            }
            CASS_ERROR_LIB_NO_PAGING_STATE => {
                Err(ErrorKind::LIB_NO_PAGING_STATE(CASS_ERROR_LIB_NO_PAGING_STATE).into())
            }
            CASS_ERROR_LIB_PARAMETER_UNSET => {
                Err(ErrorKind::LIB_PARAMETER_UNSET(CASS_ERROR_LIB_PARAMETER_UNSET).into())
            }
            CASS_ERROR_LIB_INVALID_ERROR_RESULT_TYPE => {
                Err(ErrorKind::LIB_INVALID_ERROR_RESULT_TYPE(CASS_ERROR_LIB_INVALID_ERROR_RESULT_TYPE).into())
            }
            CASS_ERROR_LIB_INVALID_FUTURE_TYPE => {
                Err(ErrorKind::LIB_INVALID_FUTURE_TYPE(CASS_ERROR_LIB_INVALID_FUTURE_TYPE).into())
            }
            CASS_ERROR_LIB_INTERNAL_ERROR => Err(ErrorKind::LIB_INTERNAL_ERROR(CASS_ERROR_LIB_INTERNAL_ERROR).into()),
            CASS_ERROR_LIB_INVALID_CUSTOM_TYPE => {
                Err(ErrorKind::LIB_INVALID_CUSTOM_TYPE(CASS_ERROR_LIB_INVALID_CUSTOM_TYPE).into())
            }
            CASS_ERROR_LIB_INVALID_DATA => Err(ErrorKind::LIB_INVALID_DATA(CASS_ERROR_LIB_INVALID_DATA).into()),
            CASS_ERROR_LIB_NOT_ENOUGH_DATA => {
                Err(ErrorKind::LIB_NOT_ENOUGH_DATA(CASS_ERROR_LIB_NOT_ENOUGH_DATA).into())
            }
            CASS_ERROR_LIB_INVALID_STATE => Err(ErrorKind::LIB_INVALID_STATE(CASS_ERROR_LIB_INVALID_STATE).into()),
            CASS_ERROR_LIB_NO_CUSTOM_PAYLOAD => {
                Err(ErrorKind::LIB_NO_CUSTOM_PAYLOAD(CASS_ERROR_LIB_NO_CUSTOM_PAYLOAD).into())
            }
            CASS_ERROR_SERVER_SERVER_ERROR => {
                Err(ErrorKind::SERVER_SERVER_ERROR(CASS_ERROR_SERVER_SERVER_ERROR).into())
            }
            CASS_ERROR_SERVER_PROTOCOL_ERROR => {
                Err(ErrorKind::SERVER_PROTOCOL_ERROR(CASS_ERROR_SERVER_PROTOCOL_ERROR).into())
            }
            CASS_ERROR_SERVER_BAD_CREDENTIALS => {
                Err(ErrorKind::SERVER_BAD_CREDENTIALS(CASS_ERROR_SERVER_BAD_CREDENTIALS).into())
            }
            CASS_ERROR_SERVER_UNAVAILABLE => Err(ErrorKind::SERVER_UNAVAILABLE(CASS_ERROR_SERVER_UNAVAILABLE).into()),
            CASS_ERROR_SERVER_OVERLOADED => Err(ErrorKind::SERVER_OVERLOADED(CASS_ERROR_SERVER_OVERLOADED).into()),
            CASS_ERROR_SERVER_IS_BOOTSTRAPPING => {
                Err(ErrorKind::SERVER_IS_BOOTSTRAPPING(CASS_ERROR_SERVER_IS_BOOTSTRAPPING).into())
            }
            CASS_ERROR_SERVER_TRUNCATE_ERROR => {
                Err(ErrorKind::SERVER_TRUNCATE_ERROR(CASS_ERROR_SERVER_TRUNCATE_ERROR).into())
            }
            CASS_ERROR_SERVER_WRITE_TIMEOUT => {
                Err(ErrorKind::SERVER_WRITE_TIMEOUT(CASS_ERROR_SERVER_WRITE_TIMEOUT).into())
            }
            CASS_ERROR_SERVER_READ_TIMEOUT => {
                Err(ErrorKind::SERVER_READ_TIMEOUT(CASS_ERROR_SERVER_READ_TIMEOUT).into())
            }
            CASS_ERROR_SERVER_READ_FAILURE => {
                Err(ErrorKind::SERVER_READ_FAILURE(CASS_ERROR_SERVER_READ_FAILURE).into())
            }
            CASS_ERROR_SERVER_FUNCTION_FAILURE => {
                Err(ErrorKind::SERVER_FUNCTION_FAILURE(CASS_ERROR_SERVER_FUNCTION_FAILURE).into())
            }
            CASS_ERROR_SERVER_WRITE_FAILURE => {
                Err(ErrorKind::SERVER_WRITE_FAILURE(CASS_ERROR_SERVER_WRITE_FAILURE).into())
            }
            CASS_ERROR_SERVER_SYNTAX_ERROR => {
                Err(ErrorKind::SERVER_SYNTAX_ERROR(CASS_ERROR_SERVER_WRITE_FAILURE).into())
            }
            CASS_ERROR_SERVER_UNAUTHORIZED => {
                Err(ErrorKind::SERVER_UNAUTHORIZED(CASS_ERROR_SERVER_UNAUTHORIZED).into())
            }
            CASS_ERROR_SERVER_INVALID_QUERY => {
                Err(ErrorKind::SERVER_INVALID_QUERY(CASS_ERROR_SERVER_INVALID_QUERY).into())
            }
            CASS_ERROR_SERVER_CONFIG_ERROR => {
                Err(ErrorKind::SERVER_CONFIG_ERROR(CASS_ERROR_SERVER_CONFIG_ERROR).into())
            }
            CASS_ERROR_SERVER_ALREADY_EXISTS => {
                Err(ErrorKind::SERVER_ALREADY_EXISTS(CASS_ERROR_SERVER_ALREADY_EXISTS).into())
            }
            CASS_ERROR_SERVER_UNPREPARED => Err(ErrorKind::SERVER_UNPREPARED(CASS_ERROR_SERVER_UNPREPARED).into()),
            CASS_ERROR_SSL_INVALID_CERT => Err(ErrorKind::SSL_INVALID_CERT(CASS_ERROR_SSL_INVALID_CERT).into()),
            CASS_ERROR_SSL_INVALID_PRIVATE_KEY => {
                Err(ErrorKind::SSL_INVALID_PRIVATE_KEY(CASS_ERROR_SSL_INVALID_PRIVATE_KEY).into())
            }
            CASS_ERROR_SSL_NO_PEER_CERT => Err(ErrorKind::SSL_NO_PEER_CERT(CASS_ERROR_SSL_NO_PEER_CERT).into()),
            CASS_ERROR_SSL_INVALID_PEER_CERT => {
                Err(ErrorKind::SSL_INVALID_PEER_CERT(CASS_ERROR_SSL_INVALID_PEER_CERT).into())
            }
            CASS_ERROR_SSL_IDENTITY_MISMATCH => {
                Err(ErrorKind::SSL_IDENTITY_MISMATCH(CASS_ERROR_SSL_IDENTITY_MISMATCH).into())
            }
            CASS_ERROR_SSL_PROTOCOL_ERROR => Err(ErrorKind::SSL_PROTOCOL_ERROR(CASS_ERROR_SSL_PROTOCOL_ERROR).into()),
            CASS_ERROR_LAST_ENTRY => Err(ErrorKind::LAST_ENTRY(CASS_ERROR_LAST_ENTRY).into()),
        }
    }
}
<commit_msg>whups needs to be pub<commit_after>use cassandra::CassError_;

error_chain! {
errors {
    LIB_BAD_PARAMS(t:CassError_){}
    LIB_NO_STREAMS (t:CassError_){}
    LIB_UNABLE_TO_INIT(t:CassError_){}
    LIB_MESSAGE_ENCODE(t:CassError_){}
    LIB_HOST_RESOLUTION(t:CassError_){}
    LIB_UNEXPECTED_RESPONSE(t:CassError_){}
    LIB_REQUEST_QUEUE_FULL(t:CassError_){}
    LIB_NO_AVAILABLE_IO_THREAD(t:CassError_){}
    LIB_WRITE_ERROR(t:CassError_){}
    LIB_NO_HOSTS_AVAILABLE(t:CassError_){}
    LIB_INDEX_OUT_OF_BOUNDS(t:CassError_){}
    LIB_INVALID_ITEM_COUNT(t:CassError_){}
    LIB_INVALID_VALUE_TYPE(t:CassError_){}
    LIB_REQUEST_TIMED_OUT(t:CassError_){}
    LIB_UNABLE_TO_SET_KEYSPACE(t:CassError_){}
    LIB_CALLBACK_ALREADY_SET(t:CassError_){}
    LIB_INVALID_STATEMENT_TYPE(t:CassError_){}
    LIB_NAME_DOES_NOT_EXIST(t:CassError_){}
    LIB_UNABLE_TO_DETERMINE_PROTOCOL(t:CassError_){}
    LIB_NULL_VALUE(t:CassError_){}
    LIB_NOT_IMPLEMENTED(t:CassError_){}
    LIB_UNABLE_TO_CONNECT(t:CassError_){}
    LIB_UNABLE_TO_CLOSE(t:CassError_){}
    LIB_NO_PAGING_STATE(t:CassError_){}
    LIB_PARAMETER_UNSET(t:CassError_){}
    LIB_INVALID_ERROR_RESULT_TYPE(t:CassError_){}
    LIB_INVALID_FUTURE_TYPE(t:CassError_){}
    LIB_INTERNAL_ERROR(t:CassError_){}
    LIB_INVALID_CUSTOM_TYPE(t:CassError_){}
    LIB_INVALID_DATA(t:CassError_){}
    LIB_NOT_ENOUGH_DATA(t:CassError_){}
    LIB_INVALID_STATE(t:CassError_){}
    LIB_NO_CUSTOM_PAYLOAD(t:CassError_){}
    SERVER_SERVER_ERROR(t:CassError_){}
    SERVER_PROTOCOL_ERROR(t:CassError_){}
    SERVER_BAD_CREDENTIALS(t:CassError_){}
    SERVER_UNAVAILABLE(t:CassError_){}
    SERVER_OVERLOADED(t:CassError_){}
    SERVER_IS_BOOTSTRAPPING(t:CassError_){}
    SERVER_TRUNCATE_ERROR(t:CassError_){}
    SERVER_WRITE_TIMEOUT(t:CassError_){}
    SERVER_READ_TIMEOUT(t:CassError_){}
    SERVER_READ_FAILURE(t:CassError_){}
    SERVER_FUNCTION_FAILURE(t:CassError_){}
    SERVER_WRITE_FAILURE(t:CassError_){}
    SERVER_SYNTAX_ERROR(t:CassError_){}
    SERVER_UNAUTHORIZED(t:CassError_){}
    SERVER_INVALID_QUERY(t:CassError_){}
    SERVER_CONFIG_ERROR(t:CassError_){}
    SERVER_ALREADY_EXISTS(t:CassError_){}
    SERVER_UNPREPARED(t:CassError_){}
    SSL_INVALID_CERT(t:CassError_){}
    SSL_INVALID_PRIVATE_KEY(t:CassError_){}
    SSL_NO_PEER_CERT(t:CassError_){}
    SSL_INVALID_PEER_CERT(t:CassError_){}
    SSL_IDENTITY_MISMATCH(t:CassError_){}
    SSL_PROTOCOL_ERROR(t:CassError_){}
    LAST_ENTRY(t:CassError_){}
    }
}

impl CassError_ {
    pub fn to_result<T>(self,T:T) -> Result<T> {
        use self::CassError_::*;
        match self {
            CASS_OK => Ok(T),
            CASS_ERROR_LIB_BAD_PARAMS => Err(ErrorKind::LIB_BAD_PARAMS(CASS_ERROR_LIB_BAD_PARAMS).into()),
            CASS_ERROR_LIB_NO_STREAMS => Err(ErrorKind::LIB_NO_STREAMS(CASS_ERROR_LIB_NO_STREAMS).into()),
            CASS_ERROR_LIB_UNABLE_TO_INIT => Err(ErrorKind::LIB_UNABLE_TO_INIT(CASS_ERROR_LIB_UNABLE_TO_INIT).into()),
            CASS_ERROR_LIB_MESSAGE_ENCODE => Err(ErrorKind::LIB_MESSAGE_ENCODE(CASS_ERROR_LIB_MESSAGE_ENCODE).into()),
            CASS_ERROR_LIB_HOST_RESOLUTION => {
                Err(ErrorKind::LIB_HOST_RESOLUTION(CASS_ERROR_LIB_HOST_RESOLUTION).into())
            }
            CASS_ERROR_LIB_UNEXPECTED_RESPONSE => {
                Err(ErrorKind::LIB_UNEXPECTED_RESPONSE(CASS_ERROR_LIB_UNEXPECTED_RESPONSE).into())
            }
            CASS_ERROR_LIB_REQUEST_QUEUE_FULL => {
                Err(ErrorKind::LIB_REQUEST_QUEUE_FULL(CASS_ERROR_LIB_UNEXPECTED_RESPONSE).into())
            }
            CASS_ERROR_LIB_NO_AVAILABLE_IO_THREAD => {
                Err(ErrorKind::LIB_NO_AVAILABLE_IO_THREAD(CASS_ERROR_LIB_NO_AVAILABLE_IO_THREAD).into())
            }
            CASS_ERROR_LIB_WRITE_ERROR => Err(ErrorKind::LIB_WRITE_ERROR(CASS_ERROR_LIB_WRITE_ERROR).into()),
            CASS_ERROR_LIB_NO_HOSTS_AVAILABLE => {
                Err(ErrorKind::LIB_NO_HOSTS_AVAILABLE(CASS_ERROR_LIB_NO_HOSTS_AVAILABLE).into())
            }
            CASS_ERROR_LIB_INDEX_OUT_OF_BOUNDS => {
                Err(ErrorKind::LIB_INDEX_OUT_OF_BOUNDS(CASS_ERROR_LIB_INDEX_OUT_OF_BOUNDS).into())
            }
            CASS_ERROR_LIB_INVALID_ITEM_COUNT => {
                Err(ErrorKind::LIB_INVALID_ITEM_COUNT(CASS_ERROR_LIB_INVALID_ITEM_COUNT).into())
            }
            CASS_ERROR_LIB_INVALID_VALUE_TYPE => {
                Err(ErrorKind::LIB_INVALID_VALUE_TYPE(CASS_ERROR_LIB_INVALID_VALUE_TYPE).into())
            }
            CASS_ERROR_LIB_REQUEST_TIMED_OUT => {
                Err(ErrorKind::LIB_REQUEST_TIMED_OUT(CASS_ERROR_LIB_REQUEST_TIMED_OUT).into())
            }
            CASS_ERROR_LIB_UNABLE_TO_SET_KEYSPACE => {
                Err(ErrorKind::LIB_UNABLE_TO_SET_KEYSPACE(CASS_ERROR_LIB_UNABLE_TO_SET_KEYSPACE).into())
            }
            CASS_ERROR_LIB_CALLBACK_ALREADY_SET => {
                Err(ErrorKind::LIB_CALLBACK_ALREADY_SET(CASS_ERROR_LIB_CALLBACK_ALREADY_SET).into())
            }
            CASS_ERROR_LIB_INVALID_STATEMENT_TYPE => {
                Err(ErrorKind::LIB_INVALID_STATEMENT_TYPE(CASS_ERROR_LIB_INVALID_STATEMENT_TYPE).into())
            }
            CASS_ERROR_LIB_NAME_DOES_NOT_EXIST => {
                Err(ErrorKind::LIB_NAME_DOES_NOT_EXIST(CASS_ERROR_LIB_NAME_DOES_NOT_EXIST).into())
            }
            CASS_ERROR_LIB_UNABLE_TO_DETERMINE_PROTOCOL => {
                Err(ErrorKind::LIB_UNABLE_TO_DETERMINE_PROTOCOL(CASS_ERROR_LIB_UNABLE_TO_DETERMINE_PROTOCOL).into())
            }
            CASS_ERROR_LIB_NULL_VALUE => Err(ErrorKind::LIB_NULL_VALUE(CASS_ERROR_LIB_NULL_VALUE).into()),
            CASS_ERROR_LIB_NOT_IMPLEMENTED => {
                Err(ErrorKind::LIB_NOT_IMPLEMENTED(CASS_ERROR_LIB_NOT_IMPLEMENTED).into())
            }
            CASS_ERROR_LIB_UNABLE_TO_CONNECT => {
                Err(ErrorKind::LIB_UNABLE_TO_CONNECT(CASS_ERROR_LIB_UNABLE_TO_CONNECT).into())
            }
            CASS_ERROR_LIB_UNABLE_TO_CLOSE => {
                Err(ErrorKind::LIB_UNABLE_TO_CLOSE(CASS_ERROR_LIB_UNABLE_TO_CLOSE).into())
            }
            CASS_ERROR_LIB_NO_PAGING_STATE => {
                Err(ErrorKind::LIB_NO_PAGING_STATE(CASS_ERROR_LIB_NO_PAGING_STATE).into())
            }
            CASS_ERROR_LIB_PARAMETER_UNSET => {
                Err(ErrorKind::LIB_PARAMETER_UNSET(CASS_ERROR_LIB_PARAMETER_UNSET).into())
            }
            CASS_ERROR_LIB_INVALID_ERROR_RESULT_TYPE => {
                Err(ErrorKind::LIB_INVALID_ERROR_RESULT_TYPE(CASS_ERROR_LIB_INVALID_ERROR_RESULT_TYPE).into())
            }
            CASS_ERROR_LIB_INVALID_FUTURE_TYPE => {
                Err(ErrorKind::LIB_INVALID_FUTURE_TYPE(CASS_ERROR_LIB_INVALID_FUTURE_TYPE).into())
            }
            CASS_ERROR_LIB_INTERNAL_ERROR => Err(ErrorKind::LIB_INTERNAL_ERROR(CASS_ERROR_LIB_INTERNAL_ERROR).into()),
            CASS_ERROR_LIB_INVALID_CUSTOM_TYPE => {
                Err(ErrorKind::LIB_INVALID_CUSTOM_TYPE(CASS_ERROR_LIB_INVALID_CUSTOM_TYPE).into())
            }
            CASS_ERROR_LIB_INVALID_DATA => Err(ErrorKind::LIB_INVALID_DATA(CASS_ERROR_LIB_INVALID_DATA).into()),
            CASS_ERROR_LIB_NOT_ENOUGH_DATA => {
                Err(ErrorKind::LIB_NOT_ENOUGH_DATA(CASS_ERROR_LIB_NOT_ENOUGH_DATA).into())
            }
            CASS_ERROR_LIB_INVALID_STATE => Err(ErrorKind::LIB_INVALID_STATE(CASS_ERROR_LIB_INVALID_STATE).into()),
            CASS_ERROR_LIB_NO_CUSTOM_PAYLOAD => {
                Err(ErrorKind::LIB_NO_CUSTOM_PAYLOAD(CASS_ERROR_LIB_NO_CUSTOM_PAYLOAD).into())
            }
            CASS_ERROR_SERVER_SERVER_ERROR => {
                Err(ErrorKind::SERVER_SERVER_ERROR(CASS_ERROR_SERVER_SERVER_ERROR).into())
            }
            CASS_ERROR_SERVER_PROTOCOL_ERROR => {
                Err(ErrorKind::SERVER_PROTOCOL_ERROR(CASS_ERROR_SERVER_PROTOCOL_ERROR).into())
            }
            CASS_ERROR_SERVER_BAD_CREDENTIALS => {
                Err(ErrorKind::SERVER_BAD_CREDENTIALS(CASS_ERROR_SERVER_BAD_CREDENTIALS).into())
            }
            CASS_ERROR_SERVER_UNAVAILABLE => Err(ErrorKind::SERVER_UNAVAILABLE(CASS_ERROR_SERVER_UNAVAILABLE).into()),
            CASS_ERROR_SERVER_OVERLOADED => Err(ErrorKind::SERVER_OVERLOADED(CASS_ERROR_SERVER_OVERLOADED).into()),
            CASS_ERROR_SERVER_IS_BOOTSTRAPPING => {
                Err(ErrorKind::SERVER_IS_BOOTSTRAPPING(CASS_ERROR_SERVER_IS_BOOTSTRAPPING).into())
            }
            CASS_ERROR_SERVER_TRUNCATE_ERROR => {
                Err(ErrorKind::SERVER_TRUNCATE_ERROR(CASS_ERROR_SERVER_TRUNCATE_ERROR).into())
            }
            CASS_ERROR_SERVER_WRITE_TIMEOUT => {
                Err(ErrorKind::SERVER_WRITE_TIMEOUT(CASS_ERROR_SERVER_WRITE_TIMEOUT).into())
            }
            CASS_ERROR_SERVER_READ_TIMEOUT => {
                Err(ErrorKind::SERVER_READ_TIMEOUT(CASS_ERROR_SERVER_READ_TIMEOUT).into())
            }
            CASS_ERROR_SERVER_READ_FAILURE => {
                Err(ErrorKind::SERVER_READ_FAILURE(CASS_ERROR_SERVER_READ_FAILURE).into())
            }
            CASS_ERROR_SERVER_FUNCTION_FAILURE => {
                Err(ErrorKind::SERVER_FUNCTION_FAILURE(CASS_ERROR_SERVER_FUNCTION_FAILURE).into())
            }
            CASS_ERROR_SERVER_WRITE_FAILURE => {
                Err(ErrorKind::SERVER_WRITE_FAILURE(CASS_ERROR_SERVER_WRITE_FAILURE).into())
            }
            CASS_ERROR_SERVER_SYNTAX_ERROR => {
                Err(ErrorKind::SERVER_SYNTAX_ERROR(CASS_ERROR_SERVER_WRITE_FAILURE).into())
            }
            CASS_ERROR_SERVER_UNAUTHORIZED => {
                Err(ErrorKind::SERVER_UNAUTHORIZED(CASS_ERROR_SERVER_UNAUTHORIZED).into())
            }
            CASS_ERROR_SERVER_INVALID_QUERY => {
                Err(ErrorKind::SERVER_INVALID_QUERY(CASS_ERROR_SERVER_INVALID_QUERY).into())
            }
            CASS_ERROR_SERVER_CONFIG_ERROR => {
                Err(ErrorKind::SERVER_CONFIG_ERROR(CASS_ERROR_SERVER_CONFIG_ERROR).into())
            }
            CASS_ERROR_SERVER_ALREADY_EXISTS => {
                Err(ErrorKind::SERVER_ALREADY_EXISTS(CASS_ERROR_SERVER_ALREADY_EXISTS).into())
            }
            CASS_ERROR_SERVER_UNPREPARED => Err(ErrorKind::SERVER_UNPREPARED(CASS_ERROR_SERVER_UNPREPARED).into()),
            CASS_ERROR_SSL_INVALID_CERT => Err(ErrorKind::SSL_INVALID_CERT(CASS_ERROR_SSL_INVALID_CERT).into()),
            CASS_ERROR_SSL_INVALID_PRIVATE_KEY => {
                Err(ErrorKind::SSL_INVALID_PRIVATE_KEY(CASS_ERROR_SSL_INVALID_PRIVATE_KEY).into())
            }
            CASS_ERROR_SSL_NO_PEER_CERT => Err(ErrorKind::SSL_NO_PEER_CERT(CASS_ERROR_SSL_NO_PEER_CERT).into()),
            CASS_ERROR_SSL_INVALID_PEER_CERT => {
                Err(ErrorKind::SSL_INVALID_PEER_CERT(CASS_ERROR_SSL_INVALID_PEER_CERT).into())
            }
            CASS_ERROR_SSL_IDENTITY_MISMATCH => {
                Err(ErrorKind::SSL_IDENTITY_MISMATCH(CASS_ERROR_SSL_IDENTITY_MISMATCH).into())
            }
            CASS_ERROR_SSL_PROTOCOL_ERROR => Err(ErrorKind::SSL_PROTOCOL_ERROR(CASS_ERROR_SSL_PROTOCOL_ERROR).into()),
            CASS_ERROR_LAST_ENTRY => Err(ErrorKind::LAST_ENTRY(CASS_ERROR_LAST_ENTRY).into()),
        }
    }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::time::Instant;

use glium::glutin::{Event, WindowEvent, KeyboardInput, ElementState, MouseButton, VirtualKeyCode};

pub struct InputState {
    /// Position of the mouse
    pub mouse_pos: (f64, f64),
    /// Movement of the mouse
    pub d_mouse: (f64, f64),
    /// Currently pressed mouse buttons with the time of the press
    pub mouse_presses: HashMap<MouseButton, Instant>,
    /// Currently pressed keys with the time of the press
    pub key_presses: HashMap<VirtualKeyCode, Instant>,
    /// Time of the last reset
    pub last_reset: Instant
}

impl InputState {
    /// Get a new empty input state
    pub fn new() -> InputState {
        InputState { mouse_pos: (0.0, 0.0),
                     d_mouse: (0.0, 0.0),
                     mouse_presses: HashMap::new(),
                     key_presses: HashMap::new(),
                     last_reset: Instant::now()
        }
    }

    /// Update the state with an event
    pub fn update(&mut self, event: &Event) {
        match *event {
            Event::WindowEvent{ref event, ..} => {
                match *event {
                    WindowEvent::MouseMoved{position:(x, y), ..} => {
                        self.d_mouse = (x - self.mouse_pos.0, y - self.mouse_pos.1);
                        self.mouse_pos = (x, y);
                    }
                    WindowEvent::MouseInput{state: ElementState::Pressed, button, ..} => {
                        self.mouse_presses.entry(button).or_insert_with(Instant::now);
                    }
                    WindowEvent::MouseInput{state: ElementState::Released, button, ..} => {
                        self.mouse_presses.remove(&button);
                    }
                    WindowEvent::KeyboardInput{input, ..} => {
                        match input {
                            KeyboardInput{state: ElementState::Pressed, virtual_keycode: Some(key), ..} => {
                                self.key_presses.entry(key).or_insert_with(Instant::now);
                            }
                            KeyboardInput{state: ElementState::Released, virtual_keycode: Some(key), ..} => {
                                self.key_presses.remove(&key);
                            }
                            _ => ()
                        }
                    }
                    WindowEvent::Focused(false) => {
                        self.mouse_presses.clear();
                        self.key_presses.clear();
                    }
                    _ => ()
                }
            }
            _ => ()
        }
    }

    /// Reset the delta values after a loop
    pub fn reset_deltas(&mut self) {
        self.d_mouse = (0.0, 0.0);
        self.last_reset = Instant::now();
    }
}
<commit_msg>Fix single arm match<commit_after>use std::collections::HashMap;
use std::time::Instant;

use glium::glutin::{Event, WindowEvent, KeyboardInput, ElementState, MouseButton, VirtualKeyCode};

pub struct InputState {
    /// Position of the mouse
    pub mouse_pos: (f64, f64),
    /// Movement of the mouse
    pub d_mouse: (f64, f64),
    /// Currently pressed mouse buttons with the time of the press
    pub mouse_presses: HashMap<MouseButton, Instant>,
    /// Currently pressed keys with the time of the press
    pub key_presses: HashMap<VirtualKeyCode, Instant>,
    /// Time of the last reset
    pub last_reset: Instant
}

impl InputState {
    /// Get a new empty input state
    pub fn new() -> InputState {
        InputState { mouse_pos: (0.0, 0.0),
                     d_mouse: (0.0, 0.0),
                     mouse_presses: HashMap::new(),
                     key_presses: HashMap::new(),
                     last_reset: Instant::now()
        }
    }

    /// Update the state with an event
    pub fn update(&mut self, event: &Event) {
        if let Event::WindowEvent{ref event, ..} = *event {
            match *event {
                WindowEvent::MouseMoved{position:(x, y), ..} => {
                    self.d_mouse = (x - self.mouse_pos.0, y - self.mouse_pos.1);
                    self.mouse_pos = (x, y);
                }
                WindowEvent::MouseInput{state: ElementState::Pressed, button, ..} => {
                    self.mouse_presses.entry(button).or_insert_with(Instant::now);
                }
                WindowEvent::MouseInput{state: ElementState::Released, button, ..} => {
                    self.mouse_presses.remove(&button);
                }
                WindowEvent::KeyboardInput{input, ..} => {
                    match input {
                        KeyboardInput{state: ElementState::Pressed, virtual_keycode: Some(key), ..} => {
                            self.key_presses.entry(key).or_insert_with(Instant::now);
                        }
                        KeyboardInput{state: ElementState::Released, virtual_keycode: Some(key), ..} => {
                            self.key_presses.remove(&key);
                        }
                        _ => ()
                    }
                }
                WindowEvent::Focused(false) => {
                    self.mouse_presses.clear();
                    self.key_presses.clear();
                }
                _ => ()
            }
        }
    }

    /// Reset the delta values after a loop
    pub fn reset_deltas(&mut self) {
        self.d_mouse = (0.0, 0.0);
        self.last_reset = Instant::now();
    }
}
<|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.

#![allow(dead_code)]

use libc::{c_int, c_ulong, ioctl};
use std::io;
use std::mem::size_of;
use std::result;

pub type Result<T> = result::Result<T, io::Error>;

const NRBITS: u8 = 8;
const TYPEBITS: u8 = 8;
const SIZEBITS: u8 = 14;
const DIRBITS: u8 = 2;

const NRSHIFT: u8 = 0;
const TYPESHIFT: u8 = (NRSHIFT + NRBITS);
const SIZESHIFT: u8 = (TYPESHIFT + TYPEBITS);
const DIRSHIFT: u8 = (SIZESHIFT + SIZEBITS);

const DIR_NONE: c_ulong = 0;
const DIR_WRITE: c_ulong = 1 << DIRSHIFT;
const DIR_READ: c_ulong = 2 << DIRSHIFT;

const SIZE_U8: c_ulong = (size_of::<u8>() as c_ulong) << SIZESHIFT;
const SIZE_U32: c_ulong = (size_of::<u32>() as c_ulong) << SIZESHIFT;

fn parse_retval(retval: c_int) -> Result<i32> {
    if retval == -1 {
        Err(io::Error::last_os_error())
    } else {
        Ok(retval)
    }
}

pub mod spidev {
    use super::*;

    const TYPE_SPI: c_ulong = (b'k' as c_ulong) << TYPESHIFT;

    const NR_MESSAGE: c_ulong = 0 << NRSHIFT;
    const NR_MODE: c_ulong = 1 << NRSHIFT;
    const NR_LSB_FIRST: c_ulong = 2 << NRSHIFT;
    const NR_BITS_PER_WORD: c_ulong = 3 << NRSHIFT;
    const NR_MAX_SPEED_HZ: c_ulong = 4 << NRSHIFT;
    const NR_MODE32: c_ulong = 5 << NRSHIFT;

    const REQ_RD_MODE: c_ulong = (DIR_READ | TYPE_SPI | NR_MODE | SIZE_U8);
    const REQ_RD_LSB_FIRST: c_ulong = (DIR_READ | TYPE_SPI | NR_LSB_FIRST | SIZE_U8);
    const REQ_RD_BITS_PER_WORD: c_ulong = (DIR_READ | TYPE_SPI | NR_BITS_PER_WORD | SIZE_U8);
    const REQ_RD_MAX_SPEED_HZ: c_ulong = (DIR_READ | TYPE_SPI | NR_MAX_SPEED_HZ | SIZE_U32);
    const REQ_RD_MODE_32: c_ulong = (DIR_READ | TYPE_SPI | NR_MODE32 | SIZE_U32);

    const REQ_WR_MODE: c_ulong = (DIR_WRITE | TYPE_SPI | NR_MODE | SIZE_U8);
    const REQ_WR_LSB_FIRST: c_ulong = (DIR_WRITE | TYPE_SPI | NR_LSB_FIRST | SIZE_U8);
    const REQ_WR_BITS_PER_WORD: c_ulong = (DIR_WRITE | TYPE_SPI | NR_BITS_PER_WORD | SIZE_U8);
    const REQ_WR_MAX_SPEED_HZ: c_ulong = (DIR_WRITE | TYPE_SPI | NR_MAX_SPEED_HZ | SIZE_U32);
    const REQ_WR_MODE_32: c_ulong = (DIR_WRITE | TYPE_SPI | NR_MODE32 | SIZE_U32);

    const REQ_WR_MESSAGE: c_ulong = (DIR_WRITE | TYPE_SPI | NR_MESSAGE);

    pub const MODE_CPHA: u8 = 0x01;
    pub const MODE_CPOL: u8 = 0x02;

    pub const MODE_0: u8 = 0;
    pub const MODE_1: u8 = MODE_CPHA;
    pub const MODE_2: u8 = MODE_CPOL;
    pub const MODE_3: u8 = MODE_CPOL | MODE_CPHA;

    pub const MODE_CS_HIGH: u8 = 0x04;
    pub const MODE_LSB_FIRST: u8 = 0x08;
    pub const MODE_3WIRE: u8 = 0x10;
    pub const MODE_LOOP: u8 = 0x20;
    pub const MODE_NO_CS: u8 = 0x40;
    pub const MODE_READY: u8 = 0x80;
    pub const MODE_TX_DUAL: u32 = 0x100;
    pub const MODE_TX_QUAD: u32 = 0x200;
    pub const MODE_RX_DUAL: u32 = 0x400;
    pub const MODE_RX_QUAD: u32 = 0x800;

    #[derive(Debug, PartialEq, Copy, Clone)]
    #[repr(C)]
    pub struct TransferSegment {
        // Pointer to transmit buffer, or 0.
        tx_buf: u64,
        // Pointer to receive buffer, or 0.
        rx_buf: u64,
        // Number of bytes to transfer in this segment.
        len: u32,
        // Set a different clock speed for this segment. Default = 0.
        speed_hz: u32,
        // Add a delay before the (optional) SS change and the next segment.
        delay_usecs: u16,
        // Not used, since we only support 8 bits (or 9 bits in LoSSI mode). Default = 0.
        bits_per_word: u8,
        // Set to 1 to briefly set SS High (inactive) between this segment and the next. If this is the last segment, keep SS Low (active).
        cs_change: u8,
        // Number of bits used for writing (dual/quad SPI). Default = 0.
        tx_nbits: u8,
        // Number of bits used for reading (dual/quad SPI). Default = 0.
        rx_nbits: u8,
        // Padding. Set to 0 for forward compatibility.
        pad: u16,
    }

    impl TransferSegment {
        pub fn new(read_buffer: Option<&mut [u8]>, write_buffer: Option<&[u8]>) -> TransferSegment {
            // Len will contain the length of the shortest of the supplied buffers
            let mut len: u32 = 0;

            let tx_buf = if let Some(buffer) = write_buffer {
                len = buffer.len() as u32;
                buffer.as_ptr() as u64
            } else {
                0
            };

            let rx_buf = if let Some(buffer) = read_buffer {
                if len > buffer.len() as u32 {
                    len = buffer.len() as u32;
                }
                buffer.as_ptr() as u64
            } else {
                0
            };

            TransferSegment {
                tx_buf,
                rx_buf,
                len,
                speed_hz: 0,
                delay_usecs: 0,
                bits_per_word: 0,
                cs_change: 0,
                tx_nbits: 0,
                rx_nbits: 0,
                pad: 0,
            }
        }

        pub fn len(&self) -> u32 {
            self.len
        }
    }

    pub unsafe fn mode(fd: c_int, value: &mut u8) -> Result<i32> {
        parse_retval(ioctl(fd, REQ_RD_MODE, value))
    }

    pub unsafe fn set_mode(fd: c_int, value: u8) -> Result<i32> {
        parse_retval(ioctl(fd, REQ_WR_MODE, &value))
    }

    pub unsafe fn lsb_first(fd: c_int, value: &mut u8) -> Result<i32> {
        parse_retval(ioctl(fd, REQ_RD_LSB_FIRST, value))
    }

    pub unsafe fn set_lsb_first(fd: c_int, value: u8) -> Result<i32> {
        parse_retval(ioctl(fd, REQ_WR_LSB_FIRST, &value))
    }

    pub unsafe fn bits_per_word(fd: c_int, value: &mut u8) -> Result<i32> {
        parse_retval(ioctl(fd, REQ_RD_BITS_PER_WORD, value))
    }

    pub unsafe fn set_bits_per_word(fd: c_int, value: u8) -> Result<i32> {
        parse_retval(ioctl(fd, REQ_WR_BITS_PER_WORD, &value))
    }

    pub unsafe fn clock_speed(fd: c_int, value: &mut u32) -> Result<i32> {
        parse_retval(ioctl(fd, REQ_RD_MAX_SPEED_HZ, value))
    }

    pub unsafe fn set_clock_speed(fd: c_int, value: u32) -> Result<i32> {
        parse_retval(ioctl(fd, REQ_WR_MAX_SPEED_HZ, &value))
    }

    pub unsafe fn mode32(fd: c_int, value: &mut u32) -> Result<i32> {
        parse_retval(ioctl(fd, REQ_RD_MODE_32, value))
    }

    pub unsafe fn set_mode32(fd: c_int, value: u32) -> Result<i32> {
        parse_retval(ioctl(fd, REQ_WR_MODE_32, &value))
    }

    pub unsafe fn transfer(fd: c_int, segments: &[TransferSegment]) -> Result<i32> {
        parse_retval(ioctl(
            fd,
            REQ_WR_MESSAGE
                | (((segments.len() * size_of::<TransferSegment>()) as c_ulong) << SIZESHIFT),
            segments,
        ))
    }
}
<commit_msg>Add PhantomData fields to link buffer lifetimes<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.

#![allow(dead_code)]

use libc::{c_int, c_ulong, ioctl};
use std::io;
use std::marker;
use std::mem::size_of;
use std::result;

pub type Result<T> = result::Result<T, io::Error>;

const NRBITS: u8 = 8;
const TYPEBITS: u8 = 8;
const SIZEBITS: u8 = 14;
const DIRBITS: u8 = 2;

const NRSHIFT: u8 = 0;
const TYPESHIFT: u8 = (NRSHIFT + NRBITS);
const SIZESHIFT: u8 = (TYPESHIFT + TYPEBITS);
const DIRSHIFT: u8 = (SIZESHIFT + SIZEBITS);

const DIR_NONE: c_ulong = 0;
const DIR_WRITE: c_ulong = 1 << DIRSHIFT;
const DIR_READ: c_ulong = 2 << DIRSHIFT;

const SIZE_U8: c_ulong = (size_of::<u8>() as c_ulong) << SIZESHIFT;
const SIZE_U32: c_ulong = (size_of::<u32>() as c_ulong) << SIZESHIFT;

fn parse_retval(retval: c_int) -> Result<i32> {
    if retval == -1 {
        Err(io::Error::last_os_error())
    } else {
        Ok(retval)
    }
}

pub mod spidev {
    use super::*;

    const TYPE_SPI: c_ulong = (b'k' as c_ulong) << TYPESHIFT;

    const NR_MESSAGE: c_ulong = 0 << NRSHIFT;
    const NR_MODE: c_ulong = 1 << NRSHIFT;
    const NR_LSB_FIRST: c_ulong = 2 << NRSHIFT;
    const NR_BITS_PER_WORD: c_ulong = 3 << NRSHIFT;
    const NR_MAX_SPEED_HZ: c_ulong = 4 << NRSHIFT;
    const NR_MODE32: c_ulong = 5 << NRSHIFT;

    const REQ_RD_MODE: c_ulong = (DIR_READ | TYPE_SPI | NR_MODE | SIZE_U8);
    const REQ_RD_LSB_FIRST: c_ulong = (DIR_READ | TYPE_SPI | NR_LSB_FIRST | SIZE_U8);
    const REQ_RD_BITS_PER_WORD: c_ulong = (DIR_READ | TYPE_SPI | NR_BITS_PER_WORD | SIZE_U8);
    const REQ_RD_MAX_SPEED_HZ: c_ulong = (DIR_READ | TYPE_SPI | NR_MAX_SPEED_HZ | SIZE_U32);
    const REQ_RD_MODE_32: c_ulong = (DIR_READ | TYPE_SPI | NR_MODE32 | SIZE_U32);

    const REQ_WR_MODE: c_ulong = (DIR_WRITE | TYPE_SPI | NR_MODE | SIZE_U8);
    const REQ_WR_LSB_FIRST: c_ulong = (DIR_WRITE | TYPE_SPI | NR_LSB_FIRST | SIZE_U8);
    const REQ_WR_BITS_PER_WORD: c_ulong = (DIR_WRITE | TYPE_SPI | NR_BITS_PER_WORD | SIZE_U8);
    const REQ_WR_MAX_SPEED_HZ: c_ulong = (DIR_WRITE | TYPE_SPI | NR_MAX_SPEED_HZ | SIZE_U32);
    const REQ_WR_MODE_32: c_ulong = (DIR_WRITE | TYPE_SPI | NR_MODE32 | SIZE_U32);

    const REQ_WR_MESSAGE: c_ulong = (DIR_WRITE | TYPE_SPI | NR_MESSAGE);

    pub const MODE_CPHA: u8 = 0x01;
    pub const MODE_CPOL: u8 = 0x02;

    pub const MODE_0: u8 = 0;
    pub const MODE_1: u8 = MODE_CPHA;
    pub const MODE_2: u8 = MODE_CPOL;
    pub const MODE_3: u8 = MODE_CPOL | MODE_CPHA;

    pub const MODE_CS_HIGH: u8 = 0x04;
    pub const MODE_LSB_FIRST: u8 = 0x08;
    pub const MODE_3WIRE: u8 = 0x10;
    pub const MODE_LOOP: u8 = 0x20;
    pub const MODE_NO_CS: u8 = 0x40;
    pub const MODE_READY: u8 = 0x80;
    pub const MODE_TX_DUAL: u32 = 0x100;
    pub const MODE_TX_QUAD: u32 = 0x200;
    pub const MODE_RX_DUAL: u32 = 0x400;
    pub const MODE_RX_QUAD: u32 = 0x800;

    #[derive(Debug, PartialEq, Copy, Clone)]
    #[repr(C)]
    pub struct TransferSegment<'a, 'b> {
        // Pointer to transmit buffer, or 0.
        tx_buf: u64,
        // Pointer to receive buffer, or 0.
        rx_buf: u64,
        // Number of bytes to transfer in this segment.
        len: u32,
        // Set a different clock speed for this segment. Default = 0.
        speed_hz: u32,
        // Add a delay before the (optional) SS change and the next segment.
        delay_usecs: u16,
        // Bits per word for this segment. The Pi only supports 8 bits (or 9 bits in LoSSI mode). Default = 0.
        bits_per_word: u8,
        // Set to 1 to briefly set SS inactive between this segment and the next. If this is the last segment, keep SS active.
        cs_change: u8,
        // Number of outgoing lines used for dual/quad SPI. Default = 0.
        tx_nbits: u8,
        // Number of incoming lines used for dual/quad SPI. Default = 0.
        rx_nbits: u8,
        // Padding. Set to 0 for forward compatibility.
        pad: u16,
        // Zero-sized variables used to link this struct to the read/write buffer lifetimes.
        read_buffer_lifetime: marker::PhantomData<&'a mut [u8]>,
        write_buffer_lifetime: marker::PhantomData<&'b [u8]>,
    }

    impl<'a, 'b> TransferSegment<'a, 'b> {
        pub fn new(
            read_buffer: Option<&'a mut [u8]>,
            write_buffer: Option<&'b [u8]>,
        ) -> TransferSegment<'a, 'b> {
            TransferSegment::with_settings(read_buffer, write_buffer, 0, 0, 0, false, 0, 0)
        }

        pub fn with_settings(
            read_buffer: Option<&'a mut [u8]>,
            write_buffer: Option<&'b [u8]>,
            clock_speed: u32,
            delay: u16,
            bits_per_word: u8,
            cs_change: bool,
            tx_nbits: u8,
            rx_nbits: u8,
        ) -> TransferSegment<'a, 'b> {
            // Len will contain the length of the shortest of the supplied buffers
            let mut len: u32 = 0;

            let tx_buf = if let Some(buffer) = write_buffer {
                len = buffer.len() as u32;
                buffer.as_ptr() as u64
            } else {
                0
            };

            let rx_buf = if let Some(buffer) = read_buffer {
                if len > buffer.len() as u32 {
                    len = buffer.len() as u32;
                }
                buffer.as_ptr() as u64
            } else {
                0
            };

            TransferSegment {
                tx_buf,
                rx_buf,
                len,
                speed_hz: clock_speed,
                delay_usecs: delay,
                bits_per_word,
                cs_change: cs_change as u8,
                tx_nbits,
                rx_nbits,
                pad: 0,
                read_buffer_lifetime: marker::PhantomData,
                write_buffer_lifetime: marker::PhantomData,
            }
        }

        pub fn len(&self) -> u32 {
            self.len
        }

        pub fn clock_speed(&self) -> u32 {
            self.speed_hz
        }

        pub fn set_clock_speed(&mut self, clock_speed: u32) {
            self.speed_hz = clock_speed;
        }

        pub fn delay(&self) -> u16 {
            self.delay_usecs
        }

        pub fn set_delay(&mut self, delay: u16) {
            self.delay_usecs = delay;
        }

        pub fn bits_per_word(&self) -> u8 {
            self.bits_per_word
        }

        pub fn set_bits_per_word(&mut self, bits_per_word: u8) {
            self.bits_per_word = bits_per_word;
        }

        pub fn cs_change(&self) -> bool {
            self.cs_change == 1
        }

        pub fn set_cs_change(&mut self, cs_change: bool) {
            self.cs_change = cs_change as u8;
        }

        pub fn rx_nbits(&self) -> u8 {
            self.rx_nbits
        }

        pub fn set_rx_nbits(&mut self, rx_nbits: u8) {
            self.rx_nbits = rx_nbits;
        }

        pub fn tx_nbits(&self) -> u8 {
            self.tx_nbits
        }

        pub fn set_tx_nbits(&mut self, tx_nbits: u8) {
            self.tx_nbits = tx_nbits;
        }
    }

    pub unsafe fn mode(fd: c_int, value: &mut u8) -> Result<i32> {
        parse_retval(ioctl(fd, REQ_RD_MODE, value))
    }

    pub unsafe fn set_mode(fd: c_int, value: u8) -> Result<i32> {
        parse_retval(ioctl(fd, REQ_WR_MODE, &value))
    }

    pub unsafe fn lsb_first(fd: c_int, value: &mut u8) -> Result<i32> {
        parse_retval(ioctl(fd, REQ_RD_LSB_FIRST, value))
    }

    pub unsafe fn set_lsb_first(fd: c_int, value: u8) -> Result<i32> {
        parse_retval(ioctl(fd, REQ_WR_LSB_FIRST, &value))
    }

    pub unsafe fn bits_per_word(fd: c_int, value: &mut u8) -> Result<i32> {
        parse_retval(ioctl(fd, REQ_RD_BITS_PER_WORD, value))
    }

    pub unsafe fn set_bits_per_word(fd: c_int, value: u8) -> Result<i32> {
        parse_retval(ioctl(fd, REQ_WR_BITS_PER_WORD, &value))
    }

    pub unsafe fn clock_speed(fd: c_int, value: &mut u32) -> Result<i32> {
        parse_retval(ioctl(fd, REQ_RD_MAX_SPEED_HZ, value))
    }

    pub unsafe fn set_clock_speed(fd: c_int, value: u32) -> Result<i32> {
        parse_retval(ioctl(fd, REQ_WR_MAX_SPEED_HZ, &value))
    }

    pub unsafe fn mode32(fd: c_int, value: &mut u32) -> Result<i32> {
        parse_retval(ioctl(fd, REQ_RD_MODE_32, value))
    }

    pub unsafe fn set_mode32(fd: c_int, value: u32) -> Result<i32> {
        parse_retval(ioctl(fd, REQ_WR_MODE_32, &value))
    }

    pub unsafe fn transfer(fd: c_int, segments: &[TransferSegment]) -> Result<i32> {
        parse_retval(ioctl(
            fd,
            REQ_WR_MESSAGE
                | (((segments.len() * size_of::<TransferSegment>()) as c_ulong) << SIZESHIFT),
            segments,
        ))
    }
}
<|endoftext|>


<commit_before>// Copyright © 2015 Sebastian Thiel
//
// 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.

// Base a lexer for LVM2's text format on the more complex (hah) json format.
// This code is based on https://github.com/Byron/json-tools.

/// A lexer for utf-8 encoded json data
pub struct Lexer<I: IntoIterator<Item=u8>> {
    chars: I::IntoIter,
    next_byte: Option<u8>,
    cursor: u64,
    buffer_type: BufferType,
}

#[derive(Debug, PartialEq, Clone)]
pub enum TokenType {
    /// `{`
    CurlyOpen,
    /// `}`
    CurlyClose,

    /// `[`
    BracketOpen,
    /// `]`
    BracketClose,

    /// `:`
    Colon,
    /// `,`
    Comma,

    /// A json string , like `"foo"`
    String,
    /// `true`
    BooleanTrue,
    /// `false`
    BooleanFalse,
    /// A Number, like `1.1234` or `123` or `-0.0` or `-1` or `.0` or `.`
    Number,

    /// any json number, like `1.24123` or `123`
    // NOTE: We can't do numbers with our simplified lexer as it would require
    // us to read a byte just to see that it's not a number and thus the previous
    // tokens are to be returned. But we cannot peek without drastically complicating
    // our so far quite speedy implementation.
    // Number,
    /// `null`
    Null,

    /// The type of the token could not be identified.
    /// Should be removed if this lexer is ever to be feature complete
    Invalid,
}

impl AsRef<str> for TokenType {
    fn as_ref(&self) -> &str {
        match *self {
            TokenType::CurlyOpen => "{",
            TokenType::CurlyClose => "}",
            TokenType::BracketOpen => "[",
            TokenType::BracketClose => "]",
            TokenType::Colon => ":",
            TokenType::Comma => ",",
            TokenType::BooleanTrue => "true",
            TokenType::BooleanFalse => "false",
            TokenType::Null => "null",

            TokenType::Invalid => panic!("Cannot convert invalid TokenType"),
            _ => panic!("Cannot convert variant TokenTypes"),
        }
    }
}

/// A pair of indices into the byte stream returned by our source
/// iterator.
/// It is an exclusive range.
#[derive(Debug, PartialEq, Clone, Default)]
pub struct Span {
    /// Index of the first the byte
    pub first: u64,
    /// Index one past the last byte
    pub end: u64,
}

/// A lexical token, identifying its kind and span.
#[derive(Debug, PartialEq, Clone)]
pub struct Token {
    /// The exact type of the token
    pub kind: TokenType,

    /// A buffer representing the bytes of this Token.
    pub buf: Buffer
}

/// Representation of a buffer containing items making up a `Token`.
///
/// It's either always `Span`, or one of the `*Byte` variants.
#[derive(Debug, PartialEq, Clone)]
pub enum Buffer {
    /// Multiple bytes making up a token. Only set for `TokenType::String` and
    /// `TokenType::Number`.
    MultiByte(Vec<u8>),
    /// The span allows to reference back into the source byte stream
    /// to obtain the string making up the token.
    /// Please note that for control characters, booleans and null (i.e
    /// anything that is not `Buffer::MultiByte` you should use
    /// `<TokenType as AsRef<str>>::as_ref()`)
    Span(Span),
}

/// The type of `Buffer` you want in each `Token`
#[derive(Debug, PartialEq, Clone)]
pub enum BufferType {
    /// Use a `Buffer::MultiByte` were appropriate. Initialize it with the
    /// given capcity (to obtain higher performance when pushing charcters)
    Bytes(usize),
    Span,
}

impl<I> Lexer<I> where I: IntoIterator<Item=u8> {
    /// Returns a new Lexer from a given byte iterator.
    pub fn new(chars: I, buffer_type: BufferType) -> Lexer<I> {
        Lexer {
            chars: chars.into_iter(),
            next_byte: None,
            cursor: 0,
            buffer_type: buffer_type,
        }
    }

    pub fn into_inner(self) -> I::IntoIter {
        self.chars
    }

    fn put_back(&mut self, c: u8) {
        debug_assert!(self.next_byte.is_none());
        self.next_byte = Some(c);
        self.cursor -= 1;
    }

    fn next_byte(&mut self) -> Option<u8> {
        match self.next_byte.take() {
            Some(c) => {
                self.cursor += 1;
                Some(c)
            },
            None => {
                let res = self.chars.next();
                match res {
                    None => None,
                    Some(_) => {
                        self.cursor += 1;
                        res
                    }
                }
            }
        }
    }
}

// Identifies the state of the lexer
enum Mode {
    // String parse mode: bool = ignore_next
    String(bool),
    // `null` parse mode: buf, buf-index
    Null([u8; 4], usize),
    // `true` parse mode
    True([u8; 4], usize),
    // `false` parse mode
    False([u8; 5], usize),
    // `Number` parse mode
    Number,
    SlowPath,
}

impl<I> Iterator for Lexer<I>
                    where I: IntoIterator<Item=u8> {
    type Item = Token;

    /// Lex the underlying bytte stream to generate tokens
    fn next(&mut self) -> Option<Token> {
        let mut t: Option<TokenType> = None;

        let mut first = 0;
        let mut state = Mode::SlowPath;
        let last_cursor = self.cursor;
        let mut buf =
            match self.buffer_type {
                BufferType::Bytes(capacity) => Some(Vec::<u8>::with_capacity(capacity)),
                BufferType::Span => None,
            };

        while let Some(c) = self.next_byte() {
            let mut set_cursor = |cursor| {
                first = cursor - 1;
            };

            match state {
                Mode::String(ref mut ign_next) => {
                    if let Some(ref mut v) = buf {
                        v.push(c);
                    }
                    if *ign_next && (c == b'"' || c == b'\\') {
                        *ign_next = false;
                        continue;
                    }
                    match c {
                        b'"' => {
                            t = Some(TokenType::String);
                            break;
                        },
                        b'\\' => {
                            *ign_next = true;
                            continue;
                        },
                        _ => {
                            continue;
                        }
                    }
                },
                Mode::Null(ref mut b, ref mut i) => {
                    b[*i] = c;
                    if *i == 3 {
                        // we know b[0] is b'n'
                        if b[1] == b'u' && b[2] == b'l' && b[3] == b'l' {
                            t = Some(TokenType::Null);
                        } else {
                            t = Some(TokenType::Invalid);
                        }
                        break;
                    } else {
                        *i += 1;
                        continue;
                    }
                },
                Mode::Number => {
                    match c {
                         b'0' ... b'9'
                        |b'-'
                        |b'.' => {
                            if let Some(ref mut v) = buf {
                                v.push(c);
                            }
                            continue;
                        },
                        _ => {
                            t = Some(TokenType::Number);
                            self.put_back(c);
                            break;
                        }
                    }
                }
                Mode::True(ref mut b, ref mut i) => {
                    b[*i] = c;
                    if *i == 3 {
                        // we know b[0] is b't'
                        if b[1] == b'r' && b[2] == b'u' && b[3] == b'e' {
                            t = Some(TokenType::BooleanTrue);
                        } else {
                            t = Some(TokenType::Invalid);
                        }
                        break;
                    } else {
                        *i += 1;
                        continue;
                    }
                },
                Mode::False(ref mut b, ref mut i) => {
                    b[*i] = c;
                    if *i == 4 {
                        // we know b[0] is b'f'
                        if b[1] == b'a' && b[2] == b'l' && b[3] == b's' && b[4] == b'e' {
                            t = Some(TokenType::BooleanFalse);
                        } else {
                            t = Some(TokenType::Invalid);
                        }
                        break;
                    } else {
                        *i += 1;
                        continue;
                    }
                },
                Mode::SlowPath => {
                    match c {
                        b'{' => { t = Some(TokenType::CurlyOpen); set_cursor(self.cursor); break; },
                        b'}' => { t = Some(TokenType::CurlyClose); set_cursor(self.cursor); break; },
                        b'"' => {
                            state = Mode::String(false);
                            if let Some(ref mut v) = buf {
                                v.push(c);
                            } else {
                                set_cursor(self.cursor);
                                // it starts at invalid, and once we know it closes, it's a string
                                t = Some(TokenType::Invalid);
                            }
                        },
                        b'n' => {
                            state = Mode::Null([c, b'x', b'x', b'x'], 1);
                            set_cursor(self.cursor);
                        },
                         b'0' ... b'9'
                        |b'-'
                        |b'.'=> {
                            state = Mode::Number;
                            if let Some(ref mut v) = buf {
                                v.push(c);
                            } else {
                                set_cursor(self.cursor);
                            }
                        },
                        b't' => {
                            state = Mode::True([c, b'x', b'x', b'x'], 1);
                            set_cursor(self.cursor);
                        },
                        b'f' => {
                            state = Mode::False([c, b'x', b'x', b'x', b'x'], 1);
                            set_cursor(self.cursor);
                        },
                        b'[' => { t = Some(TokenType::BracketOpen); set_cursor(self.cursor); break; },
                        b']' => { t = Some(TokenType::BracketClose); set_cursor(self.cursor); break; },
                        b':' => { t = Some(TokenType::Colon); set_cursor(self.cursor); break; },
                        b',' => { t = Some(TokenType::Comma); set_cursor(self.cursor); break; },
                        b'\\' => {
                            // invalid
                            t = Some(TokenType::Invalid);
                            set_cursor(self.cursor);
                            break
                        }
                        _ => {

                        },
                    }// end single byte match
                }// end case SlowPath
            }// end match state
        }// end for each byte

        match t {
            None => None,
            Some(t) => {
                if self.cursor == last_cursor {
                    None
                } else {
                    let buf =
                        match (&t, buf) {
                              (&TokenType::String, Some(b))
                             |(&TokenType::Number, Some(b)) => Buffer::MultiByte(b),
                            _ => {
                                Buffer::Span(Span {
                                            first: first,
                                            end: self.cursor
                                        })
                            }
                        };
                    Some(Token {
                        kind: t,
                        buf: buf,
                    })
                }
            }
        }
    }
}
<commit_msg>hackin' up the lexer<commit_after>// Copyright © 2015 Sebastian Thiel
//
// 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.

// Base a lexer for LVM2's text format on the more complex (hah) json format.
// This code is based on https://github.com/Byron/json-tools.

/// A lexer for utf-8 encoded json data
pub struct Lexer<I: IntoIterator<Item=u8>> {
    chars: I::IntoIter,
    next_byte: Option<u8>,
    cursor: u64,
    buffer_type: BufferType,
}

#[derive(Debug, PartialEq, Clone)]
pub enum TokenType {
    /// `{`
    CurlyOpen,
    /// `}`
    CurlyClose,

    /// `[`
    BracketOpen,
    /// `]`
    BracketClose,

    /// `=`
    Equals,
    /// `,`
    Comma,

    /// A string , like `"foo"`
    String,

    Ident,

    /// An unsigned integer number
    Number,

    /// The type of the token could not be identified.
    /// Should be removed if this lexer is ever to be feature complete
    Invalid,
}

impl AsRef<str> for TokenType {
    fn as_ref(&self) -> &str {
        match *self {
            TokenType::CurlyOpen => "{",
            TokenType::CurlyClose => "}",
            TokenType::BracketOpen => "[",
            TokenType::BracketClose => "]",
            TokenType::Equals => "=",
            TokenType::Comma => ",",

            TokenType::Invalid => panic!("Cannot convert invalid TokenType"),
            _ => panic!("Cannot convert variant TokenTypes"),
        }
    }
}

/// A pair of indices into the byte stream returned by our source
/// iterator.
/// It is an exclusive range.
#[derive(Debug, PartialEq, Clone, Default)]
pub struct Span {
    /// Index of the first the byte
    pub first: u64,
    /// Index one past the last byte
    pub end: u64,
}

/// A lexical token, identifying its kind and span.
#[derive(Debug, PartialEq, Clone)]
pub struct Token {
    /// The exact type of the token
    pub kind: TokenType,

    /// A buffer representing the bytes of this Token.
    pub buf: Buffer
}

/// Representation of a buffer containing items making up a `Token`.
///
/// It's either always `Span`, or one of the `*Byte` variants.
#[derive(Debug, PartialEq, Clone)]
pub enum Buffer {
    /// Multiple bytes making up a token. Only set for `TokenType::String` and
    /// `TokenType::Number`.
    MultiByte(Vec<u8>),
    /// The span allows to reference back into the source byte stream
    /// to obtain the string making up the token.
    /// Please note that for control characters, booleans and null (i.e
    /// anything that is not `Buffer::MultiByte` you should use
    /// `<TokenType as AsRef<str>>::as_ref()`)
    Span(Span),
}

/// The type of `Buffer` you want in each `Token`
#[derive(Debug, PartialEq, Clone)]
pub enum BufferType {
    /// Use a `Buffer::MultiByte` were appropriate. Initialize it with the
    /// given capacity (to obtain higher performance when pushing charcters)
    Bytes(usize),
    Span,
}

impl<I> Lexer<I> where I: IntoIterator<Item=u8> {
    /// Returns a new Lexer from a given byte iterator.
    pub fn new(chars: I, buffer_type: BufferType) -> Lexer<I> {
        Lexer {
            chars: chars.into_iter(),
            next_byte: None,
            cursor: 0,
            buffer_type: buffer_type,
        }
    }

    pub fn into_inner(self) -> I::IntoIter {
        self.chars
    }

    fn put_back(&mut self, c: u8) {
        debug_assert!(self.next_byte.is_none());
        self.next_byte = Some(c);
        self.cursor -= 1;
    }

    fn next_byte(&mut self) -> Option<u8> {
        match self.next_byte.take() {
            Some(c) => {
                self.cursor += 1;
                Some(c)
            },
            None => {
                let res = self.chars.next();
                match res {
                    None => None,
                    Some(_) => {
                        self.cursor += 1;
                        res
                    }
                }
            }
        }
    }
}

// Identifies the state of the lexer
enum Mode {
    // String parse mode: bool = ignore_next
    String(bool),
    Ident,
    Number,
    SlowPath,
}

impl<I> Iterator for Lexer<I>
                    where I: IntoIterator<Item=u8> {
    type Item = Token;

    /// Lex the underlying byte stream to generate tokens
    fn next(&mut self) -> Option<Token> {
        let mut t: Option<TokenType> = None;

        let mut first = 0;
        let mut state = Mode::SlowPath;
        let last_cursor = self.cursor;
        let mut buf =
            match self.buffer_type {
                BufferType::Bytes(capacity) => Some(Vec::<u8>::with_capacity(capacity)),
                BufferType::Span => None,
            };

        while let Some(c) = self.next_byte() {
            let mut set_cursor = |cursor| {
                first = cursor - 1;
            };

            match state {
                Mode::String(ref mut ign_next) => {
                    if let Some(ref mut v) = buf {
                        v.push(c);
                    }
                    if *ign_next && (c == b'"' || c == b'\\') {
                        *ign_next = false;
                        continue;
                    }
                    match c {
                        b'"' => {
                            t = Some(TokenType::String);
                            break;
                        },
                        b'\\' => {
                            *ign_next = true;
                            continue;
                        },
                        _ => {
                            continue;
                        }
                    }
                },
                Mode::Ident => {
                    if let Some(ref mut v) = buf {
                        v.push(c);
                    }
                    match c {
                        b'a' ... b'z' | b'_' => {
                            continue;
                        }
                        _ => {
                            t = Some(TokenType::Ident);
                            break;
                        }
                    }
                },
                Mode::Number => {
                    match c {
                         b'0' ... b'9'
                        |b'-'
                        |b'.' => {
                            if let Some(ref mut v) = buf {
                                v.push(c);
                            }
                            continue;
                        },
                        _ => {
                            t = Some(TokenType::Number);
                            self.put_back(c);
                            break;
                        }
                    }
                }
                Mode::SlowPath => {
                    match c {
                        b'{' => { t = Some(TokenType::CurlyOpen); set_cursor(self.cursor); break; },
                        b'}' => { t = Some(TokenType::CurlyClose); set_cursor(self.cursor); break; },
                        b'"' => {
                            state = Mode::String(false);
                            if let Some(ref mut v) = buf {
                                v.push(c);
                            } else {
                                set_cursor(self.cursor);
                                // it starts at invalid, and once we know it closes, it's a string
                                t = Some(TokenType::Invalid);
                            }
                        },
                        b'a' ... b'z' | b'_' => {
                            state = Mode::Ident;
                            if let Some(ref mut v) = buf {
                                v.push(c);
                            } else {
                                set_cursor(self.cursor);
                            }
                        },
                        b'0' ... b'9' => {
                            state = Mode::Number;
                            if let Some(ref mut v) = buf {
                                v.push(c);
                            } else {
                                set_cursor(self.cursor);
                            }
                        },
                        b'[' => { t = Some(TokenType::BracketOpen); set_cursor(self.cursor); break; },
                        b']' => { t = Some(TokenType::BracketClose); set_cursor(self.cursor); break; },
                        b'=' => { t = Some(TokenType::Equals); set_cursor(self.cursor); break; },
                        b',' => { t = Some(TokenType::Comma); set_cursor(self.cursor); break; },
                        b'\\' => {
                            // invalid
                            t = Some(TokenType::Invalid);
                            set_cursor(self.cursor);
                            break
                        }
                        _ => {

                        },
                    }// end single byte match
                }// end case SlowPath
            }// end match state
        }// end for each byte

        match t {
            None => None,
            Some(t) => {
                if self.cursor == last_cursor {
                    None
                } else {
                    let buf =
                        match (&t, buf) {
                              (&TokenType::String, Some(b))
                             |(&TokenType::Number, Some(b)) => Buffer::MultiByte(b),
                            _ => {
                                Buffer::Span(Span {
                                            first: first,
                                            end: self.cursor
                                        })
                            }
                        };
                    Some(Token {
                        kind: t,
                        buf: buf,
                    })
                }
            }
        }
    }
}
<|endoftext|>