mod number;
pub use number::{Number, NumberSign, NumberType};
use derive_more::From;
use std::{
cmp::Ordering,
collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, LinkedList, VecDeque},
convert::TryFrom,
ffi::{OsStr, OsString},
hash::{Hash, Hasher},
path::{Path, PathBuf},
};
use strum::ParseError;
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde-1", derive(serde::Serialize, serde::Deserialize))]
pub enum Value {
List(Vec<Value>),
Map(HashMap<String, Value>),
Optional(Option<Box<Value>>),
Primitive(PrimitiveValue),
Text(String),
}
impl Value {
#[inline]
pub fn is_type(&self, r#type: ValueType) -> bool {
self.to_type() == r#type
}
#[inline]
pub fn to_type(&self) -> ValueType {
ValueType::from(self)
}
#[inline]
pub fn has_same_type(&self, other: &Value) -> bool {
self.to_type() == other.to_type()
}
#[inline]
pub fn is_complex(&self) -> bool {
!self.is_primitive()
}
#[inline]
pub fn is_primitive(&self) -> bool {
matches!(self, Self::Primitive(_))
}
#[inline]
pub fn to_primitive(&self) -> Option<PrimitiveValue> {
match self {
Self::Primitive(x) => Some(*x),
_ => None,
}
}
#[inline]
pub fn to_primitive_type(&self) -> Option<PrimitiveValueType> {
self.to_primitive().map(PrimitiveValueType::from)
}
pub fn try_into_option<T: TryFrom<Value, Error = &'static str>>(
self,
) -> Result<Option<T>, &'static str> {
match self {
Self::Optional(Some(boxed_value)) => {
let t = T::try_from(boxed_value.as_ref().clone())?;
Ok(Some(t))
}
Self::Optional(None) => Ok(None),
_ => Err("Only Optional can be converted to Option<T>"),
}
}
}
impl Hash for Value {
fn hash<H: Hasher>(&self, state: &mut H) {
match self {
Self::List(x) => x.hash(state),
Self::Map(x) => {
let mut keys = x.keys().collect::<Vec<&String>>();
keys.sort_unstable();
keys.hash(state);
let mut values = x.values().collect::<Vec<&Value>>();
values.sort_unstable_by(|a, b| a.partial_cmp(b).unwrap_or(Ordering::Less));
values.hash(state);
}
Self::Optional(x) => x.hash(state),
Self::Primitive(x) => x.hash(state),
Self::Text(x) => x.hash(state),
}
}
}
impl Eq for Value {}
impl PartialEq for Value {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::List(a), Self::List(b)) => a == b,
(Self::Map(a), Self::Map(b)) => a == b,
(Self::Optional(a), Self::Optional(b)) => a == b,
(Self::Optional(Some(a)), b) => a.as_ref() == b,
(a, Self::Optional(Some(b))) => a == b.as_ref(),
(Self::Primitive(a), Self::Primitive(b)) => a == b,
(Self::Text(a), Self::Text(b)) => a == b,
_ => false,
}
}
}
impl PartialOrd for Value {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
match (self, other) {
(Self::List(a), Self::List(b)) => a.partial_cmp(b),
(Self::Optional(a), Self::Optional(b)) => match (a, b) {
(Some(a), Some(b)) => a.partial_cmp(b),
_ => None,
},
(Self::Optional(Some(a)), b) => a.as_ref().partial_cmp(b),
(a, Self::Optional(Some(b))) => a.partial_cmp(b.as_ref()),
(Self::Primitive(a), Self::Primitive(b)) => a.partial_cmp(b),
(Self::Text(a), Self::Text(b)) => a.partial_cmp(b),
(Self::Text(a), Self::Primitive(PrimitiveValue::Char(b))) => {
a.partial_cmp(&b.to_string())
}
(Self::Primitive(PrimitiveValue::Char(a)), Self::Text(b)) => {
a.to_string().partial_cmp(b)
}
_ => None,
}
}
}
impl<T: Into<Value>> From<Vec<T>> for Value {
fn from(list: Vec<T>) -> Self {
Self::List(list.into_iter().map(|v| v.into()).collect())
}
}
impl<T: Into<Value>> From<VecDeque<T>> for Value {
fn from(list: VecDeque<T>) -> Self {
Self::List(list.into_iter().map(|v| v.into()).collect())
}
}
impl<T: Into<Value>> From<LinkedList<T>> for Value {
fn from(list: LinkedList<T>) -> Self {
Self::List(list.into_iter().map(|v| v.into()).collect())
}
}
impl<T: Into<Value>> From<BinaryHeap<T>> for Value {
fn from(list: BinaryHeap<T>) -> Self {
Self::List(list.into_iter().map(|v| v.into()).collect())
}
}
impl<T: Into<Value>> From<HashSet<T>> for Value {
fn from(list: HashSet<T>) -> Self {
Self::List(list.into_iter().map(|v| v.into()).collect())
}
}
impl<T: Into<Value>> From<BTreeSet<T>> for Value {
fn from(list: BTreeSet<T>) -> Self {
Self::List(list.into_iter().map(|v| v.into()).collect())
}
}
impl<T: Into<Value>> From<HashMap<String, T>> for Value {
fn from(map: HashMap<String, T>) -> Self {
Self::Map(map.into_iter().map(|(k, v)| (k, v.into())).collect())
}
}
impl<T: Into<Value>> From<BTreeMap<String, T>> for Value {
fn from(map: BTreeMap<String, T>) -> Self {
Self::Map(map.into_iter().map(|(k, v)| (k, v.into())).collect())
}
}
impl<T: Into<Value>> From<Option<T>> for Value {
fn from(maybe: Option<T>) -> Self {
Self::Optional(maybe.map(|x| Box::from(x.into())))
}
}
impl From<PrimitiveValue> for Value {
fn from(v: PrimitiveValue) -> Self {
Self::Primitive(v)
}
}
impl From<PathBuf> for Value {
fn from(p: PathBuf) -> Self {
Self::from(p.into_os_string())
}
}
impl<'a> From<&'a Path> for Value {
fn from(p: &'a Path) -> Self {
Self::from(p.as_os_str())
}
}
impl From<OsString> for Value {
fn from(s: OsString) -> Self {
Self::Text(s.to_string_lossy().to_string())
}
}
impl<'a> From<&'a OsStr> for Value {
fn from(s: &'a OsStr) -> Self {
Self::from(s.to_os_string())
}
}
impl From<String> for Value {
fn from(s: String) -> Self {
Self::Text(s)
}
}
impl<'a> From<&'a str> for Value {
fn from(s: &'a str) -> Self {
Self::from(s.to_string())
}
}
macro_rules! impl_from_primitive {
($type:ty) => {
impl From<$type> for Value {
fn from(v: $type) -> Self {
Self::from(PrimitiveValue::from(v))
}
}
};
}
impl_from_primitive!(bool);
impl_from_primitive!(char);
impl_from_primitive!(f32);
impl_from_primitive!(f64);
impl_from_primitive!(i128);
impl_from_primitive!(i16);
impl_from_primitive!(i32);
impl_from_primitive!(i64);
impl_from_primitive!(i8);
impl_from_primitive!(isize);
impl_from_primitive!(u128);
impl_from_primitive!(u16);
impl_from_primitive!(u32);
impl_from_primitive!(u64);
impl_from_primitive!(u8);
impl_from_primitive!(usize);
macro_rules! impl_try_into {
($variant:ident, $type:ty, $convert:expr) => {
impl TryFrom<Value> for $type {
type Error = &'static str;
fn try_from(value: Value) -> Result<Self, Self::Error> {
match value {
Value::$variant(x) => $convert(x),
_ => Err(concat!(
"Only ",
stringify!($variant),
" can be converted to ",
stringify!($type)
)),
}
}
}
};
}
macro_rules! impl_generic_try_into {
($variant:ident, $type:ty, $generic:tt, $convert:expr) => {
impl<$generic: TryFrom<Value, Error = &'static str>> TryFrom<Value> for $type {
type Error = &'static str;
fn try_from(value: Value) -> Result<Self, Self::Error> {
match value {
Value::$variant(x) => $convert(x),
_ => Err(concat!(
"Only ",
stringify!($variant),
" can be converted to ",
stringify!($type)
)),
}
}
}
};
}
impl_generic_try_into!(List, Vec<T>, T, |x: Vec<Value>| x
.into_iter()
.map(T::try_from)
.collect());
impl_generic_try_into!(Map, HashMap<String, T>, T, |x: HashMap<String, Value>| x
.into_iter()
.map(|(k, v)| T::try_from(v).map(|t| (k, t)))
.collect());
impl_try_into!(Text, String, |x| Ok(x));
impl_try_into!(Text, PathBuf, |x| Ok(PathBuf::from(x)));
impl_try_into!(Text, OsString, |x| Ok(OsString::from(x)));
impl_try_into!(Primitive, bool, bool::try_from);
impl_try_into!(Primitive, char, char::try_from);
impl_try_into!(Primitive, f32, f32::try_from);
impl_try_into!(Primitive, f64, f64::try_from);
impl_try_into!(Primitive, i128, i128::try_from);
impl_try_into!(Primitive, i16, i16::try_from);
impl_try_into!(Primitive, i32, i32::try_from);
impl_try_into!(Primitive, i64, i64::try_from);
impl_try_into!(Primitive, i8, i8::try_from);
impl_try_into!(Primitive, isize, isize::try_from);
impl_try_into!(Primitive, u128, u128::try_from);
impl_try_into!(Primitive, u16, u16::try_from);
impl_try_into!(Primitive, u32, u32::try_from);
impl_try_into!(Primitive, u64, u64::try_from);
impl_try_into!(Primitive, u8, u8::try_from);
impl_try_into!(Primitive, usize, usize::try_from);
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde-1", derive(serde::Serialize, serde::Deserialize))]
pub enum ValueType {
List(Box<ValueType>),
Map(Box<ValueType>),
Optional(Box<ValueType>),
Primitive(PrimitiveValueType),
Text,
Custom,
}
impl ValueType {
pub fn is_primitive_type(&self) -> bool {
matches!(self, Self::Primitive(_))
}
pub fn to_primitive_type(&self) -> Option<PrimitiveValueType> {
match self {
Self::Primitive(x) => Some(*x),
_ => None,
}
}
pub fn from_type_name(name: &str) -> Result<Self, ParseError> {
if name.is_empty() {
return Err(ParseError::VariantNotFound);
}
let mut tokens = name.split(|c| c == '<');
let maybe_outer_str = tokens.next();
let inner_str = {
let mut x = tokens.collect::<Vec<&str>>().join("<");
if x.ends_with('>') {
x.pop();
}
x
};
match maybe_outer_str
.unwrap()
.split(|c| c == ':')
.last()
.unwrap()
.to_lowercase()
.as_str()
{
"hashmap" => {
let mut items = inner_str.split(|c| c == ',');
if let Some(s) = items.next() {
if s.trim().to_lowercase().as_str() != "string" {
return Err(ParseError::VariantNotFound);
}
}
let rest = items.collect::<String>();
Ok(ValueType::Map(Box::from(Self::from_type_name(
&rest.trim(),
)?)))
}
"vec" => Ok(ValueType::List(Box::from(Self::from_type_name(
&inner_str,
)?))),
"option" => Ok(ValueType::Optional(Box::from(Self::from_type_name(
&inner_str,
)?))),
"string" => Ok(ValueType::Text),
x => Ok(ValueType::Primitive(PrimitiveValueType::from_type_name(x)?)),
}
}
}
impl Default for ValueType {
fn default() -> Self {
Self::Primitive(Default::default())
}
}
impl std::str::FromStr for ValueType {
type Err = ParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
fn opt_to_err(maybe_type: Option<ValueType>) -> Result<ValueType, ParseError> {
match maybe_type {
Some(t) => Ok(t),
None => Err(ParseError::VariantNotFound),
}
}
fn from_tokens<'a>(
mut it: impl Iterator<Item = &'a str>,
) -> Result<Option<ValueType>, ParseError> {
match it.next() {
Some("number") => from_tokens(it),
Some(token) => {
let maybe_inner = from_tokens(it)?;
match token {
"list" => Ok(Some(ValueType::List(Box::from(opt_to_err(maybe_inner)?)))),
"map" => Ok(Some(ValueType::Map(Box::from(opt_to_err(maybe_inner)?)))),
"optional" => Ok(Some(ValueType::Optional(Box::from(opt_to_err(
maybe_inner,
)?)))),
"primitive" => Ok(Some(ValueType::Primitive(
opt_to_err(maybe_inner)?
.to_primitive_type()
.ok_or(ParseError::VariantNotFound)?,
))),
"text" => Ok(Some(ValueType::Text)),
x => Ok(Some(ValueType::Primitive(PrimitiveValueType::from_str(x)?))),
}
}
None => Ok(None),
}
}
match from_tokens(s.split(':')) {
Ok(Some(value_type)) => Ok(value_type),
Ok(None) => Err(ParseError::VariantNotFound),
Err(x) => Err(x),
}
}
}
impl std::fmt::Display for ValueType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::List(t) => write!(f, "list:{}", t),
Self::Map(t) => write!(f, "map:{}", t),
Self::Optional(t) => write!(f, "optional:{}", t),
Self::Primitive(t) => write!(f, "{}", t),
Self::Text => write!(f, "text"),
Self::Custom => write!(f, "custom"),
}
}
}
impl From<Value> for ValueType {
fn from(value: Value) -> Self {
Self::from(&value)
}
}
impl<'a> From<&'a Value> for ValueType {
fn from(v: &'a Value) -> Self {
match v {
Value::List(x) => Self::List(Box::from(
x.iter().next().map(ValueType::from).unwrap_or_default(),
)),
Value::Map(x) => Self::Map(Box::from(
x.values().next().map(ValueType::from).unwrap_or_default(),
)),
Value::Optional(x) => Self::Optional(Box::from(
x.as_ref()
.map(Box::as_ref)
.map(ValueType::from)
.unwrap_or_default(),
)),
Value::Primitive(x) => Self::Primitive(PrimitiveValueType::from(x)),
Value::Text(_) => Self::Text,
}
}
}
impl From<PrimitiveValueType> for ValueType {
fn from(t: PrimitiveValueType) -> Self {
Self::Primitive(t)
}
}
impl From<NumberType> for ValueType {
fn from(t: NumberType) -> Self {
Self::Primitive(PrimitiveValueType::Number(t))
}
}
impl Default for PrimitiveValueType {
fn default() -> Self {
Self::Unit
}
}
#[derive(Copy, Clone, Debug, From)]
#[cfg_attr(feature = "serde-1", derive(serde::Serialize, serde::Deserialize))]
pub enum PrimitiveValue {
Bool(bool),
Char(char),
Number(Number),
Unit,
}
impl PrimitiveValue {
#[inline]
pub fn is_type(&self, r#type: PrimitiveValueType) -> bool {
self.to_type() == r#type
}
#[inline]
pub fn to_type(&self) -> PrimitiveValueType {
PrimitiveValueType::from(self)
}
#[inline]
pub fn has_same_type(&self, other: &PrimitiveValue) -> bool {
self.to_type() == other.to_type()
}
}
impl Hash for PrimitiveValue {
fn hash<H: Hasher>(&self, state: &mut H) {
match self {
Self::Bool(x) => x.hash(state),
Self::Char(x) => x.hash(state),
Self::Number(x) => x.hash(state),
Self::Unit => Self::Unit.hash(state),
}
}
}
impl Eq for PrimitiveValue {}
impl PartialEq for PrimitiveValue {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::Bool(a), Self::Bool(b)) => a == b,
(Self::Char(a), Self::Char(b)) => a == b,
(Self::Number(a), Self::Number(b)) => a == b,
(Self::Unit, Self::Unit) => true,
_ => false,
}
}
}
impl PartialOrd for PrimitiveValue {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
match (self, other) {
(Self::Bool(a), Self::Bool(b)) => a.partial_cmp(b),
(Self::Char(a), Self::Char(b)) => a.partial_cmp(b),
(Self::Number(a), Self::Number(b)) => a.partial_cmp(b),
(Self::Unit, Self::Unit) => Some(Ordering::Equal),
_ => None,
}
}
}
macro_rules! impl_primitive_try_into {
($variant:ident, $type:ty, $convert:expr) => {
impl TryFrom<PrimitiveValue> for $type {
type Error = &'static str;
fn try_from(value: PrimitiveValue) -> Result<Self, Self::Error> {
match value {
PrimitiveValue::$variant(x) => $convert(x),
_ => Err(concat!(
"Only ",
stringify!($variant),
" can be converted to ",
stringify!($type)
)),
}
}
}
};
}
impl_primitive_try_into!(Bool, bool, |x| Ok(x));
impl_primitive_try_into!(Char, char, |x| Ok(x));
impl_primitive_try_into!(Number, f32, f32::try_from);
impl_primitive_try_into!(Number, f64, f64::try_from);
impl_primitive_try_into!(Number, i128, i128::try_from);
impl_primitive_try_into!(Number, i16, i16::try_from);
impl_primitive_try_into!(Number, i32, i32::try_from);
impl_primitive_try_into!(Number, i64, i64::try_from);
impl_primitive_try_into!(Number, i8, i8::try_from);
impl_primitive_try_into!(Number, isize, isize::try_from);
impl_primitive_try_into!(Number, u128, u128::try_from);
impl_primitive_try_into!(Number, u16, u16::try_from);
impl_primitive_try_into!(Number, u32, u32::try_from);
impl_primitive_try_into!(Number, u64, u64::try_from);
impl_primitive_try_into!(Number, u8, u8::try_from);
impl_primitive_try_into!(Number, usize, usize::try_from);
macro_rules! impl_to_number {
($type:ty) => {
impl From<$type> for PrimitiveValue {
fn from(v: $type) -> Self {
Self::Number(Number::from(v))
}
}
};
}
impl_to_number!(f32);
impl_to_number!(f64);
impl_to_number!(i128);
impl_to_number!(i16);
impl_to_number!(i32);
impl_to_number!(i64);
impl_to_number!(i8);
impl_to_number!(isize);
impl_to_number!(u128);
impl_to_number!(u16);
impl_to_number!(u32);
impl_to_number!(u64);
impl_to_number!(u8);
impl_to_number!(usize);
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde-1", derive(serde::Serialize, serde::Deserialize))]
pub enum PrimitiveValueType {
Bool,
Char,
Number(NumberType),
Unit,
}
impl PrimitiveValueType {
pub fn is_bool(&self) -> bool {
matches!(self, Self::Bool)
}
pub fn is_char(&self) -> bool {
matches!(self, Self::Char)
}
pub fn is_number(&self) -> bool {
matches!(self, Self::Number(_))
}
pub fn is_unit(&self) -> bool {
matches!(self, Self::Unit)
}
pub fn to_number_type(&self) -> Option<NumberType> {
match self {
Self::Number(x) => Some(*x),
_ => None,
}
}
pub fn from_type_name(tname: &str) -> Result<Self, ParseError> {
use std::str::FromStr;
match tname {
"()" => Self::from_str("unit"),
x => Self::from_str(x),
}
}
}
impl std::fmt::Display for PrimitiveValueType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Bool => write!(f, "bool"),
Self::Char => write!(f, "char"),
Self::Number(t) => write!(f, "number:{}", t),
Self::Unit => write!(f, "unit"),
}
}
}
impl From<PrimitiveValue> for PrimitiveValueType {
fn from(v: PrimitiveValue) -> Self {
Self::from(&v)
}
}
impl<'a> From<&'a PrimitiveValue> for PrimitiveValueType {
fn from(v: &'a PrimitiveValue) -> Self {
match v {
PrimitiveValue::Bool(_) => Self::Bool,
PrimitiveValue::Char(_) => Self::Char,
PrimitiveValue::Number(x) => Self::Number(x.to_type()),
PrimitiveValue::Unit => Self::Unit,
}
}
}
impl std::str::FromStr for PrimitiveValueType {
type Err = ParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut s_it = s.split(':');
let primary = s_it.next();
let secondary = s_it.next();
let has_more = s_it.next().is_some();
if has_more {
return Err(ParseError::VariantNotFound);
}
match (primary, secondary) {
(Some("bool"), None) => Ok(Self::Bool),
(Some("char"), None) => Ok(Self::Char),
(Some("number"), Some(x)) => Ok(Self::Number(NumberType::from_str(x)?)),
(Some("unit"), None) => Ok(Self::Unit),
(Some(x), None) => Ok(Self::Number(NumberType::from_str(x)?)),
_ => Err(ParseError::VariantNotFound),
}
}
}