tsukurou_table 0.2.0

A specialized map for storing values of varying types.
Documentation
// Copyright (C) 2018  Project Tsukurou!
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.

//! Implementation of the `Value` type and its default conversions.

mod serde;

pub use self::serde::{to_value, from_value};

use table::Table;

/// The types of values that can be stored by tables.
///
/// Because most file formats don't care about integer size or floating point
/// precision, those details have aslo been omitted in implementation here.
///
/// # Examples
/// 
/// TODO
#[derive(Debug, Clone, PartialEq)]
pub enum Value {

    /// The lack of a meaningful value; always gets parsed as `None`.
    Null,

    /// A boolean primitive, true or false.
    ///
    /// Can be parsed using `FromValue` and `FromValueMut` with the following
    /// types:
    ///
    /// - `bool` - Direct copy
    /// - `String` - Formatted with `Display`
    Boolean(bool),

    /// A signed 64-bit integer type.
    ///
    /// Can be parsed using `FromValue` and `FromValueMut` with the following
    /// types:
    ///
    /// - `i64` - Direct copy
    /// - Other integer types - Casted (will fail if out of range).
    /// - `f64`, `f32` - Casted
    /// - `bool` - Only false when zero.
    /// - `String` - Formatted with `Display`.
    Integer(i64),

    /// A floating-point numeric type with 64-bit precision.
    ///
    /// Can be parsed using `FromValue` and `FromValueMut` with the following
    /// types:
    ///
    /// - `f64` - Direct copy
    /// - `f32` - Casted, with truncated precison.
    /// - `String` - Formatted with `Display`.
    Number(f64),

    /// A string of text.
    /// 
    /// Can be parsed using `FromValue` and `FromValueMut` with the following
    /// types:
    ///
    /// - `&String`, `&mut String` - Direct reference
    /// - `String` - Cloned
    /// - Integers, floating point, boolean - will attempt to parse with `FromStr`
    Text(String),

    /// An ordered list of values that may or may not be the same type.
    ///
    /// Can be parsed using `FromValue` and `FromValueMut` with the following
    /// types:
    ///
    /// - `&Vec<Value>`, `&mut Vec<Value>` - Direct reference
    List(Vec<Value>),

    /// A nested table of named values.
    /// 
    /// Can be parsed using `FromValue` and `FromValueMut` with the following
    /// types:
    ///
    /// - `&Table`, `&mut Table` - Direct reference
    Table(Table),
}

/// Attempt to unwrap or parse `Self` from a referenced `Value`.
pub trait FromValue<'a>: Sized {
    /// Attempts to perform the conversion, returning `None` if the given value
    /// can not be used to produce `Self`.
    fn from_value(value: &'a Value) -> Option<Self>;
}

/// Attempt to unwrap or parse `Self` from a mutably referenced `Value`.
pub trait FromValueMut<'a>: Sized {
    /// Attempts to perform the conversion, returning `None` if the given value
    /// can not be used to produce `Self`.
    fn from_value_mut(value: &'a mut Value) -> Option<Self>;
}

impl <'a, T> FromValue<'a> for T
where
    T: From<&'a Value>,
{
    fn from_value(value: &'a Value) -> Option<T> {
        Some(T::from(value))
    }
}

impl<'a, T> FromValueMut<'a> for T
where
    T: FromValue<'a>,
{
    fn from_value_mut(value: &'a mut Value) -> Option<T> {
        T::from_value(value)
    }
}

impl From<bool> for Value {
    fn from(x: bool) -> Value {
        Value::Boolean(x)
    }
}

impl<'a> FromValue<'a> for bool {
    fn from_value(value: &'a Value) -> Option<bool> {
        match value {
            Value::Boolean(x) => Some(*x),
            Value::Integer(x) => Some(*x != 0),
            Value::Text(x) => x.parse().ok(),
            _ => None,
        }
    }
}

impl From<i64> for Value {
    fn from(x: i64) -> Value {
        Value::Integer(x)
    }
}

impl<'a> FromValue<'a> for i64 {
    fn from_value(value: &'a Value) -> Option<i64> {
        match value {
            Value::Integer(x) => Some(*x),
            Value::Number(x) => Some(*x as i64),
            Value::Text(x) => x.parse().ok(),
            _ => None,
        }
    }
}

impl From<f64> for Value {
    fn from(x: f64) -> Value {
        Value::Number(x)
    }
}

impl<'a> FromValue<'a> for f64 {
    fn from_value(value: &'a Value) -> Option<f64> {
        match value {
            Value::Integer(x) => Some(*x as f64),
            Value::Number(x) => Some(*x),
            Value::Text(x) => x.parse().ok(),
            _ => None,
        }
    }
}

impl From<String> for Value {
    fn from(x: String) -> Value {
        Value::Text(x)
    }
}

impl<'a> FromValue<'a> for &'a String {
    fn from_value(value: &'a Value) -> Option<&'a String> {
        match value {
            Value::Text(x) => Some(x),
            _ => None,
        }
    }
}

impl<'a> FromValueMut<'a> for &'a mut String {
    fn from_value_mut(value: &'a mut Value) -> Option<&'a mut String> {
        match value {
            Value::Text(x) => Some(x),
            _ => None,
        }
    }
}

impl<'a> FromValue<'a> for String {
    fn from_value(value: &'a Value) -> Option<String> {
        match value {
            Value::Boolean(x) => Some(x.to_string()),
            Value::Integer(x) => Some(x.to_string()),
            Value::Number(x) => Some(x.to_string()),
            Value::Text(x) => Some(x.to_string()),
            _ => None,
        }
    }
}

impl From<Vec<Value>> for Value {
    fn from(x: Vec<Value>) -> Value {
        Value::List(x)
    }
}

impl<'a> FromValue<'a> for &'a Vec<Value> {
    fn from_value(value: &'a Value) -> Option<&'a Vec<Value>> {
        match value {
            Value::List(x) => Some(x),
            _ => None,
        }
    }
}

impl<'a> FromValueMut<'a> for &'a mut Vec<Value> {
    fn from_value_mut(value: &'a mut Value) -> Option<&'a mut Vec<Value>> {
        match value {
            Value::List(x) => Some(x),
            _ => None,
        }
    }
}

impl From<Table> for Value {
    fn from(x: Table) -> Value {
        Value::Table(x)
    }
}

impl<'a> FromValue<'a> for &'a Table {
    fn from_value(value: &'a Value) -> Option<&'a Table> {
        match value {
            Value::Table(x) => Some(x),
            _ => None,
        }
    }
}

impl<'a> FromValueMut<'a> for &'a mut Table {
    fn from_value_mut(value: &'a mut Value) -> Option<&'a mut Table> {
        match value {
            Value::Table(x) => Some(x),
            _ => None,
        }
    }
}

impl<T> From<Option<T>> for Value
where
    Value: From<T>,
{
    fn from(x: Option<T>) -> Value {
        match x {
            None => Value::Null,
            Some(t) => Value::from(t),
        }
    }
}

//FIXME Use From/TryFrom here
macro_rules! derive_value {
    ($x:ty as $($y:ty),*) => {$(
        impl From<$y> for Value {
            fn from(y: $y) -> Value {
                Value::from(y as $x)
            }
        }

        impl<'a> FromValue<'a> for $y {
            fn from_value(value: &'a Value) -> Option<$y> {
                <$x>::from_value(value)
                    .map(|x| x as $y)
            }
        }
    )*}
}

derive_value!(i64 as i8, i16, i32, isize, u8, u16, u32, u64, usize);
derive_value!(f64 as f32);