1use std::borrow::Cow;
2
3use anyhow::{Result, anyhow, bail};
4use hashbrown::HashMap;
5use serde::{Deserialize, Serialize};
6
7use crate::{Id, SStr, data::{DataAny, DataKey}, path::PathBufDyn, strand::{IntoStrand, StrandBuf}, url::{Url, UrlBuf, UrlCow}};
8
9#[derive(Clone, Debug, Default, Deserialize, Serialize)]
11#[serde(untagged)]
12pub enum Data {
13 #[default]
14 Nil,
15 Boolean(bool),
16 Integer(i64),
17 Number(f64),
18 String(SStr),
19 List(Vec<Self>),
20 Dict(HashMap<DataKey, Self>),
21 Id(Id),
22 #[serde(skip_deserializing)]
23 Url(UrlBuf),
24 #[serde(skip)]
25 Path(PathBufDyn),
26 #[serde(skip)]
27 Bytes(Vec<u8>),
28 #[serde(skip)]
29 Any(Box<dyn DataAny>),
30}
31
32impl From<()> for Data {
33 fn from(_: ()) -> Self { Self::Nil }
34}
35
36impl From<bool> for Data {
37 fn from(value: bool) -> Self { Self::Boolean(value) }
38}
39
40impl From<i32> for Data {
41 fn from(value: i32) -> Self { Self::Integer(value as i64) }
42}
43
44impl From<i64> for Data {
45 fn from(value: i64) -> Self { Self::Integer(value) }
46}
47
48impl From<f64> for Data {
49 fn from(value: f64) -> Self { Self::Number(value) }
50}
51
52impl From<usize> for Data {
53 fn from(value: usize) -> Self { Self::Id(value.into()) }
54}
55
56impl From<String> for Data {
57 fn from(value: String) -> Self { Self::String(Cow::Owned(value)) }
58}
59
60impl From<SStr> for Data {
61 fn from(value: SStr) -> Self { Self::String(value) }
62}
63
64impl From<Id> for Data {
65 fn from(value: Id) -> Self { Self::Id(value) }
66}
67
68impl From<Url<'_>> for Data {
69 fn from(value: Url) -> Self { Self::Url(value.into()) }
70}
71
72impl From<UrlBuf> for Data {
73 fn from(value: UrlBuf) -> Self { Self::Url(value) }
74}
75
76impl From<&UrlBuf> for Data {
77 fn from(value: &UrlBuf) -> Self { Self::Url(value.clone()) }
78}
79
80impl From<&str> for Data {
81 fn from(value: &str) -> Self { Self::String(Cow::Owned(value.to_owned())) }
82}
83
84impl<T> FromIterator<T> for Data
85where
86 T: Into<Data>,
87{
88 fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
89 Self::List(iter.into_iter().map(Into::into).collect())
90 }
91}
92
93impl TryFrom<&Data> for bool {
94 type Error = anyhow::Error;
95
96 fn try_from(value: &Data) -> Result<Self, Self::Error> {
97 match value {
98 Data::Boolean(b) => Ok(*b),
99 Data::String(s) if s == "no" => Ok(false),
100 Data::String(s) if s == "yes" => Ok(true),
101 _ => bail!("not a boolean"),
102 }
103 }
104}
105
106impl<'a> TryFrom<&'a Data> for &'a str {
107 type Error = anyhow::Error;
108
109 fn try_from(value: &'a Data) -> Result<Self, Self::Error> {
110 match value {
111 Data::String(s) => Ok(s),
112 _ => bail!("not a string"),
113 }
114 }
115}
116
117impl TryFrom<Data> for SStr {
118 type Error = anyhow::Error;
119
120 fn try_from(value: Data) -> Result<Self, Self::Error> {
121 value.into_sstr().ok_or_else(|| anyhow!("not a string"))
122 }
123}
124
125impl<'a> TryFrom<&'a Data> for Cow<'a, str> {
126 type Error = anyhow::Error;
127
128 fn try_from(value: &'a Data) -> Result<Self, Self::Error> {
129 match value {
130 Data::String(s) => Ok(Cow::Borrowed(s)),
131 _ => bail!("not a string"),
132 }
133 }
134}
135
136impl TryFrom<&Data> for String {
137 type Error = anyhow::Error;
138
139 fn try_from(value: &Data) -> Result<Self, Self::Error> {
140 value.try_into().map(|s: &str| s.to_owned())
141 }
142}
143
144impl TryFrom<Data> for String {
145 type Error = anyhow::Error;
146
147 fn try_from(value: Data) -> Result<Self, Self::Error> {
148 SStr::try_from(value).map(|s| s.into_owned())
149 }
150}
151
152impl TryFrom<&Data> for HashMap<DataKey, Data> {
153 type Error = anyhow::Error;
154
155 fn try_from(_: &Data) -> Result<Self, Self::Error> {
156 bail!("cannot take ownership of dict from &Data");
157 }
158}
159
160impl TryFrom<Data> for HashMap<DataKey, Data> {
161 type Error = anyhow::Error;
162
163 fn try_from(value: Data) -> Result<Self, Self::Error> {
164 match value {
165 Data::Dict(d) => Ok(d),
166 _ => bail!("not a dict"),
167 }
168 }
169}
170
171impl TryFrom<Data> for UrlCow<'static> {
172 type Error = anyhow::Error;
173
174 fn try_from(value: Data) -> Result<Self, Self::Error> {
175 match value {
176 Data::String(s) => s.try_into(),
177 Data::Url(u) => Ok(u.into()),
178 Data::Bytes(b) => b.try_into(),
179 _ => bail!("not a URL"),
180 }
181 }
182}
183
184impl TryFrom<Data> for UrlBuf {
185 type Error = anyhow::Error;
186
187 fn try_from(value: Data) -> Result<Self, Self::Error> { UrlCow::try_from(value).map(Into::into) }
188}
189
190impl<'a> TryFrom<&'a Data> for UrlCow<'a> {
191 type Error = anyhow::Error;
192
193 fn try_from(value: &'a Data) -> Result<Self, Self::Error> {
194 match value {
195 Data::String(s) => Self::try_from(&**s),
196 Data::Url(u) => Ok(u.into()),
197 Data::Bytes(b) => b.as_slice().try_into(),
198 _ => bail!("not a URL"),
199 }
200 }
201}
202
203impl TryFrom<&Data> for UrlBuf {
204 type Error = anyhow::Error;
205
206 fn try_from(value: &Data) -> Result<Self, Self::Error> { UrlCow::try_from(value).map(Into::into) }
207}
208
209impl<'a> TryFrom<&'a Data> for &'a [u8] {
210 type Error = anyhow::Error;
211
212 fn try_from(value: &'a Data) -> Result<Self, Self::Error> {
213 match value {
214 Data::Bytes(b) => Ok(b),
215 _ => bail!("not bytes"),
216 }
217 }
218}
219
220impl TryFrom<Data> for StrandBuf {
221 type Error = anyhow::Error;
222
223 fn try_from(value: Data) -> Result<Self, Self::Error> {
224 Ok(match value {
225 Data::String(s) => s.into_owned().into(),
226 Data::Path(p) => p.into_strand(),
227 Data::Bytes(b) => Self::Bytes(b),
228 _ => bail!("cannot convert to StrandBuf"),
229 })
230 }
231}
232
233impl TryFrom<&Data> for StrandBuf {
234 type Error = anyhow::Error;
235
236 fn try_from(value: &Data) -> Result<Self, Self::Error> {
237 Ok(match value {
238 Data::String(s) => s.to_string().into(),
239 Data::Path(p) => p.into_strand(),
240 Data::Bytes(b) => Self::Bytes(b.clone()),
241 _ => bail!("cannot convert to StrandBuf"),
242 })
243 }
244}
245
246impl PartialEq<bool> for Data {
247 fn eq(&self, other: &bool) -> bool { self.try_into().is_ok_and(|b| *other == b) }
248}
249
250impl Data {
251 pub fn as_str(&self) -> Option<&str> {
252 match self {
253 Self::String(s) => Some(s),
254 _ => None,
255 }
256 }
257
258 pub fn as_any<T: 'static>(&self) -> Option<&T> {
259 match self {
260 Self::Any(b) => (**b).as_any().downcast_ref::<T>(),
261 _ => None,
262 }
263 }
264
265 pub fn into_sstr(self) -> Option<SStr> {
266 match self {
267 Self::String(s) => Some(s),
268 _ => None,
269 }
270 }
271
272 pub fn into_any<T: 'static>(self) -> Option<T> {
273 match self {
274 Self::Any(b) => b.into_any().downcast::<T>().ok().map(|b| *b),
275 _ => None,
276 }
277 }
278
279 pub fn into_any2<T: 'static>(self) -> Result<T> {
281 if let Self::Any(b) = self
282 && let Ok(t) = b.into_any().downcast::<T>()
283 {
284 Ok(*t)
285 } else {
286 bail!("Failed to downcast Data into {}", std::any::type_name::<T>())
287 }
288 }
289}
290
291impl_into_integer!(Data, i8, i16, i32, i64, isize, u8, u16, u32, u64, usize, crate::Id);
292impl_into_number!(Data, f32, f64);