<commit_before>// (c) 2016 Joost Yervante Damad <joost@productize.be>

use std::error;

use serde;
use serde::de;
use Sexp;

// loosely based on toml-rs

/// Description for errors which can occur while decoding a type.
#[derive(PartialEq, Debug)]
pub struct DecodeError {
    /// Field that this error applies to.
    pub field: Option<String>,
    /// The type of error which occurred while decoding,
    pub kind: DecodeErrorKind,
}

impl de::Error for DecodeError {
    fn custom<T: Into<String>>(msg: T) -> DecodeError {
        DecodeError {
            field: None,
            kind: DecodeErrorKind::CustomError(msg.into()),
        }
    }
    fn end_of_stream() -> DecodeError {
        DecodeError { field: None, kind: DecodeErrorKind::EndOfStream }
    }
    fn missing_field(name: &'static str) -> DecodeError {
        DecodeError {
            field: Some(name.to_string()),
            kind: DecodeErrorKind::ExpectedField(None),
        }
    }
    fn unknown_field(name: &str) -> DecodeError {
        DecodeError {
            field: Some(name.to_string()),
            kind: DecodeErrorKind::UnknownField,
        }
    }
    fn invalid_type(ty: de::Type) -> Self {
        DecodeError {
            field: None,
            kind: DecodeErrorKind::InvalidType(match ty {
                de::Type::Bool => "bool",
                de::Type::Usize |
                de::Type::U8 |
                de::Type::U16 |
                de::Type::U32 |
                de::Type::U64 |
                de::Type::Isize |
                de::Type::I8 |
                de::Type::I16 |
                de::Type::I32 |
                de::Type::I64 => "integer",
                de::Type::F32 |
                de::Type::F64 => "float",
                de::Type::Char |
                de::Type::Str |
                de::Type::String => "string",
                de::Type::Seq => "array",
                de::Type::Struct |
                de::Type::Map => "table",
                de::Type::Unit => "Unit",
                de::Type::Option => "Option",
                de::Type::UnitStruct => "UnitStruct",
                de::Type::NewtypeStruct => "NewtypeStruct",
                de::Type::TupleStruct => "TupleStruct",
                de::Type::FieldName => "FieldName",
                de::Type::Tuple => "Tuple",
                de::Type::Enum => "Enum",
                de::Type::VariantName => "VariantName",
                de::Type::StructVariant => "StructVariant",
                de::Type::TupleVariant => "TupleVariant",
                de::Type::UnitVariant => "UnitVariant",
                de::Type::Bytes => "Bytes",
            })
        }
    }
}

impl error::Error for DecodeError {
    fn description(&self) -> &str {
        match self.kind {
            ApplicationError(ref s) => &**s,
            ExpectedField(..) => "expected a field",
            UnknownField => "found an unknown field",
            ExpectedType(..) => "expected a type",
            ExpectedMapKey(..) => "expected a map key",
            ExpectedMapElement(..) => "expected a map element",
            NoEnumVariants => "no enum variants to decode to",
            NilTooLong => "nonzero length string representing nil",
            SyntaxError => "syntax error",
            EndOfStream => "end of stream",
            InvalidType(..) => "invalid type",
            CustomError(..) => "custom error",
        }
    }
}

#[derive(PartialEq, Debug)]
pub enum DecodeErrorKind {
    /// An error flagged by the application, e.g. value out of range
    ApplicationError(String),
    /// A field was expected, but none was found.
    ExpectedField(/* type */ Option<&'static str>),
    /// A field was found, but it was not an expected one.
    UnknownField,
    /// A field was found, but it had the wrong type.
    ExpectedType(/* expected */ &'static str, /* found */ &'static str),
    /// The nth map key was expected, but none was found.
    ExpectedMapKey(usize),
    /// The nth map element was expected, but none was found.
    ExpectedMapElement(usize),
    /// An enum decoding was requested, but no variants were supplied
    NoEnumVariants,
    /// The unit type was being decoded, but a non-zero length string was found
    NilTooLong,
    /// There was an error with the syntactical structure of the TOML.
    SyntaxError,
    /// A custom error was generated when decoding.
    CustomError(String),
    /// The end of the TOML input was reached too soon
    EndOfStream,
    /// Produced by serde ...
    InvalidType(&'static str),
}

use self::DecodeErrorKind::*;

pub fn decode<T:serde::Deserialize>(exp: Sexp) -> Option<T> {
    serde::Deserialize::deserialize(&mut Decoder::new(exp)).ok()
}

pub struct Decoder {
    /// The expression value left over after decoding. This can be used to inspect
    /// whether fields were decoded or not.
    pub exp: Option<Sexp>,
    cur_field: Option<String>,
}

impl Decoder {
    /// Creates a new decoder, consuming the Sexp value to decode.
    ///
    /// This decoder can be passed to the `Decodable` methods or driven
    /// manually.
    pub fn new(exp: Sexp) -> Decoder {
        Decoder::new_empty(Some(exp), None)
    }

    fn sub_decoder(&self, sexp: Option<Sexp>, field: &str) -> Decoder {
        let cur_field = if field.is_empty() {
            self.cur_field.clone()
        } else {
            match self.cur_field {
                None => Some(field.to_string()),
                Some(ref s) => Some(format!("{}.{}", s, field))
            }
        };
        Decoder::new_empty(sexp, cur_field)
    }

    fn new_empty(exp: Option<Sexp>, cur_field: Option<String>) -> Decoder {
        Decoder {
            exp: exp,
            cur_field: cur_field,
        }
    }

    fn err(&self, kind: DecodeErrorKind) -> DecodeError {
        DecodeError {
            field: self.cur_field.clone(),
            kind: kind,
        }
    }

    fn mismatch(&self, expected: &'static str,
                found: &Option<Sexp>) -> DecodeError{
        match *found {
            Some(ref val) => self.err(ExpectedType(expected, format!("{}", val))),
            None => self.err(ExpectedField(Some(expected))),
        }
    }
}

impl de::Deserializer for Decoder {
    type Error = DecodeError;

    fn deserialize<V>(&mut self, mut visitor: V)
                      -> Result<V::Value, DecodeError>
        where V: de::Visitor
    {
        match self.exp.take() {
            Some(Sexp::String(s)) => visitor.visit_string(s),
            Some(Sexp::Empty) => visitor.visit_unit(),
            Some(Sexp::List(a)) => {
                let len = a.len();
                let iter = a.into_iter();
                visitor.visit_seq(SeqDeserializer::new(iter, len, &mut self.sexp))
            }
            None => Err(self.err(DecodeErrorKind::EndOfStream)),
        }
    }

    fn deserialize_bool<V>(&mut self, mut visitor: V)
                           -> Result<V::Value, DecodeError>
        where V: de::Visitor
    {
        match self.sexp.take() {
            found@Some(Sexp::String(b)) => {
                match b.as_str() {
                    "true" | "True" => visitor.visit_bool(true),
                    "false" | "False" => visitor.visit_bool(false),
                    _ => Err(self.mismatch("bool", found)),
                }
            },
            ref found => Err(self.mismatch("bool", found)),
        }
    }

    fn deserialize_i8<V>(&mut self, visitor: V)
                         -> Result<V::Value, DecodeError>
        where V: de::Visitor
    {
        self.deserialize_i64(visitor)
    }

    fn deserialize_i16<V>(&mut self, visitor: V)
                          -> Result<V::Value, DecodeError>
        where V: de::Visitor
    {
        self.deserialize_i64(visitor)
    }

    fn deserialize_i32<V>(&mut self, visitor: V)
                          -> Result<V::Value, DecodeError>
        where V: de::Visitor
    {
        self.deserialize_i64(visitor)
    }

    fn deserialize_i64<V>(&mut self, mut visitor: V)
                          -> Result<V::Value, DecodeError>
        where V: de::Visitor
    {
        match self.sexp.take() {
            found@Some(Sexp::String(s)) => {
                match s.parse::<i64>() {
                    Ok(f) => visitor.visit_i64(f),
                    Err(_) => Err(self.mismatch("integer", found)),
                }
            },
            ref found => Err(self.mismatch("integer", found)),
        }
    }

    fn deserialize_isize<V>(&mut self, visitor: V)
                            -> Result<V::Value, DecodeError>
        where V: de::Visitor
    {
        self.deserialize_i64(visitor)
    }

    fn deserialize_u8<V>(&mut self, visitor: V)
                         -> Result<V::Value, DecodeError>
        where V: de::Visitor
    {
        self.deserialize_i64(visitor)
    }

    fn deserialize_u16<V>(&mut self, visitor: V)
                          -> Result<V::Value, DecodeError>
        where V: de::Visitor
    {
        self.deserialize_i64(visitor)
    }

    fn deserialize_u32<V>(&mut self, visitor: V)
                          -> Result<V::Value, DecodeError>
        where V: de::Visitor
    {
        self.deserialize_i64(visitor)
    }

    fn deserialize_u64<V>(&mut self, visitor: V)
                          -> Result<V::Value, DecodeError>
        where V: de::Visitor
    {
        self.deserialize_i64(visitor)
    }

    fn deserialize_usize<V>(&mut self, visitor: V)
                            -> Result<V::Value, DecodeError>
        where V: de::Visitor
    {
        self.deserialize_i64(visitor)
    }

    fn deserialize_f32<V>(&mut self, visitor: V)
                          -> Result<V::Value, DecodeError>
        where V: de::Visitor
    {
        self.deserialize_f64(visitor)
    }

    fn deserialize_f64<V>(&mut self, mut visitor: V)
                          -> Result<V::Value, DecodeError>
        where V: de::Visitor
    {
        match self.sexp.take() {
            found@Some(Sexp::String(s)) => {
                match s.parse::<f64>() {
                    Ok(f) => visitor.visit_f64(f),
                    Err(_) => Err(self.mismatch("float", found)),
                }
            },
            ref found => Err(self.mismatch("float", found)),
        }
    }

    fn deserialize_str<V>(&mut self, mut visitor: V)
                          -> Result<V::Value, Self::Error>
        where V: de::Visitor,
    {
        match self.sexp.take() {
            Some(Sexp::String(s)) => visitor.visit_string(s),
            ref found => Err(self.mismatch("string", found)),
        }
    }

    fn deserialize_string<V>(&mut self, visitor: V)
                             -> Result<V::Value, Self::Error>
        where V: de::Visitor,
    {
        self.deserialize_str(visitor)
    }

    fn deserialize_char<V>(&mut self, mut visitor: V)
                           -> Result<V::Value, DecodeError>
        where V: de::Visitor
    {
        match self.sexp.take() {
            Some(Sexp::String(ref s)) if s.chars().count() == 1 => {
                visitor.visit_char(s.chars().next().unwrap())
            }
            ref found => return Err(self.mismatch("string", found)),
        }
    }

    fn deserialize_option<V>(&mut self, mut visitor: V)
                             -> Result<V::Value, DecodeError>
        where V: de::Visitor
    {
        if self.sexp.is_none() {
            visitor.visit_none()
        } else {
            visitor.visit_some(self)
        }
    }

    fn deserialize_seq<V>(&mut self, mut visitor: V)
                          -> Result<V::Value, DecodeError>
        where V: de::Visitor,
    {
        if self.sexp.is_none() {
            let iter = None::<i32>.into_iter();
            visitor.visit_seq(de::value::SeqDeserializer::new(iter, 0))
        } else {
            self.deserialize(visitor)
        }
    }

    fn deserialize_map<V>(&mut self, mut visitor: V)
                          -> Result<V::Value, DecodeError>
        where V: de::Visitor,
    {
        match self.sexp.take() {
            Some(Sexp::List(t)) => {
                visitor.visit_map(MapVisitor {
                    iter: t.into_iter(),
                    de: self,
                    key: None,
                    value: None,
                })
            }
            ref found => Err(self.mismatch("table", found)),
        }
    }

    fn deserialize_enum<V>(&mut self,
                           _enum: &str,
                           variants: &[&str],
                           mut visitor: V) -> Result<V::Value, DecodeError>
        where V: de::EnumVisitor,
    {
        Err(self.err(CustomError("enum type unsupported".into())))
    }

    fn deserialize_newtype_struct<V>(&mut self, _name: &'static str, visitor: V)
                                     -> Result<V::Value, Self::Error>
        where V: de::Visitor
    {
        self.deserialize_seq(visitor)
    }

    fn deserialize_tuple_struct<V>(&mut self,
                                   _name: &'static str,
                                   _len: usize,
                                   visitor: V)
                                   -> Result<V::Value, Self::Error>
        where V: de::Visitor
    {
        self.deserialize_seq(visitor)
    }

    fn deserialize_struct<V>(&mut self,
                             _name: &'static str,
                             _fields: &'static [&'static str],
                             visitor: V)
                             -> Result<V::Value, Self::Error>
        where V: de::Visitor
    {
        self.deserialize_map(visitor)
    }

    fn deserialize_tuple<V>(&mut self,
                            _len: usize,
                            visitor: V)
                            -> Result<V::Value, Self::Error>
        where V: de::Visitor
    {
        self.deserialize_seq(visitor)
    }

    fn deserialize_unit<V>(&mut self, visitor:V) -> Result<V::Value, Self::Error>
        where V: de::Visitor {
        self.deserialize(visitor)
    }

    fn deserialize_unit_struct<V>(&mut self, _name: &'static str, visitor:V) -> Result<V::Value, Self::Error>
        where V: de::Visitor {
        self.deserialize(visitor)
    }

    fn deserialize_struct_field<V>(&mut self, visitor:V) -> Result<V::Value, Self::Error>
        where V: de::Visitor {
        self.deserialize(visitor)
    }
}
<commit_msg>bugfixes<commit_after>// (c) 2016 Joost Yervante Damad <joost@productize.be>

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

use serde;
use serde::de;
use Sexp;

// loosely based on toml-rs

/// Description for errors which can occur while decoding a type.
#[derive(PartialEq, Debug)]
pub struct DecodeError {
    /// Field that this error applies to.
    pub field: Option<String>,
    /// The type of error which occurred while decoding,
    pub kind: DecodeErrorKind,
}

impl de::Error for DecodeError {
    fn custom<T: Into<String>>(msg: T) -> DecodeError {
        DecodeError {
            field: None,
            kind: DecodeErrorKind::CustomError(msg.into()),
        }
    }
    fn end_of_stream() -> DecodeError {
        DecodeError { field: None, kind: DecodeErrorKind::EndOfStream }
    }
    fn missing_field(name: &'static str) -> DecodeError {
        DecodeError {
            field: Some(name.to_string()),
            kind: DecodeErrorKind::ExpectedField(None),
        }
    }
    fn unknown_field(name: &str) -> DecodeError {
        DecodeError {
            field: Some(name.to_string()),
            kind: DecodeErrorKind::UnknownField,
        }
    }
    fn invalid_type(ty: de::Type) -> Self {
        DecodeError {
            field: None,
            kind: DecodeErrorKind::InvalidType(match ty {
                de::Type::Bool => "bool",
                de::Type::Usize |
                de::Type::U8 |
                de::Type::U16 |
                de::Type::U32 |
                de::Type::U64 |
                de::Type::Isize |
                de::Type::I8 |
                de::Type::I16 |
                de::Type::I32 |
                de::Type::I64 => "integer",
                de::Type::F32 |
                de::Type::F64 => "float",
                de::Type::Char |
                de::Type::Str |
                de::Type::String => "string",
                de::Type::Seq => "array",
                de::Type::Struct |
                de::Type::Map => "table",
                de::Type::Unit => "Unit",
                de::Type::Option => "Option",
                de::Type::UnitStruct => "UnitStruct",
                de::Type::NewtypeStruct => "NewtypeStruct",
                de::Type::TupleStruct => "TupleStruct",
                de::Type::FieldName => "FieldName",
                de::Type::Tuple => "Tuple",
                de::Type::Enum => "Enum",
                de::Type::VariantName => "VariantName",
                de::Type::StructVariant => "StructVariant",
                de::Type::TupleVariant => "TupleVariant",
                de::Type::UnitVariant => "UnitVariant",
                de::Type::Bytes => "Bytes",
            })
        }
    }
}

impl error::Error for DecodeError {
    fn description(&self) -> &str {
        match self.kind {
            ApplicationError(ref s) => &**s,
            ExpectedField(..) => "expected a field",
            UnknownField => "found an unknown field",
            ExpectedType(..) => "expected a type",
            ExpectedMapKey(..) => "expected a map key",
            ExpectedMapElement(..) => "expected a map element",
            NoEnumVariants => "no enum variants to decode to",
            NilTooLong => "nonzero length string representing nil",
            SyntaxError => "syntax error",
            EndOfStream => "end of stream",
            InvalidType(..) => "invalid type",
            CustomError(..) => "custom error",
        }
    }
}

#[derive(PartialEq, Debug)]
pub enum DecodeErrorKind {
    /// An error flagged by the application, e.g. value out of range
    ApplicationError(String),
    /// A field was expected, but none was found.
    ExpectedField(/* type */ Option<&'static str>),
    /// A field was found, but it was not an expected one.
    UnknownField,
    /// A field was found, but it had the wrong type.
    ExpectedType(/* expected */ &'static str, /* found */ &'static str),
    /// The nth map key was expected, but none was found.
    ExpectedMapKey(usize),
    /// The nth map element was expected, but none was found.
    ExpectedMapElement(usize),
    /// An enum decoding was requested, but no variants were supplied
    NoEnumVariants,
    /// The unit type was being decoded, but a non-zero length string was found
    NilTooLong,
    /// There was an error with the syntactical structure of the TOML.
    SyntaxError,
    /// A custom error was generated when decoding.
    CustomError(String),
    /// The end of the TOML input was reached too soon
    EndOfStream,
    /// Produced by serde ...
    InvalidType(&'static str),
}

impl fmt::Display for DecodeError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        try!(match self.kind {
            ApplicationError(ref err) => {
                write!(f, "{}", err)
            }
            ExpectedField(expected_type) => {
                match expected_type {
                    Some("table") => write!(f, "expected a section"),
                    Some(e) => write!(f, "expected a value of type `{}`", e),
                    None => write!(f, "expected a value"),
                }
            }
            UnknownField => write!(f, "unknown field"),
            ExpectedType(expected, found) => {
                fn humanize(s: &str) -> String {
                    if s == "section" {
                        "a section".to_string()
                    } else {
                        format!("a value of type `{}`", s)
                    }
                }
                write!(f, "expected {}, but found {}",
                       humanize(expected),
                       humanize(found))
            }
            ExpectedMapKey(idx) => {
                write!(f, "expected at least {} keys", idx + 1)
            }
            ExpectedMapElement(idx) => {
                write!(f, "expected at least {} elements", idx + 1)
            }
            NoEnumVariants => {
                write!(f, "expected an enum variant to decode to")
            }
            NilTooLong => {
                write!(f, "expected 0-length string")
            }
            SyntaxError => {
                write!(f, "syntax error")
            }
            EndOfStream => {
                write!(f, "end of stream")
            }
            InvalidType(s) => {
                write!(f, "invalid type: {}", s)
            }
            CustomError(ref s) => {
                write!(f, "custom error: {}", s)
            }
        });
        match self.field {
            Some(ref s) => {
                write!(f, " for the key `{}`", s)
            }
            None => Ok(())
        }
    }
}

use self::DecodeErrorKind::*;

pub fn decode<T:serde::Deserialize>(exp: Sexp) -> Option<T> {
    serde::Deserialize::deserialize(&mut Decoder::new(exp)).ok()
}

pub struct Decoder {
    /// The expression value left over after decoding. This can be used to inspect
    /// whether fields were decoded or not.
    pub exp: Option<Sexp>,
    cur_field: Option<String>,
}

impl Decoder {
    /// Creates a new decoder, consuming the Sexp value to decode.
    ///
    /// This decoder can be passed to the `Decodable` methods or driven
    /// manually.
    pub fn new(exp: Sexp) -> Decoder {
        Decoder::new_empty(Some(exp), None)
    }

    fn sub_decoder(&self, sexp: Option<Sexp>, field: &str) -> Decoder {
        let cur_field = if field.is_empty() {
            self.cur_field.clone()
        } else {
            match self.cur_field {
                None => Some(field.to_string()),
                Some(ref s) => Some(format!("{}.{}", s, field))
            }
        };
        Decoder::new_empty(sexp, cur_field)
    }

    fn new_empty(exp: Option<Sexp>, cur_field: Option<String>) -> Decoder {
        Decoder {
            exp: exp,
            cur_field: cur_field,
        }
    }

    fn err(&self, kind: DecodeErrorKind) -> DecodeError {
        DecodeError {
            field: self.cur_field.clone(),
            kind: kind,
        }
    }

    fn mismatch(&self, expected: &'static str,
                found: &Option<Sexp>) -> DecodeError{
        match *found {
            Some(ref val) => self.err(ExpectedType(expected, &format!("{}", val))),
            None => self.err(ExpectedField(Some(expected))),
        }
    }
}

impl de::Deserializer for Decoder {
    type Error = DecodeError;

    fn deserialize<V>(&mut self, mut visitor: V)
                      -> Result<V::Value, DecodeError>
        where V: de::Visitor
    {
        match self.exp.take() {
            Some(Sexp::String(s)) => visitor.visit_string(s),
            Some(Sexp::Empty) => visitor.visit_unit(),
            Some(Sexp::List(a)) => {
                let len = a.len();
                let iter = a.into_iter();
                visitor.visit_seq(SeqDeserializer::new(iter, len, &mut self.exp))
            }
            None => Err(self.err(DecodeErrorKind::EndOfStream)),
        }
    }

    fn deserialize_bool<V>(&mut self, mut visitor: V)
                           -> Result<V::Value, DecodeError>
        where V: de::Visitor
    {
        match self.exp.take() {
            ref found@Some(Sexp::String(b)) => {
                match b.as_str() {
                    "true" | "True" => visitor.visit_bool(true),
                    "false" | "False" => visitor.visit_bool(false),
                    _ => Err(self.mismatch("bool", found)),
                }
            },
            ref found => Err(self.mismatch("bool", found)),
        }
    }

    fn deserialize_i8<V>(&mut self, visitor: V)
                         -> Result<V::Value, DecodeError>
        where V: de::Visitor
    {
        self.deserialize_i64(visitor)
    }

    fn deserialize_i16<V>(&mut self, visitor: V)
                          -> Result<V::Value, DecodeError>
        where V: de::Visitor
    {
        self.deserialize_i64(visitor)
    }

    fn deserialize_i32<V>(&mut self, visitor: V)
                          -> Result<V::Value, DecodeError>
        where V: de::Visitor
    {
        self.deserialize_i64(visitor)
    }

    fn deserialize_i64<V>(&mut self, mut visitor: V)
                          -> Result<V::Value, DecodeError>
        where V: de::Visitor
    {
        match self.exp.take() {
            ref found@Some(Sexp::String(s)) => {
                match s.parse::<i64>() {
                    Ok(f) => visitor.visit_i64(f),
                    Err(_) => Err(self.mismatch("integer", found)),
                }
            },
            ref found => Err(self.mismatch("integer", found)),
        }
    }

    fn deserialize_isize<V>(&mut self, visitor: V)
                            -> Result<V::Value, DecodeError>
        where V: de::Visitor
    {
        self.deserialize_i64(visitor)
    }

    fn deserialize_u8<V>(&mut self, visitor: V)
                         -> Result<V::Value, DecodeError>
        where V: de::Visitor
    {
        self.deserialize_i64(visitor)
    }

    fn deserialize_u16<V>(&mut self, visitor: V)
                          -> Result<V::Value, DecodeError>
        where V: de::Visitor
    {
        self.deserialize_i64(visitor)
    }

    fn deserialize_u32<V>(&mut self, visitor: V)
                          -> Result<V::Value, DecodeError>
        where V: de::Visitor
    {
        self.deserialize_i64(visitor)
    }

    fn deserialize_u64<V>(&mut self, visitor: V)
                          -> Result<V::Value, DecodeError>
        where V: de::Visitor
    {
        self.deserialize_i64(visitor)
    }

    fn deserialize_usize<V>(&mut self, visitor: V)
                            -> Result<V::Value, DecodeError>
        where V: de::Visitor
    {
        self.deserialize_i64(visitor)
    }

    fn deserialize_f32<V>(&mut self, visitor: V)
                          -> Result<V::Value, DecodeError>
        where V: de::Visitor
    {
        self.deserialize_f64(visitor)
    }

    fn deserialize_f64<V>(&mut self, mut visitor: V)
                          -> Result<V::Value, DecodeError>
        where V: de::Visitor
    {
        match self.exp.take() {
            ref found@Some(Sexp::String(s)) => {
                match s.parse::<f64>() {
                    Ok(f) => visitor.visit_f64(f),
                    Err(_) => Err(self.mismatch("float", found)),
                }
            },
            ref found => Err(self.mismatch("float", found)),
        }
    }

    fn deserialize_str<V>(&mut self, mut visitor: V)
                          -> Result<V::Value, Self::Error>
        where V: de::Visitor,
    {
        match self.exp.take() {
            Some(Sexp::String(s)) => visitor.visit_string(s),
            ref found => Err(self.mismatch("string", found)),
        }
    }

    fn deserialize_string<V>(&mut self, visitor: V)
                             -> Result<V::Value, Self::Error>
        where V: de::Visitor,
    {
        self.deserialize_str(visitor)
    }

    fn deserialize_char<V>(&mut self, mut visitor: V)
                           -> Result<V::Value, DecodeError>
        where V: de::Visitor
    {
        match self.exp.take() {
            Some(Sexp::String(ref s)) if s.chars().count() == 1 => {
                visitor.visit_char(s.chars().next().unwrap())
            }
            ref found => return Err(self.mismatch("string", found)),
        }
    }

    fn deserialize_option<V>(&mut self, mut visitor: V)
                             -> Result<V::Value, DecodeError>
        where V: de::Visitor
    {
        if self.exp.is_none() {
            visitor.visit_none()
        } else {
            visitor.visit_some(self)
        }
    }

    fn deserialize_seq<V>(&mut self, mut visitor: V)
                          -> Result<V::Value, DecodeError>
        where V: de::Visitor,
    {
        if self.exp.is_none() {
            let iter = None::<i32>.into_iter();
            visitor.visit_seq(de::value::SeqDeserializer::new(iter, 0))
        } else {
            self.deserialize(visitor)
        }
    }

    fn deserialize_map<V>(&mut self, mut visitor: V)
                          -> Result<V::Value, DecodeError>
        where V: de::Visitor,
    {
        match self.exp.take() {
            Some(Sexp::List(t)) => {
                visitor.visit_map(MapVisitor {
                    iter: t.into_iter(),
                    de: self,
                    key: None,
                    value: None,
                })
            }
            ref found => Err(self.mismatch("table", found)),
        }
    }

    fn deserialize_enum<V>(&mut self,
                           _enum: &str,
                           variants: &[&str],
                           mut visitor: V) -> Result<V::Value, DecodeError>
        where V: de::EnumVisitor,
    {
        Err(self.err(CustomError("enum type unsupported".into())))
    }

 fn deserialize_ignored_any<V>(&mut self, visitor: V)
                                  -> Result<V::Value, Self::Error>
        where V: de::Visitor
    {
        use serde::de::value::ValueDeserializer;
        let mut d = <() as ValueDeserializer<Self::Error>>::into_deserializer(());
        d.deserialize(visitor)
    }

    fn deserialize_bytes<V>(&mut self, visitor: V)
                            -> Result<V::Value, Self::Error>
        where V: de::Visitor
    {
        self.deserialize_seq(visitor)
    }

    fn deserialize_seq_fixed_size<V>(&mut self, _len: usize, visitor: V)
                                     -> Result<V::Value, Self::Error>
        where V: de::Visitor
    {
        self.deserialize_seq(visitor)
    }

    fn deserialize_newtype_struct<V>(&mut self, _name: &'static str, visitor: V)
                                     -> Result<V::Value, Self::Error>
        where V: de::Visitor
    {
        self.deserialize_seq(visitor)
    }

    fn deserialize_tuple_struct<V>(&mut self,
                                   _name: &'static str,
                                   _len: usize,
                                   visitor: V)
                                   -> Result<V::Value, Self::Error>
        where V: de::Visitor
    {
        self.deserialize_seq(visitor)
    }

    fn deserialize_struct<V>(&mut self,
                             _name: &'static str,
                             _fields: &'static [&'static str],
                             visitor: V)
                             -> Result<V::Value, Self::Error>
        where V: de::Visitor
    {
        self.deserialize_map(visitor)
    }

    fn deserialize_tuple<V>(&mut self,
                            _len: usize,
                            visitor: V)
                            -> Result<V::Value, Self::Error>
        where V: de::Visitor
    {
        self.deserialize_seq(visitor)
    }

    fn deserialize_unit<V>(&mut self, visitor:V) -> Result<V::Value, Self::Error>
        where V: de::Visitor {
        self.deserialize(visitor)
    }

    fn deserialize_unit_struct<V>(&mut self, _name: &'static str, visitor:V) -> Result<V::Value, Self::Error>
        where V: de::Visitor {
        self.deserialize(visitor)
    }

    fn deserialize_struct_field<V>(&mut self, visitor:V) -> Result<V::Value, Self::Error>
        where V: de::Visitor {
        self.deserialize(visitor)
    }
}
<|endoftext|>


<commit_before>// error-pattern:yummy
#![feature(box_syntax)]
#![feature(rustc_private)]
#![allow(unknown_lints, missing_docs_in_private_items)]

use rustc_driver::{self, driver::CompileController, Compilation};
use rustc_plugin;
use std::process::{exit, Command};

#[allow(print_stdout)]
fn show_version() {
    println!(env!("CARGO_PKG_VERSION"));
}

pub fn main() {
    use std::env;

    if std::env::args().any(|a| a == "--version" || a == "-V") {
        show_version();
        return;
    }

    let sys_root = option_env!("SYSROOT")
        .map(String::from)
        .or_else(|| std::env::var("SYSROOT").ok())
        .or_else(|| {
            let home = option_env!("RUSTUP_HOME").or(option_env!("MULTIRUST_HOME"));
            let toolchain = option_env!("RUSTUP_TOOLCHAIN").or(option_env!("MULTIRUST_TOOLCHAIN"));
            home.and_then(|home| toolchain.map(|toolchain| format!("{}/toolchains/{}", home, toolchain)))
        })
        .or_else(|| {
            Command::new("rustc")
                .arg("--print")
                .arg("sysroot")
                .output()
                .ok()
                .and_then(|out| String::from_utf8(out.stdout).ok())
                .map(|s| s.trim().to_owned())
        })
        .expect("need to specify SYSROOT env var during clippy compilation, or use rustup or multirust");

    // Setting RUSTC_WRAPPER causes Cargo to pass 'rustc' as the first argument.
    // We're invoking the compiler programmatically, so we ignore this/
    let mut orig_args: Vec<String> = env::args().collect();
    if orig_args.len() <= 1 {
        std::process::exit(1);
    }
    if orig_args[1] == "rustc" {
        // we still want to be able to invoke it normally though
        orig_args.remove(1);
    }
    // this conditional check for the --sysroot flag is there so users can call
    // `clippy_driver` directly
    // without having to pass --sysroot or anything
    let mut args: Vec<String> = if orig_args.iter().any(|s| s == "--sysroot") {
        orig_args.clone()
    } else {
        orig_args
            .clone()
            .into_iter()
            .chain(Some("--sysroot".to_owned()))
            .chain(Some(sys_root))
            .collect()
    };

    // this check ensures that dependencies are built but not linted and the final
    // crate is
    // linted but not built
    let clippy_enabled = env::var("CLIPPY_TESTS").ok().map_or(false, |val| val == "true")
        || orig_args.iter().any(|s| s == "--emit=dep-info,metadata");

    if clippy_enabled {
        args.extend_from_slice(&["--cfg".to_owned(), r#"feature="cargo-clippy""#.to_owned()]);
        if let Ok(extra_args) = env::var("CLIPPY_ARGS") {
            args.extend(
                extra_args
                    .split("__CLIPPY_HACKERY__")
                    .filter(|s| !s.is_empty())
                    .map(str::to_owned),
            );
        }
    }

    let mut controller = CompileController::basic();
    if clippy_enabled {
        controller.after_parse.callback = Box::new(move |state| {
            let mut registry = rustc_plugin::registry::Registry::new(
                state.session,
                state
                    .krate
                    .as_ref()
                    .expect(
                        "at this compilation stage \
                         the crate must be parsed",
                    )
                    .span,
            );
            registry.args_hidden = Some(Vec::new());

            let conf = clippy_lints::read_conf(&registry);
            clippy_lints::register_plugins(&mut registry, &conf);

            let rustc_plugin::registry::Registry {
                early_lint_passes,
                late_lint_passes,
                lint_groups,
                llvm_passes,
                attributes,
                ..
            } = registry;
            let sess = &state.session;
            let mut ls = sess.lint_store.borrow_mut();
            for pass in early_lint_passes {
                ls.register_early_pass(Some(sess), true, pass);
            }
            for pass in late_lint_passes {
                ls.register_late_pass(Some(sess), true, pass);
            }

            for (name, to) in lint_groups {
                ls.register_group(Some(sess), true, name, to);
            }
            clippy_lints::register_pre_expansion_lints(sess, &mut ls, &conf);

            sess.plugin_llvm_passes.borrow_mut().extend(llvm_passes);
            sess.plugin_attributes.borrow_mut().extend(attributes);
        });
    }
    controller.compilation_done.stop = Compilation::Stop;

    if rustc_driver::run_compiler(&args, Box::new(controller), None, None)
        .0
        .is_err()
    {
        exit(101);
    }
}
<commit_msg>Use the compilers exit code computation instead of rolling our own<commit_after>// error-pattern:yummy
#![feature(box_syntax)]
#![feature(rustc_private)]
#![allow(unknown_lints, missing_docs_in_private_items)]

use rustc_driver::{self, driver::CompileController, Compilation};
use rustc_plugin;
use std::process::{exit, Command};

#[allow(print_stdout)]
fn show_version() {
    println!(env!("CARGO_PKG_VERSION"));
}

pub fn main() {
    exit(rustc_driver::run(move || {
        use std::env;

        if std::env::args().any(|a| a == "--version" || a == "-V") {
            show_version();
            exit(0);
        }

        let sys_root = option_env!("SYSROOT")
            .map(String::from)
            .or_else(|| std::env::var("SYSROOT").ok())
            .or_else(|| {
                let home = option_env!("RUSTUP_HOME").or(option_env!("MULTIRUST_HOME"));
                let toolchain = option_env!("RUSTUP_TOOLCHAIN").or(option_env!("MULTIRUST_TOOLCHAIN"));
                home.and_then(|home| toolchain.map(|toolchain| format!("{}/toolchains/{}", home, toolchain)))
            })
            .or_else(|| {
                Command::new("rustc")
                    .arg("--print")
                    .arg("sysroot")
                    .output()
                    .ok()
                    .and_then(|out| String::from_utf8(out.stdout).ok())
                    .map(|s| s.trim().to_owned())
            })
            .expect("need to specify SYSROOT env var during clippy compilation, or use rustup or multirust");

        // Setting RUSTC_WRAPPER causes Cargo to pass 'rustc' as the first argument.
        // We're invoking the compiler programmatically, so we ignore this/
        let mut orig_args: Vec<String> = env::args().collect();
        if orig_args.len() <= 1 {
            std::process::exit(1);
        }
        if orig_args[1] == "rustc" {
            // we still want to be able to invoke it normally though
            orig_args.remove(1);
        }
        // this conditional check for the --sysroot flag is there so users can call
        // `clippy_driver` directly
        // without having to pass --sysroot or anything
        let mut args: Vec<String> = if orig_args.iter().any(|s| s == "--sysroot") {
            orig_args.clone()
        } else {
            orig_args
                .clone()
                .into_iter()
                .chain(Some("--sysroot".to_owned()))
                .chain(Some(sys_root))
                .collect()
        };

        // this check ensures that dependencies are built but not linted and the final
        // crate is
        // linted but not built
        let clippy_enabled = env::var("CLIPPY_TESTS").ok().map_or(false, |val| val == "true")
            || orig_args.iter().any(|s| s == "--emit=dep-info,metadata");

        if clippy_enabled {
            args.extend_from_slice(&["--cfg".to_owned(), r#"feature="cargo-clippy""#.to_owned()]);
            if let Ok(extra_args) = env::var("CLIPPY_ARGS") {
                args.extend(
                    extra_args
                        .split("__CLIPPY_HACKERY__")
                        .filter(|s| !s.is_empty())
                        .map(str::to_owned),
                );
            }
        }

        let mut controller = CompileController::basic();
        if clippy_enabled {
            controller.after_parse.callback = Box::new(move |state| {
                let mut registry = rustc_plugin::registry::Registry::new(
                    state.session,
                    state
                        .krate
                        .as_ref()
                        .expect(
                            "at this compilation stage \
                            the crate must be parsed",
                        )
                        .span,
                );
                registry.args_hidden = Some(Vec::new());

                let conf = clippy_lints::read_conf(&registry);
                clippy_lints::register_plugins(&mut registry, &conf);

                let rustc_plugin::registry::Registry {
                    early_lint_passes,
                    late_lint_passes,
                    lint_groups,
                    llvm_passes,
                    attributes,
                    ..
                } = registry;
                let sess = &state.session;
                let mut ls = sess.lint_store.borrow_mut();
                for pass in early_lint_passes {
                    ls.register_early_pass(Some(sess), true, pass);
                }
                for pass in late_lint_passes {
                    ls.register_late_pass(Some(sess), true, pass);
                }

                for (name, to) in lint_groups {
                    ls.register_group(Some(sess), true, name, to);
                }
                clippy_lints::register_pre_expansion_lints(sess, &mut ls, &conf);

                sess.plugin_llvm_passes.borrow_mut().extend(llvm_passes);
                sess.plugin_attributes.borrow_mut().extend(attributes);
            });
        }
        controller.compilation_done.stop = Compilation::Stop;

        let args = args;
        rustc_driver::run_compiler(&args, Box::new(controller), None, None)
    }) as i32)
}
<|endoftext|>


<commit_before>use shared::path;

use std::path::{Path, PathBuf};

use syn;

pub struct TemplateInput<'a> {
    pub ast: &'a syn::DeriveInput,
    pub source: Source,
    pub print: Print,
    pub escaping: EscapeMode,
    pub ext: Option<String>,
    pub parent: Option<&'a syn::Type>,
    pub path: PathBuf,
}

impl<'a> TemplateInput<'a> {
    pub fn new(ast: &'a syn::DeriveInput) -> TemplateInput<'a> {
        let attr = ast.attrs
            .iter()
            .find(|a| a.interpret_meta().unwrap().name() == "template");
        if attr.is_none() {
            panic!(format!(
                "'template' attribute not found on struct '{}'",
                ast.ident
            ));
        }

        let attr = attr.unwrap();
        let mut source = None;
        let mut print = Print::None;
        let mut escaping = None;
        let mut ext = None;
        if let syn::Meta::List(ref inner) = attr.interpret_meta().unwrap() {
            for nm_item in inner.nested.iter() {
                if let syn::NestedMeta::Meta(ref item) = *nm_item {
                    if let syn::Meta::NameValue(ref pair) = *item {
                        match pair.ident.to_string().as_ref() {
                            "path" => if let syn::Lit::Str(ref s) = pair.lit {
                                if source.is_some() {
                                    panic!("must specify 'source' or 'path', not both");
                                }
                                source = Some(Source::Path(s.value()));
                            } else {
                                panic!("template path must be string literal");
                            },
                            "source" => if let syn::Lit::Str(ref s) = pair.lit {
                                if source.is_some() {
                                    panic!("must specify 'source' or 'path', not both");
                                }
                                source = Some(Source::Source(s.value()));
                            } else {
                                panic!("template source must be string literal");
                            },
                            "print" => if let syn::Lit::Str(ref s) = pair.lit {
                                print = s.value().into();
                            } else {
                                panic!("print value must be string literal");
                            },
                            "escape" => if let syn::Lit::Str(ref s) = pair.lit {
                                escaping = Some(s.value().into());
                            } else {
                                panic!("escape value must be string literal");
                            },
                            "ext" => if let syn::Lit::Str(ref s) = pair.lit {
                                ext = Some(s.value());
                            } else {
                                panic!("ext value must be string literal");
                            },
                            attr => panic!("unsupported annotation key '{}' found", attr),
                        }
                    }
                }
            }
        }

        let source = source.expect("template path or source not found in attributes");
        match (&source, ext.is_some()) {
            (&Source::Path(_), true) => {
                panic!("'ext' attribute cannot be used with 'path' attribute")
            }
            (&Source::Source(_), false) => {
                panic!("must include 'ext' attribute when using 'source' attribute")
            }
            _ => {}
        }

        let escaping = match escaping {
            Some(m) => m,
            None => {
                let ext = match source {
                    Source::Path(ref p) => Path::new(p)
                        .extension()
                        .map(|s| s.to_str().unwrap())
                        .unwrap_or(""),
                    Source::Source(_) => ext.as_ref().unwrap(), // Already panicked if None
                };
                if HTML_EXTENSIONS.contains(&ext) {
                    EscapeMode::Html
                } else {
                    EscapeMode::None
                }
            }
        };

        let parent = match ast.data {
            syn::Data::Struct(syn::DataStruct {
                fields: syn::Fields::Named(ref fields),
                ..
            }) => fields.named.iter().filter_map(|f| {
                f.ident
                    .as_ref()
                    .and_then(|name| if name == "_parent" { Some(&f.ty) } else { None })
            }),
            _ => panic!("derive(Template) only works for struct items"),
        }.next();

        let path = match source {
            Source::Source(_) => match ext {
                Some(ref v) => PathBuf::from(format!("{}.{}", ast.ident, v)),
                None => PathBuf::new(),
            },
            Source::Path(ref s) => path::find_template_from_path(s, None),
        };

        TemplateInput {
            ast,
            source,
            print,
            escaping,
            ext,
            parent,
            path,
        }
    }
}

pub enum Source {
    Path(String),
    Source(String),
}

#[derive(PartialEq)]
pub enum EscapeMode {
    Html,
    None,
}

impl From<String> for EscapeMode {
    fn from(s: String) -> EscapeMode {
        use self::EscapeMode::*;
        match s.as_ref() {
            "html" => Html,
            "none" => None,
            v => panic!("invalid value for escape option: {}", v),
        }
    }
}

#[derive(PartialEq)]
pub enum Print {
    All,
    Ast,
    Code,
    None,
}

impl From<String> for Print {
    fn from(s: String) -> Print {
        use self::Print::*;
        match s.as_ref() {
            "all" => All,
            "ast" => Ast,
            "code" => Code,
            "none" => None,
            v => panic!("invalid value for print option: {}", v),
        }
    }
}

const HTML_EXTENSIONS: [&str; 3] = ["html", "htm", "xml"];
<commit_msg>Improve attribute handling code for efficiency and robustness<commit_after>use proc_macro2::TokenStream;

use quote::ToTokens;

use shared::path;

use std::path::{Path, PathBuf};

use syn;

pub struct TemplateInput<'a> {
    pub ast: &'a syn::DeriveInput,
    pub source: Source,
    pub print: Print,
    pub escaping: EscapeMode,
    pub ext: Option<String>,
    pub parent: Option<&'a syn::Type>,
    pub path: PathBuf,
}

impl<'a> TemplateInput<'a> {
    pub fn new(ast: &'a syn::DeriveInput) -> TemplateInput<'a> {
        let mut meta = None;
        for attr in &ast.attrs {
            match attr.interpret_meta() {
                Some(m) => if m.name() == "template" {
                    meta = Some(m)
                },
                None => {
                    let mut tokens = TokenStream::new();
                    attr.to_tokens(&mut tokens);
                    panic!("unable to interpret attribute: {}", tokens)
                }
            }
        }

        let meta_list = match meta.expect("no attribute 'template' found") {
            syn::Meta::List(inner) => inner,
            _ => panic!("attribute 'template' has incorrect type"),
        };

        let mut source = None;
        let mut print = Print::None;
        let mut escaping = None;
        let mut ext = None;
        for nm_item in meta_list.nested {
            if let syn::NestedMeta::Meta(ref item) = nm_item {
                if let syn::Meta::NameValue(ref pair) = item {
                    match pair.ident.to_string().as_ref() {
                        "path" => if let syn::Lit::Str(ref s) = pair.lit {
                            if source.is_some() {
                                panic!("must specify 'source' or 'path', not both");
                            }
                            source = Some(Source::Path(s.value()));
                        } else {
                            panic!("template path must be string literal");
                        },
                        "source" => if let syn::Lit::Str(ref s) = pair.lit {
                            if source.is_some() {
                                panic!("must specify 'source' or 'path', not both");
                            }
                            source = Some(Source::Source(s.value()));
                        } else {
                            panic!("template source must be string literal");
                        },
                        "print" => if let syn::Lit::Str(ref s) = pair.lit {
                            print = s.value().into();
                        } else {
                            panic!("print value must be string literal");
                        },
                        "escape" => if let syn::Lit::Str(ref s) = pair.lit {
                            escaping = Some(s.value().into());
                        } else {
                            panic!("escape value must be string literal");
                        },
                        "ext" => if let syn::Lit::Str(ref s) = pair.lit {
                            ext = Some(s.value());
                        } else {
                            panic!("ext value must be string literal");
                        },
                        attr => panic!("unsupported annotation key '{}' found", attr),
                    }
                }
            }
        }

        let source = source.expect("template path or source not found in attributes");
        match (&source, ext.is_some()) {
            (&Source::Path(_), true) => {
                panic!("'ext' attribute cannot be used with 'path' attribute")
            }
            (&Source::Source(_), false) => {
                panic!("must include 'ext' attribute when using 'source' attribute")
            }
            _ => {}
        }

        let escaping = match escaping {
            Some(m) => m,
            None => {
                let ext = match source {
                    Source::Path(ref p) => Path::new(p)
                        .extension()
                        .map(|s| s.to_str().unwrap())
                        .unwrap_or(""),
                    Source::Source(_) => ext.as_ref().unwrap(), // Already panicked if None
                };
                if HTML_EXTENSIONS.contains(&ext) {
                    EscapeMode::Html
                } else {
                    EscapeMode::None
                }
            }
        };

        let parent = match ast.data {
            syn::Data::Struct(syn::DataStruct {
                fields: syn::Fields::Named(ref fields),
                ..
            }) => fields.named.iter().filter_map(|f| {
                f.ident
                    .as_ref()
                    .and_then(|name| if name == "_parent" { Some(&f.ty) } else { None })
            }),
            _ => panic!("derive(Template) only works for struct items"),
        }.next();

        let path = match source {
            Source::Source(_) => match ext {
                Some(ref v) => PathBuf::from(format!("{}.{}", ast.ident, v)),
                None => PathBuf::new(),
            },
            Source::Path(ref s) => path::find_template_from_path(s, None),
        };

        TemplateInput {
            ast,
            source,
            print,
            escaping,
            ext,
            parent,
            path,
        }
    }
}

pub enum Source {
    Path(String),
    Source(String),
}

#[derive(PartialEq)]
pub enum EscapeMode {
    Html,
    None,
}

impl From<String> for EscapeMode {
    fn from(s: String) -> EscapeMode {
        use self::EscapeMode::*;
        match s.as_ref() {
            "html" => Html,
            "none" => None,
            v => panic!("invalid value for escape option: {}", v),
        }
    }
}

#[derive(PartialEq)]
pub enum Print {
    All,
    Ast,
    Code,
    None,
}

impl From<String> for Print {
    fn from(s: String) -> Print {
        use self::Print::*;
        match s.as_ref() {
            "all" => All,
            "ast" => Ast,
            "code" => Code,
            "none" => None,
            v => panic!("invalid value for print option: {}", v),
        }
    }
}

const HTML_EXTENSIONS: [&str; 3] = ["html", "htm", "xml"];
<|endoftext|>


<commit_before>use std::str;
use std::io;

pub struct ViewEngine {
  pub template: String
}
impl ViewEngine {
    pub fn new(template: String) -> ViewEngine {
    let e=ViewEngine{ template: template};
    e
  }
  pub fn generate(&self, output: &Writer) -> String {
    "".to_string()
  }
}<commit_msg>output stuff<commit_after>use std::str;
use std::io;

pub struct ViewEngine {
  pub template: String
}
impl ViewEngine {
  pub fn new(template: String) -> ViewEngine {
    let e=ViewEngine{ template: template};
    e
  }
  pub fn generate(&self, output: &mut Writer) -> String {
    for c in self.template.chars() {
      output.write_char(c);
    }
    "".to_string()
  }
}<|endoftext|>


<commit_before>//! Implementation of support for the Enttec USB DMX Pro dongle.

use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::fs;
use std::io::Write;
use std::time::Duration;
use std::{cmp::min, fmt};

use crate::{PortListing, PortOpener};

use super::{DmxPort, Error};
use serial::prelude::*;
use serial::{open, SystemPort};

// Some constants used for enttec message framing.
const START_VAL: u8 = 0x7E;
const END_VAL: u8 = 0xE7;

// Universe size constraints.
const MIN_UNIVERSE_SIZE: usize = 24;
const MAX_UNIVERSE_SIZE: usize = 512;

// Port action flags.
const SET_PARAMETERS: u8 = 4;
//const RECEIVE_DMX_PACKET: u8 = 5;
const SEND_DMX_PACKET: u8 = 6;

/// Format a byte buffer as an enttec message into the provided output buffer.
/// Maximum valid size for payload is 600; no check is made here that the payload is within this range.
fn make_packet(message_type: u8, payload: &[u8], output: &mut Vec<u8>) {
    // Enttec messages are the size of the payload plus 5 bytes for type, length, and framing.
    let payload_size = payload.len();
    output.clear();
    output.reserve(payload_size + 5);
    let (len_lsb, len_msb) = (payload_size as u8, (payload_size >> 8) as u8);
    output.push(START_VAL);
    output.push(message_type);
    output.push(len_lsb);
    output.push(len_msb);
    output.extend_from_slice(payload);
    output.push(END_VAL);
}

#[derive(Debug)]
pub struct EnttecParams {
    /// DMX output break time in 10.67 microsecond units. Valid range is 9 to 127.
    break_time: u8,
    /// DMX output Mark After Break time in 10.67 microsecond units. Valid range is 1 to 127.
    mark_after_break_time: u8,
    /// DMX output rate in packets per second. Valid range is 1 to 40, or 0 for fastest rate
    /// possible (this will make the most difference when the output universe size is smallest).
    output_rate: u8,
}

impl Default for EnttecParams {
    /// Default parameters for the enttec port.
    /// In summary: minimum break and mark times, fastest fixed framerate.
    fn default() -> Self {
        EnttecParams {
            break_time: 9,
            mark_after_break_time: 1,
            output_rate: 40,
        }
    }
}

impl EnttecParams {
    fn as_packet(&self, output: &mut Vec<u8>) {
        let payload = [
            self.break_time,
            self.mark_after_break_time,
            self.output_rate,
        ];
        make_packet(SET_PARAMETERS, &payload, output)
    }
}

pub struct EnttecDmxPort {
    params: EnttecParams,
    port: Option<SystemPort>,
    port_name: String,
    output_buffer: Vec<u8>,
}

impl EnttecDmxPort {
    /// Create an enttec port with the specified port name.
    /// The port is not opened yet.
    pub fn new<N: Into<String>>(port_name: N) -> Self {
        let port_name = port_name.into();

        let params = EnttecParams::default();

        Self {
            params: params,
            port: None,
            port_name: port_name,
            output_buffer: Vec::new(),
        }
    }

    /// Create an enttec port with the specified port name, and open it.
    pub fn opened<N: Into<String>>(port_name: N) -> Result<Self, Error> {
        let mut port = Self::new(port_name);
        port.open()?;
        Ok(port)
    }

    /// Write the current parameters out to the port.
    fn write_params(&mut self) -> Result<(), Error> {
        self.params.as_packet(&mut self.output_buffer);
        self.write_output_buffer()
    }

    /// Write the current contents of the output buffer to the port.
    fn write_output_buffer(&mut self) -> Result<(), Error> {
        match &mut self.port {
            Some(p) => p.write_all(&self.output_buffer)?,
            None => return Err(Error::PortClosed),
        }
        Ok(())
    }
}

#[typetag::serde]
impl DmxPort for EnttecDmxPort {
    /// Return the available enttec ports connected to this system.
    /// TODO: provide a mechanism to specialize this implementation depending on platform.
    fn available_ports() -> PortListing {
        match fs::read_dir("/dev/") {
            Err(_) => Vec::new(),
            Ok(dirs) => dirs
                .filter_map(|x| x.ok())
                .filter_map(|p| {
                    p.path().to_str().and_then(|p| {
                        if p.starts_with(ENTTEC_PATH_PREFIX) {
                            let port_name = p[ENTTEC_PATH_PREFIX.len()..].to_string();
                            let open_name = port_name.clone();
                            let opener: Box<PortOpener> = Box::new(move || {
                                EnttecDmxPort::opened(open_name.clone())
                                    .map(|p| Box::new(p) as Box<dyn DmxPort>)
                            });
                            Some((port_name, opener))
                        } else {
                            None
                        }
                    })
                })
                .collect(),
        }
    }

    /// Open the port.
    fn open(&mut self) -> Result<(), Error> {
        if self.port.is_some() {
            return Ok(());
        }

        let port_path = format!("{}{}", ENTTEC_PATH_PREFIX, self.port_name);
        let mut port = open(&port_path)?;

        // use a short 1 ms timeout to avoid blocking if, say, the port disappears
        port.set_timeout(Duration::from_millis(1))?;

        self.port = Some(port);

        // send the default parameters to the port
        if let Err(e) = self.write_params() {
            self.port = None;
            return Err(e);
        }
        Ok(())
    }

    fn close(&mut self) {
        self.port = None;
    }

    fn write(&mut self, frame: &[u8]) -> Result<(), Error> {
        let size = frame.len();
        if size < MIN_UNIVERSE_SIZE {
            let mut padded_frame = Vec::with_capacity(MIN_UNIVERSE_SIZE);
            padded_frame.extend_from_slice(frame);
            padded_frame.resize(MIN_UNIVERSE_SIZE, 0);
            make_packet(SEND_DMX_PACKET, &padded_frame, &mut self.output_buffer)
        } else {
            make_packet(
                SEND_DMX_PACKET,
                &frame[0..min(size, MAX_UNIVERSE_SIZE)],
                &mut self.output_buffer,
            )
        }

        self.write_output_buffer()
    }
}

impl Serialize for EnttecDmxPort {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        self.port_name.serialize(serializer)
    }
}

impl<'de> Deserialize<'de> for EnttecDmxPort {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        Ok(Self::new(String::deserialize(deserializer)?))
    }
}

const ENTTEC_PATH_PREFIX: &'static str = "/dev/tty.usbserial-";

impl fmt::Display for EnttecDmxPort {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.port_name)
    }
}
<commit_msg>Fix no output issue with enttec port, and also eliminate intermediate buffer.<commit_after>//! Implementation of support for the Enttec USB DMX Pro dongle.

use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::fs;
use std::io::Write;
use std::time::Duration;
use std::{cmp::min, fmt};

use crate::{PortListing, PortOpener};

use super::{DmxPort, Error};
use serial::prelude::*;
use serial::{open, SystemPort};

// Some constants used for enttec message framing.
const START_VAL: u8 = 0x7E;
const END_VAL: u8 = 0xE7;

// Universe size constraints.
const MIN_UNIVERSE_SIZE: usize = 24;
const MAX_UNIVERSE_SIZE: usize = 512;

// Port action flags.
const SET_PARAMETERS: u8 = 4;
//const RECEIVE_DMX_PACKET: u8 = 5;
const SEND_DMX_PACKET: u8 = 6;

/// Format a byte buffer as an enttec message into the provided writer.
/// Maximum valid size for payload is 600; no check is made here that the payload is within this range.
fn write_packet<W: Write>(
    message_type: u8,
    payload: &[u8],
    add_payload_pad_byte: bool,
    mut w: W,
) -> Result<(), Error> {
    // Enttec messages are the size of the payload plus 5 bytes for type, length, and framing.
    let payload_size = payload.len() + add_payload_pad_byte as usize;
    let (len_lsb, len_msb) = (payload_size as u8, (payload_size >> 8) as u8);
    let header = [START_VAL, message_type, len_lsb, len_msb];
    w.write_all(&header)?;
    if add_payload_pad_byte {
        w.write_all(&[0][..])?;
    }
    w.write_all(payload)?;
    w.write_all(&[END_VAL][..])?;
    Ok(())
}

#[derive(Debug)]
pub struct EnttecParams {
    /// DMX output break time in 10.67 microsecond units. Valid range is 9 to 127.
    break_time: u8,
    /// DMX output Mark After Break time in 10.67 microsecond units. Valid range is 1 to 127.
    mark_after_break_time: u8,
    /// DMX output rate in packets per second. Valid range is 1 to 40, or 0 for fastest rate
    /// possible (this will make the most difference when the output universe size is smallest).
    output_rate: u8,
}

impl Default for EnttecParams {
    /// Default parameters for the enttec port.
    /// In summary: minimum break and mark times, fastest fixed framerate.
    fn default() -> Self {
        EnttecParams {
            break_time: 9,
            mark_after_break_time: 1,
            output_rate: 40,
        }
    }
}

impl EnttecParams {
    fn write_into<W: Write>(&self, w: W) -> Result<(), Error> {
        let payload = [
            0, // user size lsb?
            0, // user size msb?
            self.break_time,
            self.mark_after_break_time,
            self.output_rate,
        ];
        write_packet(SET_PARAMETERS, &payload, false, w)
    }
}

pub struct EnttecDmxPort {
    params: EnttecParams,
    port: Option<SystemPort>,
    port_name: String,
    output_buffer: Vec<u8>,
}

impl EnttecDmxPort {
    /// Create an enttec port with the specified port name.
    /// The port is not opened yet.
    pub fn new<N: Into<String>>(port_name: N) -> Self {
        let port_name = port_name.into();

        let params = EnttecParams::default();

        Self {
            params: params,
            port: None,
            port_name: port_name,
            output_buffer: Vec::new(),
        }
    }

    /// Create an enttec port with the specified port name, and open it.
    pub fn opened<N: Into<String>>(port_name: N) -> Result<Self, Error> {
        let mut port = Self::new(port_name);
        port.open()?;
        Ok(port)
    }

    /// Write the current parameters out to the port.
    fn write_params(&mut self) -> Result<(), Error> {
        self.params
            .write_into(self.port.as_mut().ok_or(Error::PortClosed)?)
    }
}

#[typetag::serde]
impl DmxPort for EnttecDmxPort {
    /// Return the available enttec ports connected to this system.
    /// TODO: provide a mechanism to specialize this implementation depending on platform.
    fn available_ports() -> PortListing {
        match fs::read_dir("/dev/") {
            Err(_) => Vec::new(),
            Ok(dirs) => dirs
                .filter_map(|x| x.ok())
                .filter_map(|p| {
                    p.path().to_str().and_then(|p| {
                        if p.starts_with(ENTTEC_PATH_PREFIX) {
                            let port_name = p[ENTTEC_PATH_PREFIX.len()..].to_string();
                            let open_name = port_name.clone();
                            let opener: Box<PortOpener> = Box::new(move || {
                                EnttecDmxPort::opened(open_name.clone())
                                    .map(|p| Box::new(p) as Box<dyn DmxPort>)
                            });
                            Some((port_name, opener))
                        } else {
                            None
                        }
                    })
                })
                .collect(),
        }
    }

    /// Open the port.
    fn open(&mut self) -> Result<(), Error> {
        if self.port.is_some() {
            return Ok(());
        }

        let port_path = format!("{}{}", ENTTEC_PATH_PREFIX, self.port_name);
        let mut port = open(&port_path)?;

        // use a short 1 ms timeout to avoid blocking if, say, the port disappears
        port.set_timeout(Duration::from_millis(1))?;

        self.port = Some(port);

        // send the default parameters to the port
        if let Err(e) = self.write_params() {
            self.port = None;
            return Err(e);
        }
        Ok(())
    }

    fn close(&mut self) {
        self.port = None;
    }

    fn write(&mut self, frame: &[u8]) -> Result<(), Error> {
        let port = self.port.as_mut().ok_or(Error::PortClosed)?;
        let size = frame.len();
        if size < MIN_UNIVERSE_SIZE {
            let mut padded_frame = Vec::with_capacity(MIN_UNIVERSE_SIZE);
            padded_frame.extend_from_slice(frame);
            padded_frame.resize(MIN_UNIVERSE_SIZE, 0);
            write_packet(SEND_DMX_PACKET, &padded_frame, true, port)
        } else {
            write_packet(
                SEND_DMX_PACKET,
                &frame[0..min(size, MAX_UNIVERSE_SIZE)],
                true,
                port,
            )
        }
    }
}

impl Serialize for EnttecDmxPort {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        self.port_name.serialize(serializer)
    }
}

impl<'de> Deserialize<'de> for EnttecDmxPort {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        Ok(Self::new(String::deserialize(deserializer)?))
    }
}

const ENTTEC_PATH_PREFIX: &'static str = "/dev/tty.usbserial-";

impl fmt::Display for EnttecDmxPort {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.port_name)
    }
}
<|endoftext|>


<commit_before>//! Manage xml character escapes

use memchr;
use std::borrow::Cow;

#[allow(missing_docs)]
#[derive(Fail, Debug)]
pub enum EscapeError {
    #[fail(
        display = "Error while escaping character at range {:?}: Null character entity not allowed",
        _0
    )]
    EntityWithNull(::std::ops::Range<usize>),

    #[fail(
        display = "Error while escaping character at range {:?}: Unrecognized escape symbol: {:?}",
        _0, _1
    )]
    UnrecognizedSymbol(
        ::std::ops::Range<usize>,
        ::std::result::Result<String, ::std::string::FromUtf8Error>,
    ),

    #[fail(
        display = "Error while escaping character at range {:?}: Cannot find ';' after '&'",
        _0
    )]
    UnterminatedEntity(::std::ops::Range<usize>),

    #[fail(display = "Cannot convert hexadecimal to utf8")]
    TooLongHexadecimal,

    #[fail(display = "'{}' is not a valid hexadecimal character", _0)]
    InvalidHexadecimal(char),

    #[fail(display = "Cannot convert decimal to utf8")]
    TooLongDecimal,

    #[fail(display = "'{}' is not a valid decimal character", _0)]
    InvalidDecimal(char),
}

// UTF-8 ranges and tags for encoding characters
const TAG_CONT: u8 = 0b1000_0000;
const TAG_TWO_B: u8 = 0b1100_0000;
const TAG_THREE_B: u8 = 0b1110_0000;
const TAG_FOUR_B: u8 = 0b1111_0000;
const MAX_ONE_B: u32 = 0x80;
const MAX_TWO_B: u32 = 0x800;
const MAX_THREE_B: u32 = 0x10000;

enum ByteOrChar {
    Byte(u8),
    Char(u32),
}

/// helper function to escape a `&[u8]` and replace all
/// xml special characters (<, >, &, ', ") with their corresponding
/// xml escaped value.
pub fn escape(raw: &[u8]) -> Cow<[u8]> {
    let mut escapes: Vec<(usize, &'static [u8])> = Vec::new();
    let mut bytes = raw.iter();
    fn to_escape(b: u8) -> bool {
        match b {
            b'<' | b'>' | b'\'' | b'&' | b'"' => true,
            _ => false,
        }
    }

    let mut loc = 0;
    while let Some(i) = bytes.position(|&b| to_escape(b)) {
        loc += i;
        match raw[loc] {
            b'<' => escapes.push((loc, b"<")),
            b'>' => escapes.push((loc, b">")),
            b'\'' => escapes.push((loc, b"'")),
            b'&' => escapes.push((loc, b"&")),
            b'"' => escapes.push((loc, b""")),
            _ => unreachable!("Only '<', '>','\', '&' and '\"' are escaped"),
        }
        loc += 1;
    }

    if escapes.is_empty() {
        Cow::Borrowed(raw)
    } else {
        let len = raw.len();
        let mut v = Vec::with_capacity(len);
        let mut start = 0;
        for (i, r) in escapes {
            v.extend_from_slice(&raw[start..i]);
            v.extend_from_slice(r);
            start = i + 1;
        }

        if start < len {
            v.extend_from_slice(&raw[start..]);
        }
        Cow::Owned(v)
    }
}

/// helper function to unescape a `&[u8]` and replace all
/// xml escaped characters ('&...;') into their corresponding value
pub fn unescape(raw: &[u8]) -> Result<Cow<[u8]>, EscapeError> {
    let mut escapes = Vec::new();

    let mut start = 0;
    while let Some(i) = memchr::memchr(b'&', &raw[start..]) {
        start += i + 1;
        if let Some(j) = memchr::memchr(b';', &raw[start..]) {
            let end = start + j;
            // search for character correctness
            let b_o_c = match &raw[start..end] {
                b"lt" => ByteOrChar::Byte(b'<'),
                b"gt" => ByteOrChar::Byte(b'>'),
                b"amp" => ByteOrChar::Byte(b'&'),
                b"apos" => ByteOrChar::Byte(b'\''),
                b"quot" => ByteOrChar::Byte(b'\"'),
                b"#x0" | b"#0" => return Err(EscapeError::EntityWithNull(start..end)),
                bytes if bytes.starts_with(b"#x") => {
                    ByteOrChar::Char(parse_hexadecimal(&bytes[2..])?)
                }
                bytes if bytes.starts_with(b"#") => ByteOrChar::Char(parse_decimal(&bytes[1..])?),
                bytes => {
                    return Err(EscapeError::UnrecognizedSymbol(
                        start..end,
                        String::from_utf8(bytes.to_vec()),
                    ))
                }
            };
            escapes.push((start - 1..end, b_o_c));
            start = end + 1;
        } else {
            return Err(EscapeError::UnterminatedEntity(start..raw.len()));
        }
    }
    if escapes.is_empty() {
        Ok(Cow::Borrowed(raw))
    } else {
        let len = raw.len();
        let mut v = Vec::with_capacity(len);
        let mut start = 0;
        for (r, b) in escapes {
            v.extend_from_slice(&raw[start..r.start]);
            match b {
                ByteOrChar::Byte(b) => v.push(b),
                ByteOrChar::Char(c) => push_utf8(&mut v, c),
            }
            start = r.end + 1;
        }
        if start < raw.len() {
            v.extend_from_slice(&raw[start..]);
        }
        Ok(Cow::Owned(v))
    }
}

fn push_utf8(buf: &mut Vec<u8>, code: u32) {
    if code < MAX_ONE_B {
        buf.push(code as u8);
    } else if code < MAX_TWO_B {
        buf.push((code >> 6 & 0x1F) as u8 | TAG_TWO_B);
        buf.push((code & 0x3F) as u8 | TAG_CONT);
    } else if code < MAX_THREE_B {
        buf.push((code >> 12 & 0x0F) as u8 | TAG_THREE_B);
        buf.push((code >> 6 & 0x3F) as u8 | TAG_CONT);
        buf.push((code & 0x3F) as u8 | TAG_CONT);
    } else {
        buf.push((code >> 18 & 0x07) as u8 | TAG_FOUR_B);
        buf.push((code >> 12 & 0x3F) as u8 | TAG_CONT);
        buf.push((code >> 6 & 0x3F) as u8 | TAG_CONT);
        buf.push((code & 0x3F) as u8 | TAG_CONT);
    }
}

fn parse_hexadecimal(bytes: &[u8]) -> Result<u32, EscapeError> {
    // maximum code is 0x10FFFF => 6 characters
    if bytes.len() > 6 {
        return Err(EscapeError::TooLongHexadecimal);
    }
    let mut code = 0;
    for &b in bytes {
        code <<= 4;
        code += match b {
            b'0'...b'9' => b - b'0',
            b'a'...b'f' => b - b'a' + 10,
            b'A'...b'F' => b - b'A' + 10,
            b => return Err(EscapeError::InvalidHexadecimal(b as char)),
        } as u32;
    }
    Ok(code)
}

fn parse_decimal(bytes: &[u8]) -> Result<u32, EscapeError> {
    // maximum code is 0x10FFFF = 1114111 => 7 characters
    if bytes.len() > 7 {
        return Err(EscapeError::TooLongDecimal);
    }
    let mut code = 0;
    for &b in bytes {
        code *= 10;
        code += match b {
            b'0'...b'9' => b - b'0',
            b => return Err(EscapeError::InvalidDecimal(b as char)),
        } as u32;
    }
    Ok(code)
}

#[test]
fn test_unescape() {
    assert_eq!(&*unescape(b"test").unwrap(), b"test");
    assert_eq!(&*unescape(b"<test>").unwrap(), b"<test>");
    assert_eq!(&*unescape(b"0").unwrap(), b"0");
    assert_eq!(&*unescape(b"0").unwrap(), b"0");
}

#[test]
fn test_escape() {
    assert_eq!(&*escape(b"test"), b"test");
    assert_eq!(&*escape(b"<test>"), b"<test>");
    assert_eq!(&*escape(b"\"a\"bc"), b""a"bc");
    assert_eq!(&*escape(b"\"a\"b&c"), b""a"b&c");
    assert_eq!(
        &*escape(b"prefix_\"a\"b&<>c"),
        "prefix_"a"b&<>c".as_bytes()
    );
}
<commit_msg>rustfmt<commit_after>//! Manage xml character escapes

use memchr;
use std::borrow::Cow;

#[allow(missing_docs)]
#[derive(Fail, Debug)]
pub enum EscapeError {
    #[fail(
        display = "Error while escaping character at range {:?}: Null character entity not allowed",
        _0
    )]
    EntityWithNull(::std::ops::Range<usize>),

    #[fail(
        display = "Error while escaping character at range {:?}: Unrecognized escape symbol: {:?}",
        _0, _1
    )]
    UnrecognizedSymbol(
        ::std::ops::Range<usize>,
        ::std::result::Result<String, ::std::string::FromUtf8Error>,
    ),

    #[fail(
        display = "Error while escaping character at range {:?}: Cannot find ';' after '&'",
        _0
    )]
    UnterminatedEntity(::std::ops::Range<usize>),

    #[fail(display = "Cannot convert hexadecimal to utf8")]
    TooLongHexadecimal,

    #[fail(display = "'{}' is not a valid hexadecimal character", _0)]
    InvalidHexadecimal(char),

    #[fail(display = "Cannot convert decimal to utf8")]
    TooLongDecimal,

    #[fail(display = "'{}' is not a valid decimal character", _0)]
    InvalidDecimal(char),
}

// UTF-8 ranges and tags for encoding characters
const TAG_CONT: u8 = 0b1000_0000;
const TAG_TWO_B: u8 = 0b1100_0000;
const TAG_THREE_B: u8 = 0b1110_0000;
const TAG_FOUR_B: u8 = 0b1111_0000;
const MAX_ONE_B: u32 = 0x80;
const MAX_TWO_B: u32 = 0x800;
const MAX_THREE_B: u32 = 0x10000;

enum ByteOrChar {
    Byte(u8),
    Char(u32),
}

/// helper function to escape a `&[u8]` and replace all
/// xml special characters (<, >, &, ', ") with their corresponding
/// xml escaped value.
pub fn escape(raw: &[u8]) -> Cow<[u8]> {
    let mut escapes: Vec<(usize, &'static [u8])> = Vec::new();
    let mut bytes = raw.iter();
    fn to_escape(b: u8) -> bool {
        match b {
            b'<' | b'>' | b'\'' | b'&' | b'"' => true,
            _ => false,
        }
    }

    let mut loc = 0;
    while let Some(i) = bytes.position(|&b| to_escape(b)) {
        loc += i;
        match raw[loc] {
            b'<' => escapes.push((loc, b"<")),
            b'>' => escapes.push((loc, b">")),
            b'\'' => escapes.push((loc, b"'")),
            b'&' => escapes.push((loc, b"&")),
            b'"' => escapes.push((loc, b""")),
            _ => unreachable!("Only '<', '>','\', '&' and '\"' are escaped"),
        }
        loc += 1;
    }

    if escapes.is_empty() {
        Cow::Borrowed(raw)
    } else {
        let len = raw.len();
        let mut v = Vec::with_capacity(len);
        let mut start = 0;
        for (i, r) in escapes {
            v.extend_from_slice(&raw[start..i]);
            v.extend_from_slice(r);
            start = i + 1;
        }

        if start < len {
            v.extend_from_slice(&raw[start..]);
        }
        Cow::Owned(v)
    }
}

/// helper function to unescape a `&[u8]` and replace all
/// xml escaped characters ('&...;') into their corresponding value
pub fn unescape(raw: &[u8]) -> Result<Cow<[u8]>, EscapeError> {
    let mut escapes = Vec::new();

    let mut start = 0;
    while let Some(i) = memchr::memchr(b'&', &raw[start..]) {
        start += i + 1;
        if let Some(j) = memchr::memchr(b';', &raw[start..]) {
            let end = start + j;
            // search for character correctness
            let b_o_c = match &raw[start..end] {
                b"lt" => ByteOrChar::Byte(b'<'),
                b"gt" => ByteOrChar::Byte(b'>'),
                b"amp" => ByteOrChar::Byte(b'&'),
                b"apos" => ByteOrChar::Byte(b'\''),
                b"quot" => ByteOrChar::Byte(b'\"'),
                b"#x0" | b"#0" => return Err(EscapeError::EntityWithNull(start..end)),
                bytes if bytes.starts_with(b"#x") => {
                    ByteOrChar::Char(parse_hexadecimal(&bytes[2..])?)
                }
                bytes if bytes.starts_with(b"#") => ByteOrChar::Char(parse_decimal(&bytes[1..])?),
                bytes => {
                    return Err(EscapeError::UnrecognizedSymbol(
                        start..end,
                        String::from_utf8(bytes.to_vec()),
                    ));
                }
            };
            escapes.push((start - 1..end, b_o_c));
            start = end + 1;
        } else {
            return Err(EscapeError::UnterminatedEntity(start..raw.len()));
        }
    }
    if escapes.is_empty() {
        Ok(Cow::Borrowed(raw))
    } else {
        let len = raw.len();
        let mut v = Vec::with_capacity(len);
        let mut start = 0;
        for (r, b) in escapes {
            v.extend_from_slice(&raw[start..r.start]);
            match b {
                ByteOrChar::Byte(b) => v.push(b),
                ByteOrChar::Char(c) => push_utf8(&mut v, c),
            }
            start = r.end + 1;
        }
        if start < raw.len() {
            v.extend_from_slice(&raw[start..]);
        }
        Ok(Cow::Owned(v))
    }
}

fn push_utf8(buf: &mut Vec<u8>, code: u32) {
    if code < MAX_ONE_B {
        buf.push(code as u8);
    } else if code < MAX_TWO_B {
        buf.push((code >> 6 & 0x1F) as u8 | TAG_TWO_B);
        buf.push((code & 0x3F) as u8 | TAG_CONT);
    } else if code < MAX_THREE_B {
        buf.push((code >> 12 & 0x0F) as u8 | TAG_THREE_B);
        buf.push((code >> 6 & 0x3F) as u8 | TAG_CONT);
        buf.push((code & 0x3F) as u8 | TAG_CONT);
    } else {
        buf.push((code >> 18 & 0x07) as u8 | TAG_FOUR_B);
        buf.push((code >> 12 & 0x3F) as u8 | TAG_CONT);
        buf.push((code >> 6 & 0x3F) as u8 | TAG_CONT);
        buf.push((code & 0x3F) as u8 | TAG_CONT);
    }
}

fn parse_hexadecimal(bytes: &[u8]) -> Result<u32, EscapeError> {
    // maximum code is 0x10FFFF => 6 characters
    if bytes.len() > 6 {
        return Err(EscapeError::TooLongHexadecimal);
    }
    let mut code = 0;
    for &b in bytes {
        code <<= 4;
        code += match b {
            b'0'...b'9' => b - b'0',
            b'a'...b'f' => b - b'a' + 10,
            b'A'...b'F' => b - b'A' + 10,
            b => return Err(EscapeError::InvalidHexadecimal(b as char)),
        } as u32;
    }
    Ok(code)
}

fn parse_decimal(bytes: &[u8]) -> Result<u32, EscapeError> {
    // maximum code is 0x10FFFF = 1114111 => 7 characters
    if bytes.len() > 7 {
        return Err(EscapeError::TooLongDecimal);
    }
    let mut code = 0;
    for &b in bytes {
        code *= 10;
        code += match b {
            b'0'...b'9' => b - b'0',
            b => return Err(EscapeError::InvalidDecimal(b as char)),
        } as u32;
    }
    Ok(code)
}

#[test]
fn test_unescape() {
    assert_eq!(&*unescape(b"test").unwrap(), b"test");
    assert_eq!(&*unescape(b"<test>").unwrap(), b"<test>");
    assert_eq!(&*unescape(b"0").unwrap(), b"0");
    assert_eq!(&*unescape(b"0").unwrap(), b"0");
}

#[test]
fn test_escape() {
    assert_eq!(&*escape(b"test"), b"test");
    assert_eq!(&*escape(b"<test>"), b"<test>");
    assert_eq!(&*escape(b"\"a\"bc"), b""a"bc");
    assert_eq!(&*escape(b"\"a\"b&c"), b""a"b&c");
    assert_eq!(
        &*escape(b"prefix_\"a\"b&<>c"),
        "prefix_"a"b&<>c".as_bytes()
    );
}
<|endoftext|>