<commit_before>#[derive(PartialEq, Debug, Clone)]
pub enum Token {
    Word(String),

    //characters
    LeftParenthesis,
    RightParenthesis,
    SemiColon,
    SingleQuote,
    Colon
}

pub struct Lexer {
    src: Vec<char>,
}

impl Lexer {

    pub fn new(line: &str) -> Lexer {
        Lexer {
            src: line.chars().collect::<Vec<char>>()
        }
    }

    pub fn next_lexem(&mut self) -> Option<Token> {
        if self.src.is_empty() {
            None
        }
        else {
            Some(self.parse_lexem())
        }
    }

    fn parse_lexem(&mut self) -> Token {
        self.remove_front_spaces();
        if self.has_lexem_in_begining() {
            let s = self.src.remove(0);
            println!("38 s - {:?}", s);
            match s {
                '(' => return Token::LeftParenthesis,
                ',' => return Token::Colon,
                ')' => return Token::RightParenthesis,
                ';' => return Token::SemiColon,
                _ => {}
            }
        }
        let mut i = 0;
        while i < self.src.len() {
            let s = self.src[i];
            println!("50 s - {:?}", s);
            match s {
                'a'...'z' |
                'A'...'Z' |
                '0'...'9' |
                '_' => i += 1,
                '\'' => {
                    if i == 0 {
                        self.src = self.src.split_off(1);
                        return Token::SingleQuote;
                    }
                    else if self.src[i] == '\''
                            && self.src[i+1] == '\'' {
                        self.src.remove(i);
                        i += 1;
                    }
                    else {
                        break;
                    }
                },
                ' ' | '\t' | '\n' => { if i == 0 { self.src.remove(0); } else { break; } },
                '-' => {
                    if i == 0 && self.src[1] == '-' {
                        let mut j = 2;
                        let mut c = self.src[j];
                        while c != '\n' {
                            j += 1;
                            c = self.src[j];
                        }
                        self.src = self.src.split_off(j);
                    }
                }
                _ => break,
            }
        }
        let mut v = self.src.clone();
        self.src = v.split_off(i);
        Token::Word(v.iter().map(|c| *c).collect::<String>().to_lowercase())
    }

    fn has_lexem_in_begining(&self) -> bool {
        match self.src[0] {
            '(' | ')' | ',' | ';' => true,
            _ => false,
        }
    }

    fn remove_front_spaces(&mut self) {
        while self.src[0] == ' '
                || self.src[0] == '\t'
                || self.src[0] == '\n' {
            self.src.remove(0);
        }
    }
}
<commit_msg>impl: new lines in the end of lexem line<commit_after>#[derive(PartialEq, Debug, Clone)]
pub enum Token {
    Word(String),

    //characters
    LeftParenthesis,
    RightParenthesis,
    SemiColon,
    SingleQuote,
    Colon
}

pub struct Lexer {
    src: Vec<char>,
}

impl Lexer {

    pub fn new(line: &str) -> Lexer {
        Lexer {
            src: line.chars().collect::<Vec<char>>()
        }
    }

    pub fn next_lexem(&mut self) -> Option<Token> {
        self.remove_front_spaces();
        if self.src.is_empty() {
            None
        }
        else {
            Some(self.parse_lexem())
        }
    }

    fn parse_lexem(&mut self) -> Token {
        if self.has_lexem_in_begining() {
            let s = self.src.remove(0);
            println!("38 s - {:?}", s);
            match s {
                '(' => return Token::LeftParenthesis,
                ',' => return Token::Colon,
                ')' => return Token::RightParenthesis,
                ';' => return Token::SemiColon,
                _ => {}
            }
        }
        let mut i = 0;
        while i < self.src.len() {
            let s = self.src[i];
            println!("50 s - {:?}", s);
            match s {
                'a'...'z' |
                'A'...'Z' |
                '0'...'9' |
                '_' => i += 1,
                '\'' => {
                    if i == 0 {
                        self.src = self.src.split_off(1);
                        return Token::SingleQuote;
                    }
                    else if self.src[i] == '\''
                            && self.src[i+1] == '\'' {
                        self.src.remove(i);
                        i += 1;
                    }
                    else {
                        break;
                    }
                },
                ' ' | '\t' | '\n' => { if i == 0 { self.src.remove(0); } else { break; } },
                '-' => {
                    if i == 0 && self.src[1] == '-' {
                        let mut j = 2;
                        let mut c = self.src[j];
                        while c != '\n' {
                            j += 1;
                            c = self.src[j];
                        }
                        self.src = self.src.split_off(j);
                    }
                }
                _ => break,
            }
        }
        let mut v = self.src.clone();
        self.src = v.split_off(i);
        Token::Word(v.iter().map(|c| *c).collect::<String>().to_lowercase())
    }

    fn has_lexem_in_begining(&self) -> bool {
        match self.src[0] {
            '(' | ')' | ',' | ';' => true,
            _ => false,
        }
    }

    fn remove_front_spaces(&mut self) {
        while !self.src.is_empty()
                && (self.src[0] == ' '
                || self.src[0] == '\t'
                || self.src[0] == '\n') {
            self.src.remove(0);
        }
    }
}
<|endoftext|>


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

use std::io::{self, Write};
use std::path::Path;

use flate2;
use flate2::write::GzEncoder;
use tar::Builder;
use walkdir::WalkDir;
use xz2::write::XzEncoder;

use errors::*;
use util::*;

actor!{
    #[derive(Debug)]
    pub struct Tarballer {
        /// The input folder to be compressed
        input: String = "package",

        /// The prefix of the tarballs
        output: String = "./dist",

        /// The folder in which the input is to be found
        work_dir: String = "./workdir",
    }
}

impl Tarballer {
    /// Generate the actual tarballs
    pub fn run(self) -> Result<()> {
        let tar_gz = self.output.clone() + ".tar.gz";
        let tar_xz = self.output.clone() + ".tar.xz";

        // Remove any existing files
        for file in &[&tar_gz, &tar_xz] {
            if Path::new(file).exists() {
                remove_file(file)?;
            }
        }

        // Sort files by their suffix, to group files with the same name from
        // different locations (likely identical) and files with the same
        // extension (likely containing similar data).
        let (dirs, mut files) = get_recursive_paths(&self.work_dir, &self.input)
            .chain_err(|| "failed to collect file paths")?;
        files.sort_by(|a, b| a.bytes().rev().cmp(b.bytes().rev()));

        // Prepare the .tar.gz file
        let gz = GzEncoder::new(create_new_file(tar_gz)?, flate2::Compression::Best);

        // Prepare the .tar.xz file
        let xz = XzEncoder::new(create_new_file(tar_xz)?, 9);

        // Write the tar into both encoded files.  We write all directories
        // first, so files may be directly created. (see rustup.rs#1092)
        let mut builder = Builder::new(Tee(gz, xz));
        for path in dirs {
            let src = Path::new(&self.work_dir).join(&path);
            builder.append_dir(&path, &src)
                .chain_err(|| format!("failed to tar dir '{}'", src.display()))?;
        }
        for path in files {
            let src = Path::new(&self.work_dir).join(&path);
            builder.append_file(&path, &mut open_file(&src)?)
                .chain_err(|| format!("failed to tar file '{}'", src.display()))?;
        }
        let Tee(gz, xz) = builder.into_inner()
            .chain_err(|| "failed to finish writing .tar stream")?;

        // Finish both encoded files
        gz.finish().chain_err(|| "failed to finish .tar.gz file")?;
        xz.finish().chain_err(|| "failed to finish .tar.xz file")?;

        Ok(())
    }
}

/// Returns all `(directories, files)` under the source path
fn get_recursive_paths<P, Q>(root: P, name: Q) -> Result<(Vec<String>, Vec<String>)>
    where P: AsRef<Path>, Q: AsRef<Path>
{
    let root = root.as_ref();
    let name = name.as_ref();

    if !name.is_relative() && !name.starts_with(root) {
        bail!("input '{}' is not in work dir '{}'", name.display(), root.display());
    }

    let mut dirs = vec![];
    let mut files = vec![];
    for entry in WalkDir::new(root.join(name)).min_depth(1) {
        let entry = entry?;
        let path = entry.path().strip_prefix(root)?;
        let path = path_to_str(&path)?;

        if entry.file_type().is_dir() {
            dirs.push(path.to_owned());
        } else {
            files.push(path.to_owned());
        }
    }
    Ok((dirs, files))
}

struct Tee<A, B>(A, B);

impl<A: Write, B: Write> Write for Tee<A, B> {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        self.0.write_all(buf)
            .and(self.1.write_all(buf))
            .and(Ok(buf.len()))
    }

    fn flush(&mut self) -> io::Result<()> {
        self.0.flush().and(self.1.flush())
    }
}
<commit_msg>Include the root directory in tarball<commit_after>// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::io::{self, Write};
use std::path::Path;

use flate2;
use flate2::write::GzEncoder;
use tar::Builder;
use walkdir::WalkDir;
use xz2::write::XzEncoder;

use errors::*;
use util::*;

actor!{
    #[derive(Debug)]
    pub struct Tarballer {
        /// The input folder to be compressed
        input: String = "package",

        /// The prefix of the tarballs
        output: String = "./dist",

        /// The folder in which the input is to be found
        work_dir: String = "./workdir",
    }
}

impl Tarballer {
    /// Generate the actual tarballs
    pub fn run(self) -> Result<()> {
        let tar_gz = self.output.clone() + ".tar.gz";
        let tar_xz = self.output.clone() + ".tar.xz";

        // Remove any existing files
        for file in &[&tar_gz, &tar_xz] {
            if Path::new(file).exists() {
                remove_file(file)?;
            }
        }

        // Sort files by their suffix, to group files with the same name from
        // different locations (likely identical) and files with the same
        // extension (likely containing similar data).
        let (dirs, mut files) = get_recursive_paths(&self.work_dir, &self.input)
            .chain_err(|| "failed to collect file paths")?;
        files.sort_by(|a, b| a.bytes().rev().cmp(b.bytes().rev()));

        // Prepare the .tar.gz file
        let gz = GzEncoder::new(create_new_file(tar_gz)?, flate2::Compression::Best);

        // Prepare the .tar.xz file
        let xz = XzEncoder::new(create_new_file(tar_xz)?, 9);

        // Write the tar into both encoded files.  We write all directories
        // first, so files may be directly created. (see rustup.rs#1092)
        let mut builder = Builder::new(Tee(gz, xz));
        for path in dirs {
            let src = Path::new(&self.work_dir).join(&path);
            builder.append_dir(&path, &src)
                .chain_err(|| format!("failed to tar dir '{}'", src.display()))?;
        }
        for path in files {
            let src = Path::new(&self.work_dir).join(&path);
            builder.append_file(&path, &mut open_file(&src)?)
                .chain_err(|| format!("failed to tar file '{}'", src.display()))?;
        }
        let Tee(gz, xz) = builder.into_inner()
            .chain_err(|| "failed to finish writing .tar stream")?;

        // Finish both encoded files
        gz.finish().chain_err(|| "failed to finish .tar.gz file")?;
        xz.finish().chain_err(|| "failed to finish .tar.xz file")?;

        Ok(())
    }
}

/// Returns all `(directories, files)` under the source path
fn get_recursive_paths<P, Q>(root: P, name: Q) -> Result<(Vec<String>, Vec<String>)>
    where P: AsRef<Path>, Q: AsRef<Path>
{
    let root = root.as_ref();
    let name = name.as_ref();

    if !name.is_relative() && !name.starts_with(root) {
        bail!("input '{}' is not in work dir '{}'", name.display(), root.display());
    }

    let mut dirs = vec![];
    let mut files = vec![];
    for entry in WalkDir::new(root.join(name)) {
        let entry = entry?;
        let path = entry.path().strip_prefix(root)?;
        let path = path_to_str(&path)?;

        if entry.file_type().is_dir() {
            dirs.push(path.to_owned());
        } else {
            files.push(path.to_owned());
        }
    }
    Ok((dirs, files))
}

struct Tee<A, B>(A, B);

impl<A: Write, B: Write> Write for Tee<A, B> {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        self.0.write_all(buf)
            .and(self.1.write_all(buf))
            .and(Ok(buf.len()))
    }

    fn flush(&mut self) -> io::Result<()> {
        self.0.flush().and(self.1.flush())
    }
}
<|endoftext|>


<commit_before>use cocoa::base::id;
use std::convert::{From, Into};
use std::error::Error;
use std::fmt;
use std::mem;
use std::ops::{Deref, DerefMut};

/// A type which can be constructed from an Objective-C pointer.
pub trait FromRaw: Sized {
    /// Creates the object from the pointer. If there is a problem with the pointer,
    /// this function will return a FromRawError.
    fn from_raw(raw_pointer: id) -> Result<Self, FromRawError>;
}

/// A type which can be borrowed as an Objective-C pointer.
pub trait AsRaw {
    /// Get an immutable reference to the pointer.
    fn as_raw(&self) -> &id;

    /// Get a mutable reference to the pointer.
    fn as_raw_mut(&mut self) -> &mut id;
}

/// A type which can be converted into an Objective-C pointer.
pub trait IntoRaw {
    /// Get the underlying pointer to the object. Releases ownership.
    fn into_raw(self) -> id;
}

impl FromRaw for id {
    fn from_raw(ptr: id) -> Result<Self, FromRawError> {
        Ok(ptr)
    }
}

impl AsRaw for id {
    fn as_raw(&self) -> &id {
        self
    }

    fn as_raw_mut(&mut self) -> &mut id {
        self
    }
}

impl IntoRaw for id {
    fn into_raw(self) -> id {
        self
    }
}

/// An Objective-C pointer which sends the `release` message to its object on drop
#[derive(Eq, PartialEq)]
pub struct StrongPtr(id);

impl fmt::Debug for StrongPtr {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "StrongPtr({:p})", self.0)
    }
}

impl fmt::Display for StrongPtr {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Pointer::fmt(self, f)
    }
}

impl fmt::Pointer for StrongPtr {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{:p}", self.0)
    }
}

impl Deref for StrongPtr {
    type Target = id;
    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl DerefMut for StrongPtr {
    fn deref_mut(&mut self) -> &mut <Self as Deref>::Target {
        &mut self.0
    }
}

impl FromRaw for StrongPtr {
    fn from_raw(ptr: id) -> Result<Self, FromRawError> {
        Ok(From::from(ptr))
    }
}

impl AsRaw for StrongPtr {
    fn as_raw(&self) -> &id {
        self.deref()
    }

    fn as_raw_mut(&mut self) -> &mut id {
        self.deref_mut()
    }
}

impl IntoRaw for StrongPtr {
    fn into_raw(self) -> id {
        self.into()
    }
}

impl Clone for StrongPtr {
    fn clone(&self) -> Self {
        let retained = unsafe { msg_send![self.0, retain] };
        StrongPtr(retained)
    }
}

impl Drop for StrongPtr {
    fn drop(&mut self) {
        unsafe { msg_send![self.0, release] }
    }
}

impl From<id> for StrongPtr {
    fn from(ptr: id) -> Self {
        StrongPtr(ptr)
    }
}

impl Into<id> for StrongPtr {
    /// Get the underlying pointer from `Self`. The pointer will no longer be released on drop.
    fn into(self) -> id {
        let self_ptr = self.0;
        mem::forget(self);
        self_ptr
    }
}

/// Represents errors which may occur when constructing an object from a pointer.
#[derive(Clone, Debug)]
pub enum FromRawError {
    /// The pointer was nil.
    NilPointer,

    /// The pointer does not point to either:
    ///
    /// * an object of the correct type,
    /// * an object which does not have a certain base class,
    /// * an object which does not implement a particular protocol.
    WrongPointerType
}

macro_rules! impl_from_into_raw {
    ($wrapper_type:ident, of protocol $protocol:expr) => (
        impl $crate::FromRaw for $wrapper_type {
            fn from_raw(raw_pointer: id) -> Result<Self, $crate::FromRawError> {
                use $crate::internal::conforms_to_protocol;
                use cocoa::base::nil;
                if raw_pointer == nil {
                    Err($crate::FromRawError::NilPointer)
                } else if !conforms_to_protocol(raw_pointer, $protocol) {
                    Err($crate::FromRawError::WrongPointerType)
                } else {
                    Ok($wrapper_type(From::from(raw_pointer)))
                }
            }
        }

        impl $crate::AsRaw for $wrapper_type {
            fn as_raw(&self) -> &id {
                &self.0
            }

            fn as_raw_mut(&mut self) -> &mut id {
                &mut self.0
            }
        }

        impl $crate::IntoRaw for $wrapper_type {
            fn into_raw(self) -> id {
                self.0.into()
            }
        }
    );

    ($wrapper_type:ident, of class $class:expr) => (
        impl $crate::FromRaw for $wrapper_type {
            fn from_raw(raw_pointer: id) -> Result<Self, $crate::FromRawError> {
                use $crate::internal::is_kind_of_class;
                use cocoa::base::nil;
                if raw_pointer == nil {
                    Err($crate::FromRawError::NilPointer)
                } else if !is_kind_of_class(raw_pointer, $class) {
                    Err($crate::FromRawError::WrongPointerType)
                } else {
                    Ok($wrapper_type(From::from(raw_pointer)))
                }
            }
        }

        impl $crate::AsRaw for $wrapper_type {
            fn as_raw(&self) -> &id {
                &self.0
            }

            fn as_raw_mut(&mut self) -> &mut id {
                &mut self.0
            }
        }

        impl $crate::IntoRaw for $wrapper_type {
            fn into_raw(self) -> id {
                self.0.into()
            }
        }
    )
}

impl fmt::Display for FromRawError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let descr = match *self {
            FromRawError::NilPointer => "FromRawError::NilPointer",
            FromRawError::WrongPointerType => "FromRawError::WrongPointerType",
        };
        write!(f, "{}", descr)
    }
}

impl Error for FromRawError {
    fn description(&self) -> &str {
        match *self {
            FromRawError::NilPointer => "Attempted to create an object from nil",
            FromRawError::WrongPointerType => "The object pointer is not of the correct class",
        }
    }
}
<commit_msg>Nil out StrongPtr on drop<commit_after>use cocoa::base::{id, nil};
use std::convert::{From, Into};
use std::error::Error;
use std::fmt;
use std::mem;
use std::ops::{Deref, DerefMut};

/// A type which can be constructed from an Objective-C pointer.
pub trait FromRaw: Sized {
    /// Creates the object from the pointer. If there is a problem with the pointer,
    /// this function will return a FromRawError.
    fn from_raw(raw_pointer: id) -> Result<Self, FromRawError>;
}

/// A type which can be borrowed as an Objective-C pointer.
pub trait AsRaw {
    /// Get an immutable reference to the pointer.
    fn as_raw(&self) -> &id;

    /// Get a mutable reference to the pointer.
    fn as_raw_mut(&mut self) -> &mut id;
}

/// A type which can be converted into an Objective-C pointer.
pub trait IntoRaw {
    /// Get the underlying pointer to the object. Releases ownership.
    fn into_raw(self) -> id;
}

impl FromRaw for id {
    fn from_raw(ptr: id) -> Result<Self, FromRawError> {
        Ok(ptr)
    }
}

impl AsRaw for id {
    fn as_raw(&self) -> &id {
        self
    }

    fn as_raw_mut(&mut self) -> &mut id {
        self
    }
}

impl IntoRaw for id {
    fn into_raw(self) -> id {
        self
    }
}

/// An Objective-C pointer which sends the `release` message to its object on drop
#[derive(Eq, PartialEq)]
pub struct StrongPtr(id);

impl fmt::Debug for StrongPtr {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "StrongPtr({:p})", self.0)
    }
}

impl fmt::Display for StrongPtr {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Pointer::fmt(self, f)
    }
}

impl fmt::Pointer for StrongPtr {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{:p}", self.0)
    }
}

impl Deref for StrongPtr {
    type Target = id;
    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl DerefMut for StrongPtr {
    fn deref_mut(&mut self) -> &mut <Self as Deref>::Target {
        &mut self.0
    }
}

impl FromRaw for StrongPtr {
    fn from_raw(ptr: id) -> Result<Self, FromRawError> {
        Ok(From::from(ptr))
    }
}

impl AsRaw for StrongPtr {
    fn as_raw(&self) -> &id {
        self.deref()
    }

    fn as_raw_mut(&mut self) -> &mut id {
        self.deref_mut()
    }
}

impl IntoRaw for StrongPtr {
    fn into_raw(self) -> id {
        self.into()
    }
}

impl Clone for StrongPtr {
    fn clone(&self) -> Self {
        let retained = unsafe { msg_send![self.0, retain] };
        StrongPtr(retained)
    }
}

impl Drop for StrongPtr {
    fn drop(&mut self) {
        unsafe { msg_send![self.0, release] };
        self.0 = nil;
    }
}

impl From<id> for StrongPtr {
    fn from(ptr: id) -> Self {
        StrongPtr(ptr)
    }
}

impl Into<id> for StrongPtr {
    /// Get the underlying pointer from `Self`. The pointer will no longer be released on drop.
    fn into(self) -> id {
        let self_ptr = self.0;
        mem::forget(self);
        self_ptr
    }
}

/// Represents errors which may occur when constructing an object from a pointer.
#[derive(Clone, Debug)]
pub enum FromRawError {
    /// The pointer was nil.
    NilPointer,

    /// The pointer does not point to either:
    ///
    /// * an object of the correct type,
    /// * an object which does not have a certain base class,
    /// * an object which does not implement a particular protocol.
    WrongPointerType
}

macro_rules! impl_from_into_raw {
    ($wrapper_type:ident, of protocol $protocol:expr) => (
        impl $crate::FromRaw for $wrapper_type {
            fn from_raw(raw_pointer: id) -> Result<Self, $crate::FromRawError> {
                use $crate::internal::conforms_to_protocol;
                use cocoa::base::nil;
                if raw_pointer == nil {
                    Err($crate::FromRawError::NilPointer)
                } else if !conforms_to_protocol(raw_pointer, $protocol) {
                    Err($crate::FromRawError::WrongPointerType)
                } else {
                    Ok($wrapper_type(From::from(raw_pointer)))
                }
            }
        }

        impl $crate::AsRaw for $wrapper_type {
            fn as_raw(&self) -> &id {
                &self.0
            }

            fn as_raw_mut(&mut self) -> &mut id {
                &mut self.0
            }
        }

        impl $crate::IntoRaw for $wrapper_type {
            fn into_raw(self) -> id {
                self.0.into()
            }
        }
    );

    ($wrapper_type:ident, of class $class:expr) => (
        impl $crate::FromRaw for $wrapper_type {
            fn from_raw(raw_pointer: id) -> Result<Self, $crate::FromRawError> {
                use $crate::internal::is_kind_of_class;
                use cocoa::base::nil;
                if raw_pointer == nil {
                    Err($crate::FromRawError::NilPointer)
                } else if !is_kind_of_class(raw_pointer, $class) {
                    Err($crate::FromRawError::WrongPointerType)
                } else {
                    Ok($wrapper_type(From::from(raw_pointer)))
                }
            }
        }

        impl $crate::AsRaw for $wrapper_type {
            fn as_raw(&self) -> &id {
                &self.0
            }

            fn as_raw_mut(&mut self) -> &mut id {
                &mut self.0
            }
        }

        impl $crate::IntoRaw for $wrapper_type {
            fn into_raw(self) -> id {
                self.0.into()
            }
        }
    )
}

impl fmt::Display for FromRawError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let descr = match *self {
            FromRawError::NilPointer => "FromRawError::NilPointer",
            FromRawError::WrongPointerType => "FromRawError::WrongPointerType",
        };
        write!(f, "{}", descr)
    }
}

impl Error for FromRawError {
    fn description(&self) -> &str {
        match *self {
            FromRawError::NilPointer => "Attempted to create an object from nil",
            FromRawError::WrongPointerType => "The object pointer is not of the correct class",
        }
    }
}
<|endoftext|>


<commit_before>//! Rust representations of docker json structures

#[derive(Debug, RustcEncodable, RustcDecodable)]
pub struct SearchResult {
  pub description: String,
  pub is_official: bool,
  pub is_trusted: bool,
  pub name: String,
  pub star_count: u64
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
#[allow(non_snake_case)]
pub struct Image {
  pub Created: u64,
  pub Id: String,
  pub ParentId: String,
  //pub Labels: ???,
  pub RepoTags: Vec<String>,
  pub RepoDigests: Option<Vec<String>>,
  pub Size: u64,
  pub VirtualSize: u64
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
#[allow(non_snake_case)]
pub struct ImageDetails {
  pub Architecture: String,
  pub Author: String,
  pub Comment: String,
  pub Config: Config,
  pub Created: String,
  pub DockerVersion: String,
  pub Id: String,
  pub Os: String,
  pub Parent: String,
  pub Size: u64,
  pub VirtualSize: u64
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
#[allow(non_snake_case)]
pub struct Container {
  pub Created: u64,
  pub Command: String,
  pub Id: String,
  pub Image: String,
  //pub Labels: ???,
  pub Names: Vec<String>,
  pub Ports: Vec<Port>,
  pub Status: String,
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
#[allow(non_snake_case)]
pub struct ContainerDetails {
  pub AppArmorProfile: String,
  pub Args: Vec<String>,
  pub Config: Config,
  pub Created: String,
  pub Driver: String,
  pub ExecDriver: String,
  //pub ExecIDs: ??
  pub HostConfig: HostConfig
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
#[allow(non_snake_case)]
pub struct HostConfig {
  pub CgroupParent: Option<String>,
  pub ContainerIDFile: String,
  pub CpuShares: Option<u64>,
  pub CpusetCpus: Option<String>,
  pub Memory: Option<u64>,
  pub MemorySwap: Option<u64>,
  pub NetworkMode: String,
  pub PidMode: Option<String>,
  //pub PortBindings: ???
  pub Privileged: bool,
  pub PublishAllPorts: bool,
  pub ReadonlyRootfs: Option<bool>
  //pub RestartPolicy: ???
  //pub SecurityOpt: Option<???>,
  //pub Ulimits: Option<???>
 // pub VolumesFrom: Option<??/>
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
#[allow(non_snake_case)]
pub struct Config {
  AttachStderr: bool,
  AttachStdin: bool,
  AttachStdout: bool,
  Cmd: Vec<String>,
  CpuShares: u64,
  Cpuset: String,
  Domainname: String,
  Entrypoint: Vec<String>,
  Env: Vec<String>,
  //ExposedPorts
  Hostname: String,
  Image: String,
  //Labels:???
  MacAddress: String,
  Memory: u64,
  MemorySwap: u64,
  NetworkDisabled: bool,
  //OnBuild: Option<String>,
  OpenStdin: bool,
  //PortSpecs: ???,
  StdinOnce: bool,
  Tty: bool,
  User: String,
  //Volumes: ??,
  WorkingDir: String
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
#[allow(non_snake_case)]
pub struct Port {
  pub IP: Option<String>,
  pub PrivatePort: u64,
  pub PublicPort: Option<u64>,
  pub Type: String
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
pub struct Stats {
  pub read: String,
  pub network: Network,
  pub memory_stats: MemoryStats,
  pub cpu_stats: CpuStats
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
pub struct Network {
  pub rx_dropped: u64,
  pub rx_bytes: u64,
  pub rx_errors: u64,
  pub tx_packets: u64,
  pub tx_dropped: u64,
  pub rx_packets: u64,
  pub tx_errors: u64,
  pub tx_bytes: u64
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
pub struct MemoryStats {
  pub max_usage: u64,
  pub usage: u64,
  pub failcnt: u64,
  pub limit: u64,
  pub stats: MemoryStat
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
pub struct MemoryStat {
  pub total_pgmajfault: u64,
  pub cache: u64,
  pub mapped_file: u64,
  pub total_inactive_file: u64,
  pub pgpgout: u64,
  pub rss: u64,
  pub total_mapped_file: u64,
  pub writeback: u64,
  pub unevictable: u64,
  pub pgpgin: u64,
  pub total_unevictable: u64,
  pub pgmajfault: u64,
  pub total_rss: u64,
  pub total_rss_huge: u64,
  pub total_writeback: u64,
  pub total_inactive_anon: u64,
  pub rss_huge: u64,
  pub hierarchical_memory_limit: u64,
  pub hierarchical_memsw_limit: u64,
  pub total_pgfault: u64,
  pub total_active_file: u64,
  pub active_anon: u64,
  pub total_active_anon: u64,
  pub total_pgpgout: u64,
  pub total_cache: u64,
  pub inactive_anon: u64,
  pub active_file: u64,
  pub pgfault: u64,
  pub inactive_file: u64,
  pub total_pgpgin: u64,
  pub swap: u64,
  pub total_swap: u64
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
pub struct CpuStats {
  pub cpu_usage: CpuUsage,
  pub system_cpu_usage: u64,
  pub throttling_data: ThrottlingData
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
pub struct CpuUsage {
  pub percpu_usage: Vec<u64>,
  pub usage_in_usermode: u64,
  pub total_usage: u64,
  pub usage_in_kernelmode: u64
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
pub struct ThrottlingData {
  pub periods: u64,
  pub throttled_periods: u64,
  pub throttled_time: u64
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
pub struct BlkioStats {
  pub io_service_bytes_recursive: Vec<BlkioStat>,
  pub io_serviced_recursive: Vec<BlkioStat>,
  pub io_queue_recursive: Vec<BlkioStat>,
  pub io_service_time_recursive: Vec<BlkioStat>,
  pub io_wait_time_recursive: Vec<BlkioStat>,
  pub io_merged_recursive: Vec<BlkioStat>,
  pub io_time_recursive: Vec<BlkioStat>,
  pub sectors_recursive: Vec<BlkioStat>
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
pub struct BlkioStat {
  pub major: u64,
  pub minor: u64,
  pub op: String,
  pub value: u64
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
#[allow(non_snake_case)]
pub struct Change {
  pub Kind: u64,
  pub Path: String
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
#[allow(non_snake_case)]
pub struct Top {
  pub Titles: Vec<String>,
  pub Processes: Vec<Vec<String>>
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
#[allow(non_snake_case)]
pub struct Version {
  pub ApiVersion: String,
  pub Version: String,
  pub GitCommit: String,
  pub GoVersion: String
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
#[allow(non_snake_case)]
pub struct Info {
  pub Containers: u64,
  pub Images: u64,
  pub Driver: String,
  pub DockerRootDir: String,
  pub DriverStatus: Vec<Vec<String>>,
  pub ExecutionDriver: String,
  pub ID: String,
  pub KernelVersion: String,
  //pub Labels: Option<???>,
  pub MemTotal: u64,
  pub MemoryLimit: u64,
  pub NCPU: u64,
  pub NEventsListener: u64,
  pub NGoroutines: u64,
  pub Name: String,
  pub OperatingSystem: String,
  //pub RegistryConfig:???
  pub SwapLimit: u64,
  pub SystemTime: Option<String>
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
#[allow(non_snake_case)]
pub struct ContainerCreateInfo {
  pub Id: String,
  pub Warnings: Option<Vec<String>>
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
#[allow(non_snake_case)]
pub struct History {
  pub Id: String,
  pub Created: u64,
  pub CreatedBy: String
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
#[allow(non_snake_case)]
pub struct Exit {
  pub StatusCode: u64
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
pub struct Event {
  pub status: String,
  pub id: String,
  pub from: String,
  pub time: u64
}

#[derive(Debug)]
pub enum Status {
  Untagged(String),
  Deleted(String)
}
<commit_msg>expose Env as map<commit_after>//! Rust representations of docker json structures

use std::collections::HashMap;

#[derive(Debug, RustcEncodable, RustcDecodable)]
pub struct SearchResult {
  pub description: String,
  pub is_official: bool,
  pub is_trusted: bool,
  pub name: String,
  pub star_count: u64
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
#[allow(non_snake_case)]
pub struct Image {
  pub Created: u64,
  pub Id: String,
  pub ParentId: String,
  //pub Labels: ???,
  pub RepoTags: Vec<String>,
  pub RepoDigests: Option<Vec<String>>,
  pub Size: u64,
  pub VirtualSize: u64
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
#[allow(non_snake_case)]
pub struct ImageDetails {
  pub Architecture: String,
  pub Author: String,
  pub Comment: String,
  pub Config: Config,
  pub Created: String,
  pub DockerVersion: String,
  pub Id: String,
  pub Os: String,
  pub Parent: String,
  pub Size: u64,
  pub VirtualSize: u64
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
#[allow(non_snake_case)]
pub struct Container {
  pub Created: u64,
  pub Command: String,
  pub Id: String,
  pub Image: String,
  //pub Labels: ???,
  pub Names: Vec<String>,
  pub Ports: Vec<Port>,
  pub Status: String,
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
#[allow(non_snake_case)]
pub struct ContainerDetails {
  pub AppArmorProfile: String,
  pub Args: Vec<String>,
  pub Config: Config,
  pub Created: String,
  pub Driver: String,
  pub ExecDriver: String,
  //pub ExecIDs: ??
  pub HostConfig: HostConfig
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
#[allow(non_snake_case)]
pub struct HostConfig {
  pub CgroupParent: Option<String>,
  pub ContainerIDFile: String,
  pub CpuShares: Option<u64>,
  pub CpusetCpus: Option<String>,
  pub Memory: Option<u64>,
  pub MemorySwap: Option<u64>,
  pub NetworkMode: String,
  pub PidMode: Option<String>,
  //pub PortBindings: ???
  pub Privileged: bool,
  pub PublishAllPorts: bool,
  pub ReadonlyRootfs: Option<bool>
  //pub RestartPolicy: ???
  //pub SecurityOpt: Option<???>,
  //pub Ulimits: Option<???>
 // pub VolumesFrom: Option<??/>
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
#[allow(non_snake_case)]
pub struct Config {
  AttachStderr: bool,
  AttachStdin: bool,
  AttachStdout: bool,
  Cmd: Vec<String>,
  CpuShares: u64,
  Cpuset: String,
  Domainname: String,
  Entrypoint: Option<Vec<String>>,
  Env: Vec<String>,
  //ExposedPorts
  Hostname: String,
  Image: String,
  //Labels:???
  MacAddress: String,
  Memory: u64,
  MemorySwap: u64,
  NetworkDisabled: bool,
  //OnBuild: Option<String>,
  OpenStdin: bool,
  //PortSpecs: ???,
  StdinOnce: bool,
  Tty: bool,
  User: String,
  //Volumes: ??,
  WorkingDir: String
}

impl Config {
  pub fn env(&self) -> HashMap<String, String> {
    let mut map = HashMap::new();
    for e in self.Env.clone() {
      let pair: Vec<&str> = e.split("=").collect();
      map.insert(pair[0].to_owned(), pair[1].to_owned());
    }
    map
  }
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
#[allow(non_snake_case)]
pub struct Port {
  pub IP: Option<String>,
  pub PrivatePort: u64,
  pub PublicPort: Option<u64>,
  pub Type: String
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
pub struct Stats {
  pub read: String,
  pub network: Network,
  pub memory_stats: MemoryStats,
  pub cpu_stats: CpuStats
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
pub struct Network {
  pub rx_dropped: u64,
  pub rx_bytes: u64,
  pub rx_errors: u64,
  pub tx_packets: u64,
  pub tx_dropped: u64,
  pub rx_packets: u64,
  pub tx_errors: u64,
  pub tx_bytes: u64
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
pub struct MemoryStats {
  pub max_usage: u64,
  pub usage: u64,
  pub failcnt: u64,
  pub limit: u64,
  pub stats: MemoryStat
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
pub struct MemoryStat {
  pub total_pgmajfault: u64,
  pub cache: u64,
  pub mapped_file: u64,
  pub total_inactive_file: u64,
  pub pgpgout: u64,
  pub rss: u64,
  pub total_mapped_file: u64,
  pub writeback: u64,
  pub unevictable: u64,
  pub pgpgin: u64,
  pub total_unevictable: u64,
  pub pgmajfault: u64,
  pub total_rss: u64,
  pub total_rss_huge: u64,
  pub total_writeback: u64,
  pub total_inactive_anon: u64,
  pub rss_huge: u64,
  pub hierarchical_memory_limit: u64,
  pub hierarchical_memsw_limit: u64,
  pub total_pgfault: u64,
  pub total_active_file: u64,
  pub active_anon: u64,
  pub total_active_anon: u64,
  pub total_pgpgout: u64,
  pub total_cache: u64,
  pub inactive_anon: u64,
  pub active_file: u64,
  pub pgfault: u64,
  pub inactive_file: u64,
  pub total_pgpgin: u64,
  pub swap: u64,
  pub total_swap: u64
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
pub struct CpuStats {
  pub cpu_usage: CpuUsage,
  pub system_cpu_usage: u64,
  pub throttling_data: ThrottlingData
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
pub struct CpuUsage {
  pub percpu_usage: Vec<u64>,
  pub usage_in_usermode: u64,
  pub total_usage: u64,
  pub usage_in_kernelmode: u64
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
pub struct ThrottlingData {
  pub periods: u64,
  pub throttled_periods: u64,
  pub throttled_time: u64
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
pub struct BlkioStats {
  pub io_service_bytes_recursive: Vec<BlkioStat>,
  pub io_serviced_recursive: Vec<BlkioStat>,
  pub io_queue_recursive: Vec<BlkioStat>,
  pub io_service_time_recursive: Vec<BlkioStat>,
  pub io_wait_time_recursive: Vec<BlkioStat>,
  pub io_merged_recursive: Vec<BlkioStat>,
  pub io_time_recursive: Vec<BlkioStat>,
  pub sectors_recursive: Vec<BlkioStat>
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
pub struct BlkioStat {
  pub major: u64,
  pub minor: u64,
  pub op: String,
  pub value: u64
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
#[allow(non_snake_case)]
pub struct Change {
  pub Kind: u64,
  pub Path: String
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
#[allow(non_snake_case)]
pub struct Top {
  pub Titles: Vec<String>,
  pub Processes: Vec<Vec<String>>
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
#[allow(non_snake_case)]
pub struct Version {
  pub ApiVersion: String,
  pub Version: String,
  pub GitCommit: String,
  pub GoVersion: String
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
#[allow(non_snake_case)]
pub struct Info {
  pub Containers: u64,
  pub Images: u64,
  pub Driver: String,
  pub DockerRootDir: String,
  pub DriverStatus: Vec<Vec<String>>,
  pub ExecutionDriver: String,
  pub ID: String,
  pub KernelVersion: String,
  //pub Labels: Option<???>,
  pub MemTotal: u64,
  pub MemoryLimit: u64,
  pub NCPU: u64,
  pub NEventsListener: u64,
  pub NGoroutines: u64,
  pub Name: String,
  pub OperatingSystem: String,
  //pub RegistryConfig:???
  pub SwapLimit: u64,
  pub SystemTime: Option<String>
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
#[allow(non_snake_case)]
pub struct ContainerCreateInfo {
  pub Id: String,
  pub Warnings: Option<Vec<String>>
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
#[allow(non_snake_case)]
pub struct History {
  pub Id: String,
  pub Created: u64,
  pub CreatedBy: String
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
#[allow(non_snake_case)]
pub struct Exit {
  pub StatusCode: u64
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
pub struct Event {
  pub status: String,
  pub id: String,
  pub from: String,
  pub time: u64
}

#[derive(Debug)]
pub enum Status {
  Untagged(String),
  Deleted(String)
}
<|endoftext|>


<commit_before>use std::io::fs::PathExtensions;
use std::io::{File, Open, ReadWrite};
use std::ascii::AsciiExt;
use todo::entry::Entry;

pub struct List {
    file: File,
    entries: Vec<Entry>
}

impl List {
    pub fn new(path: Path) -> List {
        let file = if path.exists() {
            match File::open_mode(&path, Open, ReadWrite) {
                Ok(result) => result,
                Err(reason) => panic!("Couldn't open file `{}` for writing: {}", path.display(), reason.desc)
            }
        } else {
            File::create(&path).unwrap()
        };

        List {
            file: file,
            entries: vec![]
        }
    }

    pub fn load(&mut self) {
        let text = &self.file.read_to_string().unwrap();
        let mut i = 0;
        for line in text.lines() {
            i += 1;
            &self.entries.push(Entry::new(i, line.to_string()));
        }
    }

    pub fn save(&mut self) {
        &self.file.write("Hi!".as_bytes());
    }

    pub fn index(&self, context: Option<String>) {
        for entry in (&self.entries).iter().filter(|current| List::filter(current, &context)) {
            println!("({}) {}", entry.id, entry.content);
        }
    }

    pub fn add(&self, context: Option<String>) {
        println!("adding!");
    }

    pub fn edit(&self, context: Option<String>) {
        println!("editting!");
    }

    pub fn remove(&self, context: Option<String>) {
        println!("removing!");
    }

    pub fn finish(&self, context: Option<String>) {
        println!("finishing!");
    }

    pub fn count(&self, context: Option<String>) {
        println!("counting!");
    }

    fn filter(entry: &&Entry, context: &Option<String>) -> bool {
        match context.clone() {
            Some(val) => val.eq_ignore_ascii_case(entry.status.as_slice()),
            None => true
        }
    }
}<commit_msg>Provide a convenience method that retrieves the entry count<commit_after>use std::io::fs::PathExtensions;
use std::io::{File, Open, ReadWrite};
use std::ascii::AsciiExt;
use todo::entry::Entry;

pub struct List {
    file: File,
    entries: Vec<Entry>
}

impl List {
    pub fn new(path: Path) -> List {
        let file = if path.exists() {
            match File::open_mode(&path, Open, ReadWrite) {
                Ok(result) => result,
                Err(reason) => panic!("Couldn't open file `{}` for writing: {}", path.display(), reason.desc)
            }
        } else {
            File::create(&path).unwrap()
        };

        List {
            file: file,
            entries: vec![]
        }
    }

    pub fn load(&mut self) {
        let text = &self.file.read_to_string().unwrap();
        let mut i = 0;
        for line in text.lines() {
            i += 1;
            &self.entries.push(Entry::new(i, line.to_string()));
        }
    }

    pub fn save(&mut self) {
        &self.file.write("Hi!".as_bytes());
    }

    pub fn index(&self, context: Option<String>) {
        for entry in (&self.entries).iter().filter(|current| List::filter(current, &context)) {
            println!("({}) {}", entry.id, entry.content);
        }
    }

    pub fn add(&self, context: Option<String>) {
        println!("adding!");
    }

    pub fn edit(&self, context: Option<String>) {
        println!("editting!");
    }

    pub fn remove(&self, context: Option<String>) {
        println!("removing!");
    }

    pub fn finish(&self, context: Option<String>) {
        println!("finishing!");
    }

    pub fn count(&self, context: Option<String>) {
        let total = (&self.entries).iter().filter(|current| List::filter(current, &context)).count();
        println!("{}", total);
    }

    fn filter(entry: &&Entry, context: &Option<String>) -> bool {
        match context.clone() {
            Some(val) => val.eq_ignore_ascii_case(entry.status.as_slice()),
            None => true
        }
    }
}<|endoftext|>


<commit_before>// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! # Command Tokenization
//!
//! The command parser needs to be able to tokenize commands
//! into their constituent words and whitespace.
//!
//! The `tokenizer` breaks source text into a vector of tokens
//! which can be either whitespace or a word. The tokenizer
//! handles using double quotes to provide a single token
//! which may include whitespace.
//!
//! Tokens also track their source location within the source
//! text. This allows the parser using the tokenizer to provide
//! better error highlighting and other functionality.
//!
//! # Examples
//!
//! ```
//! use commands::tokenizer::tokenize;
//!
//! if let Ok(tokens) = tokenize("word") {
//!     assert_eq!(tokens.len(), 1);
//! }
//!
//! if let Ok(tokens) = tokenize("show interface") {
//!     assert_eq!(tokens.len(), 3);
//! }
//!
//! if let Ok(tokens) = tokenize("echo -n \"a b c\"") {
//!     assert_eq!(tokens.len(), 5);
//! }
//!
//! if let Ok(tokens) = tokenize("ls My\\ Documents") {
//!     assert_eq!(tokens.len(), 3);
//!     assert_eq!(tokens[2].text, "My\\ Documents");
//! }
//! ```

use std::fmt;
use std::error::Error;

/// A position within a body of text.
///
/// The `SourceOffset` tracks 2 different ways of locating the
/// position:
///
/// * The index of the character within the body of text.
/// * The column and line number of the character.
///
/// The `SourceOffset` is typically used as a pair of offsets
/// indicating the start and end of a range of text as used
/// by the `SourceLocation`.
#[derive(Clone,Copy,Debug,PartialEq)]
pub struct SourceOffset {
    /// The index of this character within the body of text.
    pub char: usize,
    /// The line number on which this character may be found.
    pub line: usize,
    /// The column on which this character may be found.
    pub column: usize,
}

impl SourceOffset {
    /// Construct a `SourceOffset`.
    pub fn new(char: usize, line: usize, column: usize) -> SourceOffset {
        SourceOffset {
            char: char,
            line: line,
            column: column,
        }
    }
}

/// A range within a body of text.
#[derive(Clone,Copy,Debug,PartialEq)]
pub struct SourceLocation {
    /// The start of the range.
    pub start: SourceOffset,
    /// The end of the range.
    pub end: SourceOffset,
}

impl SourceLocation {
    /// Construct a `SourceLocation`.
    pub fn new(start: SourceOffset, end: SourceOffset) -> SourceLocation {
        SourceLocation {
            start: start,
            end: end,
        }
    }
}

/// Errors
#[derive(Clone,Debug)]
pub enum TokenizerError {
    /// Character not allowed here
    CharacterNotAllowedHere(usize),

    /// Special not yet implemented
    SpecialNotYetImplemented(usize),

    /// Escaping backslash at end of input
    EscapingBackslashAtEndOfInput,

    /// Unclosed double quote at end of input
    UnclosedDoubleQuoteAtEndOfInput,

    /// Unclosed single quote at end of input
    UnclosedSingleQuoteAtEndOfInput,
}

impl Error for TokenizerError {
    fn description(&self) -> &str {
        match *self {
            TokenizerError::CharacterNotAllowedHere(_) => "Character not allowed here",
            TokenizerError::SpecialNotYetImplemented(_) => "Special not yet implemented",
            TokenizerError::EscapingBackslashAtEndOfInput => "Escaping backlash at end of input",
            TokenizerError::UnclosedDoubleQuoteAtEndOfInput => {
                "Unclosed double quote at end of input"
            },
            TokenizerError::UnclosedSingleQuoteAtEndOfInput => {
                "Unclosed single quote at end of input"
            }
        }
    }
}

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

/// The role that a token plays: `Whitespace` or `Word`.
#[derive(Clone,Copy,Debug,PartialEq)]
pub enum TokenType {
    /// Internal usage only.
    Invalid,
    /// The token represents whitespace and not a word.
    Whitespace,
    /// The token represents a word within the body of text. This
    /// takes double quoted strings into account.
    Word,
}

/// A token from a body of text.
///
/// The lifetime parameter `'t` refers to the lifetime
/// of the body of text that was tokenized, creating this token.
#[derive(Clone,Copy,Debug,PartialEq)]
pub struct Token<'t> {
    /// The text of the token.
    pub text: &'t str,
    /// The type of the token (`Whitespace` or `Word`).
    pub token_type: TokenType,
    /// The location of the token in the source body of text.
    pub location: SourceLocation,
}

impl<'t> Token<'t> {
    /// Construct a `Token`. The lifetime parameter `'t` refers to the lifetime of the
    /// text being tokenized.
    pub fn new(text: &'t str, token_type: TokenType, location: SourceLocation) -> Token {
        Token {
            text: text,
            token_type: token_type,
            location: location,
        }
    }
}

#[derive(Clone,Copy,PartialEq)]
enum State {
    Initial,
    Special,
    Whitespace,
    Doublequote,
    DoublequoteBackslash,
    Singlequote,
    SinglequoteBackslash,
    Word,
    WordBackslash,
}

struct Tokenizer<'t> {
    text: &'t str,
    state: State,
    token_type: TokenType,
    token_start: usize,
    token_end: usize,
    tokens: Vec<Token<'t>>,
}

impl<'t> Tokenizer<'t> {
    fn new(text: &'t str) -> Tokenizer {
        Tokenizer {
            text: text,
            state: State::Initial,
            token_type: TokenType::Invalid,
            token_start: 0,
            token_end: 0,
            tokens: vec![],
        }
    }

    fn reset(&mut self) {
        self.state = State::Initial;
        self.token_type = TokenType::Invalid;
        self.token_start = 0;
        self.token_end = 0;
    }

    fn reduce(&mut self) {
        let token_text = &self.text[self.token_start..self.token_end + 1];
        let loc = SourceLocation::new(SourceOffset::new(self.token_start, 0, self.token_start),
                                      SourceOffset::new(self.token_end, 0, self.token_end));
        self.tokens.push(Token::new(token_text, self.token_type, loc));
        self.reset();
    }

    fn shift(&mut self, offset: usize, next_state: State) {
        self.recognize(offset, next_state);
        self.token_end = offset;
        self.state = next_state;
    }

    fn recognize(&mut self, offset: usize, next_state: State) {
        if self.token_type == TokenType::Invalid {
            self.token_type = if next_state == State::Whitespace {
                TokenType::Whitespace
            } else {
                TokenType::Word
            };
            self.token_start = offset;
        }
    }

    fn special(&mut self, offset: usize) {
        self.shift(offset, State::Special);
        self.reduce();
    }

    fn initial(&mut self, offset: usize, c: char) {
        if c.is_whitespace() {
            self.shift(offset, State::Whitespace);
        } else if c == ';' || c == '?' || c == '|' {
            self.special(offset);
        } else if c == '"' {
            self.shift(offset, State::Doublequote);
        } else if c == '\'' {
            self.shift(offset, State::Singlequote);
        } else if c == '\\' {
            self.recognize(offset, State::Word);
            self.shift(offset, State::WordBackslash);
        } else {
            self.shift(offset, State::Word);
        }
    }

    fn tokenize(&mut self) -> Result<(), TokenizerError> {
        for (offset, c) in self.text.chars().enumerate() {
            match self.state {
                State::Initial => self.initial(offset, c),
                State::Whitespace => {
                    if c.is_whitespace() {
                        self.shift(offset, State::Whitespace);
                    } else {
                        self.reduce();
                        self.initial(offset, c);
                    };
                }
                State::Word => {
                    if c.is_whitespace() {
                        self.reduce();
                        self.shift(offset, State::Whitespace);
                    } else if c == ';' || c == '|' {
                        self.reduce();
                        self.special(offset);
                    } else if c == '"' {
                        self.reduce();
                        self.shift(offset, State::Doublequote);
                    } else if c == '\'' {
                        self.reduce();
                        self.shift(offset, State::Singlequote);
                    } else if c == '\\' {
                        self.shift(offset, State::WordBackslash);
                    } else {
                        self.shift(offset, State::Word);
                    }
                }
                State::WordBackslash => {
                    // XXX: This should be if !c.is_control() perhaps?
                    if c.is_alphanumeric() || c.is_whitespace() {
                        self.shift(offset, State::Word);
                    } else {
                        return Err(TokenizerError::CharacterNotAllowedHere(offset));
                    };
                }
                State::Doublequote => {
                    if c == '"' {
                        self.shift(offset, State::Doublequote);
                        self.reduce();
                    } else if c == '\\' {
                        self.shift(offset, State::DoublequoteBackslash);
                    } else {
                        self.shift(offset, State::Doublequote);
                    };
                }
                State::DoublequoteBackslash => {
                    if !c.is_whitespace() {
                        self.shift(offset, State::Doublequote);
                    } else {
                        return Err(TokenizerError::CharacterNotAllowedHere(offset));
                    };
                }
                State::Singlequote => {
                    if c == '\'' {
                        self.shift(offset, State::Singlequote);
                        self.reduce();
                    } else if c == '\\' {
                        self.shift(offset, State::SinglequoteBackslash);
                    } else {
                        self.shift(offset, State::Singlequote);
                    };
                }
                State::SinglequoteBackslash => {
                    if !c.is_whitespace() {
                        self.shift(offset, State::Singlequote);
                    } else {
                        return Err(TokenizerError::CharacterNotAllowedHere(offset));
                    };
                }
                State::Special => {
                    return Err(TokenizerError::SpecialNotYetImplemented(offset));
                }
            }
        }

        // Now for the end of the text...
        match self.state {
            State::Initial => {}
            State::Word | State::Whitespace => self.reduce(),
            State::WordBackslash => return Err(TokenizerError::EscapingBackslashAtEndOfInput),
            State::Doublequote => return Err(TokenizerError::UnclosedDoubleQuoteAtEndOfInput),
            State::Singlequote => return Err(TokenizerError::UnclosedSingleQuoteAtEndOfInput),
            State::DoublequoteBackslash | State::SinglequoteBackslash => {
                return Err(TokenizerError::EscapingBackslashAtEndOfInput)
            }
            State::Special => {
                return Err(TokenizerError::SpecialNotYetImplemented(self.text.len() - 1))
            }
        }

        Ok(())
    }
}

/// Tokenize a body of text.
pub fn tokenize(text: &str) -> Result<Vec<Token>, TokenizerError> {
    let mut tokenizer = Tokenizer::new(text);
    match tokenizer.tokenize() {
        Ok(_) => Ok(tokenizer.tokens),
        Err(error) => Err(error),
    }
}

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

    fn mk_token(text: &str, token_type: TokenType, start: usize, end: usize) -> Token {
        Token::new(text,
                   token_type,
                   SourceLocation::new(SourceOffset::new(start, 0, start),
                                       SourceOffset::new(end, 0, end)))
    }

    #[test]
    fn empty_test() {
        match tokenize("") {
            Ok(ts) => assert_eq!(ts.len(), 0),
            _ => {}
        };
    }

    #[test]
    fn single_word() {
        match tokenize("a") {
            Ok(ts) => {
                assert_eq!(ts.len(), 1);
                assert_eq!(ts[0], mk_token("a", TokenType::Word, 0, 0));
            }
            _ => {}
        };
    }

    #[test]
    fn multiple_words() {
        match tokenize(" aa bb  ccc ") {
            Ok(ts) => {
                assert_eq!(ts.len(), 7);
                assert_eq!(ts[0], mk_token(" ", TokenType::Whitespace, 0, 0));
                assert_eq!(ts[1], mk_token("aa", TokenType::Word, 1, 2));
                assert_eq!(ts[2], mk_token(" ", TokenType::Whitespace, 3, 3));
                assert_eq!(ts[3], mk_token("bb", TokenType::Word, 4, 5));
                assert_eq!(ts[4], mk_token("  ", TokenType::Whitespace, 6, 7));
                assert_eq!(ts[5], mk_token("ccc", TokenType::Word, 8, 10));
                assert_eq!(ts[6], mk_token(" ", TokenType::Whitespace, 11, 11));
            }
            _ => {}
        };
    }

    #[test]
    fn double_quoted_text() {
        match tokenize("a \"b c\"") {
            Ok(ts) => {
                assert_eq!(ts.len(), 3);
                assert_eq!(ts[0], mk_token("a", TokenType::Word, 0, 0));
                assert_eq!(ts[1], mk_token(" ", TokenType::Whitespace, 1, 1));
                assert_eq!(ts[2], mk_token("\"b c\"", TokenType::Word, 2, 6));
            }
            _ => {}
        };
    }

    #[test]
    fn single_quoted_text() {
        match tokenize("a '\"b c\"'") {
            Ok(ts) => {
                assert_eq!(ts.len(), 3);
                assert_eq!(ts[0], mk_token("a", TokenType::Word, 0, 0));
                assert_eq!(ts[1], mk_token(" ", TokenType::Whitespace, 1, 1));
                assert_eq!(ts[2], mk_token("\'\"b c\"\'", TokenType::Word, 2, 8));
            }
            _ => {}
        };
    }

    #[test]
    fn escaped_whitespace_in_word() {
        match tokenize("a\\ b") {
            Ok(ts) => {
                assert_eq!(ts.len(), 1);
                assert_eq!(ts[0], mk_token("a\\ b", TokenType::Word, 0, 3));
            }
            _ => {}
        };
    }

    #[test]
    fn character_not_allowed_here() {
        match tokenize("ab \\!") {
            Err(TokenizerError::CharacterNotAllowedHere(_)) => {}
            _ => panic!(),
        };

        match tokenize("ab \"\\ ab") {
            Err(TokenizerError::CharacterNotAllowedHere(_)) => {}
            _ => panic!(),
        };
    }

    // TODO: Test TokenizeError::SpecialNotYetImplemented

    #[test]
    #[should_panic]
    fn escaping_backslash_at_end_of_input() {
        match tokenize("ab \\") {
            Err(TokenizerError::EscapingBackslashAtEndOfInput) => panic!(),
            _ => {}
        }
    }

    #[test]
    #[should_panic]
    fn unclosed_double_quote_at_end_of_input() {
        match tokenize("ab \"") {
            Err(TokenizerError::UnclosedDoubleQuoteAtEndOfInput) => panic!(),
            _ => {}
        }
    }

    #[test]
    #[should_panic]
    fn escaped_double_quote_at_end_of_input() {
        match tokenize("ab \"\\") {
            Err(TokenizerError::EscapingBackslashAtEndOfInput) => panic!(),
            _ => {}
        }
    }
}
<commit_msg>rustfmt<commit_after>// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! # Command Tokenization
//!
//! The command parser needs to be able to tokenize commands
//! into their constituent words and whitespace.
//!
//! The `tokenizer` breaks source text into a vector of tokens
//! which can be either whitespace or a word. The tokenizer
//! handles using double quotes to provide a single token
//! which may include whitespace.
//!
//! Tokens also track their source location within the source
//! text. This allows the parser using the tokenizer to provide
//! better error highlighting and other functionality.
//!
//! # Examples
//!
//! ```
//! use commands::tokenizer::tokenize;
//!
//! if let Ok(tokens) = tokenize("word") {
//!     assert_eq!(tokens.len(), 1);
//! }
//!
//! if let Ok(tokens) = tokenize("show interface") {
//!     assert_eq!(tokens.len(), 3);
//! }
//!
//! if let Ok(tokens) = tokenize("echo -n \"a b c\"") {
//!     assert_eq!(tokens.len(), 5);
//! }
//!
//! if let Ok(tokens) = tokenize("ls My\\ Documents") {
//!     assert_eq!(tokens.len(), 3);
//!     assert_eq!(tokens[2].text, "My\\ Documents");
//! }
//! ```

use std::fmt;
use std::error::Error;

/// A position within a body of text.
///
/// The `SourceOffset` tracks 2 different ways of locating the
/// position:
///
/// * The index of the character within the body of text.
/// * The column and line number of the character.
///
/// The `SourceOffset` is typically used as a pair of offsets
/// indicating the start and end of a range of text as used
/// by the `SourceLocation`.
#[derive(Clone,Copy,Debug,PartialEq)]
pub struct SourceOffset {
    /// The index of this character within the body of text.
    pub char: usize,
    /// The line number on which this character may be found.
    pub line: usize,
    /// The column on which this character may be found.
    pub column: usize,
}

impl SourceOffset {
    /// Construct a `SourceOffset`.
    pub fn new(char: usize, line: usize, column: usize) -> SourceOffset {
        SourceOffset {
            char: char,
            line: line,
            column: column,
        }
    }
}

/// A range within a body of text.
#[derive(Clone,Copy,Debug,PartialEq)]
pub struct SourceLocation {
    /// The start of the range.
    pub start: SourceOffset,
    /// The end of the range.
    pub end: SourceOffset,
}

impl SourceLocation {
    /// Construct a `SourceLocation`.
    pub fn new(start: SourceOffset, end: SourceOffset) -> SourceLocation {
        SourceLocation {
            start: start,
            end: end,
        }
    }
}

/// Errors
#[derive(Clone,Debug)]
pub enum TokenizerError {
    /// Character not allowed here
    CharacterNotAllowedHere(usize),

    /// Special not yet implemented
    SpecialNotYetImplemented(usize),

    /// Escaping backslash at end of input
    EscapingBackslashAtEndOfInput,

    /// Unclosed double quote at end of input
    UnclosedDoubleQuoteAtEndOfInput,

    /// Unclosed single quote at end of input
    UnclosedSingleQuoteAtEndOfInput,
}

impl Error for TokenizerError {
    fn description(&self) -> &str {
        match *self {
            TokenizerError::CharacterNotAllowedHere(_) => "Character not allowed here",
            TokenizerError::SpecialNotYetImplemented(_) => "Special not yet implemented",
            TokenizerError::EscapingBackslashAtEndOfInput => "Escaping backlash at end of input",
            TokenizerError::UnclosedDoubleQuoteAtEndOfInput => {
                "Unclosed double quote at end of input"
            }
            TokenizerError::UnclosedSingleQuoteAtEndOfInput => {
                "Unclosed single quote at end of input"
            }
        }
    }
}

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

/// The role that a token plays: `Whitespace` or `Word`.
#[derive(Clone,Copy,Debug,PartialEq)]
pub enum TokenType {
    /// Internal usage only.
    Invalid,
    /// The token represents whitespace and not a word.
    Whitespace,
    /// The token represents a word within the body of text. This
    /// takes double quoted strings into account.
    Word,
}

/// A token from a body of text.
///
/// The lifetime parameter `'t` refers to the lifetime
/// of the body of text that was tokenized, creating this token.
#[derive(Clone,Copy,Debug,PartialEq)]
pub struct Token<'t> {
    /// The text of the token.
    pub text: &'t str,
    /// The type of the token (`Whitespace` or `Word`).
    pub token_type: TokenType,
    /// The location of the token in the source body of text.
    pub location: SourceLocation,
}

impl<'t> Token<'t> {
    /// Construct a `Token`. The lifetime parameter `'t` refers to the lifetime of the
    /// text being tokenized.
    pub fn new(text: &'t str, token_type: TokenType, location: SourceLocation) -> Token {
        Token {
            text: text,
            token_type: token_type,
            location: location,
        }
    }
}

#[derive(Clone,Copy,PartialEq)]
enum State {
    Initial,
    Special,
    Whitespace,
    Doublequote,
    DoublequoteBackslash,
    Singlequote,
    SinglequoteBackslash,
    Word,
    WordBackslash,
}

struct Tokenizer<'t> {
    text: &'t str,
    state: State,
    token_type: TokenType,
    token_start: usize,
    token_end: usize,
    tokens: Vec<Token<'t>>,
}

impl<'t> Tokenizer<'t> {
    fn new(text: &'t str) -> Tokenizer {
        Tokenizer {
            text: text,
            state: State::Initial,
            token_type: TokenType::Invalid,
            token_start: 0,
            token_end: 0,
            tokens: vec![],
        }
    }

    fn reset(&mut self) {
        self.state = State::Initial;
        self.token_type = TokenType::Invalid;
        self.token_start = 0;
        self.token_end = 0;
    }

    fn reduce(&mut self) {
        let token_text = &self.text[self.token_start..self.token_end + 1];
        let loc = SourceLocation::new(SourceOffset::new(self.token_start, 0, self.token_start),
                                      SourceOffset::new(self.token_end, 0, self.token_end));
        self.tokens.push(Token::new(token_text, self.token_type, loc));
        self.reset();
    }

    fn shift(&mut self, offset: usize, next_state: State) {
        self.recognize(offset, next_state);
        self.token_end = offset;
        self.state = next_state;
    }

    fn recognize(&mut self, offset: usize, next_state: State) {
        if self.token_type == TokenType::Invalid {
            self.token_type = if next_state == State::Whitespace {
                TokenType::Whitespace
            } else {
                TokenType::Word
            };
            self.token_start = offset;
        }
    }

    fn special(&mut self, offset: usize) {
        self.shift(offset, State::Special);
        self.reduce();
    }

    fn initial(&mut self, offset: usize, c: char) {
        if c.is_whitespace() {
            self.shift(offset, State::Whitespace);
        } else if c == ';' || c == '?' || c == '|' {
            self.special(offset);
        } else if c == '"' {
            self.shift(offset, State::Doublequote);
        } else if c == '\'' {
            self.shift(offset, State::Singlequote);
        } else if c == '\\' {
            self.recognize(offset, State::Word);
            self.shift(offset, State::WordBackslash);
        } else {
            self.shift(offset, State::Word);
        }
    }

    fn tokenize(&mut self) -> Result<(), TokenizerError> {
        for (offset, c) in self.text.chars().enumerate() {
            match self.state {
                State::Initial => self.initial(offset, c),
                State::Whitespace => {
                    if c.is_whitespace() {
                        self.shift(offset, State::Whitespace);
                    } else {
                        self.reduce();
                        self.initial(offset, c);
                    };
                }
                State::Word => {
                    if c.is_whitespace() {
                        self.reduce();
                        self.shift(offset, State::Whitespace);
                    } else if c == ';' || c == '|' {
                        self.reduce();
                        self.special(offset);
                    } else if c == '"' {
                        self.reduce();
                        self.shift(offset, State::Doublequote);
                    } else if c == '\'' {
                        self.reduce();
                        self.shift(offset, State::Singlequote);
                    } else if c == '\\' {
                        self.shift(offset, State::WordBackslash);
                    } else {
                        self.shift(offset, State::Word);
                    }
                }
                State::WordBackslash => {
                    // XXX: This should be if !c.is_control() perhaps?
                    if c.is_alphanumeric() || c.is_whitespace() {
                        self.shift(offset, State::Word);
                    } else {
                        return Err(TokenizerError::CharacterNotAllowedHere(offset));
                    };
                }
                State::Doublequote => {
                    if c == '"' {
                        self.shift(offset, State::Doublequote);
                        self.reduce();
                    } else if c == '\\' {
                        self.shift(offset, State::DoublequoteBackslash);
                    } else {
                        self.shift(offset, State::Doublequote);
                    };
                }
                State::DoublequoteBackslash => {
                    if !c.is_whitespace() {
                        self.shift(offset, State::Doublequote);
                    } else {
                        return Err(TokenizerError::CharacterNotAllowedHere(offset));
                    };
                }
                State::Singlequote => {
                    if c == '\'' {
                        self.shift(offset, State::Singlequote);
                        self.reduce();
                    } else if c == '\\' {
                        self.shift(offset, State::SinglequoteBackslash);
                    } else {
                        self.shift(offset, State::Singlequote);
                    };
                }
                State::SinglequoteBackslash => {
                    if !c.is_whitespace() {
                        self.shift(offset, State::Singlequote);
                    } else {
                        return Err(TokenizerError::CharacterNotAllowedHere(offset));
                    };
                }
                State::Special => {
                    return Err(TokenizerError::SpecialNotYetImplemented(offset));
                }
            }
        }

        // Now for the end of the text...
        match self.state {
            State::Initial => {}
            State::Word | State::Whitespace => self.reduce(),
            State::WordBackslash => return Err(TokenizerError::EscapingBackslashAtEndOfInput),
            State::Doublequote => return Err(TokenizerError::UnclosedDoubleQuoteAtEndOfInput),
            State::Singlequote => return Err(TokenizerError::UnclosedSingleQuoteAtEndOfInput),
            State::DoublequoteBackslash |
            State::SinglequoteBackslash => {
                return Err(TokenizerError::EscapingBackslashAtEndOfInput)
            }
            State::Special => {
                return Err(TokenizerError::SpecialNotYetImplemented(self.text.len() - 1))
            }
        }

        Ok(())
    }
}

/// Tokenize a body of text.
pub fn tokenize(text: &str) -> Result<Vec<Token>, TokenizerError> {
    let mut tokenizer = Tokenizer::new(text);
    match tokenizer.tokenize() {
        Ok(_) => Ok(tokenizer.tokens),
        Err(error) => Err(error),
    }
}

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

    fn mk_token(text: &str, token_type: TokenType, start: usize, end: usize) -> Token {
        Token::new(text,
                   token_type,
                   SourceLocation::new(SourceOffset::new(start, 0, start),
                                       SourceOffset::new(end, 0, end)))
    }

    #[test]
    fn empty_test() {
        match tokenize("") {
            Ok(ts) => assert_eq!(ts.len(), 0),
            _ => {}
        };
    }

    #[test]
    fn single_word() {
        match tokenize("a") {
            Ok(ts) => {
                assert_eq!(ts.len(), 1);
                assert_eq!(ts[0], mk_token("a", TokenType::Word, 0, 0));
            }
            _ => {}
        };
    }

    #[test]
    fn multiple_words() {
        match tokenize(" aa bb  ccc ") {
            Ok(ts) => {
                assert_eq!(ts.len(), 7);
                assert_eq!(ts[0], mk_token(" ", TokenType::Whitespace, 0, 0));
                assert_eq!(ts[1], mk_token("aa", TokenType::Word, 1, 2));
                assert_eq!(ts[2], mk_token(" ", TokenType::Whitespace, 3, 3));
                assert_eq!(ts[3], mk_token("bb", TokenType::Word, 4, 5));
                assert_eq!(ts[4], mk_token("  ", TokenType::Whitespace, 6, 7));
                assert_eq!(ts[5], mk_token("ccc", TokenType::Word, 8, 10));
                assert_eq!(ts[6], mk_token(" ", TokenType::Whitespace, 11, 11));
            }
            _ => {}
        };
    }

    #[test]
    fn double_quoted_text() {
        match tokenize("a \"b c\"") {
            Ok(ts) => {
                assert_eq!(ts.len(), 3);
                assert_eq!(ts[0], mk_token("a", TokenType::Word, 0, 0));
                assert_eq!(ts[1], mk_token(" ", TokenType::Whitespace, 1, 1));
                assert_eq!(ts[2], mk_token("\"b c\"", TokenType::Word, 2, 6));
            }
            _ => {}
        };
    }

    #[test]
    fn single_quoted_text() {
        match tokenize("a '\"b c\"'") {
            Ok(ts) => {
                assert_eq!(ts.len(), 3);
                assert_eq!(ts[0], mk_token("a", TokenType::Word, 0, 0));
                assert_eq!(ts[1], mk_token(" ", TokenType::Whitespace, 1, 1));
                assert_eq!(ts[2], mk_token("\'\"b c\"\'", TokenType::Word, 2, 8));
            }
            _ => {}
        };
    }

    #[test]
    fn escaped_whitespace_in_word() {
        match tokenize("a\\ b") {
            Ok(ts) => {
                assert_eq!(ts.len(), 1);
                assert_eq!(ts[0], mk_token("a\\ b", TokenType::Word, 0, 3));
            }
            _ => {}
        };
    }

    #[test]
    fn character_not_allowed_here() {
        match tokenize("ab \\!") {
            Err(TokenizerError::CharacterNotAllowedHere(_)) => {}
            _ => panic!(),
        };

        match tokenize("ab \"\\ ab") {
            Err(TokenizerError::CharacterNotAllowedHere(_)) => {}
            _ => panic!(),
        };
    }

    // TODO: Test TokenizeError::SpecialNotYetImplemented

    #[test]
    #[should_panic]
    fn escaping_backslash_at_end_of_input() {
        match tokenize("ab \\") {
            Err(TokenizerError::EscapingBackslashAtEndOfInput) => panic!(),
            _ => {}
        }
    }

    #[test]
    #[should_panic]
    fn unclosed_double_quote_at_end_of_input() {
        match tokenize("ab \"") {
            Err(TokenizerError::UnclosedDoubleQuoteAtEndOfInput) => panic!(),
            _ => {}
        }
    }

    #[test]
    #[should_panic]
    fn escaped_double_quote_at_end_of_input() {
        match tokenize("ab \"\\") {
            Err(TokenizerError::EscapingBackslashAtEndOfInput) => panic!(),
            _ => {}
        }
    }
}
<|endoftext|>


<commit_before>use std::io::{self, Write, Read, Cursor};
use std::net::{TcpStream, Shutdown};
use std::time::Duration;
use std::borrow::BorrowMut;
use byteorder::{BigEndian, WriteBytesExt, ReadBytesExt};
use enum_primitive::FromPrimitive;
use {Function, Reason, Result, ExceptionCode, Error, Coil, binary, Client};

const MODBUS_PROTOCOL_TCP: u16 = 0x0000;
const MODBUS_TCP_DEFAULT_PORT: u16 = 502;
const MODBUS_HEADER_SIZE: usize = 7;
const MODBUS_MAX_PACKET_SIZE: usize = 260;

/// Config structure for more control over the tcp socket settings
#[derive(Clone, Copy)]
pub struct Config {
    /// The TCP port to use for communication (Default: `502`)
    pub tcp_port: u16,
    /// Timeout when reading from the TCP socket (Default: `infinite`)
    pub tcp_read_timeout: Option<Duration>,
    /// Timeout when writing to the TCP socket (Default: `infinite`)
    pub tcp_write_timeout: Option<Duration>,
    /// The modbus Unit Identifier used in the modbus layer (Default: `1`)
    pub modbus_uid: u8,
}

impl Default for Config {
    fn default() -> Config {
        Config {
            tcp_port: MODBUS_TCP_DEFAULT_PORT,
            tcp_read_timeout: None,
            tcp_write_timeout: None,
            modbus_uid: 1,
        }
    }
}

#[derive(Debug, PartialEq)]
struct Header {
    tid: u16,
    pid: u16,
    len: u16,
    uid: u8,
}

impl Header {
    fn new(transport: &mut Transport, len: u16) -> Header {
        Header {
            tid: transport.new_tid(),
            pid: MODBUS_PROTOCOL_TCP,
            len: len - MODBUS_HEADER_SIZE as u16,
            uid: transport.uid,
        }
    }

    fn pack(&self) -> Result<Vec<u8>> {
        let mut buff = vec![];
        buff.write_u16::<BigEndian>(self.tid)?;
        buff.write_u16::<BigEndian>(self.pid)?;
        buff.write_u16::<BigEndian>(self.len)?;
        buff.write_u8(self.uid)?;
        Ok(buff)
    }

    fn unpack(buff: &[u8]) -> Result<Header> {
        let mut rdr = Cursor::new(buff);
        Ok(Header {
            tid: rdr.read_u16::<BigEndian>()?,
            pid: rdr.read_u16::<BigEndian>()?,
            len: rdr.read_u16::<BigEndian>()?,
            uid: rdr.read_u8()?
        })
    }
}

/// Context object which holds state for all modbus operations.
pub struct Transport {
    tid: u16,
    uid: u8,
    stream: TcpStream,
}

impl Transport {
    /// Create a new context context object and connect it to `addr` on modbus-tcp default
    /// port (502)
    pub fn new(addr: &str) -> io::Result<Transport> {
        Self::new_with_cfg(addr, Config::default())
    }

    /// Create a new context object and connect it to `addr` on port `port`
    pub fn new_with_cfg(addr: &str, cfg: Config) -> io::Result<Transport> {
        match TcpStream::connect((addr, cfg.tcp_port)) {
            Ok(s) => {
                s.set_read_timeout(cfg.tcp_read_timeout)?;
                s.set_write_timeout(cfg.tcp_write_timeout)?;
                s.set_nodelay(true)?;
                Ok(Transport {
                    tid: 0,
                    uid: cfg.modbus_uid,
                    stream: s,
                })
            }
            Err(e) => Err(e),
        }
    }

    // Create a new transaction Id, incrementing the previous one.
    // The Id is wrapping around if the Id reaches `u16::MAX`.
    fn new_tid(&mut self) -> u16 {
        self.tid = self.tid.wrapping_add(1);
        self.tid
    }

    fn read(self: &mut Self, fun: Function) -> Result<Vec<u8>> {
        let packed_size = |v: u16| {
            v / 8 +
            if v % 8 > 0 {
                1
            } else {
                0
            }
        };
        let (addr, count, expected_bytes) = match fun {
            Function::ReadCoils(a, c) |
            Function::ReadDiscreteInputs(a, c) => (a, c, packed_size(c) as usize),
            Function::ReadHoldingRegisters(a, c) |
            Function::ReadInputRegisters(a, c) => (a, c, 2 * c as usize),
            _ => return Err(Error::InvalidFunction),
        };

        if count < 1 {
            return Err(Error::InvalidData(Reason::RecvBufferEmpty));
        }

        if count as usize > MODBUS_MAX_PACKET_SIZE {
            return Err(Error::InvalidData(Reason::UnexpectedReplySize));
        }

        let header = Header::new(self, MODBUS_HEADER_SIZE as u16 + 6u16);
        let mut buff = header.pack()?;
        buff.write_u8(fun.code())?;
        buff.write_u16::<BigEndian>(addr)?;
        buff.write_u16::<BigEndian>(count)?;

        match self.stream.write_all(&buff) {
            Ok(_s) => {
                let mut reply = vec![0; MODBUS_HEADER_SIZE + expected_bytes + 2];
                match self.stream.read(&mut reply) {
                    Ok(_s) => {
                        let resp_hd = Header::unpack(&reply[..MODBUS_HEADER_SIZE])?;
                        Transport::validate_response_header(&header, &resp_hd)?;
                        Transport::validate_response_code(&buff, &reply)?;
                        Transport::get_reply_data(&reply, expected_bytes)
                    }
                    Err(e) => Err(Error::Io(e)),
                }
            }
            Err(e) => Err(Error::Io(e)),
        }
    }

    fn validate_response_header(req: &Header, resp: &Header) -> Result<()> {
        if req.tid != resp.tid || resp.pid != MODBUS_PROTOCOL_TCP {
            Err(Error::InvalidResponse)
        } else {
            Ok(())
        }
    }

    fn validate_response_code(req: &[u8], resp: &[u8]) -> Result<()> {
        if req[7] + 0x80 == resp[7] {
            match ExceptionCode::from_u8(resp[8]) {
                Some(code) => Err(Error::Exception(code)),
                None => Err(Error::InvalidResponse),
            }
        } else if req[7] == resp[7] {
            Ok(())
        } else {
            Err(Error::InvalidResponse)
        }
    }

    fn get_reply_data(reply: &[u8], expected_bytes: usize) -> Result<Vec<u8>> {
        if reply[8] as usize != expected_bytes ||
           reply.len() != MODBUS_HEADER_SIZE + expected_bytes + 2 {
            Err(Error::InvalidData(Reason::UnexpectedReplySize))
        } else {
            let mut d = Vec::new();
            d.extend_from_slice(&reply[MODBUS_HEADER_SIZE + 2..]);
            Ok(d)
        }
    }

    fn write_single(self: &mut Self, fun: Function) -> Result<()> {
        let (addr, value) = match fun {
            Function::WriteSingleCoil(a, v) |
            Function::WriteSingleRegister(a, v) => (a, v),
            _ => return Err(Error::InvalidFunction),
        };

        let mut buff = vec![0; MODBUS_HEADER_SIZE];  // Header gets filled in later
        buff.write_u8(fun.code())?;
        buff.write_u16::<BigEndian>(addr)?;
        buff.write_u16::<BigEndian>(value)?;
        self.write(&mut buff)
    }

    fn write_multiple(self: &mut Self, fun: Function) -> Result<()> {
        let (addr, quantity, values) = match fun {
            Function::WriteMultipleCoils(a, q, v) |
            Function::WriteMultipleRegisters(a, q, v) => (a, q, v),
            _ => return Err(Error::InvalidFunction),
        };

        let mut buff = vec![0; MODBUS_HEADER_SIZE];  // Header gets filled in later
        buff.write_u8(fun.code())?;
        buff.write_u16::<BigEndian>(addr)?;
        buff.write_u16::<BigEndian>(quantity)?;
        buff.write_u8(values.len() as u8)?;
        for v in values {
            buff.write_u8(*v)?;
        }
        self.write(&mut buff)
    }

    fn write(self: &mut Self, buff: &mut [u8]) -> Result<()> {
        if buff.len() < 1 {
            return Err(Error::InvalidData(Reason::SendBufferEmpty));
        }

        if buff.len() > MODBUS_MAX_PACKET_SIZE {
            return Err(Error::InvalidData(Reason::SendBufferTooBig));
        }

        let header = Header::new(self, buff.len() as u16 + 1u16);
        let head_buff = header.pack()?;
        {
            let mut start = Cursor::new(buff.borrow_mut());
            start.write(&head_buff)?;
        }
        match self.stream.write_all(buff) {
            Ok(_s) => {
                let reply = &mut [0; 12];
                match self.stream.read(reply) {
                    Ok(_s) => {
                        let resp_hd = Header::unpack(reply)?;
                        Transport::validate_response_header(&header, &resp_hd)?;
                        Transport::validate_response_code(buff, reply)
                    }
                    Err(e) => Err(Error::Io(e)),
                }
            }
            Err(e) => Err(Error::Io(e)),
        }
    }

    pub fn close(self: &mut Self) -> Result<()> {
        self.stream.shutdown(Shutdown::Both).map_err(Error::Io)
    }
}

impl Client for Transport {
    /// Read `count` bits starting at address `addr`.
    fn read_coils(self: &mut Self, addr: u16, count: u16) -> Result<Vec<Coil>> {
        let bytes = self.read(Function::ReadCoils(addr, count))?;
        Ok(binary::unpack_bits(&bytes, count))
    }

    /// Read `count` input bits starting at address `addr`.
    fn read_discrete_inputs(self: &mut Self, addr: u16, count: u16) -> Result<Vec<Coil>> {
        let bytes = self.read(Function::ReadDiscreteInputs(addr, count))?;
        Ok(binary::unpack_bits(&bytes, count))
    }

    /// Read `count` 16bit registers starting at address `addr`.
    fn read_holding_registers(self: &mut Self, addr: u16, count: u16) -> Result<Vec<u16>> {
        let bytes = self.read(Function::ReadHoldingRegisters(addr, count))?;
        binary::pack_bytes(&bytes[..])
    }

    /// Read `count` 16bit input registers starting at address `addr`.
    fn read_input_registers(self: &mut Self, addr: u16, count: u16) -> Result<Vec<u16>> {
        let bytes = self.read(Function::ReadInputRegisters(addr, count))?;
        binary::pack_bytes(&bytes[..])
    }

    /// Write a single coil (bit) to address `addr`.
    fn write_single_coil(self: &mut Self, addr: u16, value: Coil) -> Result<()> {
        self.write_single(Function::WriteSingleCoil(addr, value.code()))
    }

    /// Write a single 16bit register to address `addr`.
    fn write_single_register(self: &mut Self, addr: u16, value: u16) -> Result<()> {
        self.write_single(Function::WriteSingleRegister(addr, value))
    }

    /// Write a multiple coils (bits) starting at address `addr`.
    fn write_multiple_coils(self: &mut Self, addr: u16, values: &[Coil]) -> Result<()> {
        let bytes = binary::pack_bits(values);
        self.write_multiple(Function::WriteMultipleCoils(addr, values.len() as u16, &bytes))
    }

    /// Write a multiple 16bit registers starting at address `addr`.
    fn write_multiple_registers(self: &mut Self, addr: u16, values: &[u16]) -> Result<()> {
        let bytes = binary::unpack_bytes(values);
        self.write_multiple(Function::WriteMultipleRegisters(addr, values.len() as u16, &bytes))
    }
}

#[cfg(test)]
mod tests {

    use super::*;

    #[test]
    fn serialize_header(){
        let header = Header{tid: 12816, pid: 3930, len: 99, uid: 68 };
        let serialized = header.pack().unwrap();
        let deserialized = Header::unpack(&vec![50, 16, 15, 90, 0, 99, 68]).unwrap();
        let re_deserialized = Header::unpack(&serialized).unwrap();
        assert_eq!(serialized, vec![50, 16, 15, 90, 0, 99, 68]);
        assert_eq!(deserialized, header);
        assert_eq!(re_deserialized, header);
    }
}
<commit_msg>Use `write_all` function as suggested by `clippy`<commit_after>use std::io::{self, Write, Read, Cursor};
use std::net::{TcpStream, Shutdown};
use std::time::Duration;
use std::borrow::BorrowMut;
use byteorder::{BigEndian, WriteBytesExt, ReadBytesExt};
use enum_primitive::FromPrimitive;
use {Function, Reason, Result, ExceptionCode, Error, Coil, binary, Client};

const MODBUS_PROTOCOL_TCP: u16 = 0x0000;
const MODBUS_TCP_DEFAULT_PORT: u16 = 502;
const MODBUS_HEADER_SIZE: usize = 7;
const MODBUS_MAX_PACKET_SIZE: usize = 260;

/// Config structure for more control over the tcp socket settings
#[derive(Clone, Copy)]
pub struct Config {
    /// The TCP port to use for communication (Default: `502`)
    pub tcp_port: u16,
    /// Timeout when reading from the TCP socket (Default: `infinite`)
    pub tcp_read_timeout: Option<Duration>,
    /// Timeout when writing to the TCP socket (Default: `infinite`)
    pub tcp_write_timeout: Option<Duration>,
    /// The modbus Unit Identifier used in the modbus layer (Default: `1`)
    pub modbus_uid: u8,
}

impl Default for Config {
    fn default() -> Config {
        Config {
            tcp_port: MODBUS_TCP_DEFAULT_PORT,
            tcp_read_timeout: None,
            tcp_write_timeout: None,
            modbus_uid: 1,
        }
    }
}

#[derive(Debug, PartialEq)]
struct Header {
    tid: u16,
    pid: u16,
    len: u16,
    uid: u8,
}

impl Header {
    fn new(transport: &mut Transport, len: u16) -> Header {
        Header {
            tid: transport.new_tid(),
            pid: MODBUS_PROTOCOL_TCP,
            len: len - MODBUS_HEADER_SIZE as u16,
            uid: transport.uid,
        }
    }

    fn pack(&self) -> Result<Vec<u8>> {
        let mut buff = vec![];
        buff.write_u16::<BigEndian>(self.tid)?;
        buff.write_u16::<BigEndian>(self.pid)?;
        buff.write_u16::<BigEndian>(self.len)?;
        buff.write_u8(self.uid)?;
        Ok(buff)
    }

    fn unpack(buff: &[u8]) -> Result<Header> {
        let mut rdr = Cursor::new(buff);
        Ok(Header {
            tid: rdr.read_u16::<BigEndian>()?,
            pid: rdr.read_u16::<BigEndian>()?,
            len: rdr.read_u16::<BigEndian>()?,
            uid: rdr.read_u8()?
        })
    }
}

/// Context object which holds state for all modbus operations.
pub struct Transport {
    tid: u16,
    uid: u8,
    stream: TcpStream,
}

impl Transport {
    /// Create a new context context object and connect it to `addr` on modbus-tcp default
    /// port (502)
    pub fn new(addr: &str) -> io::Result<Transport> {
        Self::new_with_cfg(addr, Config::default())
    }

    /// Create a new context object and connect it to `addr` on port `port`
    pub fn new_with_cfg(addr: &str, cfg: Config) -> io::Result<Transport> {
        match TcpStream::connect((addr, cfg.tcp_port)) {
            Ok(s) => {
                s.set_read_timeout(cfg.tcp_read_timeout)?;
                s.set_write_timeout(cfg.tcp_write_timeout)?;
                s.set_nodelay(true)?;
                Ok(Transport {
                    tid: 0,
                    uid: cfg.modbus_uid,
                    stream: s,
                })
            }
            Err(e) => Err(e),
        }
    }

    // Create a new transaction Id, incrementing the previous one.
    // The Id is wrapping around if the Id reaches `u16::MAX`.
    fn new_tid(&mut self) -> u16 {
        self.tid = self.tid.wrapping_add(1);
        self.tid
    }

    fn read(self: &mut Self, fun: Function) -> Result<Vec<u8>> {
        let packed_size = |v: u16| {
            v / 8 +
            if v % 8 > 0 {
                1
            } else {
                0
            }
        };
        let (addr, count, expected_bytes) = match fun {
            Function::ReadCoils(a, c) |
            Function::ReadDiscreteInputs(a, c) => (a, c, packed_size(c) as usize),
            Function::ReadHoldingRegisters(a, c) |
            Function::ReadInputRegisters(a, c) => (a, c, 2 * c as usize),
            _ => return Err(Error::InvalidFunction),
        };

        if count < 1 {
            return Err(Error::InvalidData(Reason::RecvBufferEmpty));
        }

        if count as usize > MODBUS_MAX_PACKET_SIZE {
            return Err(Error::InvalidData(Reason::UnexpectedReplySize));
        }

        let header = Header::new(self, MODBUS_HEADER_SIZE as u16 + 6u16);
        let mut buff = header.pack()?;
        buff.write_u8(fun.code())?;
        buff.write_u16::<BigEndian>(addr)?;
        buff.write_u16::<BigEndian>(count)?;

        match self.stream.write_all(&buff) {
            Ok(_s) => {
                let mut reply = vec![0; MODBUS_HEADER_SIZE + expected_bytes + 2];
                match self.stream.read(&mut reply) {
                    Ok(_s) => {
                        let resp_hd = Header::unpack(&reply[..MODBUS_HEADER_SIZE])?;
                        Transport::validate_response_header(&header, &resp_hd)?;
                        Transport::validate_response_code(&buff, &reply)?;
                        Transport::get_reply_data(&reply, expected_bytes)
                    }
                    Err(e) => Err(Error::Io(e)),
                }
            }
            Err(e) => Err(Error::Io(e)),
        }
    }

    fn validate_response_header(req: &Header, resp: &Header) -> Result<()> {
        if req.tid != resp.tid || resp.pid != MODBUS_PROTOCOL_TCP {
            Err(Error::InvalidResponse)
        } else {
            Ok(())
        }
    }

    fn validate_response_code(req: &[u8], resp: &[u8]) -> Result<()> {
        if req[7] + 0x80 == resp[7] {
            match ExceptionCode::from_u8(resp[8]) {
                Some(code) => Err(Error::Exception(code)),
                None => Err(Error::InvalidResponse),
            }
        } else if req[7] == resp[7] {
            Ok(())
        } else {
            Err(Error::InvalidResponse)
        }
    }

    fn get_reply_data(reply: &[u8], expected_bytes: usize) -> Result<Vec<u8>> {
        if reply[8] as usize != expected_bytes ||
           reply.len() != MODBUS_HEADER_SIZE + expected_bytes + 2 {
            Err(Error::InvalidData(Reason::UnexpectedReplySize))
        } else {
            let mut d = Vec::new();
            d.extend_from_slice(&reply[MODBUS_HEADER_SIZE + 2..]);
            Ok(d)
        }
    }

    fn write_single(self: &mut Self, fun: Function) -> Result<()> {
        let (addr, value) = match fun {
            Function::WriteSingleCoil(a, v) |
            Function::WriteSingleRegister(a, v) => (a, v),
            _ => return Err(Error::InvalidFunction),
        };

        let mut buff = vec![0; MODBUS_HEADER_SIZE];  // Header gets filled in later
        buff.write_u8(fun.code())?;
        buff.write_u16::<BigEndian>(addr)?;
        buff.write_u16::<BigEndian>(value)?;
        self.write(&mut buff)
    }

    fn write_multiple(self: &mut Self, fun: Function) -> Result<()> {
        let (addr, quantity, values) = match fun {
            Function::WriteMultipleCoils(a, q, v) |
            Function::WriteMultipleRegisters(a, q, v) => (a, q, v),
            _ => return Err(Error::InvalidFunction),
        };

        let mut buff = vec![0; MODBUS_HEADER_SIZE];  // Header gets filled in later
        buff.write_u8(fun.code())?;
        buff.write_u16::<BigEndian>(addr)?;
        buff.write_u16::<BigEndian>(quantity)?;
        buff.write_u8(values.len() as u8)?;
        for v in values {
            buff.write_u8(*v)?;
        }
        self.write(&mut buff)
    }

    fn write(self: &mut Self, buff: &mut [u8]) -> Result<()> {
        if buff.len() < 1 {
            return Err(Error::InvalidData(Reason::SendBufferEmpty));
        }

        if buff.len() > MODBUS_MAX_PACKET_SIZE {
            return Err(Error::InvalidData(Reason::SendBufferTooBig));
        }

        let header = Header::new(self, buff.len() as u16 + 1u16);
        let head_buff = header.pack()?;
        {
            let mut start = Cursor::new(buff.borrow_mut());
            start.write_all(&head_buff)?;
        }
        match self.stream.write_all(buff) {
            Ok(_s) => {
                let reply = &mut [0; 12];
                match self.stream.read(reply) {
                    Ok(_s) => {
                        let resp_hd = Header::unpack(reply)?;
                        Transport::validate_response_header(&header, &resp_hd)?;
                        Transport::validate_response_code(buff, reply)
                    }
                    Err(e) => Err(Error::Io(e)),
                }
            }
            Err(e) => Err(Error::Io(e)),
        }
    }

    pub fn close(self: &mut Self) -> Result<()> {
        self.stream.shutdown(Shutdown::Both).map_err(Error::Io)
    }
}

impl Client for Transport {
    /// Read `count` bits starting at address `addr`.
    fn read_coils(self: &mut Self, addr: u16, count: u16) -> Result<Vec<Coil>> {
        let bytes = self.read(Function::ReadCoils(addr, count))?;
        Ok(binary::unpack_bits(&bytes, count))
    }

    /// Read `count` input bits starting at address `addr`.
    fn read_discrete_inputs(self: &mut Self, addr: u16, count: u16) -> Result<Vec<Coil>> {
        let bytes = self.read(Function::ReadDiscreteInputs(addr, count))?;
        Ok(binary::unpack_bits(&bytes, count))
    }

    /// Read `count` 16bit registers starting at address `addr`.
    fn read_holding_registers(self: &mut Self, addr: u16, count: u16) -> Result<Vec<u16>> {
        let bytes = self.read(Function::ReadHoldingRegisters(addr, count))?;
        binary::pack_bytes(&bytes[..])
    }

    /// Read `count` 16bit input registers starting at address `addr`.
    fn read_input_registers(self: &mut Self, addr: u16, count: u16) -> Result<Vec<u16>> {
        let bytes = self.read(Function::ReadInputRegisters(addr, count))?;
        binary::pack_bytes(&bytes[..])
    }

    /// Write a single coil (bit) to address `addr`.
    fn write_single_coil(self: &mut Self, addr: u16, value: Coil) -> Result<()> {
        self.write_single(Function::WriteSingleCoil(addr, value.code()))
    }

    /// Write a single 16bit register to address `addr`.
    fn write_single_register(self: &mut Self, addr: u16, value: u16) -> Result<()> {
        self.write_single(Function::WriteSingleRegister(addr, value))
    }

    /// Write a multiple coils (bits) starting at address `addr`.
    fn write_multiple_coils(self: &mut Self, addr: u16, values: &[Coil]) -> Result<()> {
        let bytes = binary::pack_bits(values);
        self.write_multiple(Function::WriteMultipleCoils(addr, values.len() as u16, &bytes))
    }

    /// Write a multiple 16bit registers starting at address `addr`.
    fn write_multiple_registers(self: &mut Self, addr: u16, values: &[u16]) -> Result<()> {
        let bytes = binary::unpack_bytes(values);
        self.write_multiple(Function::WriteMultipleRegisters(addr, values.len() as u16, &bytes))
    }
}

#[cfg(test)]
mod tests {

    use super::*;

    #[test]
    fn serialize_header(){
        let header = Header{tid: 12816, pid: 3930, len: 99, uid: 68 };
        let serialized = header.pack().unwrap();
        let deserialized = Header::unpack(&vec![50, 16, 15, 90, 0, 99, 68]).unwrap();
        let re_deserialized = Header::unpack(&serialized).unwrap();
        assert_eq!(serialized, vec![50, 16, 15, 90, 0, 99, 68]);
        assert_eq!(deserialized, header);
        assert_eq!(re_deserialized, header);
    }
}
<|endoftext|>