<commit_before>use error::*;
use serde::de::{Deserialize, Deserializer, Visitor};
use std::collections::HashMap;
use std::fmt;
use std::fmt::Display;

/// Underlying kind of the configuration value.
#[derive(Debug, Clone, PartialEq)]
pub enum ValueKind {
    Nil,
    Boolean(bool),
    Integer(i64),
    Float(f64),
    String(String),
    Table(Table),
    Array(Array),
}

pub type Array = Vec<Value>;
pub type Table = HashMap<String, Value>;

impl Default for ValueKind {
    fn default() -> Self {
        ValueKind::Nil
    }
}

impl<T> From<Option<T>> for ValueKind
where
    T: Into<ValueKind>,
{
    fn from(value: Option<T>) -> Self {
        match value {
            Some(value) => value.into(),
            None => ValueKind::Nil,
        }
    }
}

impl From<String> for ValueKind {
    fn from(value: String) -> Self {
        ValueKind::String(value)
    }
}

impl<'a> From<&'a str> for ValueKind {
    fn from(value: &'a str) -> Self {
        ValueKind::String(value.into())
    }
}

impl From<i64> for ValueKind {
    fn from(value: i64) -> Self {
        ValueKind::Integer(value)
    }
}

impl From<f64> for ValueKind {
    fn from(value: f64) -> Self {
        ValueKind::Float(value)
    }
}

impl From<bool> for ValueKind {
    fn from(value: bool) -> Self {
        ValueKind::Boolean(value)
    }
}

impl<T> From<HashMap<String, T>> for ValueKind
where
    T: Into<Value>,
{
    fn from(values: HashMap<String, T>) -> Self {
        let mut r = HashMap::new();

        for (k, v) in values {
            r.insert(k.clone(), v.into());
        }

        ValueKind::Table(r)
    }
}

impl<T> From<Vec<T>> for ValueKind
where
    T: Into<Value>,
{
    fn from(values: Vec<T>) -> Self {
        let mut l = Vec::new();

        for v in values {
            l.push(v.into());
        }

        ValueKind::Array(l)
    }
}

impl Display for ValueKind {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            ValueKind::String(ref value) => write!(f, "{}", value),
            ValueKind::Boolean(value) => write!(f, "{}", value),
            ValueKind::Integer(value) => write!(f, "{}", value),
            ValueKind::Float(value) => write!(f, "{}", value),
            ValueKind::Nil => write!(f, "nil"),

            // TODO: Figure out a nice Display for these
            ValueKind::Table(ref table) => write!(f, "{:?}", table),
            ValueKind::Array(ref array) => write!(f, "{:?}", array),
        }
    }
}

/// A configuration value.
#[derive(Default, Debug, Clone, PartialEq)]
pub struct Value {
    /// A description of the original location of the value.
    ///
    /// A Value originating from a File might contain:
    /// ```text
    /// Settings.toml
    /// ```
    ///
    /// A Value originating from the environment would contain:
    /// ```text
    /// the envrionment
    /// ```
    ///
    /// A Value originating from a remote source might contain:
    /// ```text
    /// etcd+http://127.0.0.1:2379
    /// ```
    origin: Option<String>,

    /// Underlying kind of the configuration value.
    pub kind: ValueKind,
}

impl Value {
    /// Create a new value instance that will remember its source uri.
    pub fn new<V>(origin: Option<&String>, kind: V) -> Self
    where
        V: Into<ValueKind>,
    {
        Value {
            origin: origin.cloned(),
            kind: kind.into(),
        }
    }

    /// Attempt to deserialize this value into the requested type.
    pub fn try_into<'de, T: Deserialize<'de>>(self) -> Result<T> {
        T::deserialize(self)
    }

    /// Returns `self` as a bool, if possible.
    // FIXME: Should this not be `try_into_*` ?
    pub fn into_bool(self) -> Result<bool> {
        match self.kind {
            ValueKind::Boolean(value) => Ok(value),
            ValueKind::Integer(value) => Ok(value != 0),
            ValueKind::Float(value) => Ok(value != 0.0),

            ValueKind::String(ref value) => {
                match value.to_lowercase().as_ref() {
                    "1" | "true" | "on" | "yes" => Ok(true),
                    "0" | "false" | "off" | "no" => Ok(false),

                    // Unexpected string value
                    s => Err(ConfigError::invalid_type(
                        self.origin.clone(),
                        Unexpected::Str(s.into()),
                        "a boolean",
                    )),
                }
            }

            // Unexpected type
            ValueKind::Nil => Err(ConfigError::invalid_type(
                self.origin,
                Unexpected::Unit,
                "a boolean",
            )),
            ValueKind::Table(_) => Err(ConfigError::invalid_type(
                self.origin,
                Unexpected::Map,
                "a boolean",
            )),
            ValueKind::Array(_) => Err(ConfigError::invalid_type(
                self.origin,
                Unexpected::Seq,
                "a boolean",
            )),
        }
    }

    /// Returns `self` into an i64, if possible.
    // FIXME: Should this not be `try_into_*` ?
    pub fn into_int(self) -> Result<i64> {
        match self.kind {
            ValueKind::Integer(value) => Ok(value),

            ValueKind::String(ref s) => {
                match s.to_lowercase().as_ref() {
                    "true" | "on" | "yes" => Ok(1),
                    "false" | "off" | "no" => Ok(0),
                    _ => {
                        s.parse().map_err(|_| {
                            // Unexpected string
                            ConfigError::invalid_type(
                                self.origin.clone(),
                                Unexpected::Str(s.clone()),
                                "an integer",
                            )
                        })
                    }
                }
            }

            ValueKind::Boolean(value) => Ok(if value { 1 } else { 0 }),
            ValueKind::Float(value) => Ok(value.round() as i64),

            // Unexpected type
            ValueKind::Nil => Err(ConfigError::invalid_type(
                self.origin,
                Unexpected::Unit,
                "an integer",
            )),
            ValueKind::Table(_) => Err(ConfigError::invalid_type(
                self.origin,
                Unexpected::Map,
                "an integer",
            )),
            ValueKind::Array(_) => Err(ConfigError::invalid_type(
                self.origin,
                Unexpected::Seq,
                "an integer",
            )),
        }
    }

    /// Returns `self` into a f64, if possible.
    // FIXME: Should this not be `try_into_*` ?
    pub fn into_float(self) -> Result<f64> {
        match self.kind {
            ValueKind::Float(value) => Ok(value),

            ValueKind::String(ref s) => {
                match s.to_lowercase().as_ref() {
                    "true" | "on" | "yes" => Ok(1.0),
                    "false" | "off" | "no" => Ok(0.0),
                    _ => {
                        s.parse().map_err(|_| {
                            // Unexpected string
                            ConfigError::invalid_type(
                                self.origin.clone(),
                                Unexpected::Str(s.clone()),
                                "a floating point",
                            )
                        })
                    }
                }
            }

            ValueKind::Integer(value) => Ok(value as f64),
            ValueKind::Boolean(value) => Ok(if value { 1.0 } else { 0.0 }),

            // Unexpected type
            ValueKind::Nil => Err(ConfigError::invalid_type(
                self.origin,
                Unexpected::Unit,
                "a floating point",
            )),
            ValueKind::Table(_) => Err(ConfigError::invalid_type(
                self.origin,
                Unexpected::Map,
                "a floating point",
            )),
            ValueKind::Array(_) => Err(ConfigError::invalid_type(
                self.origin,
                Unexpected::Seq,
                "a floating point",
            )),
        }
    }

    /// Returns `self` into a string, if possible.
    // FIXME: Should this not be `try_into_*` ?
    pub fn into_string(self) -> Result<String> {
        match self.kind {
            ValueKind::String(value) => Ok(value),

            ValueKind::Boolean(value) => Ok(value.to_string()),
            ValueKind::Integer(value) => Ok(value.to_string()),
            ValueKind::Float(value) => Ok(value.to_string()),

            // Cannot convert
            ValueKind::Nil => Err(ConfigError::invalid_type(
                self.origin,
                Unexpected::Unit,
                "a string",
            )),
            ValueKind::Table(_) => Err(ConfigError::invalid_type(
                self.origin,
                Unexpected::Map,
                "a string",
            )),
            ValueKind::Array(_) => Err(ConfigError::invalid_type(
                self.origin,
                Unexpected::Seq,
                "a string",
            )),
        }
    }

    /// Returns `self` into an array, if possible
    // FIXME: Should this not be `try_into_*` ?
    pub fn into_array(self) -> Result<Vec<Value>> {
        match self.kind {
            ValueKind::Array(value) => Ok(value),

            // Cannot convert
            ValueKind::Float(value) => Err(ConfigError::invalid_type(
                self.origin,
                Unexpected::Float(value),
                "an array",
            )),
            ValueKind::String(value) => Err(ConfigError::invalid_type(
                self.origin,
                Unexpected::Str(value),
                "an array",
            )),
            ValueKind::Integer(value) => Err(ConfigError::invalid_type(
                self.origin,
                Unexpected::Integer(value),
                "an array",
            )),
            ValueKind::Boolean(value) => Err(ConfigError::invalid_type(
                self.origin,
                Unexpected::Bool(value),
                "an array",
            )),
            ValueKind::Nil => Err(ConfigError::invalid_type(
                self.origin,
                Unexpected::Unit,
                "an array",
            )),
            ValueKind::Table(_) => Err(ConfigError::invalid_type(
                self.origin,
                Unexpected::Map,
                "an array",
            )),
        }
    }

    /// If the `Value` is a Table, returns the associated Map.
    // FIXME: Should this not be `try_into_*` ?
    pub fn into_table(self) -> Result<HashMap<String, Value>> {
        match self.kind {
            ValueKind::Table(value) => Ok(value),

            // Cannot convert
            ValueKind::Float(value) => Err(ConfigError::invalid_type(
                self.origin,
                Unexpected::Float(value),
                "a map",
            )),
            ValueKind::String(value) => Err(ConfigError::invalid_type(
                self.origin,
                Unexpected::Str(value),
                "a map",
            )),
            ValueKind::Integer(value) => Err(ConfigError::invalid_type(
                self.origin,
                Unexpected::Integer(value),
                "a map",
            )),
            ValueKind::Boolean(value) => Err(ConfigError::invalid_type(
                self.origin,
                Unexpected::Bool(value),
                "a map",
            )),
            ValueKind::Nil => Err(ConfigError::invalid_type(
                self.origin,
                Unexpected::Unit,
                "a map",
            )),
            ValueKind::Array(_) => Err(ConfigError::invalid_type(
                self.origin,
                Unexpected::Seq,
                "a map",
            )),
        }
    }
}

impl<'de> Deserialize<'de> for Value {
    #[inline]
    fn deserialize<D>(deserializer: D) -> ::std::result::Result<Value, D::Error>
    where
        D: Deserializer<'de>,
    {
        struct ValueVisitor;

        impl<'de> Visitor<'de> for ValueVisitor {
            type Value = Value;

            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
                formatter.write_str("any valid configuration value")
            }

            #[inline]
            fn visit_bool<E>(self, value: bool) -> ::std::result::Result<Value, E> {
                Ok(value.into())
            }

            #[inline]
            fn visit_i8<E>(self, value: i8) -> ::std::result::Result<Value, E> {
                Ok((value as i64).into())
            }

            #[inline]
            fn visit_i16<E>(self, value: i16) -> ::std::result::Result<Value, E> {
                Ok((value as i64).into())
            }

            #[inline]
            fn visit_i32<E>(self, value: i32) -> ::std::result::Result<Value, E> {
                Ok((value as i64).into())
            }

            #[inline]
            fn visit_i64<E>(self, value: i64) -> ::std::result::Result<Value, E> {
                Ok(value.into())
            }

            #[inline]
            fn visit_u8<E>(self, value: u8) -> ::std::result::Result<Value, E> {
                Ok((value as i64).into())
            }

            #[inline]
            fn visit_u16<E>(self, value: u16) -> ::std::result::Result<Value, E> {
                Ok((value as i64).into())
            }

            #[inline]
            fn visit_u32<E>(self, value: u32) -> ::std::result::Result<Value, E> {
                Ok((value as i64).into())
            }

            #[inline]
            fn visit_u64<E>(self, value: u64) -> ::std::result::Result<Value, E> {
                // FIXME: This is bad
                Ok((value as i64).into())
            }

            #[inline]
            fn visit_f64<E>(self, value: f64) -> ::std::result::Result<Value, E> {
                Ok(value.into())
            }

            #[inline]
            fn visit_str<E>(self, value: &str) -> ::std::result::Result<Value, E>
            where
                E: ::serde::de::Error,
            {
                self.visit_string(String::from(value))
            }

            #[inline]
            fn visit_string<E>(self, value: String) -> ::std::result::Result<Value, E> {
                Ok(value.into())
            }

            #[inline]
            fn visit_none<E>(self) -> ::std::result::Result<Value, E> {
                Ok(Value::new(None, ValueKind::Nil))
            }

            #[inline]
            fn visit_some<D>(self, deserializer: D) -> ::std::result::Result<Value, D::Error>
            where
                D: Deserializer<'de>,
            {
                Deserialize::deserialize(deserializer)
            }

            #[inline]
            fn visit_unit<E>(self) -> ::std::result::Result<Value, E> {
                Ok(Value::new(None, ValueKind::Nil))
            }

            #[inline]
            fn visit_seq<V>(self, mut visitor: V) -> ::std::result::Result<Value, V::Error>
            where
                V: ::serde::de::SeqAccess<'de>,
            {
                let mut vec = Array::new();

                while let Some(elem) = visitor.next_element()? {
                    vec.push(elem);
                }

                Ok(vec.into())
            }

            fn visit_map<V>(self, mut visitor: V) -> ::std::result::Result<Value, V::Error>
            where
                V: ::serde::de::MapAccess<'de>,
            {
                let mut values = Table::new();

                while let Some((key, value)) = visitor.next_entry()? {
                    values.insert(key, value);
                }

                Ok(values.into())
            }
        }

        deserializer.deserialize_any(ValueVisitor)
    }
}

impl<T> From<T> for Value
where
    T: Into<ValueKind>,
{
    fn from(value: T) -> Self {
        Value {
            origin: None,
            kind: value.into(),
        }
    }
}

impl Display for Value {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.kind)
    }
}
<commit_msg>Simplify impl of From<HashMap<String, T>> for ValueKind<commit_after>use error::*;
use serde::de::{Deserialize, Deserializer, Visitor};
use std::collections::HashMap;
use std::fmt;
use std::fmt::Display;

/// Underlying kind of the configuration value.
#[derive(Debug, Clone, PartialEq)]
pub enum ValueKind {
    Nil,
    Boolean(bool),
    Integer(i64),
    Float(f64),
    String(String),
    Table(Table),
    Array(Array),
}

pub type Array = Vec<Value>;
pub type Table = HashMap<String, Value>;

impl Default for ValueKind {
    fn default() -> Self {
        ValueKind::Nil
    }
}

impl<T> From<Option<T>> for ValueKind
where
    T: Into<ValueKind>,
{
    fn from(value: Option<T>) -> Self {
        match value {
            Some(value) => value.into(),
            None => ValueKind::Nil,
        }
    }
}

impl From<String> for ValueKind {
    fn from(value: String) -> Self {
        ValueKind::String(value)
    }
}

impl<'a> From<&'a str> for ValueKind {
    fn from(value: &'a str) -> Self {
        ValueKind::String(value.into())
    }
}

impl From<i64> for ValueKind {
    fn from(value: i64) -> Self {
        ValueKind::Integer(value)
    }
}

impl From<f64> for ValueKind {
    fn from(value: f64) -> Self {
        ValueKind::Float(value)
    }
}

impl From<bool> for ValueKind {
    fn from(value: bool) -> Self {
        ValueKind::Boolean(value)
    }
}

impl<T> From<HashMap<String, T>> for ValueKind
where
    T: Into<Value>,
{
    fn from(values: HashMap<String, T>) -> Self {
        let t = values.into_iter().map(|(k, v)| (k, v.into())).collect();
        ValueKind::Table(t)
    }
}

impl<T> From<Vec<T>> for ValueKind
where
    T: Into<Value>,
{
    fn from(values: Vec<T>) -> Self {
        let mut l = Vec::new();

        for v in values {
            l.push(v.into());
        }

        ValueKind::Array(l)
    }
}

impl Display for ValueKind {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            ValueKind::String(ref value) => write!(f, "{}", value),
            ValueKind::Boolean(value) => write!(f, "{}", value),
            ValueKind::Integer(value) => write!(f, "{}", value),
            ValueKind::Float(value) => write!(f, "{}", value),
            ValueKind::Nil => write!(f, "nil"),

            // TODO: Figure out a nice Display for these
            ValueKind::Table(ref table) => write!(f, "{:?}", table),
            ValueKind::Array(ref array) => write!(f, "{:?}", array),
        }
    }
}

/// A configuration value.
#[derive(Default, Debug, Clone, PartialEq)]
pub struct Value {
    /// A description of the original location of the value.
    ///
    /// A Value originating from a File might contain:
    /// ```text
    /// Settings.toml
    /// ```
    ///
    /// A Value originating from the environment would contain:
    /// ```text
    /// the envrionment
    /// ```
    ///
    /// A Value originating from a remote source might contain:
    /// ```text
    /// etcd+http://127.0.0.1:2379
    /// ```
    origin: Option<String>,

    /// Underlying kind of the configuration value.
    pub kind: ValueKind,
}

impl Value {
    /// Create a new value instance that will remember its source uri.
    pub fn new<V>(origin: Option<&String>, kind: V) -> Self
    where
        V: Into<ValueKind>,
    {
        Value {
            origin: origin.cloned(),
            kind: kind.into(),
        }
    }

    /// Attempt to deserialize this value into the requested type.
    pub fn try_into<'de, T: Deserialize<'de>>(self) -> Result<T> {
        T::deserialize(self)
    }

    /// Returns `self` as a bool, if possible.
    // FIXME: Should this not be `try_into_*` ?
    pub fn into_bool(self) -> Result<bool> {
        match self.kind {
            ValueKind::Boolean(value) => Ok(value),
            ValueKind::Integer(value) => Ok(value != 0),
            ValueKind::Float(value) => Ok(value != 0.0),

            ValueKind::String(ref value) => {
                match value.to_lowercase().as_ref() {
                    "1" | "true" | "on" | "yes" => Ok(true),
                    "0" | "false" | "off" | "no" => Ok(false),

                    // Unexpected string value
                    s => Err(ConfigError::invalid_type(
                        self.origin.clone(),
                        Unexpected::Str(s.into()),
                        "a boolean",
                    )),
                }
            }

            // Unexpected type
            ValueKind::Nil => Err(ConfigError::invalid_type(
                self.origin,
                Unexpected::Unit,
                "a boolean",
            )),
            ValueKind::Table(_) => Err(ConfigError::invalid_type(
                self.origin,
                Unexpected::Map,
                "a boolean",
            )),
            ValueKind::Array(_) => Err(ConfigError::invalid_type(
                self.origin,
                Unexpected::Seq,
                "a boolean",
            )),
        }
    }

    /// Returns `self` into an i64, if possible.
    // FIXME: Should this not be `try_into_*` ?
    pub fn into_int(self) -> Result<i64> {
        match self.kind {
            ValueKind::Integer(value) => Ok(value),

            ValueKind::String(ref s) => {
                match s.to_lowercase().as_ref() {
                    "true" | "on" | "yes" => Ok(1),
                    "false" | "off" | "no" => Ok(0),
                    _ => {
                        s.parse().map_err(|_| {
                            // Unexpected string
                            ConfigError::invalid_type(
                                self.origin.clone(),
                                Unexpected::Str(s.clone()),
                                "an integer",
                            )
                        })
                    }
                }
            }

            ValueKind::Boolean(value) => Ok(if value { 1 } else { 0 }),
            ValueKind::Float(value) => Ok(value.round() as i64),

            // Unexpected type
            ValueKind::Nil => Err(ConfigError::invalid_type(
                self.origin,
                Unexpected::Unit,
                "an integer",
            )),
            ValueKind::Table(_) => Err(ConfigError::invalid_type(
                self.origin,
                Unexpected::Map,
                "an integer",
            )),
            ValueKind::Array(_) => Err(ConfigError::invalid_type(
                self.origin,
                Unexpected::Seq,
                "an integer",
            )),
        }
    }

    /// Returns `self` into a f64, if possible.
    // FIXME: Should this not be `try_into_*` ?
    pub fn into_float(self) -> Result<f64> {
        match self.kind {
            ValueKind::Float(value) => Ok(value),

            ValueKind::String(ref s) => {
                match s.to_lowercase().as_ref() {
                    "true" | "on" | "yes" => Ok(1.0),
                    "false" | "off" | "no" => Ok(0.0),
                    _ => {
                        s.parse().map_err(|_| {
                            // Unexpected string
                            ConfigError::invalid_type(
                                self.origin.clone(),
                                Unexpected::Str(s.clone()),
                                "a floating point",
                            )
                        })
                    }
                }
            }

            ValueKind::Integer(value) => Ok(value as f64),
            ValueKind::Boolean(value) => Ok(if value { 1.0 } else { 0.0 }),

            // Unexpected type
            ValueKind::Nil => Err(ConfigError::invalid_type(
                self.origin,
                Unexpected::Unit,
                "a floating point",
            )),
            ValueKind::Table(_) => Err(ConfigError::invalid_type(
                self.origin,
                Unexpected::Map,
                "a floating point",
            )),
            ValueKind::Array(_) => Err(ConfigError::invalid_type(
                self.origin,
                Unexpected::Seq,
                "a floating point",
            )),
        }
    }

    /// Returns `self` into a string, if possible.
    // FIXME: Should this not be `try_into_*` ?
    pub fn into_string(self) -> Result<String> {
        match self.kind {
            ValueKind::String(value) => Ok(value),

            ValueKind::Boolean(value) => Ok(value.to_string()),
            ValueKind::Integer(value) => Ok(value.to_string()),
            ValueKind::Float(value) => Ok(value.to_string()),

            // Cannot convert
            ValueKind::Nil => Err(ConfigError::invalid_type(
                self.origin,
                Unexpected::Unit,
                "a string",
            )),
            ValueKind::Table(_) => Err(ConfigError::invalid_type(
                self.origin,
                Unexpected::Map,
                "a string",
            )),
            ValueKind::Array(_) => Err(ConfigError::invalid_type(
                self.origin,
                Unexpected::Seq,
                "a string",
            )),
        }
    }

    /// Returns `self` into an array, if possible
    // FIXME: Should this not be `try_into_*` ?
    pub fn into_array(self) -> Result<Vec<Value>> {
        match self.kind {
            ValueKind::Array(value) => Ok(value),

            // Cannot convert
            ValueKind::Float(value) => Err(ConfigError::invalid_type(
                self.origin,
                Unexpected::Float(value),
                "an array",
            )),
            ValueKind::String(value) => Err(ConfigError::invalid_type(
                self.origin,
                Unexpected::Str(value),
                "an array",
            )),
            ValueKind::Integer(value) => Err(ConfigError::invalid_type(
                self.origin,
                Unexpected::Integer(value),
                "an array",
            )),
            ValueKind::Boolean(value) => Err(ConfigError::invalid_type(
                self.origin,
                Unexpected::Bool(value),
                "an array",
            )),
            ValueKind::Nil => Err(ConfigError::invalid_type(
                self.origin,
                Unexpected::Unit,
                "an array",
            )),
            ValueKind::Table(_) => Err(ConfigError::invalid_type(
                self.origin,
                Unexpected::Map,
                "an array",
            )),
        }
    }

    /// If the `Value` is a Table, returns the associated Map.
    // FIXME: Should this not be `try_into_*` ?
    pub fn into_table(self) -> Result<HashMap<String, Value>> {
        match self.kind {
            ValueKind::Table(value) => Ok(value),

            // Cannot convert
            ValueKind::Float(value) => Err(ConfigError::invalid_type(
                self.origin,
                Unexpected::Float(value),
                "a map",
            )),
            ValueKind::String(value) => Err(ConfigError::invalid_type(
                self.origin,
                Unexpected::Str(value),
                "a map",
            )),
            ValueKind::Integer(value) => Err(ConfigError::invalid_type(
                self.origin,
                Unexpected::Integer(value),
                "a map",
            )),
            ValueKind::Boolean(value) => Err(ConfigError::invalid_type(
                self.origin,
                Unexpected::Bool(value),
                "a map",
            )),
            ValueKind::Nil => Err(ConfigError::invalid_type(
                self.origin,
                Unexpected::Unit,
                "a map",
            )),
            ValueKind::Array(_) => Err(ConfigError::invalid_type(
                self.origin,
                Unexpected::Seq,
                "a map",
            )),
        }
    }
}

impl<'de> Deserialize<'de> for Value {
    #[inline]
    fn deserialize<D>(deserializer: D) -> ::std::result::Result<Value, D::Error>
    where
        D: Deserializer<'de>,
    {
        struct ValueVisitor;

        impl<'de> Visitor<'de> for ValueVisitor {
            type Value = Value;

            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
                formatter.write_str("any valid configuration value")
            }

            #[inline]
            fn visit_bool<E>(self, value: bool) -> ::std::result::Result<Value, E> {
                Ok(value.into())
            }

            #[inline]
            fn visit_i8<E>(self, value: i8) -> ::std::result::Result<Value, E> {
                Ok((value as i64).into())
            }

            #[inline]
            fn visit_i16<E>(self, value: i16) -> ::std::result::Result<Value, E> {
                Ok((value as i64).into())
            }

            #[inline]
            fn visit_i32<E>(self, value: i32) -> ::std::result::Result<Value, E> {
                Ok((value as i64).into())
            }

            #[inline]
            fn visit_i64<E>(self, value: i64) -> ::std::result::Result<Value, E> {
                Ok(value.into())
            }

            #[inline]
            fn visit_u8<E>(self, value: u8) -> ::std::result::Result<Value, E> {
                Ok((value as i64).into())
            }

            #[inline]
            fn visit_u16<E>(self, value: u16) -> ::std::result::Result<Value, E> {
                Ok((value as i64).into())
            }

            #[inline]
            fn visit_u32<E>(self, value: u32) -> ::std::result::Result<Value, E> {
                Ok((value as i64).into())
            }

            #[inline]
            fn visit_u64<E>(self, value: u64) -> ::std::result::Result<Value, E> {
                // FIXME: This is bad
                Ok((value as i64).into())
            }

            #[inline]
            fn visit_f64<E>(self, value: f64) -> ::std::result::Result<Value, E> {
                Ok(value.into())
            }

            #[inline]
            fn visit_str<E>(self, value: &str) -> ::std::result::Result<Value, E>
            where
                E: ::serde::de::Error,
            {
                self.visit_string(String::from(value))
            }

            #[inline]
            fn visit_string<E>(self, value: String) -> ::std::result::Result<Value, E> {
                Ok(value.into())
            }

            #[inline]
            fn visit_none<E>(self) -> ::std::result::Result<Value, E> {
                Ok(Value::new(None, ValueKind::Nil))
            }

            #[inline]
            fn visit_some<D>(self, deserializer: D) -> ::std::result::Result<Value, D::Error>
            where
                D: Deserializer<'de>,
            {
                Deserialize::deserialize(deserializer)
            }

            #[inline]
            fn visit_unit<E>(self) -> ::std::result::Result<Value, E> {
                Ok(Value::new(None, ValueKind::Nil))
            }

            #[inline]
            fn visit_seq<V>(self, mut visitor: V) -> ::std::result::Result<Value, V::Error>
            where
                V: ::serde::de::SeqAccess<'de>,
            {
                let mut vec = Array::new();

                while let Some(elem) = visitor.next_element()? {
                    vec.push(elem);
                }

                Ok(vec.into())
            }

            fn visit_map<V>(self, mut visitor: V) -> ::std::result::Result<Value, V::Error>
            where
                V: ::serde::de::MapAccess<'de>,
            {
                let mut values = Table::new();

                while let Some((key, value)) = visitor.next_entry()? {
                    values.insert(key, value);
                }

                Ok(values.into())
            }
        }

        deserializer.deserialize_any(ValueVisitor)
    }
}

impl<T> From<T> for Value
where
    T: Into<ValueKind>,
{
    fn from(value: T) -> Self {
        Value {
            origin: None,
            kind: value.into(),
        }
    }
}

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


<commit_before>// This file is part of Grust, GObject introspection bindings for Rust
//
// Copyright (C) 2014, 2015  Mikhail Zabaluev <mikhail.zabaluev@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

use boxed;
use boxed::BoxedType;
use enumeration;
use enumeration::{EnumType, IntrospectedEnum};
use gstr::{GStr, OwnedGStr};
use gtype::GType;
use object;
use object::ObjectType;
use types::{gboolean, gchar, gdouble, gfloat, gint, glong, gpointer};
use types::{guchar, guint, gulong};
use util::is_true;

use gobject as ffi;

use std::fmt;
use std::mem;
use std::ops::Deref;

#[unsafe_no_drop_flag]
pub struct Value(ffi::GValue);

impl Drop for Value {
    fn drop(&mut self) {
        if self.as_raw().g_type == 0 {
            return;
        }
        unsafe {
            ffi::g_value_unset(self.as_mut_raw());
        }
    }
}

impl Clone for Value {
    fn clone(&self) -> Value {
        Value(unsafe {
            let mut raw: ffi::GValue = mem::zeroed();
            ffi::g_value_init(&mut raw, self.as_raw().g_type);
            ffi::g_value_copy(self.as_raw(), &mut raw);
            raw
        })
    }
}

impl Value {
    pub fn new(g_type: GType) -> Value {
        Value(unsafe {
            let mut raw: ffi::GValue = mem::zeroed();
            ffi::g_value_init(&mut raw, g_type.to_raw());
            raw
        })
    }

    #[inline]
    fn as_raw(&self) -> &ffi::GValue {
        &self.0
    }

    #[inline]
    unsafe fn as_mut_raw(&mut self) -> &mut ffi::GValue {
        &mut self.0
    }

    #[inline]
    pub fn value_type(&self) -> GType {
        unsafe { GType::from_raw(self.as_raw().g_type) }
    }

    pub fn reset(&mut self) {
        unsafe {
            ffi::g_value_reset(self.as_mut_raw());
        }
    }

    pub fn get_boolean(&self) -> bool {
        is_true(unsafe { ffi::g_value_get_boolean(self.as_raw()) })
    }

    pub fn set_boolean(&mut self, val: bool) {
        unsafe { ffi::g_value_set_boolean(self.as_mut_raw(), val as gboolean) };
    }

    pub fn get_char(&self) -> gchar {
        unsafe { ffi::g_value_get_char(self.as_raw()) }
    }

    pub fn set_char(&mut self, val: gchar) {
        unsafe { ffi::g_value_set_char(self.as_mut_raw(), val) };
    }

    pub fn get_schar(&self) -> i8 {
        unsafe { ffi::g_value_get_schar(self.as_raw()) }
    }

    pub fn set_schar(&mut self, val: i8) {
        unsafe { ffi::g_value_set_schar(self.as_mut_raw(), val) };
    }

    pub fn get_uchar(&self) -> guchar {
        unsafe { ffi::g_value_get_uchar(self.as_raw()) }
    }

    pub fn set_uchar(&mut self, val: guchar) {
        unsafe { ffi::g_value_set_uchar(self.as_mut_raw(), val) };
    }

    pub fn get_int(&self) -> gint {
        unsafe { ffi::g_value_get_int(self.as_raw()) }
    }

    pub fn set_int(&mut self, val: gint) {
        unsafe { ffi::g_value_set_int(self.as_mut_raw(), val) };
    }

    pub fn get_uint(&self) -> guint {
        unsafe { ffi::g_value_get_uint(self.as_raw()) }
    }

    pub fn set_uint(&mut self, val: guint) {
        unsafe { ffi::g_value_set_uint(self.as_mut_raw(), val) };
    }

    pub fn get_long(&self) -> glong {
        unsafe { ffi::g_value_get_long(self.as_raw()) }
    }

    pub fn set_long(&mut self, val: glong) {
        unsafe { ffi::g_value_set_long(self.as_mut_raw(), val) };
    }

    pub fn get_ulong(&self) -> gulong {
        unsafe { ffi::g_value_get_ulong(self.as_raw()) }
    }

    pub fn set_ulong(&mut self, val: gulong) {
        unsafe { ffi::g_value_set_ulong(self.as_mut_raw(), val) };
    }

    pub fn get_int64(&self) -> i64 {
        unsafe { ffi::g_value_get_int64(self.as_raw()) }
    }

    pub fn set_int64(&mut self, val: i64) {
        unsafe { ffi::g_value_set_int64(self.as_mut_raw(), val) };
    }

    pub fn get_uint64(&self) -> u64 {
        unsafe { ffi::g_value_get_uint64(self.as_raw()) }
    }

    pub fn set_uint64(&mut self, val: u64) {
        unsafe { ffi::g_value_set_uint64(self.as_mut_raw(), val) };
    }

    pub fn get_float(&self) -> gfloat {
        unsafe { ffi::g_value_get_float(self.as_raw()) }
    }

    pub fn set_float(&mut self, val: gfloat) {
        unsafe { ffi::g_value_set_float(self.as_mut_raw(), val) };
    }

    pub fn get_double(&self) -> gdouble {
        unsafe { ffi::g_value_get_double(self.as_raw()) }
    }

    pub fn set_double(&mut self, val: gdouble) {
        unsafe { ffi::g_value_set_double(self.as_mut_raw(), val) };
    }

    fn assert_enum_type<T>(&self) where T: EnumType {
        debug_assert!(self.value_type() == enumeration::type_of::<T>(),
                      "GValue does not have the enumeration type {}",
                      enumeration::type_of::<T>().name());
    }

    pub fn get_enum<T>(&self) -> Option<T> where T: EnumType {
        self.assert_enum_type::<T>();
        unsafe {
            let v = ffi::g_value_get_enum(self.as_raw());
            IntrospectedEnum::from_int(v)
        }
    }

    pub fn set_enum<T>(&mut self, val: T) where T: EnumType {
        self.assert_enum_type::<T>();
        unsafe {
            ffi::g_value_set_enum(self.as_mut_raw(), val.to_int());
        }
    }

    pub fn get_string(&self) -> Option<&GStr> {
        unsafe {
            let ptr = ffi::g_value_get_string(self.as_raw());
            if ptr.is_null() {
                return None;
            }
            Some(GStr::from_ptr(ptr))
        }
    }

    pub fn set_string(&mut self, val: &GStr) {
        unsafe { ffi::g_value_set_string(self.as_mut_raw(), val.as_ptr()) }
    }

    pub fn set_static_string(&mut self, val: &'static GStr) {
        unsafe {
            ffi::g_value_set_static_string(self.as_mut_raw(), val.as_ptr())
        }
    }

    pub fn take_string(&mut self, consumed: OwnedGStr) {
        unsafe {
            let ptr = consumed.as_ptr() as *mut gchar;
            ffi::g_value_take_string(self.as_mut_raw(), ptr);
            mem::forget(consumed);
        }
    }

    pub fn get_object<T>(&self) -> Option<&T>
        where T: ObjectType
    {
        assert!(self.value_type() == object::type_of::<T>(),
                "GValue does not have the object type {}",
                object::type_of::<T>().name());
        let p = unsafe { ffi::g_value_get_object(self.as_raw()) as *const T };
        if p.is_null() {
            return None;
        }
        Some(unsafe { mem::copy_lifetime(self, &*p) })
    }

    pub fn set_object<T>(&mut self, val: &T)
        where T: ObjectType
    {
        let p = val as *const T;
        unsafe { ffi::g_value_set_object(self.as_mut_raw(), p as gpointer) }
    }

    fn assert_boxed_type<T>(&self)
        where T: BoxedType
    {
        assert!(self.value_type() == boxed::type_of::<T>(),
                "GValue does not have the boxed type {}",
                boxed::type_of::<T>().name());
    }

    pub fn dup_boxed<T>(&self) -> Option<T>
        where T: BoxedType
    {
        self.assert_boxed_type::<T>();
        let p = unsafe { ffi::g_value_dup_boxed(self.as_raw()) };
        if p.is_null() {
            return None;
        }
        Some(unsafe {
            let boxed: T = BoxedType::from_ptr(p);
            boxed
        })
    }

    pub fn deref_boxed<T>(&self) -> Option<&<T as Deref>::Target>
        where T: BoxedType + Deref
    {
        self.assert_boxed_type::<T>();
        let p = unsafe { ffi::g_value_get_boxed(self.as_raw()) };
        if p.is_null() {
            return None;
        }
        unsafe {
            let boxed: T = BoxedType::from_ptr(p);
            let ret = Some(mem::copy_lifetime(self, &*boxed));

            // Lose the box by "subliming" it back into the raw pointer
            let q = boxed.into_ptr();
            debug_assert!(q == p,
                "BoxedType invariant violation: into_ptr does not reverse from_ptr");

            ret
        }
    }

    pub fn take_boxed<T>(&mut self, val: T) where T: BoxedType {
        unsafe {
            let p = val.into_ptr();
            ffi::g_value_take_boxed(self.as_mut_raw(), p);
        }
    }
}

impl fmt::Debug for Value {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let contents = unsafe {
            let ptr = ffi::g_strdup_value_contents(self.as_raw());
            OwnedGStr::from_ptr(ptr)
        };
        write!(f, "GValue({})", String::from_utf8_lossy(contents.to_bytes()))
    }
}
<commit_msg>value: Make get_enum return Result instead of Option<commit_after>// This file is part of Grust, GObject introspection bindings for Rust
//
// Copyright (C) 2014, 2015  Mikhail Zabaluev <mikhail.zabaluev@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

use boxed;
use boxed::BoxedType;
use enumeration;
use enumeration::{EnumType, IntrospectedEnum};
use gstr::{GStr, OwnedGStr};
use gtype::GType;
use object;
use object::ObjectType;
use types::{gboolean, gchar, gdouble, gfloat, gint, glong, gpointer};
use types::{guchar, guint, gulong};
use util::is_true;

use gobject as ffi;

use std::fmt;
use std::mem;
use std::ops::Deref;

#[unsafe_no_drop_flag]
pub struct Value(ffi::GValue);

impl Drop for Value {
    fn drop(&mut self) {
        if self.as_raw().g_type == 0 {
            return;
        }
        unsafe {
            ffi::g_value_unset(self.as_mut_raw());
        }
    }
}

impl Clone for Value {
    fn clone(&self) -> Value {
        Value(unsafe {
            let mut raw: ffi::GValue = mem::zeroed();
            ffi::g_value_init(&mut raw, self.as_raw().g_type);
            ffi::g_value_copy(self.as_raw(), &mut raw);
            raw
        })
    }
}

impl Value {
    pub fn new(g_type: GType) -> Value {
        Value(unsafe {
            let mut raw: ffi::GValue = mem::zeroed();
            ffi::g_value_init(&mut raw, g_type.to_raw());
            raw
        })
    }

    #[inline]
    fn as_raw(&self) -> &ffi::GValue {
        &self.0
    }

    #[inline]
    unsafe fn as_mut_raw(&mut self) -> &mut ffi::GValue {
        &mut self.0
    }

    #[inline]
    pub fn value_type(&self) -> GType {
        unsafe { GType::from_raw(self.as_raw().g_type) }
    }

    pub fn reset(&mut self) {
        unsafe {
            ffi::g_value_reset(self.as_mut_raw());
        }
    }

    pub fn get_boolean(&self) -> bool {
        is_true(unsafe { ffi::g_value_get_boolean(self.as_raw()) })
    }

    pub fn set_boolean(&mut self, val: bool) {
        unsafe { ffi::g_value_set_boolean(self.as_mut_raw(), val as gboolean) };
    }

    pub fn get_char(&self) -> gchar {
        unsafe { ffi::g_value_get_char(self.as_raw()) }
    }

    pub fn set_char(&mut self, val: gchar) {
        unsafe { ffi::g_value_set_char(self.as_mut_raw(), val) };
    }

    pub fn get_schar(&self) -> i8 {
        unsafe { ffi::g_value_get_schar(self.as_raw()) }
    }

    pub fn set_schar(&mut self, val: i8) {
        unsafe { ffi::g_value_set_schar(self.as_mut_raw(), val) };
    }

    pub fn get_uchar(&self) -> guchar {
        unsafe { ffi::g_value_get_uchar(self.as_raw()) }
    }

    pub fn set_uchar(&mut self, val: guchar) {
        unsafe { ffi::g_value_set_uchar(self.as_mut_raw(), val) };
    }

    pub fn get_int(&self) -> gint {
        unsafe { ffi::g_value_get_int(self.as_raw()) }
    }

    pub fn set_int(&mut self, val: gint) {
        unsafe { ffi::g_value_set_int(self.as_mut_raw(), val) };
    }

    pub fn get_uint(&self) -> guint {
        unsafe { ffi::g_value_get_uint(self.as_raw()) }
    }

    pub fn set_uint(&mut self, val: guint) {
        unsafe { ffi::g_value_set_uint(self.as_mut_raw(), val) };
    }

    pub fn get_long(&self) -> glong {
        unsafe { ffi::g_value_get_long(self.as_raw()) }
    }

    pub fn set_long(&mut self, val: glong) {
        unsafe { ffi::g_value_set_long(self.as_mut_raw(), val) };
    }

    pub fn get_ulong(&self) -> gulong {
        unsafe { ffi::g_value_get_ulong(self.as_raw()) }
    }

    pub fn set_ulong(&mut self, val: gulong) {
        unsafe { ffi::g_value_set_ulong(self.as_mut_raw(), val) };
    }

    pub fn get_int64(&self) -> i64 {
        unsafe { ffi::g_value_get_int64(self.as_raw()) }
    }

    pub fn set_int64(&mut self, val: i64) {
        unsafe { ffi::g_value_set_int64(self.as_mut_raw(), val) };
    }

    pub fn get_uint64(&self) -> u64 {
        unsafe { ffi::g_value_get_uint64(self.as_raw()) }
    }

    pub fn set_uint64(&mut self, val: u64) {
        unsafe { ffi::g_value_set_uint64(self.as_mut_raw(), val) };
    }

    pub fn get_float(&self) -> gfloat {
        unsafe { ffi::g_value_get_float(self.as_raw()) }
    }

    pub fn set_float(&mut self, val: gfloat) {
        unsafe { ffi::g_value_set_float(self.as_mut_raw(), val) };
    }

    pub fn get_double(&self) -> gdouble {
        unsafe { ffi::g_value_get_double(self.as_raw()) }
    }

    pub fn set_double(&mut self, val: gdouble) {
        unsafe { ffi::g_value_set_double(self.as_mut_raw(), val) };
    }

    fn assert_enum_type<T>(&self) where T: EnumType {
        debug_assert!(self.value_type() == enumeration::type_of::<T>(),
                      "GValue does not have the enumeration type {}",
                      enumeration::type_of::<T>().name());
    }

    pub fn get_enum<T>(&self) -> Result<T, gint> where T: EnumType {
        self.assert_enum_type::<T>();
        unsafe {
            let v = ffi::g_value_get_enum(self.as_raw());
            IntrospectedEnum::from_int(v).ok_or(v)
        }
    }

    pub fn set_enum<T>(&mut self, val: T) where T: EnumType {
        self.assert_enum_type::<T>();
        unsafe {
            ffi::g_value_set_enum(self.as_mut_raw(), val.to_int());
        }
    }

    pub fn get_string(&self) -> Option<&GStr> {
        unsafe {
            let ptr = ffi::g_value_get_string(self.as_raw());
            if ptr.is_null() {
                return None;
            }
            Some(GStr::from_ptr(ptr))
        }
    }

    pub fn set_string(&mut self, val: &GStr) {
        unsafe { ffi::g_value_set_string(self.as_mut_raw(), val.as_ptr()) }
    }

    pub fn set_static_string(&mut self, val: &'static GStr) {
        unsafe {
            ffi::g_value_set_static_string(self.as_mut_raw(), val.as_ptr())
        }
    }

    pub fn take_string(&mut self, consumed: OwnedGStr) {
        unsafe {
            let ptr = consumed.as_ptr() as *mut gchar;
            ffi::g_value_take_string(self.as_mut_raw(), ptr);
            mem::forget(consumed);
        }
    }

    pub fn get_object<T>(&self) -> Option<&T>
        where T: ObjectType
    {
        assert!(self.value_type() == object::type_of::<T>(),
                "GValue does not have the object type {}",
                object::type_of::<T>().name());
        let p = unsafe { ffi::g_value_get_object(self.as_raw()) as *const T };
        if p.is_null() {
            return None;
        }
        Some(unsafe { mem::copy_lifetime(self, &*p) })
    }

    pub fn set_object<T>(&mut self, val: &T)
        where T: ObjectType
    {
        let p = val as *const T;
        unsafe { ffi::g_value_set_object(self.as_mut_raw(), p as gpointer) }
    }

    fn assert_boxed_type<T>(&self)
        where T: BoxedType
    {
        assert!(self.value_type() == boxed::type_of::<T>(),
                "GValue does not have the boxed type {}",
                boxed::type_of::<T>().name());
    }

    pub fn dup_boxed<T>(&self) -> Option<T>
        where T: BoxedType
    {
        self.assert_boxed_type::<T>();
        let p = unsafe { ffi::g_value_dup_boxed(self.as_raw()) };
        if p.is_null() {
            return None;
        }
        Some(unsafe {
            let boxed: T = BoxedType::from_ptr(p);
            boxed
        })
    }

    pub fn deref_boxed<T>(&self) -> Option<&<T as Deref>::Target>
        where T: BoxedType + Deref
    {
        self.assert_boxed_type::<T>();
        let p = unsafe { ffi::g_value_get_boxed(self.as_raw()) };
        if p.is_null() {
            return None;
        }
        unsafe {
            let boxed: T = BoxedType::from_ptr(p);
            let ret = Some(mem::copy_lifetime(self, &*boxed));

            // Lose the box by "subliming" it back into the raw pointer
            let q = boxed.into_ptr();
            debug_assert!(q == p,
                "BoxedType invariant violation: into_ptr does not reverse from_ptr");

            ret
        }
    }

    pub fn take_boxed<T>(&mut self, val: T) where T: BoxedType {
        unsafe {
            let p = val.into_ptr();
            ffi::g_value_take_boxed(self.as_mut_raw(), p);
        }
    }
}

impl fmt::Debug for Value {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let contents = unsafe {
            let ptr = ffi::g_strdup_value_contents(self.as_raw());
            OwnedGStr::from_ptr(ptr)
        };
        write!(f, "GValue({})", String::from_utf8_lossy(contents.to_bytes()))
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2015 T. Okubo
// This file is part of vlc-rs.
// Licensed under the MIT license, see the LICENSE file.

use sys;
use ::{Instance, EventManager};
use ::enums::{State, Meta, TrackType};
use ::tools::{to_cstr, from_cstr, path_to_cstr};
use std::path::Path;

pub struct Media {
    pub(crate) ptr: *mut sys::libvlc_media_t,
}

impl Media {
    /// Create a media with a certain given media resource location, for instance a valid URL.
    pub fn new_location(instance: &Instance, mrl: &str) -> Option<Media> {
        let cstr = to_cstr(mrl);

        unsafe{
            let p = sys::libvlc_media_new_location(instance.ptr, cstr.as_ptr());
            if p.is_null() {
                return None;
            }

            Some(Media{ptr: p})
        }
    }

    /// Create a media for a certain file path.
    pub fn new_path<T: AsRef<Path>>(instance: &Instance, path: T) -> Option<Media> {
        let cstr = match path_to_cstr(path.as_ref()) {
            Ok(s) => s,
            Err(_) => { return None; },
        };

        unsafe{
            let p = sys::libvlc_media_new_path(instance.ptr, cstr.as_ptr());
            if p.is_null() {
                return None;
            }

            Some(Media{ptr: p})
        }
    }

    pub fn new_fd(instance: &Instance, fd: i32) -> Option<Media> {
        unsafe{
            let p = sys::libvlc_media_new_fd(instance.ptr, fd);
            if p.is_null() {
                return None;
            }

            Some(Media{ptr: p})
        }
    }

    pub fn mrl(&self) -> Option<String> {
        unsafe{
            let p_str = sys::libvlc_media_get_mrl(self.ptr);
            let s = from_cstr(p_str);
            sys::libvlc_free(p_str as *mut ::libc::c_void);
            s
        }
    }

    pub fn event_manager<'a>(&'a self) -> EventManager<'a> {
        unsafe{
            let p = sys::libvlc_media_event_manager(self.ptr);
            assert!(!p.is_null());
            EventManager{ptr: p, _phantomdata: ::std::marker::PhantomData}
        }
    }

    /// Read the meta of the media.
    /// If the media has not yet been parsed this will return None.
    pub fn get_meta(&self, meta: Meta) -> Option<String> {
        unsafe{
            let p_str = sys::libvlc_media_get_meta(self.ptr, meta);
            let s = from_cstr(p_str);
            sys::libvlc_free(p_str as *mut ::libc::c_void);
            s
        }
    }

    /// Set the meta of the media.
    /// (This function will not save the meta, call save_meta in order to save the meta)
    pub fn set_meta(&self, meta: Meta, value: &str) {
        unsafe{
            sys::libvlc_media_set_meta(self.ptr, meta, to_cstr(value).as_ptr());
        }
    }

    /// Save the meta previously set.
    pub fn save_meta(&self) -> bool {
        if unsafe{ sys::libvlc_media_save_meta(self.ptr) } == 0 { false }else{ true }
    }

    /// Get current state of media descriptor object.
    pub fn state(&self) -> State {
        unsafe{ sys::libvlc_media_get_state(self.ptr) }
    }

    /// Get duration (in ms) of media descriptor object item.
    pub fn duration(&self) -> Option<i64> {
        let time = unsafe{
            sys::libvlc_media_get_duration(self.ptr)
        };
        if time != -1 { Some(time) }else{ None }
    }

    /// Parse a media.
    pub fn parse(&self) {
        unsafe{ sys::libvlc_media_parse(self.ptr) };
    }

    /// Parse a media.
    pub fn parse_async(&self) {
        unsafe{ sys::libvlc_media_parse_async(self.ptr) };
    }

    /// Get Parsed status for media descriptor object.
    pub fn is_parsed(&self) -> bool {
        if unsafe{ sys::libvlc_media_is_parsed(self.ptr) } == 0 { false }else{ true }
    }

    pub fn tracks(&self) -> Option<Vec<MediaTrack>> {
        unsafe{
            let mut p_track: *mut *mut sys::libvlc_media_track_t = ::std::ptr::null_mut();
            let n = sys::libvlc_media_tracks_get(self.ptr, &mut p_track);
            if n == 0 {
                return None;
            }

            let mut track = Vec::new();

            for i in 0..n {
                let p = p_track.offset(i as isize);
                let type_specific_data = match (**p).i_type {
                    TrackType::Audio => {
                        let audio = (**p).audio();
                        MediaTrackUnion::Audio(AudioTrack{
                            channels: (*audio).i_channels,
                            rate:     (*audio).i_rate,
                        })
                    },
                    TrackType::Video => {
                        let video = (**p).video();
                        MediaTrackUnion::Video(VideoTrack{
                            height:         (*video).i_height,
                            width:          (*video).i_width,
                            sar_num:        (*video).i_sar_num,
                            sar_den:        (*video).i_sar_den,
                            frame_rate_num: (*video).i_frame_rate_num,
                            frame_rate_den: (*video).i_frame_rate_den,
                        })
                    },
                    TrackType::Text => {
                        let subtitle = (**p).subtitle();
                        MediaTrackUnion::Subtitle(SubtitleTrack{
                            encoding: from_cstr((*subtitle).psz_encoding)
                        })
                    },
                    TrackType::Unknown => MediaTrackUnion::None,
                };
                track.push(MediaTrack{
                    codec:              (**p).i_codec,
                    original_fourcc:    (**p).i_original_fourcc,
                    id:                 (**p).i_id,
                    track_type:         (**p).i_type,
                    profile:            (**p).i_profile,
                    level:              (**p).i_level,
                    bitrate:            (**p).i_bitrate,
                    language:           from_cstr((**p).psz_language),
                    description:        from_cstr((**p).psz_description),
                    type_specific_data: type_specific_data,
                });
            }

            sys::libvlc_media_tracks_release(p_track, n);
            Some(track)
        }
    }

    /// Returns raw pointer
    pub fn raw(&self) -> *mut sys::libvlc_media_t {
        self.ptr
    }
}

impl Drop for Media {
    fn drop(&mut self) {
        unsafe{ sys::libvlc_media_release(self.ptr) };
    }
}

#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub struct MediaTrack {
    pub codec: u32,
    pub original_fourcc: u32,
    pub id: i32,
    pub track_type: TrackType,
    pub profile: i32,
    pub level: i32,
    pub bitrate: u32,
    pub language: Option<String>,
    pub description: Option<String>,
    pub type_specific_data: MediaTrackUnion,
}

#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub enum MediaTrackUnion {
    Audio(AudioTrack), Video(VideoTrack), Subtitle(SubtitleTrack), None,
}

#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub struct AudioTrack {
    pub channels: u32,
    pub rate: u32,
}

#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub struct VideoTrack {
    pub height: u32,
    pub width: u32,
    pub sar_num: u32,
    pub sar_den: u32,
    pub frame_rate_num: u32,
    pub frame_rate_den: u32,
}

#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub struct SubtitleTrack {
    pub encoding: Option<String>,
}

<commit_msg>media: fix crate path<commit_after>// Copyright (c) 2015 T. Okubo
// This file is part of vlc-rs.
// Licensed under the MIT license, see the LICENSE file.

use crate::sys;
use crate::{Instance, EventManager};
use crate::enums::{State, Meta, TrackType};
use crate::tools::{to_cstr, from_cstr, path_to_cstr};
use std::path::Path;

pub struct Media {
    pub(crate) ptr: *mut sys::libvlc_media_t,
}

impl Media {
    /// Create a media with a certain given media resource location, for instance a valid URL.
    pub fn new_location(instance: &Instance, mrl: &str) -> Option<Media> {
        let cstr = to_cstr(mrl);

        unsafe{
            let p = sys::libvlc_media_new_location(instance.ptr, cstr.as_ptr());
            if p.is_null() {
                return None;
            }

            Some(Media{ptr: p})
        }
    }

    /// Create a media for a certain file path.
    pub fn new_path<T: AsRef<Path>>(instance: &Instance, path: T) -> Option<Media> {
        let cstr = match path_to_cstr(path.as_ref()) {
            Ok(s) => s,
            Err(_) => { return None; },
        };

        unsafe{
            let p = sys::libvlc_media_new_path(instance.ptr, cstr.as_ptr());
            if p.is_null() {
                return None;
            }

            Some(Media{ptr: p})
        }
    }

    pub fn new_fd(instance: &Instance, fd: i32) -> Option<Media> {
        unsafe{
            let p = sys::libvlc_media_new_fd(instance.ptr, fd);
            if p.is_null() {
                return None;
            }

            Some(Media{ptr: p})
        }
    }

    pub fn mrl(&self) -> Option<String> {
        unsafe{
            let p_str = sys::libvlc_media_get_mrl(self.ptr);
            let s = from_cstr(p_str);
            sys::libvlc_free(p_str as *mut ::libc::c_void);
            s
        }
    }

    pub fn event_manager<'a>(&'a self) -> EventManager<'a> {
        unsafe{
            let p = sys::libvlc_media_event_manager(self.ptr);
            assert!(!p.is_null());
            EventManager{ptr: p, _phantomdata: ::std::marker::PhantomData}
        }
    }

    /// Read the meta of the media.
    /// If the media has not yet been parsed this will return None.
    pub fn get_meta(&self, meta: Meta) -> Option<String> {
        unsafe{
            let p_str = sys::libvlc_media_get_meta(self.ptr, meta);
            let s = from_cstr(p_str);
            sys::libvlc_free(p_str as *mut ::libc::c_void);
            s
        }
    }

    /// Set the meta of the media.
    /// (This function will not save the meta, call save_meta in order to save the meta)
    pub fn set_meta(&self, meta: Meta, value: &str) {
        unsafe{
            sys::libvlc_media_set_meta(self.ptr, meta, to_cstr(value).as_ptr());
        }
    }

    /// Save the meta previously set.
    pub fn save_meta(&self) -> bool {
        if unsafe{ sys::libvlc_media_save_meta(self.ptr) } == 0 { false }else{ true }
    }

    /// Get current state of media descriptor object.
    pub fn state(&self) -> State {
        unsafe{ sys::libvlc_media_get_state(self.ptr) }
    }

    /// Get duration (in ms) of media descriptor object item.
    pub fn duration(&self) -> Option<i64> {
        let time = unsafe{
            sys::libvlc_media_get_duration(self.ptr)
        };
        if time != -1 { Some(time) }else{ None }
    }

    /// Parse a media.
    pub fn parse(&self) {
        unsafe{ sys::libvlc_media_parse(self.ptr) };
    }

    /// Parse a media.
    pub fn parse_async(&self) {
        unsafe{ sys::libvlc_media_parse_async(self.ptr) };
    }

    /// Get Parsed status for media descriptor object.
    pub fn is_parsed(&self) -> bool {
        if unsafe{ sys::libvlc_media_is_parsed(self.ptr) } == 0 { false }else{ true }
    }

    pub fn tracks(&self) -> Option<Vec<MediaTrack>> {
        unsafe{
            let mut p_track: *mut *mut sys::libvlc_media_track_t = ::std::ptr::null_mut();
            let n = sys::libvlc_media_tracks_get(self.ptr, &mut p_track);
            if n == 0 {
                return None;
            }

            let mut track = Vec::new();

            for i in 0..n {
                let p = p_track.offset(i as isize);
                let type_specific_data = match (**p).i_type {
                    TrackType::Audio => {
                        let audio = (**p).audio();
                        MediaTrackUnion::Audio(AudioTrack{
                            channels: (*audio).i_channels,
                            rate:     (*audio).i_rate,
                        })
                    },
                    TrackType::Video => {
                        let video = (**p).video();
                        MediaTrackUnion::Video(VideoTrack{
                            height:         (*video).i_height,
                            width:          (*video).i_width,
                            sar_num:        (*video).i_sar_num,
                            sar_den:        (*video).i_sar_den,
                            frame_rate_num: (*video).i_frame_rate_num,
                            frame_rate_den: (*video).i_frame_rate_den,
                        })
                    },
                    TrackType::Text => {
                        let subtitle = (**p).subtitle();
                        MediaTrackUnion::Subtitle(SubtitleTrack{
                            encoding: from_cstr((*subtitle).psz_encoding)
                        })
                    },
                    TrackType::Unknown => MediaTrackUnion::None,
                };
                track.push(MediaTrack{
                    codec:              (**p).i_codec,
                    original_fourcc:    (**p).i_original_fourcc,
                    id:                 (**p).i_id,
                    track_type:         (**p).i_type,
                    profile:            (**p).i_profile,
                    level:              (**p).i_level,
                    bitrate:            (**p).i_bitrate,
                    language:           from_cstr((**p).psz_language),
                    description:        from_cstr((**p).psz_description),
                    type_specific_data: type_specific_data,
                });
            }

            sys::libvlc_media_tracks_release(p_track, n);
            Some(track)
        }
    }

    /// Returns raw pointer
    pub fn raw(&self) -> *mut sys::libvlc_media_t {
        self.ptr
    }
}

impl Drop for Media {
    fn drop(&mut self) {
        unsafe{ sys::libvlc_media_release(self.ptr) };
    }
}

#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub struct MediaTrack {
    pub codec: u32,
    pub original_fourcc: u32,
    pub id: i32,
    pub track_type: TrackType,
    pub profile: i32,
    pub level: i32,
    pub bitrate: u32,
    pub language: Option<String>,
    pub description: Option<String>,
    pub type_specific_data: MediaTrackUnion,
}

#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub enum MediaTrackUnion {
    Audio(AudioTrack), Video(VideoTrack), Subtitle(SubtitleTrack), None,
}

#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub struct AudioTrack {
    pub channels: u32,
    pub rate: u32,
}

#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub struct VideoTrack {
    pub height: u32,
    pub width: u32,
    pub sar_num: u32,
    pub sar_den: u32,
    pub frame_rate_num: u32,
    pub frame_rate_den: u32,
}

#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub struct SubtitleTrack {
    pub encoding: Option<String>,
}

<|endoftext|>


<commit_before>/*
 * Copyright (C) 2017 Ryan Huang
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

pub struct Model {
    pub board: Board,
    pub turn: Turn,
    pub white_pieces: u32,
    pub white_hexes: u32,
    pub black_pieces: u32,
    pub black_hexes: u32,
    pub selected_piece: Option<FieldCoord>,
}

impl Model {
    pub fn new() -> Model {
        Model {
            board: Board::new(),
            turn: Turn::White,
            white_pieces: 18,
            white_hexes: 0,
            black_pieces: 18,
            black_hexes: 0,
            selected_piece: None,
        }
    }
}

pub enum Turn {
    White,
    Black,
}

impl Turn {
    pub fn switch_turns(&mut self) {
        *self = match *self {
            Turn::White => Turn::Black,
            Turn::Black => Turn::White,
        }
    }
}

pub struct Board {
    /*
                ____
               /    \
          ____/  +y  \____
         /    \      /    \
        /      \____/  +x  \
        \      /    \      /
         \____/      \____/
         /    \      /    \
        /  -x  \____/      \
        \      /    \      /
         \____/  -y  \____/
              \      /
               \____/

        The hex board uses an axial coordinate system with (0, 0) at the center. The x-axis slopes
        up to the right, and the y-axis goes up and down. The board is stored as a dense 2D array,
        because a ragged array won't work (since each row would have to be a different type).

        See http://www.redblobgames.com/grids/hexagons/#coordinates-axial for more info.
    */
    board: [[Option<Hex>; 5]; 5],
}

// Fields are numbered clockwise from the top. Even indicies are black, odd indicies are white.
type Hex = [Field; 6];

// There's no need to store the color of the piece on this field, since only white pieces can be on
// white fields, and vice versa.
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Field {
    Piece,
    Empty,
}

impl Board {
    #[cfg_attr(rustfmt, rustfmt_skip)]
    /// Create a new board with the "Laurentius" starting position.
    pub fn new() -> Board {
        let mut board = Board {
            board: [[None; 5]; 5]
        };

        // (0, 0) is the only empty hex.
        board.set_hex(&HexCoord::new(0, 0), Some([Field::Empty; 6]));

        // All other hexes have exactly two pieces on them in the starting position.
        let piece_locations = [
            (-2,  2, 0, 4),
            (-2,  1, 0, 3),
            (-2,  0, 3, 5),
            (-1,  2, 1, 4),
            (-1,  1, 0, 4),
            (-1,  0, 3, 5),
            (-1, -1, 2, 5),
            ( 0,  2, 1, 5),
            ( 0,  1, 1, 5),
            ( 0, -1, 2, 4),
            ( 0, -2, 2, 4),
            ( 1,  1, 2, 5),
            ( 1,  0, 0, 2),
            ( 1, -1, 1, 3),
            ( 1, -2, 1, 4),
            ( 2,  0, 0, 2),
            ( 2, -1, 0, 3),
            ( 2, -2, 1, 3),
        ];

        for &(x, y, f1, f2) in &piece_locations {
            let mut hex = [Field::Empty; 6];
            hex[f1] = Field::Piece;
            hex[f2] = Field::Piece;
            board.set_hex(&HexCoord::new(x, y), Some(hex));
        }

        board
    }

    pub fn get_field(&self, coord: &FieldCoord) -> &Field {
        match *self.get_hex(&coord.to_hex()) {
            Some(ref hex) => &hex[coord.f as usize],
            None => panic!(
                "Cannot get field {} on removed hex at {:?}",
                coord.f,
                coord.to_hex()
            ),
        }
    }
    fn set_field(&mut self, coord: &FieldCoord, field: Field) {
        match *self.get_hex(&coord.to_hex()) {
            Some(mut hex) => hex[coord.f as usize] = field,
            None => panic!(
                "Cannot set field {} on removed hex at {:?}",
                coord.f,
                coord.to_hex()
            ),
        }
    }
    /// Return fields that share an edge with the given field. These fields are always the opposite
    /// color of the given field. If all of a piece's edge neighbors are occupied, that piece might
    /// be capturable.
    pub fn get_field_edge_neighbors(&self, coord: &FieldCoord) -> Vec<FieldCoord> {
        let mut neighbors = vec![
            // There are always two edge neighbors on the same hex as the given field
            FieldCoord::new(coord.x, coord.y, (coord.f + 1) % 6),
            FieldCoord::new(coord.x, coord.y, (coord.f + 5) % 6),
        ];

        for (i, neighbor) in coord.to_hex().get_neighbors() {
            if coord.f == i && self.get_hex(&neighbor).is_some() {
                let f = (i + 3) % 6;
                neighbors.push(neighbor.to_field(f));
                break;
            }
        }

        neighbors
    }
    /// Return fields that share a vertex with the given field and have the same color as the given
    /// field. Pieces can move to fields that are vertex neighbors of the field they are on.
    pub fn get_field_vertex_neighbors(&self, coord: &FieldCoord) -> Vec<FieldCoord> {
        // A field's vertex neighbors can be defined as the edge neighbors of its edge neighbors
        let mut neighbors = vec![];
        for neighbor in self.get_field_edge_neighbors(coord) {
            neighbors.append(&mut self.get_field_edge_neighbors(&neighbor));
        }
        // A field is not its own neighbor
        neighbors.into_iter().filter(|n| n != coord).collect()
    }
    pub fn move_piece(&mut self, from: &FieldCoord, to: &FieldCoord) {
        assert_eq!(
            self.get_field(from),
            &Field::Piece,
            "There is no piece at {:?} to move",
            from
        );
        assert!(
            self.get_field_vertex_neighbors(from).contains(to),
            "Cannot move piece at {:?} to non-(vertex neighbor) {:?}",
            from,
            to
        );
        assert_eq!(
            self.get_field(to),
            &Field::Empty,
            "Cannot move piece at {:?} to occupied neighbor {:?}",
            from,
            to
        );

        self.set_field(from, Field::Empty);
        self.set_field(to, Field::Piece);
    }
    pub fn remove_piece(&mut self, coord: &FieldCoord) {
        assert_eq!(
            self.get_field(coord),
            &Field::Piece,
            "There is no piece at {:?} to remove",
            coord
        );
        self.set_field(coord, Field::Empty);
    }

    fn get_hex(&self, coord: &HexCoord) -> &Option<Hex> {
        &self.board[(coord.x + 2) as usize][(coord.y + 2) as usize]
    }
    fn set_hex(&mut self, coord: &HexCoord, hex: Option<Hex>) {
        self.board[(coord.x + 2) as usize][(coord.y + 2) as usize] = hex;
    }
    pub fn get_hex_neighbors(&self, coord: &HexCoord) -> Vec<HexCoord> {
        coord
            .get_neighbors()
            .iter()
            .filter_map(|&(_, c)| match *self.get_hex(&c) {
                Some(_) => Some(c),
                None => None,
            })
            .collect()
    }
    /// Return fields that share an edge with the given hex and are outside of the given hex. If a
    /// hex is removed from the board, pieces occupying that hex's field neighbors might be
    /// capturable.
    pub fn get_hex_field_neighbors(&self, coord: &HexCoord) -> Vec<FieldCoord> {
        let mut neighbors = vec![];

        for (i, neighbor) in coord.get_neighbors() {
            if self.get_hex(&neighbor).is_some() {
                let f = (i + 3) % 6;
                neighbors.push(neighbor.to_field(f));
            }
        }
        neighbors
    }
    /// A hex is removable (and must be removed) if it is empty and is "attached to the board by 3
    /// or less adjacent sides."
    pub fn is_hex_removable(&self, coord: &HexCoord) -> bool {
        match *self.get_hex(coord) {
            Some(hex) => if hex.iter().any(|&f| f == Field::Piece) {
                return false;
            },
            None => panic!("The hex at {:?} has already been removed", coord),
        }

        let neighbor_idxs: Vec<u32> = coord.get_neighbors().iter().map(|&(i, _)| i).collect();
        let neighbor_idxs_slice = neighbor_idxs.as_slice();

        match neighbor_idxs_slice.len() {
            0 => panic!("A hex at {:?} is empty and has no neighbors", coord),
            1 => true,
            2 => {
                let valid_idx_combos = [[0, 1], [1, 2], [2, 3], [3, 4], [4, 5], [0, 5]];
                valid_idx_combos.iter().any(|c| c == neighbor_idxs_slice)
            }
            3 => {
                let valid_idx_combos = [
                    [0, 1, 2],
                    [1, 2, 3],
                    [2, 3, 4],
                    [3, 4, 5],
                    [0, 4, 5],
                    [0, 1, 5],
                ];
                valid_idx_combos.iter().any(|c| c == neighbor_idxs_slice)
            }
            _ => false,
        }
    }
    pub fn remove_hex(&mut self, coord: &HexCoord) {
        assert!(
            self.is_hex_removable(coord),
            "Cannot remove the hex at {:?} which is {:?} and has {} neighbors",
            coord,
            self.get_hex(coord),
            self.get_hex_neighbors(coord).len(),
        );
        self.set_hex(coord, None);
    }
    /// > extant (adj.): Still in existence; not destroyed, lost, or extinct (The Free Dictionary)
    ///
    /// Return the coordinates of the hexes that have not been removed yet.
    pub fn extant_hexes(&self) -> Vec<HexCoord> {
        let mut coords = vec![];

        for x in -2..3 {
            for y in -2..3 {
                let coord = HexCoord::new(x, y);
                if (x + y).abs() <= 2 && self.get_hex(&coord).is_some() {
                    coords.push(coord);
                }
            }
        }
        coords
    }
}

#[derive(Debug, PartialEq)]
pub struct FieldCoord {
    x: i32,
    y: i32,
    f: u32,
}

#[derive(Clone, Copy, Debug)]
pub struct HexCoord {
    x: i32,
    y: i32,
}

impl FieldCoord {
    pub fn new(x: i32, y: i32, f: u32) -> FieldCoord {
        assert!((x + y).abs() <= 2 && x.abs() <= 2 && y.abs() <= 2 && f < 6);
        FieldCoord { x, y, f }
    }
    pub fn to_hex(&self) -> HexCoord {
        HexCoord::new(self.x, self.y)
    }
}

impl HexCoord {
    pub fn new(x: i32, y: i32) -> HexCoord {
        assert!((x + y).abs() <= 2 && x.abs() <= 2 && y.abs() <= 2);
        HexCoord { x, y }
    }
    pub fn to_field(&self, f: u32) -> FieldCoord {
        FieldCoord::new(self.x, self.y, f)
    }
    // We return a Vec of tuples so that get_hex_field_neighbors and is_hex_removable know which
    // neighbors are on which side of the hex. They need to know this for different reasons:
    //   * get_hex_field_neighbors: the index of each neighboring field depends on which hex
    //                              neighbor that field neighbor is on
    //   * is_hex_removable: a hex is removable only if it is "attached to the board by 3 or less
    //                       adjacent sides"
    fn get_neighbors(&self) -> Vec<(u32, HexCoord)> {
        let mut neighbors = vec![];

        if self.y < 2 && (self.x + self.y) != 2 {
            neighbors.push((0, HexCoord::new(self.x, self.y + 1)));
        }
        if (self.x + self.y) != 2 && self.x < 2 {
            neighbors.push((1, HexCoord::new(self.x + 1, self.y)));
        }
        if self.x < 2 && self.y > -2 {
            neighbors.push((2, HexCoord::new(self.x + 1, self.y - 1)));
        }
        if self.y > -2 && (self.x + self.y) > -2 {
            neighbors.push((3, HexCoord::new(self.x, self.y - 1)));
        }
        if (self.x + self.y) > -2 && self.x > -2 {
            neighbors.push((4, HexCoord::new(self.x - 1, self.y)));
        }
        if self.x > -2 && self.y < 2 {
            neighbors.push((5, HexCoord::new(self.x - 1, self.y + 1)));
        }

        neighbors
    }
}
<commit_msg>Add HexCoord/FieldCoord getters and is_valid_coord<commit_after>/*
 * Copyright (C) 2017 Ryan Huang
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

pub struct Model {
    pub board: Board,
    pub turn: Turn,
    pub white_pieces: u32,
    pub white_hexes: u32,
    pub black_pieces: u32,
    pub black_hexes: u32,
    pub selected_piece: Option<FieldCoord>,
}

impl Model {
    pub fn new() -> Model {
        Model {
            board: Board::new(),
            turn: Turn::White,
            white_pieces: 18,
            white_hexes: 0,
            black_pieces: 18,
            black_hexes: 0,
            selected_piece: None,
        }
    }
}

pub enum Turn {
    White,
    Black,
}

impl Turn {
    pub fn switch_turns(&mut self) {
        *self = match *self {
            Turn::White => Turn::Black,
            Turn::Black => Turn::White,
        }
    }
}

pub struct Board {
    /*
                ____
               /    \
          ____/  +y  \____
         /    \      /    \
        /      \____/  +x  \
        \      /    \      /
         \____/      \____/
         /    \      /    \
        /  -x  \____/      \
        \      /    \      /
         \____/  -y  \____/
              \      /
               \____/

        The hex board uses an axial coordinate system with (0, 0) at the center. The x-axis slopes
        up to the right, and the y-axis goes up and down. The board is stored as a dense 2D array,
        because a ragged array won't work (since each row would have to be a different type).

        See http://www.redblobgames.com/grids/hexagons/#coordinates-axial for more info.
    */
    board: [[Option<Hex>; 5]; 5],
}

// Fields are numbered clockwise from the top. Even indicies are black, odd indicies are white.
type Hex = [Field; 6];

// There's no need to store the color of the piece on this field, since only white pieces can be on
// white fields, and vice versa.
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Field {
    Piece,
    Empty,
}

impl Board {
    #[cfg_attr(rustfmt, rustfmt_skip)]
    /// Create a new board with the "Laurentius" starting position.
    pub fn new() -> Board {
        let mut board = Board {
            board: [[None; 5]; 5]
        };

        // (0, 0) is the only empty hex.
        board.set_hex(&HexCoord::new(0, 0), Some([Field::Empty; 6]));

        // All other hexes have exactly two pieces on them in the starting position.
        let piece_locations = [
            (-2,  2, 0, 4),
            (-2,  1, 0, 3),
            (-2,  0, 3, 5),
            (-1,  2, 1, 4),
            (-1,  1, 0, 4),
            (-1,  0, 3, 5),
            (-1, -1, 2, 5),
            ( 0,  2, 1, 5),
            ( 0,  1, 1, 5),
            ( 0, -1, 2, 4),
            ( 0, -2, 2, 4),
            ( 1,  1, 2, 5),
            ( 1,  0, 0, 2),
            ( 1, -1, 1, 3),
            ( 1, -2, 1, 4),
            ( 2,  0, 0, 2),
            ( 2, -1, 0, 3),
            ( 2, -2, 1, 3),
        ];

        for &(x, y, f1, f2) in &piece_locations {
            let mut hex = [Field::Empty; 6];
            hex[f1] = Field::Piece;
            hex[f2] = Field::Piece;
            board.set_hex(&HexCoord::new(x, y), Some(hex));
        }

        board
    }

    pub fn get_field(&self, coord: &FieldCoord) -> &Field {
        match *self.get_hex(&coord.to_hex()) {
            Some(ref hex) => &hex[coord.f as usize],
            None => panic!(
                "Cannot get field {} on removed hex at {:?}",
                coord.f,
                coord.to_hex()
            ),
        }
    }
    fn set_field(&mut self, coord: &FieldCoord, field: Field) {
        match *self.get_hex(&coord.to_hex()) {
            Some(mut hex) => hex[coord.f as usize] = field,
            None => panic!(
                "Cannot set field {} on removed hex at {:?}",
                coord.f,
                coord.to_hex()
            ),
        }
    }
    /// Return fields that share an edge with the given field. These fields are always the opposite
    /// color of the given field. If all of a piece's edge neighbors are occupied, that piece might
    /// be capturable.
    pub fn get_field_edge_neighbors(&self, coord: &FieldCoord) -> Vec<FieldCoord> {
        let mut neighbors = vec![
            // There are always two edge neighbors on the same hex as the given field
            FieldCoord::new(coord.x, coord.y, (coord.f + 1) % 6),
            FieldCoord::new(coord.x, coord.y, (coord.f + 5) % 6),
        ];

        for (i, neighbor) in coord.to_hex().get_neighbors() {
            if coord.f == i && self.get_hex(&neighbor).is_some() {
                let f = (i + 3) % 6;
                neighbors.push(neighbor.to_field(f));
                break;
            }
        }

        neighbors
    }
    /// Return fields that share a vertex with the given field and have the same color as the given
    /// field. Pieces can move to fields that are vertex neighbors of the field they are on.
    pub fn get_field_vertex_neighbors(&self, coord: &FieldCoord) -> Vec<FieldCoord> {
        // A field's vertex neighbors can be defined as the edge neighbors of its edge neighbors
        let mut neighbors = vec![];
        for neighbor in self.get_field_edge_neighbors(coord) {
            neighbors.append(&mut self.get_field_edge_neighbors(&neighbor));
        }
        // A field is not its own neighbor
        neighbors.into_iter().filter(|n| n != coord).collect()
    }
    pub fn move_piece(&mut self, from: &FieldCoord, to: &FieldCoord) {
        assert_eq!(
            self.get_field(from),
            &Field::Piece,
            "There is no piece at {:?} to move",
            from
        );
        assert!(
            self.get_field_vertex_neighbors(from).contains(to),
            "Cannot move piece at {:?} to non-(vertex neighbor) {:?}",
            from,
            to
        );
        assert_eq!(
            self.get_field(to),
            &Field::Empty,
            "Cannot move piece at {:?} to occupied neighbor {:?}",
            from,
            to
        );

        self.set_field(from, Field::Empty);
        self.set_field(to, Field::Piece);
    }
    pub fn remove_piece(&mut self, coord: &FieldCoord) {
        assert_eq!(
            self.get_field(coord),
            &Field::Piece,
            "There is no piece at {:?} to remove",
            coord
        );
        self.set_field(coord, Field::Empty);
    }

    fn get_hex(&self, coord: &HexCoord) -> &Option<Hex> {
        &self.board[(coord.x + 2) as usize][(coord.y + 2) as usize]
    }
    fn set_hex(&mut self, coord: &HexCoord, hex: Option<Hex>) {
        self.board[(coord.x + 2) as usize][(coord.y + 2) as usize] = hex;
    }
    pub fn get_hex_neighbors(&self, coord: &HexCoord) -> Vec<HexCoord> {
        coord
            .get_neighbors()
            .iter()
            .filter_map(|&(_, c)| match *self.get_hex(&c) {
                Some(_) => Some(c),
                None => None,
            })
            .collect()
    }
    /// Return fields that share an edge with the given hex and are outside of the given hex. If a
    /// hex is removed from the board, pieces occupying that hex's field neighbors might be
    /// capturable.
    pub fn get_hex_field_neighbors(&self, coord: &HexCoord) -> Vec<FieldCoord> {
        let mut neighbors = vec![];

        for (i, neighbor) in coord.get_neighbors() {
            if self.get_hex(&neighbor).is_some() {
                let f = (i + 3) % 6;
                neighbors.push(neighbor.to_field(f));
            }
        }
        neighbors
    }
    /// A hex is removable (and must be removed) if it is empty and is "attached to the board by 3
    /// or less adjacent sides."
    pub fn is_hex_removable(&self, coord: &HexCoord) -> bool {
        match *self.get_hex(coord) {
            Some(hex) => if hex.iter().any(|&f| f == Field::Piece) {
                return false;
            },
            None => panic!("The hex at {:?} has already been removed", coord),
        }

        let neighbor_idxs: Vec<u32> = coord.get_neighbors().iter().map(|&(i, _)| i).collect();
        let neighbor_idxs_slice = neighbor_idxs.as_slice();

        match neighbor_idxs_slice.len() {
            0 => panic!("A hex at {:?} is empty and has no neighbors", coord),
            1 => true,
            2 => {
                let valid_idx_combos = [[0, 1], [1, 2], [2, 3], [3, 4], [4, 5], [0, 5]];
                valid_idx_combos.iter().any(|c| c == neighbor_idxs_slice)
            }
            3 => {
                let valid_idx_combos = [
                    [0, 1, 2],
                    [1, 2, 3],
                    [2, 3, 4],
                    [3, 4, 5],
                    [0, 4, 5],
                    [0, 1, 5],
                ];
                valid_idx_combos.iter().any(|c| c == neighbor_idxs_slice)
            }
            _ => false,
        }
    }
    pub fn remove_hex(&mut self, coord: &HexCoord) {
        assert!(
            self.is_hex_removable(coord),
            "Cannot remove the hex at {:?} which is {:?} and has {} neighbors",
            coord,
            self.get_hex(coord),
            self.get_hex_neighbors(coord).len(),
        );
        self.set_hex(coord, None);
    }
    /// > extant (adj.): Still in existence; not destroyed, lost, or extinct (The Free Dictionary)
    ///
    /// Return the coordinates of the hexes that have not been removed yet.
    pub fn extant_hexes(&self) -> Vec<HexCoord> {
        let mut coords = vec![];

        for x in -2..3 {
            for y in -2..3 {
                if HexCoord::is_valid_coord(x, y) {
                    let coord = HexCoord::new(x, y);
                    if self.get_hex(&coord).is_some() {
                        coords.push(coord);
                    }
                }
            }
        }
        coords
    }
}

#[derive(Debug, PartialEq)]
pub struct FieldCoord {
    x: i32,
    y: i32,
    f: u32,
}

#[derive(Clone, Copy, Debug)]
pub struct HexCoord {
    x: i32,
    y: i32,
}

impl FieldCoord {
    pub fn new(x: i32, y: i32, f: u32) -> FieldCoord {
        assert!(Self::is_valid_coord(x, y, f));
        FieldCoord { x, y, f }
    }
    pub fn x(&self) -> i32 {
        self.x
    }
    pub fn y(&self) -> i32 {
        self.y
    }
    pub fn f(&self) -> u32 {
        self.f
    }
    pub fn to_hex(&self) -> HexCoord {
        HexCoord::new(self.x, self.y)
    }
    pub fn is_valid_coord(x: i32, y: i32, f: u32) -> bool {
        (x + y).abs() <= 2 && x.abs() <= 2 && y.abs() <= 2 && f < 6
    }
}

impl HexCoord {
    pub fn new(x: i32, y: i32) -> HexCoord {
        assert!(Self::is_valid_coord(x, y));
        HexCoord { x, y }
    }
    pub fn x(&self) -> i32 {
        self.x
    }
    pub fn y(&self) -> i32 {
        self.y
    }
    pub fn to_field(&self, f: u32) -> FieldCoord {
        FieldCoord::new(self.x, self.y, f)
    }
    pub fn is_valid_coord(x: i32, y: i32) -> bool {
        (x + y).abs() <= 2 && x.abs() <= 2 && y.abs() <= 2
    }
    // We return a Vec of tuples so that get_hex_field_neighbors and is_hex_removable know which
    // neighbors are on which side of the hex. They need to know this for different reasons:
    //   * get_hex_field_neighbors: the index of each neighboring field depends on which hex
    //                              neighbor that field neighbor is on
    //   * is_hex_removable: a hex is removable only if it is "attached to the board by 3 or less
    //                       adjacent sides"
    fn get_neighbors(&self) -> Vec<(u32, HexCoord)> {
        let mut neighbors = vec![];

        if self.y < 2 && (self.x + self.y) != 2 {
            neighbors.push((0, HexCoord::new(self.x, self.y + 1)));
        }
        if (self.x + self.y) != 2 && self.x < 2 {
            neighbors.push((1, HexCoord::new(self.x + 1, self.y)));
        }
        if self.x < 2 && self.y > -2 {
            neighbors.push((2, HexCoord::new(self.x + 1, self.y - 1)));
        }
        if self.y > -2 && (self.x + self.y) > -2 {
            neighbors.push((3, HexCoord::new(self.x, self.y - 1)));
        }
        if (self.x + self.y) > -2 && self.x > -2 {
            neighbors.push((4, HexCoord::new(self.x - 1, self.y)));
        }
        if self.x > -2 && self.y < 2 {
            neighbors.push((5, HexCoord::new(self.x - 1, self.y + 1)));
        }

        neighbors
    }
}
<|endoftext|>