1use std::{collections::HashMap, fmt::Display};
2
3use crate::{Primitive, CastleError};
4
5#[derive(Debug, PartialEq, Clone)]
15pub enum Input {
16 Primitive(Primitive),
17 Variant(Variant),
18 Map(HashMap<Box<str>, Input>),
19 List(Vec<Input>),
20}
21
22pub type Inputs = HashMap<Box<str>, Input>;
23
24impl Input {
25 pub fn as_str(&self) -> Option<&str> {
26 match self {
27 Input::Primitive(Primitive::String(str)) => return Some(&**str),
28 _ => None,
29 }
30 }
31 pub fn as_map(&self) -> Option<&HashMap<Box<str>, Input>> {
32 match self {
33 Input::Map(map) => return Some(map),
34 _ => None,
35 }
36 }
37 pub fn as_list(&self) -> Option<&Vec<Input>> {
38 match self {
39 Input::List(list) => return Some(list),
40 _ => None,
41 }
42 }
43 pub fn as_variant(&self) -> Option<&Variant> {
44 match self {
45 Input::Variant(variant) => return Some(variant),
46 _ => None,
47 }
48 }
49}
50
51impl TryFrom<&Input> for Option<String> {
52 type Error = CastleError;
53
54 fn try_from(input: &Input) -> Result<Self, Self::Error> {
55 match input {
56 Input::Variant(Variant { ident, value: VariantType::Tuple(tuple)}) if &**ident == "Some" => {
57 match tuple.first() {
58 Some(item) => Ok(Some(item.try_into()?)),
59 _ => Err(CastleError::Validation("Expected value in tuple".into())),
60 }
61 },
62 Input::Variant(Variant { ident, value: VariantType::Unit}) if &**ident == "None" => Ok(None),
63 _ => Err(CastleError::Validation("Expected variant 'Some(..)' or 'None'".into())),
64 }
65 }
66}
67
68impl TryFrom<&Input> for String {
69 type Error = CastleError;
70
71 fn try_from(input: &Input) -> Result<Self, Self::Error> {
72 match input {
73 Input::Primitive(Primitive::String(str)) => Ok(str.to_string()),
74 _ => Err(CastleError::Validation("Expected string".into())),
75 }
76 }
77}
78
79impl<'a> TryFrom<&'a Input> for Option<&'a str> {
80 type Error = CastleError;
81
82 fn try_from(input: &'a Input) -> Result<Self, Self::Error> {
83 match input {
84 Input::Variant(Variant { ident, value: VariantType::Tuple(tuple)}) if &**ident == "Some" => {
85 match tuple.first() {
86 Some(item) => Ok(Some(item.try_into()?)),
87 _ => Err(CastleError::Validation("Expected value in tuple".into())),
88 }
89 }
90 Input::Variant(Variant { ident, value: VariantType::Unit}) if &**ident == "None" => Ok(None),
91 _ => Err(CastleError::Validation("Expected variant 'Some(..)' or 'None'".into())),
92 }
93 }
94}
95
96impl<'a> TryFrom<&'a Input> for &'a str {
97 type Error = CastleError;
98
99 fn try_from(input: &'a Input) -> Result<Self, Self::Error> {
100 match input {
101 Input::Primitive(Primitive::String(str)) => Ok(&**str),
102 _ => Err(CastleError::Validation("Expected string".into())),
103 }
104 }
105}
106
107impl TryFrom<&Input> for bool {
108 type Error = CastleError;
109
110 fn try_from(input: &Input) -> Result<Self, Self::Error> {
111 match input {
112 Input::Primitive(Primitive::Boolean(bool)) => Ok(*bool),
113 _ => Err(CastleError::Validation("Expected boolean".into())),
114 }
115 }
116}
117
118impl TryFrom<&Input> for Option<bool> {
119 type Error = CastleError;
120
121 fn try_from(input: &Input) -> Result<Self, Self::Error> {
122 match input {
123 Input::Variant(Variant { ident, value: VariantType::Tuple(tuple)}) if &**ident == "Some" => {
124 match tuple.first() {
125 Some(item) => Ok(Some(item.try_into()?)),
126 _ => panic!("Expected value in tuple"),
127 }
128 }
129 Input::Variant(Variant { ident, value: VariantType::Unit}) if &**ident == "None" => Ok(None),
130 _ => Err(CastleError::Validation("Expected variant 'Some(..)' or 'None'".into())),
131 }
132 }
133}
134
135impl<'a, T: TryFrom<&'a Input, Error = CastleError>> TryFrom<&'a Input> for Vec<T> {
136 type Error = CastleError;
137 fn try_from(input: &'a Input) -> Result<Self, Self::Error> {
138 match input {
139 Input::List(list) => list.iter().map(|input| T::try_from(input)).collect(),
140 _ => Ok(vec![]),
141 }
142 }
143}
144
145macro_rules! impl_from_input {
147 ($($t:ty),*) => {
148 $(
149 impl TryFrom<&Input> for Option<$t> {
150 type Error = CastleError;
151
152 fn try_from(input: &Input) -> Result<Self, Self::Error> {
153 match input {
154 Input::Variant(Variant { ident, value: VariantType::Tuple(tuple)}) if &**ident == "Some" => {
155 match tuple.first() {
156 Some(item) => Ok(Some(item.try_into()?)),
157 _ => Err(CastleError::Validation("Expected value in tuple".into())),
158 }
159 }
160 Input::Variant(Variant { ident, value: VariantType::Unit}) if &**ident == "None" => Ok(None),
161 _ => Err(CastleError::Validation("Expected variant 'Some(..)' or 'None'".into())),
162 }
163 }
164 }
165
166 impl TryFrom<&Input> for $t {
167 type Error = CastleError;
168
169 fn try_from(input: &Input) -> Result<Self, Self::Error> {
170 match input {
171 Input::Primitive(Primitive::Number(number)) => Ok(number.clone().into()),
172 _ => Err(CastleError::Validation("Expected number".into())),
173 }
174 }
175 }
176 )*
177 };
178}
179
180impl_from_input!(i8, i16, i32, i64, u8, u16, u32, u64, f32, f64, usize, isize);
181
182#[derive(Debug, PartialEq, Clone)]
183pub struct Variant {
184 pub ident: Box<str>,
185 pub value: VariantType,
186}
187
188#[derive(Debug, PartialEq, Clone)]
189pub enum VariantType {
190 Unit,
191 Tuple(Vec<Input>),
192 Map(HashMap<Box<str>, Input>),
193}
194
195impl Display for Input {
196 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
197 match self {
198 Input::Primitive(primitive) => write!(f, "{}", primitive),
199 Input::Variant(variant) => write!(f, "{}", variant),
200 Input::Map(map) => write!(f, "{:#?}", map),
201 Input::List(list) => write!(
202 f,
203 "{}",
204 list.iter()
205 .map(|item| format!("{}", item))
206 .collect::<Vec<String>>()
207 .join(", ")
208 ),
209 }
210 }
211}
212
213impl Display for Variant {
214 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
215 write!(f, "{}", self.ident)?;
216 match &self.value {
217 VariantType::Unit => write!(f, "()"),
218 VariantType::Tuple(tuple) => write!(
219 f,
220 "({})",
221 tuple
222 .iter()
223 .map(|val| format!("{}", val))
224 .collect::<Vec<String>>()
225 .join(", ")
226 ),
227 VariantType::Map(map) => write!(f, "{:#?}", map),
228 }
229 }
230}