use std::{collections::HashMap, fmt::Display};
use crate::{Primitive, CastleError};
#[derive(Debug, PartialEq, Clone)]
pub enum Input {
Primitive(Primitive),
Variant(Variant),
Map(HashMap<Box<str>, Input>),
List(Vec<Input>),
}
pub type Inputs = HashMap<Box<str>, Input>;
impl Input {
pub fn as_str(&self) -> Option<&str> {
match self {
Input::Primitive(Primitive::String(str)) => return Some(&**str),
_ => None,
}
}
pub fn as_map(&self) -> Option<&HashMap<Box<str>, Input>> {
match self {
Input::Map(map) => return Some(map),
_ => None,
}
}
pub fn as_list(&self) -> Option<&Vec<Input>> {
match self {
Input::List(list) => return Some(list),
_ => None,
}
}
pub fn as_variant(&self) -> Option<&Variant> {
match self {
Input::Variant(variant) => return Some(variant),
_ => None,
}
}
}
impl TryFrom<&Input> for Option<String> {
type Error = CastleError;
fn try_from(input: &Input) -> Result<Self, Self::Error> {
match input {
Input::Variant(Variant { ident, value: VariantType::Tuple(tuple)}) if &**ident == "Some" => {
match tuple.first() {
Some(item) => Ok(Some(item.try_into()?)),
_ => Err(CastleError::Validation("Expected value in tuple".into())),
}
},
Input::Variant(Variant { ident, value: VariantType::Unit}) if &**ident == "None" => Ok(None),
_ => Err(CastleError::Validation("Expected variant 'Some(..)' or 'None'".into())),
}
}
}
impl TryFrom<&Input> for String {
type Error = CastleError;
fn try_from(input: &Input) -> Result<Self, Self::Error> {
match input {
Input::Primitive(Primitive::String(str)) => Ok(str.to_string()),
_ => Err(CastleError::Validation("Expected string".into())),
}
}
}
impl<'a> TryFrom<&'a Input> for Option<&'a str> {
type Error = CastleError;
fn try_from(input: &'a Input) -> Result<Self, Self::Error> {
match input {
Input::Variant(Variant { ident, value: VariantType::Tuple(tuple)}) if &**ident == "Some" => {
match tuple.first() {
Some(item) => Ok(Some(item.try_into()?)),
_ => Err(CastleError::Validation("Expected value in tuple".into())),
}
}
Input::Variant(Variant { ident, value: VariantType::Unit}) if &**ident == "None" => Ok(None),
_ => Err(CastleError::Validation("Expected variant 'Some(..)' or 'None'".into())),
}
}
}
impl<'a> TryFrom<&'a Input> for &'a str {
type Error = CastleError;
fn try_from(input: &'a Input) -> Result<Self, Self::Error> {
match input {
Input::Primitive(Primitive::String(str)) => Ok(&**str),
_ => Err(CastleError::Validation("Expected string".into())),
}
}
}
impl TryFrom<&Input> for bool {
type Error = CastleError;
fn try_from(input: &Input) -> Result<Self, Self::Error> {
match input {
Input::Primitive(Primitive::Boolean(bool)) => Ok(*bool),
_ => Err(CastleError::Validation("Expected boolean".into())),
}
}
}
impl TryFrom<&Input> for Option<bool> {
type Error = CastleError;
fn try_from(input: &Input) -> Result<Self, Self::Error> {
match input {
Input::Variant(Variant { ident, value: VariantType::Tuple(tuple)}) if &**ident == "Some" => {
match tuple.first() {
Some(item) => Ok(Some(item.try_into()?)),
_ => panic!("Expected value in tuple"),
}
}
Input::Variant(Variant { ident, value: VariantType::Unit}) if &**ident == "None" => Ok(None),
_ => Err(CastleError::Validation("Expected variant 'Some(..)' or 'None'".into())),
}
}
}
impl<'a, T: TryFrom<&'a Input, Error = CastleError>> TryFrom<&'a Input> for Vec<T> {
type Error = CastleError;
fn try_from(input: &'a Input) -> Result<Self, Self::Error> {
match input {
Input::List(list) => list.iter().map(|input| T::try_from(input)).collect(),
_ => Ok(vec![]),
}
}
}
macro_rules! impl_from_input {
($($t:ty),*) => {
$(
impl TryFrom<&Input> for Option<$t> {
type Error = CastleError;
fn try_from(input: &Input) -> Result<Self, Self::Error> {
match input {
Input::Variant(Variant { ident, value: VariantType::Tuple(tuple)}) if &**ident == "Some" => {
match tuple.first() {
Some(item) => Ok(Some(item.try_into()?)),
_ => Err(CastleError::Validation("Expected value in tuple".into())),
}
}
Input::Variant(Variant { ident, value: VariantType::Unit}) if &**ident == "None" => Ok(None),
_ => Err(CastleError::Validation("Expected variant 'Some(..)' or 'None'".into())),
}
}
}
impl TryFrom<&Input> for $t {
type Error = CastleError;
fn try_from(input: &Input) -> Result<Self, Self::Error> {
match input {
Input::Primitive(Primitive::Number(number)) => Ok(number.clone().into()),
_ => Err(CastleError::Validation("Expected number".into())),
}
}
}
)*
};
}
impl_from_input!(i8, i16, i32, i64, u8, u16, u32, u64, f32, f64, usize, isize);
#[derive(Debug, PartialEq, Clone)]
pub struct Variant {
pub ident: Box<str>,
pub value: VariantType,
}
#[derive(Debug, PartialEq, Clone)]
pub enum VariantType {
Unit,
Tuple(Vec<Input>),
Map(HashMap<Box<str>, Input>),
}
impl Display for Input {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Input::Primitive(primitive) => write!(f, "{}", primitive),
Input::Variant(variant) => write!(f, "{}", variant),
Input::Map(map) => write!(f, "{:#?}", map),
Input::List(list) => write!(
f,
"{}",
list.iter()
.map(|item| format!("{}", item))
.collect::<Vec<String>>()
.join(", ")
),
}
}
}
impl Display for Variant {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.ident)?;
match &self.value {
VariantType::Unit => write!(f, "()"),
VariantType::Tuple(tuple) => write!(
f,
"({})",
tuple
.iter()
.map(|val| format!("{}", val))
.collect::<Vec<String>>()
.join(", ")
),
VariantType::Map(map) => write!(f, "{:#?}", map),
}
}
}