use std::str::Split;
use std::collections::BTreeMap;
use serde::Serialize;
use crate::value::{Tag, ValueSerializer};
use crate::error::{Error, Actual};
pub type Map<K, V> = BTreeMap<K, V>;
pub type Dict = Map<String, Value>;
#[derive(Debug, Clone)]
pub enum Value {
String(Tag, String),
Char(Tag, char),
Bool(Tag, bool),
Num(Tag, Num),
Empty(Tag, Empty),
Dict(Tag, Dict),
Array(Tag, Vec<Value>),
}
macro_rules! conversion_fn {
($RT:ty, $([$star:tt])? $Variant:ident => $T:ty, $fn_name:ident) => {
conversion_fn!(
concat!(
"Converts `self` into a `", stringify!($T), "` if `self` is a \
`Value::", stringify!($Variant), "`.\n\n",
"# Example\n\n",
"```\n",
"use figment::value::Value;\n\n",
"let value: Value = 123.into();\n",
"let converted = value.", stringify!($fn_name), "();\n",
"```"
),
$RT, $([$star])? $Variant => $T, $fn_name
);
};
($doc:expr, $RT:ty, $([$star:tt])? $Variant:ident => $T:ty, $fn_name:ident) => {
#[doc = $doc]
pub fn $fn_name(self: $RT) -> Option<$T> {
match $($star)? self {
Value::$Variant(_, v) => Some(v),
_ => None
}
}
};
}
impl Value {
pub fn serialize<T: Serialize>(value: T) -> Result<Self, Error> {
value.serialize(ValueSerializer)
}
pub fn deserialize<'de, T: serde::Deserialize<'de>>(&self) -> Result<T, Error> {
T::deserialize(self)
}
pub fn find(self, path: &str) -> Option<Value> {
fn find(mut keys: Split<char>, value: Value) -> Option<Value> {
match keys.next() {
Some(k) if !k.is_empty() => find(keys, value.into_dict()?.remove(k)?),
Some(_) | None => Some(value)
}
}
find(path.split('.'), self)
}
pub fn find_ref<'a>(&'a self, path: &str) -> Option<&'a Value> {
fn find<'a, 'v>(mut keys: Split<'a, char>, value: &'v Value) -> Option<&'v Value> {
match keys.next() {
Some(k) if !k.is_empty() => find(keys, value.as_dict()?.get(k)?),
Some(_) | None => Some(value)
}
}
find(path.split('.'), self)
}
pub fn tag(&self) -> Tag {
match *self {
Value::String(tag, ..) => tag,
Value::Char(tag, ..) => tag,
Value::Bool(tag, ..) => tag,
Value::Num(tag, ..) => tag,
Value::Dict(tag, ..) => tag,
Value::Array(tag, ..) => tag,
Value::Empty(tag, ..) => tag,
}
}
conversion_fn!(&Value, String => &str, as_str);
conversion_fn!(Value, String => String, into_string);
conversion_fn!(&Value, [*]Char => char, to_char);
conversion_fn!(&Value, [*]Bool => bool, to_bool);
conversion_fn!(&Value, [*]Num => Num, to_num);
conversion_fn!(&Value, [*]Empty => Empty, to_empty);
conversion_fn!(&Value, Dict => &Dict, as_dict);
conversion_fn!(Value, Dict => Dict, into_dict);
conversion_fn!(&Value, Array => &[Value], as_array);
conversion_fn!(Value, Array => Vec<Value>, into_array);
pub fn to_u128(&self) -> Option<u128> {
self.to_num()?.to_u128()
}
pub fn to_i128(&self) -> Option<i128> {
self.to_num()?.to_i128()
}
pub fn to_f64(&self) -> Option<f64> {
self.to_num()?.to_f64()
}
pub fn to_actual(&self) -> Actual {
match self {
Value::String(_, s) => Actual::Str(s.into()),
Value::Char(_, c) => Actual::Char(*c),
Value::Bool(_, b) => Actual::Bool(*b),
Value::Num(_, n) => n.to_actual(),
Value::Empty(_, e) => e.to_actual(),
Value::Dict(_, _) => Actual::Map,
Value::Array(_, _) => Actual::Seq,
}
}
pub(crate) fn tag_mut(&mut self) -> &mut Tag {
match self {
Value::String(tag, ..) => tag,
Value::Char(tag, ..) => tag,
Value::Bool(tag, ..) => tag,
Value::Num(tag, ..) => tag,
Value::Dict(tag, ..) => tag,
Value::Array(tag, ..) => tag,
Value::Empty(tag, ..) => tag,
}
}
pub(crate) fn map_tag<F>(&mut self, mut f: F)
where F: FnMut(&mut Tag) + Copy
{
if *self.tag_mut() == Tag::Default {
f(self.tag_mut());
}
match self {
Value::Dict(_, v) => v.iter_mut().for_each(|(_, v)| v.map_tag(f)),
Value::Array(_, v) => v.iter_mut().for_each(|v| v.map_tag(f)),
_ => { }
}
}
}
impl PartialEq for Value {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Value::String(_, v1), Value::String(_, v2)) => v1 == v2,
(Value::Char(_, v1), Value::Char(_, v2)) => v1 == v2,
(Value::Bool(_, v1), Value::Bool(_, v2)) => v1 == v2,
(Value::Num(_, v1), Value::Num(_, v2)) => v1 == v2,
(Value::Empty(_, v1), Value::Empty(_, v2)) => v1 == v2,
(Value::Dict(_, v1), Value::Dict(_, v2)) => v1 == v2,
(Value::Array(_, v1), Value::Array(_, v2)) => v1 == v2,
_ => false,
}
}
}
macro_rules! impl_from_array {
($($N:literal),*) => ($(impl_from_array!(@$N);)*);
(@$N:literal) => (
impl<'a, T: Into<Value> + Clone> From<&'a [T; $N]> for Value {
#[inline(always)]
fn from(value: &'a [T; $N]) -> Value {
Value::from(&value[..])
}
}
)
}
impl_from_array!(1, 2, 3, 4, 5, 6, 7, 8);
impl From<&str> for Value {
fn from(value: &str) -> Value {
Value::String(Tag::Default, value.to_string())
}
}
impl<'a, T: Into<Value> + Clone> From<&'a [T]> for Value {
fn from(value: &'a [T]) -> Value {
Value::Array(Tag::Default, value.iter().map(|v| v.clone().into()).collect())
}
}
impl<'a, T: Into<Value>> From<Vec<T>> for Value {
fn from(vec: Vec<T>) -> Value {
let vector = vec.into_iter().map(|v| v.into()).collect();
Value::Array(Tag::Default, vector)
}
}
impl<K: AsRef<str>, V: Into<Value>> From<Map<K, V>> for Value {
fn from(map: Map<K, V>) -> Value {
let dict: Dict = map.into_iter()
.map(|(k, v)| (k.as_ref().to_string(), v.into()))
.collect();
Value::Dict(Tag::Default, dict)
}
}
macro_rules! impl_from_for_value {
($($T:ty: $V:ident),*) => ($(
impl From<$T> for Value {
fn from(value: $T) -> Value { Value::$V(Tag::Default, value.into()) }
}
)*)
}
impl_from_for_value! {
String: String, char: Char, bool: Bool,
u8: Num, u16: Num, u32: Num, u64: Num, u128: Num, usize: Num,
i8: Num, i16: Num, i32: Num, i64: Num, i128: Num, isize: Num,
f32: Num, f64: Num, Num: Num, Empty: Empty
}
#[derive(Debug, Clone, Copy)]
pub enum Num {
U8(u8),
U16(u16),
U32(u32),
U64(u64),
U128(u128),
USize(usize),
I8(i8),
I16(i16),
I32(i32),
I64(i64),
I128(i128),
ISize(isize),
F32(f32),
F64(f64),
}
impl Num {
pub fn to_u32(self) -> Option<u32> {
Some(match self {
Num::U8(v) => v as u32,
Num::U16(v) => v as u32,
Num::U32(v) => v as u32,
_ => return None,
})
}
pub fn to_u128(self) -> Option<u128> {
Some(match self {
Num::U8(v) => v as u128,
Num::U16(v) => v as u128,
Num::U32(v) => v as u128,
Num::U64(v) => v as u128,
Num::U128(v) => v as u128,
Num::USize(v) => v as u128,
_ => return None,
})
}
pub fn to_i128(self) -> Option<i128> {
Some(match self {
Num::I8(v) => v as i128,
Num::I16(v) => v as i128,
Num::I32(v) => v as i128,
Num::I64(v) => v as i128,
Num::I128(v) => v as i128,
Num::ISize(v) => v as i128,
_ => return None,
})
}
pub fn to_f64(&self) -> Option<f64> {
Some(match *self {
Num::F32(v) => v as f64,
Num::F64(v) => v as f64,
_ => return None,
})
}
pub fn to_actual(&self) -> Actual {
match *self {
Num::U8(v) => Actual::Unsigned(v as u128),
Num::U16(v) => Actual::Unsigned(v as u128),
Num::U32(v) => Actual::Unsigned(v as u128),
Num::U64(v) => Actual::Unsigned(v as u128),
Num::U128(v) => Actual::Unsigned(v as u128),
Num::USize(v) => Actual::Unsigned(v as u128),
Num::I8(v) => Actual::Signed(v as i128),
Num::I16(v) => Actual::Signed(v as i128),
Num::I32(v) => Actual::Signed(v as i128),
Num::I64(v) => Actual::Signed(v as i128),
Num::I128(v) => Actual::Signed(v as i128),
Num::ISize(v) => Actual::Signed(v as i128),
Num::F32(v) => Actual::Float(v as f64),
Num::F64(v) => Actual::Float(v as f64),
}
}
}
impl PartialEq for Num {
fn eq(&self, other: &Self) -> bool {
self.to_actual() == other.to_actual()
}
}
macro_rules! impl_from_for_num_value {
($($T:ty: $V:ident),*) => ($(
impl From<$T> for Num {
fn from(value: $T) -> Num {
Num::$V(value)
}
}
)*)
}
impl_from_for_num_value! {
u8: U8, u16: U16, u32: U32, u64: U64, u128: U128, usize: USize,
i8: I8, i16: I16, i32: I32, i64: I64, i128: I128, isize: ISize,
f32: F32, f64: F64
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Empty {
None,
Unit
}
impl Empty {
pub fn to_actual(&self) -> Actual {
match self {
Empty::None => Actual::Option,
Empty::Unit => Actual::Unit,
}
}
}