<commit_before>use crate::decor::{Decor, Formatted, InternalString};
use crate::formatted;
use crate::key::Key;
use crate::parser;
use crate::table::{Item, Iter, KeyValuePairs, TableKeyValue, TableLike};
use chrono::{self, FixedOffset};
use combine::stream::state::State;
use linked_hash_map::LinkedHashMap;
use std::mem;
use std::str::FromStr;

/// Representation of a TOML Value (as part of a Key/Value Pair).
#[derive(Debug, Clone)]
pub enum Value {
    /// A 64-bit integer value.
    Integer(Formatted<i64>),
    /// A string value.
    String(Formatted<String>),
    /// A 64-bit float value.
    Float(Formatted<f64>),
    /// A Date-Time value.
    DateTime(Formatted<DateTime>),
    /// A boolean value.
    Boolean(Formatted<bool>),
    /// An inline array of values.
    Array(Array),
    /// An inline table of key/value pairs.
    InlineTable(InlineTable),
}

/// Type representing a TOML Date-Time,
/// payload of the `Value::DateTime` variant's value
#[derive(Eq, PartialEq, Clone, Debug, Hash)]
pub enum DateTime {
    /// An RFC 3339 formatted date-time with offset.
    OffsetDateTime(chrono::DateTime<FixedOffset>),
    /// An RFC 3339 formatted date-time without offset.
    LocalDateTime(chrono::NaiveDateTime),
    /// Date portion of an RFC 3339 formatted date-time.
    LocalDate(chrono::NaiveDate),
    /// Time portion of an RFC 3339 formatted date-time.
    LocalTime(chrono::NaiveTime),
}

/// Type representing a TOML array,
/// payload of the `Value::Array` variant's value
#[derive(Debug, Default, Clone)]
pub struct Array {
    // always Vec<Item::Value>
    pub(crate) values: Vec<Item>,
    // `trailing` represents whitespaces, newlines
    // and comments in an empty array or after the trailing comma
    pub(crate) trailing: InternalString,
    pub(crate) trailing_comma: bool,
    // prefix before `[` and suffix after `]`
    pub(crate) decor: Decor,
}

/// Type representing a TOML inline table,
/// payload of the `Value::InlineTable` variant
#[derive(Debug, Default, Clone)]
pub struct InlineTable {
    pub(crate) items: KeyValuePairs,
    // `preamble` represents whitespaces in an empty table
    pub(crate) preamble: InternalString,
    // prefix before `{` and suffix after `}`
    pub(crate) decor: Decor,
}

#[derive(Eq, PartialEq, Clone, Copy, Debug, Hash)]
pub(crate) enum ValueType {
    None,
    Integer,
    String,
    Float,
    DateTime,
    Boolean,
    Array,
    InlineTable,
}

/// An iterator type over `Array`'s values.
pub type ArrayIter<'a> = Box<dyn Iterator<Item = &'a Value> + 'a>;

impl Array {
    /// Returns the length of the underlying Vec.
    /// To get the actual number of items use `a.iter().count()`.
    pub fn len(&self) -> usize {
        self.values.len()
    }

    /// Return true iff `self.len() == 0`.
    pub fn is_empty(&self) -> bool {
        self.len() == 0
    }

    /// Returns an iterator over all values.
    pub fn iter(&self) -> ArrayIter<'_> {
        Box::new(self.values.iter().filter_map(Item::as_value))
    }

    /// Appends a new value.
    pub fn push<V: Into<Value>>(&mut self, v: V) -> bool {
        self.push_value(v.into(), true)
    }

    /// Return an optional reference to the value at the given index.
    pub fn get(&mut self, index: usize) -> Option<&Value> {
        self.values.get(index).and_then(Item::as_value)
    }

    /// Removes the value at the given index.
    pub fn remove(&mut self, index: usize) -> Value {
        let removed = self.values.remove(index);
        if self.is_empty() {
            self.trailing_comma = false;
        }
        match removed {
            Item::Value(v) => v,
            x => panic!("non-value item {:?} in an array", x),
        }
    }

    /// Auto formats the array.
    pub fn fmt(&mut self) {
        formatted::decorate_array(self);
    }

    pub(crate) fn push_value(&mut self, v: Value, decorate: bool) -> bool {
        let mut value = v;
        if !self.is_empty() && decorate {
            formatted::decorate(&mut value, " ", "");
        } else if decorate {
            formatted::decorate(&mut value, "", "");
        }
        if self.is_empty() || value.get_type() == self.value_type() {
            self.values.push(Item::Value(value));
            true
        } else {
            false
        }
    }

    pub(crate) fn value_type(&self) -> ValueType {
        if let Some(value) = self.values.get(0).and_then(Item::as_value) {
            value.get_type()
        } else {
            ValueType::None
        }
    }
}

/// An iterator type over key/value pairs of an inline table.
pub type InlineTableIter<'a> = Box<dyn Iterator<Item = (&'a str, &'a Value)> + 'a>;

impl InlineTable {
    /// Returns the number of key/value pairs.
    pub fn len(&self) -> usize {
        self.iter().count()
    }

    /// Returns true iff the table is empty.
    pub fn is_empty(&self) -> bool {
        self.len() == 0
    }

    /// Returns an iterator over key/value pairs.
    pub fn iter(&self) -> InlineTableIter<'_> {
        Box::new(
            self.items
                .iter()
                .filter(|&(_, kv)| kv.value.is_value())
                .map(|(k, kv)| (&k[..], kv.value.as_value().unwrap())),
        )
    }

    /// Sorts the key/value pairs by key.
    pub fn sort(&mut self) {
        sort_key_value_pairs(&mut self.items);
    }

    /// Returns true iff the table contains given key.
    pub fn contains_key(&self, key: &str) -> bool {
        if let Some(kv) = self.items.get(key) {
            !kv.value.is_none()
        } else {
            false
        }
    }

    /// Merges the key/value pairs into the `other` table leaving
    /// `self` empty.
    pub fn merge_into(&mut self, other: &mut InlineTable) {
        let items = mem::replace(&mut self.items, KeyValuePairs::new());
        for (k, kv) in items {
            other.items.insert(k, kv);
        }
    }

    /// Inserts a key/value pair if the table does not contain the key.
    /// Returns a mutable reference to the corresponding value.
    pub fn get_or_insert<V: Into<Value>>(&mut self, key: &str, value: V) -> &mut Value {
        let parsed = key.parse::<Key>().expect("invalid key");
        self.items
            .entry(parsed.get().to_owned())
            .or_insert(formatted::to_key_value(key, value.into()))
            .value
            .as_value_mut()
            .expect("non-value type in inline table")
    }

    /// Auto formats the table.
    pub fn fmt(&mut self) {
        formatted::decorate_inline_table(self);
    }

    /// Removes a key/value pair given the key.
    pub fn remove(&mut self, key: &str) -> Option<Value> {
        self.items
            .remove(key)
            .and_then(|kv| kv.value.as_value().cloned())
    }

    /// Return an optional reference to the value at the given the key.
    pub fn get(&self, key: &str) -> Option<&Value> {
        self.items.get(key).and_then(|kv| kv.value.as_value())
    }

    /// Return an optional mutable reference to the value at the given the key.
    pub fn get_mut(&mut self, key: &str) -> Option<&mut Value> {
        self.items
            .get_mut(key)
            .and_then(|kv| kv.value.as_value_mut())
    }
}

impl TableLike for InlineTable {
    fn iter(&self) -> Iter<'_> {
        Box::new(self.items.iter().map(|(key, kv)| (&key[..], &kv.value)))
    }
    fn get<'s>(&'s self, key: &str) -> Option<&'s Item> {
        self.items.get(key).map(|kv| &kv.value)
    }
}

/// Downcasting
impl DateTime {
    /// Casts `self` to offset date-time.
    pub fn as_offset_date_time(&self) -> Option<&chrono::DateTime<FixedOffset>> {
        match *self {
            DateTime::OffsetDateTime(ref dt) => Some(dt),
            _ => None,
        }
    }
    /// Casts `self` to local date-time.
    pub fn as_local_date_time(&self) -> Option<&chrono::NaiveDateTime> {
        match *self {
            DateTime::LocalDateTime(ref dt) => Some(dt),
            _ => None,
        }
    }
    /// Casts `self` to local date.
    pub fn as_local_date(&self) -> Option<&chrono::NaiveDate> {
        match *self {
            DateTime::LocalDate(ref d) => Some(d),
            _ => None,
        }
    }
    /// Casts `self` to local time.
    pub fn as_local_time(&self) -> Option<&chrono::NaiveTime> {
        match *self {
            DateTime::LocalTime(ref t) => Some(t),
            _ => None,
        }
    }
    /// Returns true iff `self` is an offset date-time.
    pub fn is_offset_date_time(&self) -> bool {
        self.as_offset_date_time().is_some()
    }
    /// Returns true iff `self` is a local date-time.
    pub fn is_local_date_time(&self) -> bool {
        self.as_local_date_time().is_some()
    }
    /// Returns true iff `self` is a local date.
    pub fn is_local_date(&self) -> bool {
        self.as_local_date().is_some()
    }
    /// Returns true iff `self` is a local time.
    pub fn is_local_time(&self) -> bool {
        self.as_local_time().is_some()
    }
}

/// Downcasting
impl Value {
    /// Casts `self` to integer.
    pub fn as_integer(&self) -> Option<i64> {
        match *self {
            Value::Integer(ref value) => Some(*value.value()),
            _ => None,
        }
    }

    /// Returns true iff `self` is an integer.
    pub fn is_integer(&self) -> bool {
        self.as_integer().is_some()
    }

    /// Casts `self` to float.
    pub fn as_float(&self) -> Option<f64> {
        match *self {
            Value::Float(ref value) => Some(*value.value()),
            _ => None,
        }
    }

    /// Returns true iff `self` is a float.
    pub fn is_float(&self) -> bool {
        self.as_float().is_some()
    }

    /// Casts `self` to boolean.
    pub fn as_bool(&self) -> Option<bool> {
        match *self {
            Value::Boolean(ref value) => Some(*value.value()),
            _ => None,
        }
    }

    /// Returns true iff `self` is a boolean.
    pub fn is_bool(&self) -> bool {
        self.as_bool().is_some()
    }

    /// Casts `self` to str.
    pub fn as_str(&self) -> Option<&str> {
        match *self {
            Value::String(ref value) => Some(value.value()),
            _ => None,
        }
    }

    /// Returns true iff `self` is a string.
    pub fn is_str(&self) -> bool {
        self.as_str().is_some()
    }

    /// Casts `self` to date-time.
    pub fn as_date_time(&self) -> Option<&DateTime> {
        match *self {
            Value::DateTime(ref value) => Some(value.value()),
            _ => None,
        }
    }

    /// Returns true iff `self` is a date-time.
    pub fn is_date_time(&self) -> bool {
        self.as_date_time().is_some()
    }

    /// Casts `self` to array.
    pub fn as_array(&self) -> Option<&Array> {
        match *self {
            Value::Array(ref value) => Some(value),
            _ => None,
        }
    }

    /// Casts `self` to mutable array.
    pub fn as_array_mut(&mut self) -> Option<&mut Array> {
        match *self {
            Value::Array(ref mut value) => Some(value),
            _ => None,
        }
    }

    /// Returns true iff `self` is an array.
    pub fn is_array(&self) -> bool {
        self.as_array().is_some()
    }

    /// Casts `self` to inline table.
    pub fn as_inline_table(&self) -> Option<&InlineTable> {
        match *self {
            Value::InlineTable(ref value) => Some(value),
            _ => None,
        }
    }

    /// Casts `self` to mutable inline table.
    pub fn as_inline_table_mut(&mut self) -> Option<&mut InlineTable> {
        match *self {
            Value::InlineTable(ref mut value) => Some(value),
            _ => None,
        }
    }

    /// Returns true iff `self` is an inline table.
    pub fn is_inline_table(&self) -> bool {
        self.as_inline_table().is_some()
    }

    pub(crate) fn get_type(&self) -> ValueType {
        match *self {
            Value::Integer(..) => ValueType::Integer,
            Value::String(..) => ValueType::String,
            Value::Float(..) => ValueType::Float,
            Value::DateTime(..) => ValueType::DateTime,
            Value::Boolean(..) => ValueType::Boolean,
            Value::Array(..) => ValueType::Array,
            Value::InlineTable(..) => ValueType::InlineTable,
        }
    }
}

impl Value {
    /// Get the decoration of the value.
    /// # Example
    /// ```rust
    /// let v = toml_edit::Value::from(true);
    /// assert_eq!(v.decor().suffix(), "");
    ///```
    pub fn decor(&self) -> &Decor {
        match *self {
            Value::Integer(ref f) => &f.repr.decor,
            Value::String(ref f) => &f.repr.decor,
            Value::Float(ref f) => &f.repr.decor,
            Value::DateTime(ref f) => &f.repr.decor,
            Value::Boolean(ref f) => &f.repr.decor,
            Value::Array(ref a) => &a.decor,
            Value::InlineTable(ref t) => &t.decor,
        }
    }
}

pub(crate) fn sort_key_value_pairs(items: &mut LinkedHashMap<InternalString, TableKeyValue>) {
    let mut keys: Vec<InternalString> = items
        .iter()
        .filter_map(|i| (i.1).value.as_value().map(|_| i.0))
        .cloned()
        .collect();
    keys.sort();
    for key in keys {
        items.get_refresh(&key);
    }
}

impl FromStr for Value {
    type Err = parser::TomlError;

    /// Parses a value from a &str
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        use combine::Parser;
        let parsed = parser::value_parser().easy_parse(State::new(s));
        match parsed {
            Ok((_, ref rest)) if !rest.input.is_empty() => {
                Err(Self::Err::from_unparsed(rest.positioner, s))
            }
            Ok((value, _)) => Ok(value),
            Err(e) => Err(Self::Err::new(e, s)),
        }
    }
}
<commit_msg>make Array::get be &self, not &mut self<commit_after>use crate::decor::{Decor, Formatted, InternalString};
use crate::formatted;
use crate::key::Key;
use crate::parser;
use crate::table::{Item, Iter, KeyValuePairs, TableKeyValue, TableLike};
use chrono::{self, FixedOffset};
use combine::stream::state::State;
use linked_hash_map::LinkedHashMap;
use std::mem;
use std::str::FromStr;

/// Representation of a TOML Value (as part of a Key/Value Pair).
#[derive(Debug, Clone)]
pub enum Value {
    /// A 64-bit integer value.
    Integer(Formatted<i64>),
    /// A string value.
    String(Formatted<String>),
    /// A 64-bit float value.
    Float(Formatted<f64>),
    /// A Date-Time value.
    DateTime(Formatted<DateTime>),
    /// A boolean value.
    Boolean(Formatted<bool>),
    /// An inline array of values.
    Array(Array),
    /// An inline table of key/value pairs.
    InlineTable(InlineTable),
}

/// Type representing a TOML Date-Time,
/// payload of the `Value::DateTime` variant's value
#[derive(Eq, PartialEq, Clone, Debug, Hash)]
pub enum DateTime {
    /// An RFC 3339 formatted date-time with offset.
    OffsetDateTime(chrono::DateTime<FixedOffset>),
    /// An RFC 3339 formatted date-time without offset.
    LocalDateTime(chrono::NaiveDateTime),
    /// Date portion of an RFC 3339 formatted date-time.
    LocalDate(chrono::NaiveDate),
    /// Time portion of an RFC 3339 formatted date-time.
    LocalTime(chrono::NaiveTime),
}

/// Type representing a TOML array,
/// payload of the `Value::Array` variant's value
#[derive(Debug, Default, Clone)]
pub struct Array {
    // always Vec<Item::Value>
    pub(crate) values: Vec<Item>,
    // `trailing` represents whitespaces, newlines
    // and comments in an empty array or after the trailing comma
    pub(crate) trailing: InternalString,
    pub(crate) trailing_comma: bool,
    // prefix before `[` and suffix after `]`
    pub(crate) decor: Decor,
}

/// Type representing a TOML inline table,
/// payload of the `Value::InlineTable` variant
#[derive(Debug, Default, Clone)]
pub struct InlineTable {
    pub(crate) items: KeyValuePairs,
    // `preamble` represents whitespaces in an empty table
    pub(crate) preamble: InternalString,
    // prefix before `{` and suffix after `}`
    pub(crate) decor: Decor,
}

#[derive(Eq, PartialEq, Clone, Copy, Debug, Hash)]
pub(crate) enum ValueType {
    None,
    Integer,
    String,
    Float,
    DateTime,
    Boolean,
    Array,
    InlineTable,
}

/// An iterator type over `Array`'s values.
pub type ArrayIter<'a> = Box<dyn Iterator<Item = &'a Value> + 'a>;

impl Array {
    /// Returns the length of the underlying Vec.
    /// To get the actual number of items use `a.iter().count()`.
    pub fn len(&self) -> usize {
        self.values.len()
    }

    /// Return true iff `self.len() == 0`.
    pub fn is_empty(&self) -> bool {
        self.len() == 0
    }

    /// Returns an iterator over all values.
    pub fn iter(&self) -> ArrayIter<'_> {
        Box::new(self.values.iter().filter_map(Item::as_value))
    }

    /// Appends a new value.
    pub fn push<V: Into<Value>>(&mut self, v: V) -> bool {
        self.push_value(v.into(), true)
    }

    /// Returns a reference to the value at the given index, or `None` if the index is out of
    /// bounds.
    pub fn get(&self, index: usize) -> Option<&Value> {
        self.values.get(index).and_then(Item::as_value)
    }

    /// Removes the value at the given index.
    pub fn remove(&mut self, index: usize) -> Value {
        let removed = self.values.remove(index);
        if self.is_empty() {
            self.trailing_comma = false;
        }
        match removed {
            Item::Value(v) => v,
            x => panic!("non-value item {:?} in an array", x),
        }
    }

    /// Auto formats the array.
    pub fn fmt(&mut self) {
        formatted::decorate_array(self);
    }

    pub(crate) fn push_value(&mut self, v: Value, decorate: bool) -> bool {
        let mut value = v;
        if !self.is_empty() && decorate {
            formatted::decorate(&mut value, " ", "");
        } else if decorate {
            formatted::decorate(&mut value, "", "");
        }
        if self.is_empty() || value.get_type() == self.value_type() {
            self.values.push(Item::Value(value));
            true
        } else {
            false
        }
    }

    pub(crate) fn value_type(&self) -> ValueType {
        if let Some(value) = self.values.get(0).and_then(Item::as_value) {
            value.get_type()
        } else {
            ValueType::None
        }
    }
}

/// An iterator type over key/value pairs of an inline table.
pub type InlineTableIter<'a> = Box<dyn Iterator<Item = (&'a str, &'a Value)> + 'a>;

impl InlineTable {
    /// Returns the number of key/value pairs.
    pub fn len(&self) -> usize {
        self.iter().count()
    }

    /// Returns true iff the table is empty.
    pub fn is_empty(&self) -> bool {
        self.len() == 0
    }

    /// Returns an iterator over key/value pairs.
    pub fn iter(&self) -> InlineTableIter<'_> {
        Box::new(
            self.items
                .iter()
                .filter(|&(_, kv)| kv.value.is_value())
                .map(|(k, kv)| (&k[..], kv.value.as_value().unwrap())),
        )
    }

    /// Sorts the key/value pairs by key.
    pub fn sort(&mut self) {
        sort_key_value_pairs(&mut self.items);
    }

    /// Returns true iff the table contains given key.
    pub fn contains_key(&self, key: &str) -> bool {
        if let Some(kv) = self.items.get(key) {
            !kv.value.is_none()
        } else {
            false
        }
    }

    /// Merges the key/value pairs into the `other` table leaving
    /// `self` empty.
    pub fn merge_into(&mut self, other: &mut InlineTable) {
        let items = mem::replace(&mut self.items, KeyValuePairs::new());
        for (k, kv) in items {
            other.items.insert(k, kv);
        }
    }

    /// Inserts a key/value pair if the table does not contain the key.
    /// Returns a mutable reference to the corresponding value.
    pub fn get_or_insert<V: Into<Value>>(&mut self, key: &str, value: V) -> &mut Value {
        let parsed = key.parse::<Key>().expect("invalid key");
        self.items
            .entry(parsed.get().to_owned())
            .or_insert(formatted::to_key_value(key, value.into()))
            .value
            .as_value_mut()
            .expect("non-value type in inline table")
    }

    /// Auto formats the table.
    pub fn fmt(&mut self) {
        formatted::decorate_inline_table(self);
    }

    /// Removes a key/value pair given the key.
    pub fn remove(&mut self, key: &str) -> Option<Value> {
        self.items
            .remove(key)
            .and_then(|kv| kv.value.as_value().cloned())
    }

    /// Return an optional reference to the value at the given the key.
    pub fn get(&self, key: &str) -> Option<&Value> {
        self.items.get(key).and_then(|kv| kv.value.as_value())
    }

    /// Return an optional mutable reference to the value at the given the key.
    pub fn get_mut(&mut self, key: &str) -> Option<&mut Value> {
        self.items
            .get_mut(key)
            .and_then(|kv| kv.value.as_value_mut())
    }
}

impl TableLike for InlineTable {
    fn iter(&self) -> Iter<'_> {
        Box::new(self.items.iter().map(|(key, kv)| (&key[..], &kv.value)))
    }
    fn get<'s>(&'s self, key: &str) -> Option<&'s Item> {
        self.items.get(key).map(|kv| &kv.value)
    }
}

/// Downcasting
impl DateTime {
    /// Casts `self` to offset date-time.
    pub fn as_offset_date_time(&self) -> Option<&chrono::DateTime<FixedOffset>> {
        match *self {
            DateTime::OffsetDateTime(ref dt) => Some(dt),
            _ => None,
        }
    }
    /// Casts `self` to local date-time.
    pub fn as_local_date_time(&self) -> Option<&chrono::NaiveDateTime> {
        match *self {
            DateTime::LocalDateTime(ref dt) => Some(dt),
            _ => None,
        }
    }
    /// Casts `self` to local date.
    pub fn as_local_date(&self) -> Option<&chrono::NaiveDate> {
        match *self {
            DateTime::LocalDate(ref d) => Some(d),
            _ => None,
        }
    }
    /// Casts `self` to local time.
    pub fn as_local_time(&self) -> Option<&chrono::NaiveTime> {
        match *self {
            DateTime::LocalTime(ref t) => Some(t),
            _ => None,
        }
    }
    /// Returns true iff `self` is an offset date-time.
    pub fn is_offset_date_time(&self) -> bool {
        self.as_offset_date_time().is_some()
    }
    /// Returns true iff `self` is a local date-time.
    pub fn is_local_date_time(&self) -> bool {
        self.as_local_date_time().is_some()
    }
    /// Returns true iff `self` is a local date.
    pub fn is_local_date(&self) -> bool {
        self.as_local_date().is_some()
    }
    /// Returns true iff `self` is a local time.
    pub fn is_local_time(&self) -> bool {
        self.as_local_time().is_some()
    }
}

/// Downcasting
impl Value {
    /// Casts `self` to integer.
    pub fn as_integer(&self) -> Option<i64> {
        match *self {
            Value::Integer(ref value) => Some(*value.value()),
            _ => None,
        }
    }

    /// Returns true iff `self` is an integer.
    pub fn is_integer(&self) -> bool {
        self.as_integer().is_some()
    }

    /// Casts `self` to float.
    pub fn as_float(&self) -> Option<f64> {
        match *self {
            Value::Float(ref value) => Some(*value.value()),
            _ => None,
        }
    }

    /// Returns true iff `self` is a float.
    pub fn is_float(&self) -> bool {
        self.as_float().is_some()
    }

    /// Casts `self` to boolean.
    pub fn as_bool(&self) -> Option<bool> {
        match *self {
            Value::Boolean(ref value) => Some(*value.value()),
            _ => None,
        }
    }

    /// Returns true iff `self` is a boolean.
    pub fn is_bool(&self) -> bool {
        self.as_bool().is_some()
    }

    /// Casts `self` to str.
    pub fn as_str(&self) -> Option<&str> {
        match *self {
            Value::String(ref value) => Some(value.value()),
            _ => None,
        }
    }

    /// Returns true iff `self` is a string.
    pub fn is_str(&self) -> bool {
        self.as_str().is_some()
    }

    /// Casts `self` to date-time.
    pub fn as_date_time(&self) -> Option<&DateTime> {
        match *self {
            Value::DateTime(ref value) => Some(value.value()),
            _ => None,
        }
    }

    /// Returns true iff `self` is a date-time.
    pub fn is_date_time(&self) -> bool {
        self.as_date_time().is_some()
    }

    /// Casts `self` to array.
    pub fn as_array(&self) -> Option<&Array> {
        match *self {
            Value::Array(ref value) => Some(value),
            _ => None,
        }
    }

    /// Casts `self` to mutable array.
    pub fn as_array_mut(&mut self) -> Option<&mut Array> {
        match *self {
            Value::Array(ref mut value) => Some(value),
            _ => None,
        }
    }

    /// Returns true iff `self` is an array.
    pub fn is_array(&self) -> bool {
        self.as_array().is_some()
    }

    /// Casts `self` to inline table.
    pub fn as_inline_table(&self) -> Option<&InlineTable> {
        match *self {
            Value::InlineTable(ref value) => Some(value),
            _ => None,
        }
    }

    /// Casts `self` to mutable inline table.
    pub fn as_inline_table_mut(&mut self) -> Option<&mut InlineTable> {
        match *self {
            Value::InlineTable(ref mut value) => Some(value),
            _ => None,
        }
    }

    /// Returns true iff `self` is an inline table.
    pub fn is_inline_table(&self) -> bool {
        self.as_inline_table().is_some()
    }

    pub(crate) fn get_type(&self) -> ValueType {
        match *self {
            Value::Integer(..) => ValueType::Integer,
            Value::String(..) => ValueType::String,
            Value::Float(..) => ValueType::Float,
            Value::DateTime(..) => ValueType::DateTime,
            Value::Boolean(..) => ValueType::Boolean,
            Value::Array(..) => ValueType::Array,
            Value::InlineTable(..) => ValueType::InlineTable,
        }
    }
}

impl Value {
    /// Get the decoration of the value.
    /// # Example
    /// ```rust
    /// let v = toml_edit::Value::from(true);
    /// assert_eq!(v.decor().suffix(), "");
    ///```
    pub fn decor(&self) -> &Decor {
        match *self {
            Value::Integer(ref f) => &f.repr.decor,
            Value::String(ref f) => &f.repr.decor,
            Value::Float(ref f) => &f.repr.decor,
            Value::DateTime(ref f) => &f.repr.decor,
            Value::Boolean(ref f) => &f.repr.decor,
            Value::Array(ref a) => &a.decor,
            Value::InlineTable(ref t) => &t.decor,
        }
    }
}

pub(crate) fn sort_key_value_pairs(items: &mut LinkedHashMap<InternalString, TableKeyValue>) {
    let mut keys: Vec<InternalString> = items
        .iter()
        .filter_map(|i| (i.1).value.as_value().map(|_| i.0))
        .cloned()
        .collect();
    keys.sort();
    for key in keys {
        items.get_refresh(&key);
    }
}

impl FromStr for Value {
    type Err = parser::TomlError;

    /// Parses a value from a &str
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        use combine::Parser;
        let parsed = parser::value_parser().easy_parse(State::new(s));
        match parsed {
            Ok((_, ref rest)) if !rest.input.is_empty() => {
                Err(Self::Err::from_unparsed(rest.positioner, s))
            }
            Ok((value, _)) => Ok(value),
            Err(e) => Err(Self::Err::new(e, s)),
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2015 Nicholas Bishop
//
// Orientation code adapted from "Real-Time Collision Detection" by
// Christer Ericson, published by Morgan Kaufmann Publishers,
// Copyright 2005 Elsevier Inc
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::ops::{Add, Div, Mul, Sub};
use util::CastF32;
use vec3f::Vec3f;

/// Vector with two f32 components
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Vec2f {
    pub x: f32,
    pub y: f32
}

impl Vec2f {
    /// Create a Vec2f from two components
    pub fn new(x: f32, y: f32) -> Vec2f {
        Vec2f { x: x,
                y: y }
    }

    pub fn cross(self, other: Vec2f) -> f32 {
        self.x * other.y - self.y * other.x
    }

    pub fn lerp(self, other: Vec2f, t: f32) -> Vec2f {
        self * (1.0 - t) + other * t
    }

    pub fn dot(self, other: Vec2f) -> f32 {
        self.x * other.x + self.y * other.y
    }

    pub fn vec3f(self) -> Vec3f {
        Vec3f::new(self.x, self.y, 0.0)
    }

    pub fn distance_squared(self, other: Vec2f) -> f32 {
        (self - other).magnitude_squared()
    }

    pub fn distance(self, other: Vec2f) -> f32 {
        self.distance_squared(other).sqrt()
    }

    pub fn magnitude_squared(self) -> f32 {
        self.dot(self)
    }

    pub fn magnitude(self) -> f32 {
        self.magnitude_squared().sqrt()
    }

    pub fn normalized(self) -> Option<Vec2f> {
        let m = self.magnitude();
        if m == 0.0 {
            None
        }
        else {
            Some(Vec2f::new(self.x / m, self.y / m))
        }
    }
}

impl Add for Vec2f {
    type Output = Vec2f;
    fn add(self, v: Vec2f) -> Vec2f {
        Vec2f::new(self.x + v.x, self.y + v.y)
    }
}

impl Sub for Vec2f {
    type Output = Vec2f;
    fn sub(self, v: Vec2f) -> Vec2f {
        Vec2f::new(self.x - v.x, self.y - v.y)
    }
}

impl Mul<f32> for Vec2f {
    type Output = Vec2f;
    fn mul(self, s: f32) -> Vec2f {
        Vec2f::new(self.x * s, self.y * s)
    }
}

impl Div<f32> for Vec2f {
    type Output = Vec2f;
    fn div(self, s: f32) -> Vec2f {
        Vec2f::new(self.x / s, self.y / s)
    }
}

pub fn detf_2x2(top_left: f32, top_right: f32,
                bot_left: f32, bot_right: f32) -> f32 {
    top_left * bot_right - top_right * bot_left
}

#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Line2f {
    pub points: (Vec2f, Vec2f)
}

impl Line2f {
    pub fn new(a: Vec2f, b: Vec2f) -> Line2f {
        Line2f { points: (a, b) }
    }

    pub fn closest_parametric_point(self, point: Vec2f) -> f32 {
        let p0p = point - self.points.0;
        let p0p1 = self.points.1 - self.points.0;

        p0p.dot(p0p1) / p0p1.dot(p0p1)
    }

    pub fn cart_from_para(self, t: f32) -> Vec2f {
        self.points.0.lerp(self.points.1, t)
    }

    /// If result is greater than zero `point` lies to the left of the
    /// line. If result is less than zero `point` lies to the
    /// right. If result is zero, the point is on the line.
    ///
    /// The value is also twice the signed area of the triangle
    /// `(points.0, points.1, point)` (positive if counterclockwise,
    /// otherwise negative).
    ///
    /// Adapted from "Real-Time Collision Detection" by Christer
    /// Ericson, published by Morgan Kaufmann Publishers, Copyright
    /// 2005 Elsevier Inc
    pub fn orient(self, point: Vec2f) -> f32 {
        detf_2x2(self.points.0.x - point.x, self.points.0.y - point.y,
                 self.points.1.x - point.x, self.points.1.y - point.y)
    }
}

/// Create a Vec2f from x and y inputs
///
/// This is a convenience function that provides a little more
/// flexibility than Vec2f::new in that it will happily take numbers
/// that aren't f32 (including a mix of different types for each
/// component). Vec2f is such a common type that it seems reasonable
/// to provide a little extra ease of use.
pub fn vec2f<X: CastF32, Y: CastF32>(x: X, y: Y) -> Vec2f {
    Vec2f { x: x.as_f32(), y: y.as_f32() }
}
<commit_msg>Add Line2f::point_distance_squared<commit_after>// Copyright 2015 Nicholas Bishop
//
// Orientation code adapted from "Real-Time Collision Detection" by
// Christer Ericson, published by Morgan Kaufmann Publishers,
// Copyright 2005 Elsevier Inc
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::ops::{Add, Div, Mul, Sub};
use util::CastF32;
use vec3f::Vec3f;

/// Vector with two f32 components
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Vec2f {
    pub x: f32,
    pub y: f32
}

impl Vec2f {
    /// Create a Vec2f from two components
    pub fn new(x: f32, y: f32) -> Vec2f {
        Vec2f { x: x,
                y: y }
    }

    pub fn cross(self, other: Vec2f) -> f32 {
        self.x * other.y - self.y * other.x
    }

    pub fn lerp(self, other: Vec2f, t: f32) -> Vec2f {
        self * (1.0 - t) + other * t
    }

    pub fn dot(self, other: Vec2f) -> f32 {
        self.x * other.x + self.y * other.y
    }

    pub fn vec3f(self) -> Vec3f {
        Vec3f::new(self.x, self.y, 0.0)
    }

    pub fn distance_squared(self, other: Vec2f) -> f32 {
        (self - other).magnitude_squared()
    }

    pub fn distance(self, other: Vec2f) -> f32 {
        self.distance_squared(other).sqrt()
    }

    pub fn magnitude_squared(self) -> f32 {
        self.dot(self)
    }

    pub fn magnitude(self) -> f32 {
        self.magnitude_squared().sqrt()
    }

    pub fn normalized(self) -> Option<Vec2f> {
        let m = self.magnitude();
        if m == 0.0 {
            None
        }
        else {
            Some(Vec2f::new(self.x / m, self.y / m))
        }
    }
}

impl Add for Vec2f {
    type Output = Vec2f;
    fn add(self, v: Vec2f) -> Vec2f {
        Vec2f::new(self.x + v.x, self.y + v.y)
    }
}

impl Sub for Vec2f {
    type Output = Vec2f;
    fn sub(self, v: Vec2f) -> Vec2f {
        Vec2f::new(self.x - v.x, self.y - v.y)
    }
}

impl Mul<f32> for Vec2f {
    type Output = Vec2f;
    fn mul(self, s: f32) -> Vec2f {
        Vec2f::new(self.x * s, self.y * s)
    }
}

impl Div<f32> for Vec2f {
    type Output = Vec2f;
    fn div(self, s: f32) -> Vec2f {
        Vec2f::new(self.x / s, self.y / s)
    }
}

pub fn detf_2x2(top_left: f32, top_right: f32,
                bot_left: f32, bot_right: f32) -> f32 {
    top_left * bot_right - top_right * bot_left
}

#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Line2f {
    pub points: (Vec2f, Vec2f)
}

impl Line2f {
    pub fn new(a: Vec2f, b: Vec2f) -> Line2f {
        Line2f { points: (a, b) }
    }

    pub fn closest_parametric_point(self, point: Vec2f) -> f32 {
        let p0p = point - self.points.0;
        let p0p1 = self.points.1 - self.points.0;

        p0p.dot(p0p1) / p0p1.dot(p0p1)
    }

    pub fn point_distance_squared(self, point: Vec2f) -> f32 {
        // TODO(nicholasbishop): may be a more way to calculate this
        let para = self.closest_parametric_point(point);
        let cart = self.cart_from_para(para);
        cart.distance_squared(point)
    }

    pub fn cart_from_para(self, t: f32) -> Vec2f {
        self.points.0.lerp(self.points.1, t)
    }

    /// If result is greater than zero `point` lies to the left of the
    /// line. If result is less than zero `point` lies to the
    /// right. If result is zero, the point is on the line.
    ///
    /// The value is also twice the signed area of the triangle
    /// `(points.0, points.1, point)` (positive if counterclockwise,
    /// otherwise negative).
    ///
    /// Adapted from "Real-Time Collision Detection" by Christer
    /// Ericson, published by Morgan Kaufmann Publishers, Copyright
    /// 2005 Elsevier Inc
    pub fn orient(self, point: Vec2f) -> f32 {
        detf_2x2(self.points.0.x - point.x, self.points.0.y - point.y,
                 self.points.1.x - point.x, self.points.1.y - point.y)
    }
}

/// Create a Vec2f from x and y inputs
///
/// This is a convenience function that provides a little more
/// flexibility than Vec2f::new in that it will happily take numbers
/// that aren't f32 (including a mix of different types for each
/// component). Vec2f is such a common type that it seems reasonable
/// to provide a little extra ease of use.
pub fn vec2f<X: CastF32, Y: CastF32>(x: X, y: Y) -> Vec2f {
    Vec2f { x: x.as_f32(), y: y.as_f32() }
}
<|endoftext|>


<commit_before>//! Types used in this bot implementation.

use std::fmt;

use errors::CommandParseError;

#[derive(Debug, Clone)]
pub struct Command {
    pub name: String,
    pub params: Vec<String>,
}

/// Parse a text message, return a command if possible
impl<'a> ::conv::TryFrom<&'a str> for Command {
    type Err = CommandParseError;
    fn try_from(text: &'a str) -> Result<Self, CommandParseError> {

        // Verify if this is actually a command
        if !text.starts_with("/") {
            return Err(CommandParseError::NoCommand);
        }

        // Split text into words iterator
        let mut words = text.split(' ');

        // Parse out name and params
        if let Some(name) = words.next() {
            let params: Vec<String> = words.map(|s| s.into()).collect();
            Ok(Command {
                name: name[1..].into(),
                params: params,
            })
        } else {
            Err(CommandParseError::NoCommand)
        }
    }
}

impl fmt::Display for Command {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "/{} {}", self.name, self.params.join(" "))
    }
}
<commit_msg>Fix bugs in Command::try_from<commit_after>//! Types used in this bot implementation.

use std::fmt;

use errors::CommandParseError;

#[derive(Debug, Clone)]
pub struct Command {
    pub name: String,
    pub params: Vec<String>,
}

/// Parse a text message, return a command if possible
impl<'a> ::conv::TryFrom<&'a str> for Command {
    type Err = CommandParseError;
    fn try_from(text: &'a str) -> Result<Self, CommandParseError> {

        // Verify if this is actually a command
        if !text.starts_with("/") {
            return Err(CommandParseError::NoCommand);
        }

        // Split text into words iterator
        let mut words = text.split(' ');

        // Parse out name and params
        if let Some(command) = words.next() {

            // Strip leading slash
            let name: String = command[1..].into();

            // If name is an empty string, it's not valid
            if name.len() == 0 {
                return Err(CommandParseError::NoCommand);
            }

            // Parse parameters
            let params: Vec<String> = words.map(|s| s.into()).collect();

            // Return command
            Ok(Command {
                name: name.into(),
                params: params,
            })

        } else {
            Err(CommandParseError::NoCommand)
        }
    }
}

impl fmt::Display for Command {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "/{} {}", self.name, self.params.join(" "))
    }
}

#[cfg(test)]
mod tests {
    use super::Command;
    use conv::TryFrom;

    #[test]
    fn command_parse_str_ok() {
        let text_simple: String = "/help".into();
        let text_params = "/list all";

        let command_simple = Command::try_from(&*text_simple).unwrap();
        let command_params = Command::try_from(text_params).unwrap();

        assert_eq!(command_simple.name, "help");
        assert_eq!(command_simple.params, Vec::<String>::new());
        assert_eq!(command_params.name, "list");
        assert_eq!(command_params.params, vec!["all"]);
    }

    #[test]
    fn command_parse_string_err() {
        let texts = vec![
            "",
            "/",
            "no initial slash",
            " /preceding space",
            "/ params but no name",
        ];

        for &text in texts.iter() {
            assert!(Command::try_from(text).is_err());
        }
    }

}
<|endoftext|>


<commit_before>//! Types defined by Linux-PAM
//!
//! This modules contains struct and enum definitions used by `pam-sys`.

use libc::{c_char, c_int, c_void};

/// Opaque struct internal to Linux-PAM
///
/// From `_pam_types.h`:
///
/// "This is a blind structure. Users aren't allowed to see
/// inside a pam_handle_t, so we don't define struct pam_handle here.
/// This is defined in a file private to the PAM library.
/// (i.e., it's private to PAM service modules, too!)"
#[repr(C)]
pub struct PamHandle;

/// Message struct to transfer authentication data to the user
///
/// From `_pam_types.h`:
///
/// "Used to pass prompting text, error messages, or other informatory text to the user.
/// This structure is allocated and freed by the PAM library (or loaded module)."
#[repr(C)]
#[derive(Debug)]
pub struct PamMessage {
    pub msg_style:  c_int,
    pub msg:        *const c_char
}

/// Response struct to transfer the user's response back to Linux-PAM
///
/// From `_pam_types.h`:
///
/// "Used to return the user's response to the PAM library.
/// This structure is allocated by the application program,
/// and free()'d by the Linux-PAM library (or calling module)."
#[repr(C)]
#[derive(Debug)]
pub struct PamResponse {
    pub resp:           *mut c_char,
    /// currently un-used, zero expected
    pub resp_retcode:   c_int
}

/// Conversation structure containing the `converse` function and authentication data
///
/// From `_pam_types.h`:
///
/// "The actual conversation structure itself"
#[repr(C)]
pub struct PamConversation {
    /* int (*conv)(int num_msg, const struct pam_message **msg,
		struct pam_response **resp, void *appdata_ptr); */
    pub conv:       Option<extern "C" fn (c_int, *mut *mut PamMessage, *mut *mut PamResponse, *mut c_void) -> c_int>,
    //pub conv:       *const c_void,
    pub data_ptr:   *mut c_void
}

/// Special struct for the PAM_XAUTHDATA pam item
///
/// From `_pam_types.h`:
///
/// "Used by the PAM_XAUTHDATA pam item. Contains X authentication
/// data used by modules to connect to the user's X display.
/// Note: this structure is intentionally compatible with xcb_auth_info_t."
#[repr(C)]
#[derive(Debug)]
pub struct PamXAuthData {
    pub namelen:    c_int,
    pub name:       *mut c_char,
    pub datalen:    c_int,
    pub data:       *mut c_char
}

/// The Linux-PAM return values
#[derive(Debug, PartialEq)]
pub enum PamReturnCode {
    /// Successful function return
    SUCCESS                 = 0,

    /// dlopen() failure when dynamically loading a service module
    OPEN_ERR                = 1,

    /// Symbol not found
    SYMBOL_ERR              = 2,

    /// Error in service module
    SERVICE_ERR             = 3,

    /// System error
    SYSTEM_ERR              = 4,

    /// Memory buffer error
    BUF_ERR                 = 5,

    /// Permission denied
    PERM_DENIED             = 6,

    /// Authentication failure
    AUTH_ERR                = 7,

    /// Can not access authentication data due to insufficient credentials
    CRED_INSUFFICIENT       = 8,

    /// Underlying authentication service can not retrieve authentication information
    AUTHINFO_UNAVAIL        = 9,

    /// User not known to the underlying authentication module
    USER_UNKNOWN            = 10,

    /// An authentication service has maintained a retry count which has been reached.
    /// No further retries should be attempted
    MAXTRIES                = 11,

    /// New authentication token required.
    /// This is normally returned if the machine security policies require
    /// that the password should be changed beccause the password is NULL or it has aged
    NEW_AUTHTOK_REQD        = 12,

    /// User account has expired
    ACCT_EXPIRED            = 13,

    /// Can not make/remove an entry for the specified session
    SESSION_ERR             = 14,

    /// Underlying authentication service can not retrieve user credentials unavailable
    CRED_UNAVAIL            = 15,

    /// User credentials expired
    CRED_EXPIRED            = 16,

    /// Failure setting user credentials
    CRED_ERR                = 17,

    /// No module specific data is present
    NO_MODULE_DATA          = 18,

    /// Conversation error
    CONV_ERR                = 19,

    /// Authentication token manipulation error
    AUTHTOK_ERR             = 20,

    /// Authentication information cannot be recovered
    AUTHTOK_RECOVERY_ERR    = 21,

    /// Authentication token lock busy
    AUTHTOK_LOCK_BUSY       = 22,

    /// Authentication token aging disabled
    AUTHTOK_DISABLE_AGING   = 23,

    /// Preliminary check by password service
    TRY_AGAIN               = 24,

    /// Ignore underlying account module regardless of whether
    /// the control flag is required, optional, or sufficient
    IGNORE                  = 25,

    /// Critical error (?module fail now request)
    AUTHTOK_EXPIRED         = 27,

    /// user's authentication token has expired
    ABORT                   = 26,

    /// module is not known
    MODULE_UNKNOWN          = 28,

    /// Bad item passed to pam_*_item()
    BAD_ITEM                = 29,

    /// conversation function is event driven and data is not available yet
    CONV_AGAIN              = 30,

    /// please call this function again to complete authentication stack.
    /// Before calling again, verify that conversation is completed
    INCOMPLETE              = 31
}

impl PamReturnCode {
    pub fn from_i32(status: i32) -> PamReturnCode {
        match status {
            0   => PamReturnCode::SUCCESS               ,
            1   => PamReturnCode::OPEN_ERR              ,
            2   => PamReturnCode::SYMBOL_ERR            ,
            3   => PamReturnCode::SERVICE_ERR           ,
            4   => PamReturnCode::SYSTEM_ERR            ,
            5   => PamReturnCode::BUF_ERR               ,
            6   => PamReturnCode::PERM_DENIED           ,
            7   => PamReturnCode::AUTH_ERR              ,
            8   => PamReturnCode::CRED_INSUFFICIENT     ,
            9   => PamReturnCode::AUTHINFO_UNAVAIL      ,
            10  => PamReturnCode::USER_UNKNOWN          ,
            11  => PamReturnCode::MAXTRIES              ,
            12  => PamReturnCode::NEW_AUTHTOK_REQD      ,
            13  => PamReturnCode::ACCT_EXPIRED          ,
            14  => PamReturnCode::SESSION_ERR           ,
            15  => PamReturnCode::CRED_UNAVAIL          ,
            16  => PamReturnCode::CRED_EXPIRED          ,
            17  => PamReturnCode::CRED_ERR              ,
            18  => PamReturnCode::NO_MODULE_DATA        ,
            19  => PamReturnCode::CONV_ERR              ,
            20  => PamReturnCode::AUTHTOK_ERR           ,
            21  => PamReturnCode::AUTHTOK_RECOVERY_ERR  ,
            22  => PamReturnCode::AUTHTOK_LOCK_BUSY     ,
            23  => PamReturnCode::AUTHTOK_DISABLE_AGING ,
            24  => PamReturnCode::TRY_AGAIN             ,
            25  => PamReturnCode::IGNORE                ,
            26  => PamReturnCode::ABORT                 ,
            27  => PamReturnCode::AUTHTOK_EXPIRED       ,
            28  => PamReturnCode::MODULE_UNKNOWN        ,
            29  => PamReturnCode::BAD_ITEM              ,
            30  => PamReturnCode::CONV_AGAIN            ,
            31  => PamReturnCode::INCOMPLETE            ,
            _   => PamReturnCode::SYSTEM_ERR
        }
    }
}

/// The Linux-PAM flags
#[derive(Debug, PartialEq)]
pub enum PamFlag {
    /// Authentication service should not generate any messages
    SILENT                  = 0x8000,

    /// The authentication service should return AUTH_ERROR
    /// if the user has a null authentication token
    /// (used by pam_authenticate{,_secondary}())
    DISALLOW_NULL_AUTHTOK   = 0x0001,

    /// Set user credentials for an authentication service
    /// (used for pam_setcred())
    ESTABLISH_CRED          = 0x0002,

    /// Delete user credentials associated with an authentication service
    /// (used for pam_setcred())
    DELETE_CRED             = 0x0004,

    /// Reinitialize user credentials
    /// (used for pam_setcred())
    REINITIALIZE_CRED       = 0x0008,

    /// Extend lifetime of user credentials
    /// (used for pam_setcred())
    REFRESH_CRED            = 0x0010,

    /// The password service should only update those passwords that have aged.
    /// If this flag is not passed, the password service should update all passwords.
    /// (used by pam_chauthtok)
    CHANGE_EXPIRED_AUTHTOK  = 0x0020,

    NONE                    = 0x0000
}

/// The Linux-PAM item types
///
/// These defines are used by pam_set_item() and pam_get_item().
/// Please check the spec which are allowed for use by applications
/// and which are only allowed for use by modules.
#[derive(Debug, PartialEq)]
pub enum PamItemType {
    /// The service name
    SERVICE         = 1,

    /// The user name
    USER            = 2,

    /// The tty name
    TTY             = 3,

    /// The remote host name
    RHOST           = 4,

    /// The pam_conv structure
    CONV            = 5,

    /// The authentication token (password)
    AUTHTOK         = 6,

    /// The old authentication token
    OLDAUTHTOK      = 7,

    /// The remote user name
    RUSER           = 8,

    /// the prompt for getting a username Linux-PAM extensions
    USER_PROMPT     = 9,

    /// app supplied function to override failure delays
    FAIL_DELAY      = 10,

    /// X display name
    XDISPLAY        = 11,

    /// X server authentication data
    XAUTHDATA       = 12,

    /// The type for pam_get_authtok
    AUTHTOK_TYPE    = 13

}

/// The Linux-PAM message styles
#[derive(Debug, PartialEq)]
pub enum PamMessageStyle {
    PROMPT_ECHO_OFF = 1,
    PROMPT_ECHO_ON  = 2,
    ERROR_MSG       = 3,
    TEXT_INFO       = 4
}

impl PamMessageStyle {
    pub fn from_i32(style: i32) -> PamMessageStyle {
        match style {
            1 => PamMessageStyle::PROMPT_ECHO_OFF,
            2 => PamMessageStyle::PROMPT_ECHO_ON,
            3 => PamMessageStyle::ERROR_MSG,
            4 => PamMessageStyle::TEXT_INFO,
            _ => PamMessageStyle::ERROR_MSG
        }
    }
}
<commit_msg>Derived Clone and Copy for some ffi types<commit_after>//! Types defined by Linux-PAM
//!
//! This modules contains struct and enum definitions used by `pam-sys`.

use libc::{c_char, c_int, c_void};

/// Opaque struct internal to Linux-PAM
///
/// From `_pam_types.h`:
///
/// "This is a blind structure. Users aren't allowed to see
/// inside a pam_handle_t, so we don't define struct pam_handle here.
/// This is defined in a file private to the PAM library.
/// (i.e., it's private to PAM service modules, too!)"
#[repr(C)]
pub struct PamHandle;

/// Message struct to transfer authentication data to the user
///
/// From `_pam_types.h`:
///
/// "Used to pass prompting text, error messages, or other informatory text to the user.
/// This structure is allocated and freed by the PAM library (or loaded module)."
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct PamMessage {
    pub msg_style:  c_int,
    pub msg:        *const c_char
}

/// Response struct to transfer the user's response back to Linux-PAM
///
/// From `_pam_types.h`:
///
/// "Used to return the user's response to the PAM library.
/// This structure is allocated by the application program,
/// and free()'d by the Linux-PAM library (or calling module)."
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct PamResponse {
    pub resp:           *mut c_char,
    /// currently un-used, zero expected
    pub resp_retcode:   c_int
}

/// Conversation structure containing the `converse` function and authentication data
///
/// From `_pam_types.h`:
///
/// "The actual conversation structure itself"
#[repr(C)]
pub struct PamConversation {
    /* int (*conv)(int num_msg, const struct pam_message **msg,
		struct pam_response **resp, void *appdata_ptr); */
    pub conv:       Option<extern "C" fn (c_int, *mut *mut PamMessage, *mut *mut PamResponse, *mut c_void) -> c_int>,
    //pub conv:       *const c_void,
    pub data_ptr:   *mut c_void
}

/// Special struct for the PAM_XAUTHDATA pam item
///
/// From `_pam_types.h`:
///
/// "Used by the PAM_XAUTHDATA pam item. Contains X authentication
/// data used by modules to connect to the user's X display.
/// Note: this structure is intentionally compatible with xcb_auth_info_t."
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct PamXAuthData {
    pub namelen:    c_int,
    pub name:       *mut c_char,
    pub datalen:    c_int,
    pub data:       *mut c_char
}

/// The Linux-PAM return values
#[derive(Debug, PartialEq)]
pub enum PamReturnCode {
    /// Successful function return
    SUCCESS                 = 0,

    /// dlopen() failure when dynamically loading a service module
    OPEN_ERR                = 1,

    /// Symbol not found
    SYMBOL_ERR              = 2,

    /// Error in service module
    SERVICE_ERR             = 3,

    /// System error
    SYSTEM_ERR              = 4,

    /// Memory buffer error
    BUF_ERR                 = 5,

    /// Permission denied
    PERM_DENIED             = 6,

    /// Authentication failure
    AUTH_ERR                = 7,

    /// Can not access authentication data due to insufficient credentials
    CRED_INSUFFICIENT       = 8,

    /// Underlying authentication service can not retrieve authentication information
    AUTHINFO_UNAVAIL        = 9,

    /// User not known to the underlying authentication module
    USER_UNKNOWN            = 10,

    /// An authentication service has maintained a retry count which has been reached.
    /// No further retries should be attempted
    MAXTRIES                = 11,

    /// New authentication token required.
    /// This is normally returned if the machine security policies require
    /// that the password should be changed beccause the password is NULL or it has aged
    NEW_AUTHTOK_REQD        = 12,

    /// User account has expired
    ACCT_EXPIRED            = 13,

    /// Can not make/remove an entry for the specified session
    SESSION_ERR             = 14,

    /// Underlying authentication service can not retrieve user credentials unavailable
    CRED_UNAVAIL            = 15,

    /// User credentials expired
    CRED_EXPIRED            = 16,

    /// Failure setting user credentials
    CRED_ERR                = 17,

    /// No module specific data is present
    NO_MODULE_DATA          = 18,

    /// Conversation error
    CONV_ERR                = 19,

    /// Authentication token manipulation error
    AUTHTOK_ERR             = 20,

    /// Authentication information cannot be recovered
    AUTHTOK_RECOVERY_ERR    = 21,

    /// Authentication token lock busy
    AUTHTOK_LOCK_BUSY       = 22,

    /// Authentication token aging disabled
    AUTHTOK_DISABLE_AGING   = 23,

    /// Preliminary check by password service
    TRY_AGAIN               = 24,

    /// Ignore underlying account module regardless of whether
    /// the control flag is required, optional, or sufficient
    IGNORE                  = 25,

    /// Critical error (?module fail now request)
    AUTHTOK_EXPIRED         = 27,

    /// user's authentication token has expired
    ABORT                   = 26,

    /// module is not known
    MODULE_UNKNOWN          = 28,

    /// Bad item passed to pam_*_item()
    BAD_ITEM                = 29,

    /// conversation function is event driven and data is not available yet
    CONV_AGAIN              = 30,

    /// please call this function again to complete authentication stack.
    /// Before calling again, verify that conversation is completed
    INCOMPLETE              = 31
}

impl PamReturnCode {
    pub fn from_i32(status: i32) -> PamReturnCode {
        match status {
            0   => PamReturnCode::SUCCESS               ,
            1   => PamReturnCode::OPEN_ERR              ,
            2   => PamReturnCode::SYMBOL_ERR            ,
            3   => PamReturnCode::SERVICE_ERR           ,
            4   => PamReturnCode::SYSTEM_ERR            ,
            5   => PamReturnCode::BUF_ERR               ,
            6   => PamReturnCode::PERM_DENIED           ,
            7   => PamReturnCode::AUTH_ERR              ,
            8   => PamReturnCode::CRED_INSUFFICIENT     ,
            9   => PamReturnCode::AUTHINFO_UNAVAIL      ,
            10  => PamReturnCode::USER_UNKNOWN          ,
            11  => PamReturnCode::MAXTRIES              ,
            12  => PamReturnCode::NEW_AUTHTOK_REQD      ,
            13  => PamReturnCode::ACCT_EXPIRED          ,
            14  => PamReturnCode::SESSION_ERR           ,
            15  => PamReturnCode::CRED_UNAVAIL          ,
            16  => PamReturnCode::CRED_EXPIRED          ,
            17  => PamReturnCode::CRED_ERR              ,
            18  => PamReturnCode::NO_MODULE_DATA        ,
            19  => PamReturnCode::CONV_ERR              ,
            20  => PamReturnCode::AUTHTOK_ERR           ,
            21  => PamReturnCode::AUTHTOK_RECOVERY_ERR  ,
            22  => PamReturnCode::AUTHTOK_LOCK_BUSY     ,
            23  => PamReturnCode::AUTHTOK_DISABLE_AGING ,
            24  => PamReturnCode::TRY_AGAIN             ,
            25  => PamReturnCode::IGNORE                ,
            26  => PamReturnCode::ABORT                 ,
            27  => PamReturnCode::AUTHTOK_EXPIRED       ,
            28  => PamReturnCode::MODULE_UNKNOWN        ,
            29  => PamReturnCode::BAD_ITEM              ,
            30  => PamReturnCode::CONV_AGAIN            ,
            31  => PamReturnCode::INCOMPLETE            ,
            _   => PamReturnCode::SYSTEM_ERR
        }
    }
}

/// The Linux-PAM flags
#[derive(Debug, PartialEq)]
pub enum PamFlag {
    /// Authentication service should not generate any messages
    SILENT                  = 0x8000,

    /// The authentication service should return AUTH_ERROR
    /// if the user has a null authentication token
    /// (used by pam_authenticate{,_secondary}())
    DISALLOW_NULL_AUTHTOK   = 0x0001,

    /// Set user credentials for an authentication service
    /// (used for pam_setcred())
    ESTABLISH_CRED          = 0x0002,

    /// Delete user credentials associated with an authentication service
    /// (used for pam_setcred())
    DELETE_CRED             = 0x0004,

    /// Reinitialize user credentials
    /// (used for pam_setcred())
    REINITIALIZE_CRED       = 0x0008,

    /// Extend lifetime of user credentials
    /// (used for pam_setcred())
    REFRESH_CRED            = 0x0010,

    /// The password service should only update those passwords that have aged.
    /// If this flag is not passed, the password service should update all passwords.
    /// (used by pam_chauthtok)
    CHANGE_EXPIRED_AUTHTOK  = 0x0020,

    NONE                    = 0x0000
}

/// The Linux-PAM item types
///
/// These defines are used by pam_set_item() and pam_get_item().
/// Please check the spec which are allowed for use by applications
/// and which are only allowed for use by modules.
#[derive(Debug, PartialEq)]
pub enum PamItemType {
    /// The service name
    SERVICE         = 1,

    /// The user name
    USER            = 2,

    /// The tty name
    TTY             = 3,

    /// The remote host name
    RHOST           = 4,

    /// The pam_conv structure
    CONV            = 5,

    /// The authentication token (password)
    AUTHTOK         = 6,

    /// The old authentication token
    OLDAUTHTOK      = 7,

    /// The remote user name
    RUSER           = 8,

    /// the prompt for getting a username Linux-PAM extensions
    USER_PROMPT     = 9,

    /// app supplied function to override failure delays
    FAIL_DELAY      = 10,

    /// X display name
    XDISPLAY        = 11,

    /// X server authentication data
    XAUTHDATA       = 12,

    /// The type for pam_get_authtok
    AUTHTOK_TYPE    = 13

}

/// The Linux-PAM message styles
#[derive(Debug, PartialEq)]
pub enum PamMessageStyle {
    PROMPT_ECHO_OFF = 1,
    PROMPT_ECHO_ON  = 2,
    ERROR_MSG       = 3,
    TEXT_INFO       = 4
}

impl PamMessageStyle {
    pub fn from_i32(style: i32) -> PamMessageStyle {
        match style {
            1 => PamMessageStyle::PROMPT_ECHO_OFF,
            2 => PamMessageStyle::PROMPT_ECHO_ON,
            3 => PamMessageStyle::ERROR_MSG,
            4 => PamMessageStyle::TEXT_INFO,
            _ => PamMessageStyle::ERROR_MSG
        }
    }
}
<|endoftext|>


<commit_before>#![feature(globs, phase)]
#[phase(plugin, link)]

extern crate log;
extern crate xlib;
extern crate xinerama;

use config::get_config;
use workspaces::Workspaces;
use xlib_window_system::XlibWindowSystem;
use xlib_window_system::XlibEvent::{ XMapRequest,
                          XConfigurationNotify,
                          XConfigurationRequest,
                          XDestroy,
                          XUnmapNotify,
                          XEnterNotify,
                          XFocusOut,
                          XKeyPress,
                          XButtonPress};

mod config;
mod keycode;
mod commands;
mod xlib_window_system;
mod workspaces;
mod layout;

fn main() {
  let mut config = get_config();

  let ws = &XlibWindowSystem::new();
  ws.grab_modifier(config.mod_key);

  let mut workspaces = Workspaces::new(&mut config, ws.get_screen_infos().len());

  loop {
    match ws.get_event() {
      XMapRequest(window) => {
        debug!("XMapRequest: {}", window);
        if !workspaces.contains(window) {
          let class = ws.get_class_name(window);
          let mut is_hooked = false;

          for hook in config.manage_hooks.iter() {
            if hook.class_name == class {
              is_hooked = true;
              hook.cmd.call(ws, &mut workspaces, &config, window);
            }
          }

          if !is_hooked {
            workspaces.current_mut().add_window(ws, &config, window);
            workspaces.current_mut().focus_window(ws, &config, window);
          }
        }
      },
      XDestroy(window) => {
        debug!("XDestroy: {}", window);
        workspaces.remove_window(ws, &config, window);
      },
      XUnmapNotify(window) => {
        debug!("XUnmapNotify: {}", window);
        workspaces.remove_window(ws, &config, window);
      },
      XConfigurationNotify(_) => {
        debug!("XConfigurationNotify");
        workspaces.rescreen(ws, &config);
      },
      XConfigurationRequest(window, changes, mask) => {
        debug!("XConfigurationRequest: {}, {}, {}", window, changes, mask);
        ws.configure_window(window, changes, mask);
      },
      XEnterNotify(window) => {
        debug!("XConfigurationRequest: {}", window);
        workspaces.current_mut().focus_window(ws, &config, window);
      },
      XFocusOut(_) => {
        debug!("XFocusOut");
        workspaces.current_mut().unfocus_window(ws, &config);
      },
      XButtonPress(window) => {
        debug!("XButtonPress: {}", window);
        workspaces.current_mut().focus_window(ws, &config, window);
      },
      XKeyPress(_, mods, key) => {
        debug!("XButtonPress: {}, {}", mods, key);
        let mods = mods & !(config.mod_key | 0b10010);

        for binding in config.keybindings.iter() {
          if binding.mods == mods && binding.key == key {
            binding.cmd.call(ws, &mut workspaces, &config);
          }
        }
      },
      _ => {}
    }

    if let Some(ref mut loghook) = (&mut config).log_hook {
      loghook.call(ws, &workspaces);
    }
  }
}
<commit_msg>fixed wrong debug output<commit_after>#![feature(globs, phase)]
#[phase(plugin, link)]

extern crate log;
extern crate xlib;
extern crate xinerama;

use config::get_config;
use workspaces::Workspaces;
use xlib_window_system::XlibWindowSystem;
use xlib_window_system::XlibEvent::{ XMapRequest,
                          XConfigurationNotify,
                          XConfigurationRequest,
                          XDestroy,
                          XUnmapNotify,
                          XEnterNotify,
                          XFocusOut,
                          XKeyPress,
                          XButtonPress};

mod config;
mod keycode;
mod commands;
mod xlib_window_system;
mod workspaces;
mod layout;

fn main() {
  let mut config = get_config();

  let ws = &XlibWindowSystem::new();
  ws.grab_modifier(config.mod_key);

  let mut workspaces = Workspaces::new(&mut config, ws.get_screen_infos().len());

  loop {
    match ws.get_event() {
      XMapRequest(window) => {
        debug!("XMapRequest: {}", window);
        if !workspaces.contains(window) {
          let class = ws.get_class_name(window);
          let mut is_hooked = false;

          for hook in config.manage_hooks.iter() {
            if hook.class_name == class {
              is_hooked = true;
              hook.cmd.call(ws, &mut workspaces, &config, window);
            }
          }

          if !is_hooked {
            workspaces.current_mut().add_window(ws, &config, window);
            workspaces.current_mut().focus_window(ws, &config, window);
          }
        }
      },
      XDestroy(window) => {
        debug!("XDestroy: {}", window);
        workspaces.remove_window(ws, &config, window);
      },
      XUnmapNotify(window) => {
        debug!("XUnmapNotify: {}", window);
        workspaces.remove_window(ws, &config, window);
      },
      XConfigurationNotify(_) => {
        debug!("XConfigurationNotify");
        workspaces.rescreen(ws, &config);
      },
      XConfigurationRequest(window, changes, mask) => {
        debug!("XConfigurationRequest: {}, {}, {}", window, changes, mask);
        ws.configure_window(window, changes, mask);
      },
      XEnterNotify(window) => {
        debug!("XEnterNotify: {}", window);
        workspaces.current_mut().focus_window(ws, &config, window);
      },
      XFocusOut(_) => {
        debug!("XFocusOut");
        workspaces.current_mut().unfocus_window(ws, &config);
      },
      XButtonPress(window) => {
        debug!("XButtonPress: {}", window);
        workspaces.current_mut().focus_window(ws, &config, window);
      },
      XKeyPress(_, mods, key) => {
        debug!("XButtonPress: {}, {}", mods, key);
        let mods = mods & !(config.mod_key | 0b10010);

        for binding in config.keybindings.iter() {
          if binding.mods == mods && binding.key == key {
            binding.cmd.call(ws, &mut workspaces, &config);
          }
        }
      },
      _ => {}
    }

    if let Some(ref mut loghook) = (&mut config).log_hook {
      loghook.call(ws, &workspaces);
    }
  }
}
<|endoftext|>


<commit_before>extern crate base64;

use self::base64::encode;
use http::retrieve_asset;
use regex::Regex;
use std::collections::HashMap;
use url::{ParseError, Url};

lazy_static! {
    static ref HAS_PROTOCOL: Regex = Regex::new(r"^[a-z0-9]+:").unwrap();
    static ref REGEX_URL: Regex = Regex::new(r"^https?://").unwrap();
}

const MAGIC: [[&[u8]; 2]; 19] = [
    // Image
    [b"GIF87a", b"image/gif"],
    [b"GIF89a", b"image/gif"],
    [b"\xFF\xD8\xFF", b"image/jpeg"],
    [b"\x89PNG\x0D\x0A\x1A\x0A", b"image/png"],
    [b"<?xml ", b"image/svg+xml"],
    [b"<svg ", b"image/svg+xml"],
    [b"RIFF....WEBPVP8 ", b"image/webp"],
    [b"\x00\x00\x01\x00", b"image/x-icon"],
    // Audio
    [b"ID3", b"audio/mpeg"],
    [b"\xFF\x0E", b"audio/mpeg"],
    [b"\xFF\x0F", b"audio/mpeg"],
    [b"OggS", b"audio/ogg"],
    [b"RIFF....WAVEfmt ", b"audio/wav"],
    [b"fLaC", b"audio/x-flac"],
    // Video
    [b"RIFF....AVI LIST", b"video/avi"],
    [b"....ftyp", b"video/mp4"],
    [b"\x00\x00\x01\x0B", b"video/mpeg"],
    [b"....moov", b"video/quicktime"],
    [b"\x1A\x45\xDF\xA3", b"video/webm"],
];

pub fn data_to_dataurl(mime: &str, data: &[u8]) -> String {
    let mimetype = if mime.is_empty() {
        detect_mimetype(data)
    } else {
        mime.to_string()
    };
    format!("data:{};base64,{}", mimetype, encode(data))
}

pub fn detect_mimetype(data: &[u8]) -> String {
    for item in MAGIC.iter() {
        if data.starts_with(item[0]) {
            return String::from_utf8(item[1].to_vec()).unwrap();
        }
    }
    "".to_owned()
}

pub fn url_has_protocol<T: AsRef<str>>(url: T) -> bool {
    HAS_PROTOCOL.is_match(url.as_ref().to_lowercase().as_str())
}

pub fn is_data_url<T: AsRef<str>>(url: T) -> Result<bool, ParseError> {
    Url::parse(url.as_ref()).and_then(|u| Ok(u.scheme() == "data"))
}

pub fn is_valid_url<T: AsRef<str>>(path: T) -> bool {
    REGEX_URL.is_match(path.as_ref())
}

pub fn resolve_url<T: AsRef<str>, U: AsRef<str>>(from: T, to: U) -> Result<String, ParseError> {
    let result = if is_valid_url(to.as_ref()) {
        to.as_ref().to_string()
    } else {
        Url::parse(from.as_ref())?
            .join(to.as_ref())?
            .as_ref()
            .to_string()
    };
    Ok(result)
}

pub fn resolve_css_imports(
    cache: &mut HashMap<String, String>,
    css_string: &str,
    as_dataurl: bool,
    href: &str,
    opt_user_agent: &str,
    opt_silent: bool,
    opt_insecure: bool,
) -> String {
    let mut resolved_css = String::from(css_string);
    let re =
        Regex::new(r###"(?P<import>@import )?url\((?P<to_repl>['"]?(?P<url>[^"'\)]+)['"]?)\)"###).unwrap();

    for link in re.captures_iter(&css_string) {
        let target_link = link.name("url").unwrap().as_str();

        // Generate absolute URL for content
        let embedded_url = match resolve_url(href, target_link) {
            Ok(url) => url,
            Err(_) => continue, // Malformed URL
        };

        // Download the asset.  If it's more CSS, resolve that too
        let content = match link.name("import") {
            // The link is an @import link
            Some(_) => retrieve_asset(
                cache,
                &embedded_url,
                false,      // Formating as data URL will be done later
                "text/css", // Expect CSS
                opt_user_agent,
                opt_silent,
                opt_insecure,
            )
            .map(|(content, _)| {
                resolve_css_imports(
                    cache,
                    &content,
                    true, //NOW, convert to data URL
                    &embedded_url,
                    opt_user_agent,
                    opt_silent,
                    opt_insecure,
                )
            }),

            // The link is some other, non-@import link
            None => retrieve_asset(
                cache,
                &embedded_url,
                true, // Format as data URL
                "",   // Unknown MIME type
                opt_user_agent,
                opt_silent,
                opt_insecure,
            )
            .map(|(a, _)| a),
        }
        .unwrap_or_else(|e| {
            eprintln!("Warning: {}", e,);

            //If failed to resolve, replace with absolute URL
            embedded_url
        });

        let replacement = format!("\"{}\"", &content);
        let dest = link.name("to_repl").unwrap();

        resolved_css.replace_range(dest.start()..dest.end(), &replacement);
    }

    if as_dataurl {
        data_to_dataurl("text/css", resolved_css.as_bytes())
    } else {
        resolved_css
    }
}
<commit_msg>Moved regex compilation to lazy_static<commit_after>extern crate base64;

use self::base64::encode;
use http::retrieve_asset;
use regex::Regex;
use std::collections::HashMap;
use url::{ParseError, Url};

const CSS_URL_REGEX_STR: &str =
    r###"(?P<import>@import )?url\((?P<to_repl>['"]?(?P<url>[^"'\)]+)['"]?)\)"###;

lazy_static! {
    static ref HAS_PROTOCOL: Regex = Regex::new(r"^[a-z0-9]+:").unwrap();
    static ref REGEX_URL: Regex = Regex::new(r"^https?://").unwrap();
    static ref REGEX_CSS_URL: Regex = Regex::new(CSS_URL_REGEX_STR).unwrap();
}

const MAGIC: [[&[u8]; 2]; 19] = [
    // Image
    [b"GIF87a", b"image/gif"],
    [b"GIF89a", b"image/gif"],
    [b"\xFF\xD8\xFF", b"image/jpeg"],
    [b"\x89PNG\x0D\x0A\x1A\x0A", b"image/png"],
    [b"<?xml ", b"image/svg+xml"],
    [b"<svg ", b"image/svg+xml"],
    [b"RIFF....WEBPVP8 ", b"image/webp"],
    [b"\x00\x00\x01\x00", b"image/x-icon"],
    // Audio
    [b"ID3", b"audio/mpeg"],
    [b"\xFF\x0E", b"audio/mpeg"],
    [b"\xFF\x0F", b"audio/mpeg"],
    [b"OggS", b"audio/ogg"],
    [b"RIFF....WAVEfmt ", b"audio/wav"],
    [b"fLaC", b"audio/x-flac"],
    // Video
    [b"RIFF....AVI LIST", b"video/avi"],
    [b"....ftyp", b"video/mp4"],
    [b"\x00\x00\x01\x0B", b"video/mpeg"],
    [b"....moov", b"video/quicktime"],
    [b"\x1A\x45\xDF\xA3", b"video/webm"],
];

pub fn data_to_dataurl(mime: &str, data: &[u8]) -> String {
    let mimetype = if mime.is_empty() {
        detect_mimetype(data)
    } else {
        mime.to_string()
    };
    format!("data:{};base64,{}", mimetype, encode(data))
}

pub fn detect_mimetype(data: &[u8]) -> String {
    for item in MAGIC.iter() {
        if data.starts_with(item[0]) {
            return String::from_utf8(item[1].to_vec()).unwrap();
        }
    }
    "".to_owned()
}

pub fn url_has_protocol<T: AsRef<str>>(url: T) -> bool {
    HAS_PROTOCOL.is_match(url.as_ref().to_lowercase().as_str())
}

pub fn is_data_url<T: AsRef<str>>(url: T) -> Result<bool, ParseError> {
    Url::parse(url.as_ref()).and_then(|u| Ok(u.scheme() == "data"))
}

pub fn is_valid_url<T: AsRef<str>>(path: T) -> bool {
    REGEX_URL.is_match(path.as_ref())
}

pub fn resolve_url<T: AsRef<str>, U: AsRef<str>>(from: T, to: U) -> Result<String, ParseError> {
    let result = if is_valid_url(to.as_ref()) {
        to.as_ref().to_string()
    } else {
        Url::parse(from.as_ref())?
            .join(to.as_ref())?
            .as_ref()
            .to_string()
    };
    Ok(result)
}

pub fn resolve_css_imports(
    cache: &mut HashMap<String, String>,
    css_string: &str,
    as_dataurl: bool,
    href: &str,
    opt_user_agent: &str,
    opt_silent: bool,
    opt_insecure: bool,
) -> String {
    let mut resolved_css = String::from(css_string);

    for link in REGEX_CSS_URL.captures_iter(&css_string) {
        let target_link = link.name("url").unwrap().as_str();

        // Generate absolute URL for content
        let embedded_url = match resolve_url(href, target_link) {
            Ok(url) => url,
            Err(_) => continue, // Malformed URL
        };

        // Download the asset.  If it's more CSS, resolve that too
        let content = match link.name("import") {
            // The link is an @import link
            Some(_) => retrieve_asset(
                cache,
                &embedded_url,
                false,      // Formating as data URL will be done later
                "text/css", // Expect CSS
                opt_user_agent,
                opt_silent,
                opt_insecure,
            )
            .map(|(content, _)| {
                resolve_css_imports(
                    cache,
                    &content,
                    true, //NOW, convert to data URL
                    &embedded_url,
                    opt_user_agent,
                    opt_silent,
                    opt_insecure,
                )
            }),

            // The link is some other, non-@import link
            None => retrieve_asset(
                cache,
                &embedded_url,
                true, // Format as data URL
                "",   // Unknown MIME type
                opt_user_agent,
                opt_silent,
                opt_insecure,
            )
            .map(|(a, _)| a),
        }
        .unwrap_or_else(|e| {
            eprintln!("Warning: {}", e,);

            //If failed to resolve, replace with absolute URL
            embedded_url
        });

        let replacement = format!("\"{}\"", &content);
        let dest = link.name("to_repl").unwrap();

        resolved_css.replace_range(dest.start()..dest.end(), &replacement);
    }

    if as_dataurl {
        data_to_dataurl("text/css", resolved_css.as_bytes())
    } else {
        resolved_css
    }
}
<|endoftext|>


<commit_before>#![doc(html_root_url = "https://urschrei.github.io/lonlat_bng/")]
//! This module provides utilities to the conversions module
use conversions::MIN_X_SHIFT;
use conversions::MIN_Y_SHIFT;
use conversions::MIN_Z_SHIFT;

use std;
use std::fmt;
use ostn02_phf::ostn02_lookup;

/// Bounds checking for input values
pub fn check<T>(to_check: T, bounds: (T, T)) -> Result<T, ()>
    where T: std::cmp::PartialOrd + fmt::Display + Copy
{
    match to_check {
        to_check if bounds.0 <= to_check && to_check <= bounds.1 => Ok(to_check),
        _ => Err(()),
    }
}

// TODO Herbie's going to have a field day with this
/// Round Easting, Northing, and Orthometric height to the nearest millimetre
pub fn round_to_nearest_mm(x: f64, y: f64, z: f64) -> (f64, f64, f64) {
    let new_x = (x * 1000.).round() as f64 / 1000.;
    let new_y = (y * 1000.).round() as f64 / 1000.;
    let new_z = (z * 1000.).round() as f64 / 1000.;
    (new_x, new_y, new_z)
}

// TODO Herbie's going to have a field day with this
/// Round a float to nine decimal places
pub fn round_to_eight(x: f64, y: f64) -> (f64, f64) {
    let new_x = (x * 100000000.).round() as f64 / 100000000.;
    let new_y = (y * 100000000.).round() as f64 / 100000000.;
    (new_x, new_y)
}

// Try to get OSTN02 shift parameters, and calculate offsets
pub fn get_ostn_ref(x: &i32, y: &i32) -> Result<(f64, f64, f64), ()> {
    let key = format!("{:03x}{:03x}", y, x);
    // some or None, so try! this
    let result = try!(ostn02_lookup(&*key).ok_or(()));
    Ok((result.0 as f64 / 1000. + MIN_X_SHIFT,
        result.1 as f64 / 1000. + MIN_Y_SHIFT,
        result.2 as f64 / 1000. + MIN_Z_SHIFT))

}

// Input values must be valid ETRS89 grid references
// See p20 of the transformation user guide at
// https://www.ordnancesurvey.co.uk/business-and-government/help-and-support/navigation-technology/os-net/formats-for-developers.html
pub fn ostn02_shifts(x: &f64, y: &f64) -> Result<(f64, f64, f64), ()> {
    let e_index = (*x / 1000.) as i32;
    let n_index = (*y / 1000.) as i32;

    // eastings and northings of the south-west corner of the cell
    let x0 = e_index * 1000;
    let y0 = n_index * 1000;

    // The easting, northing and geoid shifts for the four corners of the cell
    // any of these could be Err, so use try!
    let s0: (f64, f64, f64) = try!(get_ostn_ref(&(e_index + 0), &(n_index + 0)));
    let s1: (f64, f64, f64) = try!(get_ostn_ref(&(e_index + 1), &(n_index + 0)));
    let s2: (f64, f64, f64) = try!(get_ostn_ref(&(e_index + 0), &(n_index + 1)));
    let s3: (f64, f64, f64) = try!(get_ostn_ref(&(e_index + 1), &(n_index + 1)));

    // offset within square
    let dx = *x - (x0 as f64);
    let dy = *y - (y0 as f64);

    let t = dx / 1000.;
    let u = dy / 1000.;

    let f0 = (1. - t as f64) * (1. - u as f64);
    let f1 = t as f64 * (1. - u as f64);
    let f2 = (1. - t as f64) * u as f64;
    let f3 = t as f64 * u as f64;

    // bilinear interpolation, to obtain the actual shifts
    let se = f0 * s0.0 + f1 * s1.0 + f2 * s2.0 + f3 * s3.0;
    let sn = f0 * s0.1 + f1 * s1.1 + f2 * s2.1 + f3 * s3.1;
    let sg = f0 * s0.2 + f1 * s1.2 + f2 * s2.2 + f3 * s3.2;

    Ok(round_to_nearest_mm(se, sn, sg))

}

#[cfg(test)]
mod tests {
    use super::get_ostn_ref;
    use super::ostn02_shifts;
    use super::check;

    #[test]
    // original coordinates are 651307.003, 313255.686
    fn test_ostn_hashmap_retrieval() {
        let eastings = 651;
        let northings = 313;
        let expected = (102.775, -78.244, 44.252);
        assert_eq!(expected, get_ostn_ref(&eastings, &northings).unwrap());
    }

    #[test]
    #[should_panic]
    fn test_failed_ostn_hashmap_retrieval() {
        // we're try!ing this in the shift calculation function, so an Err is fine
        let eastings = 999;
        let northings = 999;
        let expected = (1., 1., 1.);
        assert_eq!(expected, get_ostn_ref(&eastings, &northings).unwrap());
    }

    #[test]
    fn test_ostn02_shift_incorporation() {
        // these are the input values and corrections on p20-21
        let eastings = 651307.003;
        let northings = 313255.686;
        let expected = (102.789, -78.238, 44.244);
        assert_eq!(expected, ostn02_shifts(&eastings, &northings).unwrap());
    }

    #[test]
    #[should_panic]
    fn test_min_lon_extents() {
        let max_lon = 1.768960;
        let min_lon = -6.379880;
        // below min_lon
        check(&-6.379881, (&min_lon, &max_lon)).unwrap();
    }

    #[test]
    #[should_panic]
    fn test_min_lat_extents() {
        let max_lat = 55.811741;
        let min_lat = 49.871159;
        // below min lat
        check(&49.871158, (&min_lat, &max_lat)).unwrap();
    }

    #[test]
    #[should_panic]
    fn test_max_lon_extents() {
        let max_lon = 1.768960;
        let min_lon = -6.379880;
        // above max lon
        check(&1.768961, (&min_lon, &max_lon)).unwrap();
    }

    #[test]
    #[should_panic]
    fn test_max_lat_extents() {
        let max_lat = 55.811741;
        let min_lat = 49.871159;
        // above max lat
        check(&55.811742, (&min_lat, &max_lat)).unwrap();
    }

}
<commit_msg>Tidy up comments<commit_after>#![doc(html_root_url = "https://urschrei.github.io/lonlat_bng/")]
//! This module provides utilities to the conversions module
use conversions::MIN_X_SHIFT;
use conversions::MIN_Y_SHIFT;
use conversions::MIN_Z_SHIFT;

use std;
use std::fmt;
use ostn02_phf::ostn02_lookup;

/// Bounds checking for input values
pub fn check<T>(to_check: T, bounds: (T, T)) -> Result<T, ()>
    where T: std::cmp::PartialOrd + fmt::Display + Copy
{
    match to_check {
        to_check if bounds.0 <= to_check && to_check <= bounds.1 => Ok(to_check),
        _ => Err(()),
    }
}

/// Round Easting, Northing, and Orthometric height to the nearest millimetre
pub fn round_to_nearest_mm(x: f64, y: f64, z: f64) -> (f64, f64, f64) {
    let new_x = (x * 1000.).round() as f64 / 1000.;
    let new_y = (y * 1000.).round() as f64 / 1000.;
    let new_z = (z * 1000.).round() as f64 / 1000.;
    (new_x, new_y, new_z)
}

/// Round a float to nine decimal places
pub fn round_to_eight(x: f64, y: f64) -> (f64, f64) {
    let new_x = (x * 100000000.).round() as f64 / 100000000.;
    let new_y = (y * 100000000.).round() as f64 / 100000000.;
    (new_x, new_y)
}

/// Try to get OSTN02 shift parameters, and calculate offsets
pub fn get_ostn_ref(x: &i32, y: &i32) -> Result<(f64, f64, f64), ()> {
    let key = format!("{:03x}{:03x}", y, x);
    // some or None, so try! this
    let result = try!(ostn02_lookup(&*key).ok_or(()));
    Ok((result.0 as f64 / 1000. + MIN_X_SHIFT,
        result.1 as f64 / 1000. + MIN_Y_SHIFT,
        result.2 as f64 / 1000. + MIN_Z_SHIFT))

}

// Input values must be valid ETRS89 grid references
// See p20 of the transformation user guide at
// https://www.ordnancesurvey.co.uk/business-and-government/help-and-support/navigation-technology/os-net/formats-for-developers.html
pub fn ostn02_shifts(x: &f64, y: &f64) -> Result<(f64, f64, f64), ()> {
    let e_index = (*x / 1000.) as i32;
    let n_index = (*y / 1000.) as i32;

    // eastings and northings of the south-west corner of the cell
    let x0 = e_index * 1000;
    let y0 = n_index * 1000;

    // The easting, northing and geoid shifts for the four corners of the cell
    // any of these could be Err, so use try!
    let s0: (f64, f64, f64) = try!(get_ostn_ref(&(e_index + 0), &(n_index + 0)));
    let s1: (f64, f64, f64) = try!(get_ostn_ref(&(e_index + 1), &(n_index + 0)));
    let s2: (f64, f64, f64) = try!(get_ostn_ref(&(e_index + 0), &(n_index + 1)));
    let s3: (f64, f64, f64) = try!(get_ostn_ref(&(e_index + 1), &(n_index + 1)));

    // offset within square
    let dx = *x - (x0 as f64);
    let dy = *y - (y0 as f64);

    let t = dx / 1000.;
    let u = dy / 1000.;

    let f0 = (1. - t as f64) * (1. - u as f64);
    let f1 = t as f64 * (1. - u as f64);
    let f2 = (1. - t as f64) * u as f64;
    let f3 = t as f64 * u as f64;

    // bilinear interpolation, to obtain the actual shifts
    let se = f0 * s0.0 + f1 * s1.0 + f2 * s2.0 + f3 * s3.0;
    let sn = f0 * s0.1 + f1 * s1.1 + f2 * s2.1 + f3 * s3.1;
    let sg = f0 * s0.2 + f1 * s1.2 + f2 * s2.2 + f3 * s3.2;

    Ok(round_to_nearest_mm(se, sn, sg))

}

#[cfg(test)]
mod tests {
    use super::get_ostn_ref;
    use super::ostn02_shifts;
    use super::check;

    #[test]
    // original coordinates are 651307.003, 313255.686
    fn test_ostn_hashmap_retrieval() {
        let eastings = 651;
        let northings = 313;
        let expected = (102.775, -78.244, 44.252);
        assert_eq!(expected, get_ostn_ref(&eastings, &northings).unwrap());
    }

    #[test]
    #[should_panic]
    fn test_failed_ostn_hashmap_retrieval() {
        // we're try!ing this in the shift calculation function, so an Err is fine
        let eastings = 999;
        let northings = 999;
        let expected = (1., 1., 1.);
        assert_eq!(expected, get_ostn_ref(&eastings, &northings).unwrap());
    }

    #[test]
    fn test_ostn02_shift_incorporation() {
        // these are the input values and corrections on p20-21
        let eastings = 651307.003;
        let northings = 313255.686;
        let expected = (102.789, -78.238, 44.244);
        assert_eq!(expected, ostn02_shifts(&eastings, &northings).unwrap());
    }

    #[test]
    #[should_panic]
    fn test_min_lon_extents() {
        let max_lon = 1.768960;
        let min_lon = -6.379880;
        // below min_lon
        check(&-6.379881, (&min_lon, &max_lon)).unwrap();
    }

    #[test]
    #[should_panic]
    fn test_min_lat_extents() {
        let max_lat = 55.811741;
        let min_lat = 49.871159;
        // below min lat
        check(&49.871158, (&min_lat, &max_lat)).unwrap();
    }

    #[test]
    #[should_panic]
    fn test_max_lon_extents() {
        let max_lon = 1.768960;
        let min_lon = -6.379880;
        // above max lon
        check(&1.768961, (&min_lon, &max_lon)).unwrap();
    }

    #[test]
    #[should_panic]
    fn test_max_lat_extents() {
        let max_lat = 55.811741;
        let min_lat = 49.871159;
        // above max lat
        check(&55.811742, (&min_lat, &max_lat)).unwrap();
    }

}
<|endoftext|>