llsd_rs/
lib.rs

1use std::{collections::HashMap, ops};
2
3use anyhow::Result;
4use chrono::{DateTime, FixedOffset, Utc};
5use enum_as_inner::EnumAsInner;
6use url::Url;
7use uuid::Uuid;
8
9pub mod binary;
10pub mod notation;
11pub mod rpc;
12pub mod xml;
13
14#[derive(Debug, Default, Clone, PartialEq, Eq)]
15pub enum Uri {
16    #[default]
17    Empty,
18    Url(Url),
19    String(String, url::ParseError),
20}
21
22impl Uri {
23    pub fn new() -> Self {
24        Uri::Empty
25    }
26
27    pub fn parse(uri: &str) -> Self {
28        let uri = uri.trim();
29        if uri.is_empty() {
30            return Uri::Empty;
31        }
32        match Url::parse(uri) {
33            Ok(url) => Uri::Url(url),
34            Err(e) => Uri::String(uri.to_string(), e),
35        }
36    }
37
38    pub fn as_str(&self) -> &str {
39        match self {
40            Uri::Url(url) => url.as_str(),
41            Uri::String(s, _) => s,
42            Uri::Empty => "",
43        }
44    }
45
46    pub fn is_empty(&self) -> bool {
47        matches!(self, Uri::Empty)
48    }
49
50    pub fn is_url(&self) -> bool {
51        matches!(self, Uri::Url(_))
52    }
53
54    pub fn error(&self) -> Option<url::ParseError> {
55        match self {
56            Uri::String(_, e) => Some(*e),
57            _ => None,
58        }
59    }
60}
61
62impl From<Url> for Uri {
63    fn from(uri: Url) -> Self {
64        Uri::Url(uri)
65    }
66}
67
68impl From<&str> for Uri {
69    fn from(uri: &str) -> Self {
70        Self::parse(uri)
71    }
72}
73
74impl From<String> for Uri {
75    fn from(uri: String) -> Self {
76        Self::parse(&uri)
77    }
78}
79
80impl From<&Uri> for String {
81    fn from(uri: &Uri) -> Self {
82        match uri {
83            Uri::Url(url) => url.to_string(),
84            Uri::String(s, _) => s.clone(),
85            Uri::Empty => String::new(),
86        }
87    }
88}
89
90impl<'a> From<&'a Uri> for &'a str {
91    fn from(uri: &'a Uri) -> Self {
92        match uri {
93            Uri::Url(url) => url.as_str(),
94            Uri::String(s, _) => s,
95            Uri::Empty => "",
96        }
97    }
98}
99
100impl TryFrom<&Uri> for Url {
101    type Error = url::ParseError;
102
103    fn try_from(uri: &Uri) -> core::result::Result<Self, Self::Error> {
104        match uri {
105            Uri::Url(url) => Ok(url.clone()),
106            Uri::String(_, e) => Err(*e),
107            Uri::Empty => Err(url::ParseError::EmptyHost),
108        }
109    }
110}
111
112#[derive(Debug, Default, Clone, EnumAsInner, PartialEq)]
113pub enum Llsd {
114    #[default]
115    Undefined,
116    Boolean(bool),
117    Integer(i32),
118    Real(f64),
119    String(String),
120    Uri(Uri),
121    Uuid(Uuid),
122    Date(DateTime<Utc>),
123    Binary(Vec<u8>),
124    Array(Vec<Llsd>),
125    Map(HashMap<String, Llsd>),
126}
127
128impl Llsd {
129    pub fn new() -> Self {
130        Llsd::Undefined
131    }
132
133    pub fn array() -> Self {
134        Llsd::Array(Vec::new())
135    }
136
137    pub fn map() -> Self {
138        Llsd::Map(HashMap::new())
139    }
140
141    pub fn clear(&mut self) {
142        *self = Llsd::Undefined;
143    }
144
145    pub fn push<T: Into<Llsd>>(mut self, llsd: T) -> Result<Self> {
146        match &mut self {
147            Llsd::Array(array) => array.push(llsd.into()),
148            Llsd::Undefined => {
149                self = Llsd::Array(vec![llsd.into()]);
150            }
151            _ => return Err(anyhow::Error::msg("not an array")),
152        }
153        Ok(self)
154    }
155
156    pub fn insert<K: Into<String>, T: Into<Llsd>>(mut self, key: K, llsd: T) -> Result<Self> {
157        match &mut self {
158            Llsd::Map(map) => {
159                map.insert(key.into(), llsd.into());
160            }
161            Llsd::Undefined => {
162                let mut map = HashMap::new();
163                map.insert(key.into(), llsd.into());
164                self = Llsd::Map(map);
165            }
166            _ => return Err(anyhow::Error::msg("not a map")),
167        }
168        Ok(self)
169    }
170
171    pub fn get(&self, index: impl Index) -> Option<&Llsd> {
172        index.index_into(self)
173    }
174
175    pub fn get_mut(&mut self, index: impl Index) -> Option<&mut Llsd> {
176        index.index_into_mut(self)
177    }
178
179    pub fn contains(&self, index: impl Index) -> bool {
180        self.get(index).is_some()
181    }
182
183    pub fn len(&self) -> usize {
184        match self {
185            Llsd::Array(a) => a.len(),
186            Llsd::Map(m) => m.len(),
187            _ => 0,
188        }
189    }
190
191    pub fn is_empty(&self) -> bool {
192        self.len() == 0
193    }
194
195    pub fn pointer(&self, pointer: &str) -> Option<&Llsd> {
196        if pointer.is_empty() {
197            return Some(self);
198        }
199        if !pointer.starts_with('/') {
200            return None;
201        }
202        pointer
203            .split('/')
204            .skip(1)
205            .map(|x| x.replace("~1", "/").replace("~0", "~"))
206            .try_fold(self, |target, token| match target {
207                Llsd::Array(array) => token.parse::<usize>().ok().and_then(|x| array.get(x)),
208                Llsd::Map(map) => map.get(&token),
209                _ => None,
210            })
211    }
212
213    pub fn pointer_mut(&mut self, pointer: &str) -> Option<&mut Llsd> {
214        if pointer.is_empty() {
215            return Some(self);
216        }
217        if !pointer.starts_with('/') {
218            return None;
219        }
220        pointer
221            .split('/')
222            .skip(1)
223            .map(|x| x.replace("~1", "/").replace("~0", "~"))
224            .try_fold(self, |target, token| match target {
225                Llsd::Array(array) => token.parse::<usize>().ok().and_then(|x| array.get_mut(x)),
226                Llsd::Map(map) => map.get_mut(&token),
227                _ => None,
228            })
229    }
230
231    pub fn take(&mut self) -> Self {
232        std::mem::replace(self, Llsd::Undefined)
233    }
234}
235
236impl From<bool> for Llsd {
237    fn from(llsd: bool) -> Self {
238        Llsd::Boolean(llsd)
239    }
240}
241
242macro_rules! impl_from_int {
243	($($t:ty),*) => {
244		$(
245			impl From<$t> for Llsd {
246				fn from(llsd: $t) -> Self {
247					Llsd::Integer(llsd as i32)
248				}
249			}
250		)*
251	};
252}
253
254impl_from_int!(u8, u16, u32, u64, i8, i16, i32, i64);
255
256macro_rules! impl_from_real {
257    ($($t:ty),*) => {
258        $(
259            impl From<$t> for Llsd {
260                fn from(llsd: $t) -> Self {
261                    Llsd::Real(llsd as f64)
262                }
263            }
264        )*
265    };
266}
267
268impl_from_real!(f32, f64);
269
270impl From<&str> for Llsd {
271    fn from(llsd: &str) -> Self {
272        Llsd::String(llsd.to_string())
273    }
274}
275
276impl From<Uuid> for Llsd {
277    fn from(llsd: Uuid) -> Self {
278        Llsd::Uuid(llsd)
279    }
280}
281
282impl From<Url> for Llsd {
283    fn from(llsd: Url) -> Self {
284        Llsd::Uri(llsd.into())
285    }
286}
287
288impl From<DateTime<Utc>> for Llsd {
289    fn from(llsd: DateTime<Utc>) -> Self {
290        Llsd::Date(llsd)
291    }
292}
293
294impl From<DateTime<FixedOffset>> for Llsd {
295    fn from(llsd: DateTime<FixedOffset>) -> Self {
296        Llsd::Date(llsd.with_timezone(&Utc))
297    }
298}
299
300impl From<&[u8]> for Llsd {
301    fn from(llsd: &[u8]) -> Self {
302        Llsd::Binary(Vec::from(llsd))
303    }
304}
305
306impl<const N: usize> From<[u8; N]> for Llsd {
307    fn from(llsd: [u8; N]) -> Self {
308        Llsd::Binary(llsd.to_vec())
309    }
310}
311
312impl<T: Into<Llsd>> From<Vec<T>> for Llsd {
313    fn from(llsd: Vec<T>) -> Self {
314        Llsd::Array(llsd.into_iter().map(Into::into).collect())
315    }
316}
317
318impl<K: Into<String>, V: Into<Llsd>> From<HashMap<K, V>> for Llsd {
319    fn from(llsd: HashMap<K, V>) -> Self {
320        Llsd::Map(
321            llsd.into_iter()
322                .map(|(k, v)| (k.into(), v.into()))
323                .collect(),
324        )
325    }
326}
327
328impl<T: Into<Llsd>> FromIterator<T> for Llsd {
329    fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
330        Llsd::Array(iter.into_iter().map(Into::into).collect())
331    }
332}
333
334impl<K: Into<String>, V: Into<Llsd>> FromIterator<(K, V)> for Llsd {
335    fn from_iter<I: IntoIterator<Item = (K, V)>>(iter: I) -> Self {
336        Llsd::Map(
337            iter.into_iter()
338                .map(|(k, v)| (k.into(), v.into()))
339                .collect(),
340        )
341    }
342}
343
344impl TryFrom<&Llsd> for Uuid {
345    type Error = anyhow::Error;
346
347    fn try_from(llsd: &Llsd) -> Result<Self> {
348        match llsd {
349            Llsd::Uuid(llsd) => Ok(*llsd),
350            Llsd::String(llsd) => Ok(Uuid::parse_str(llsd.as_str())?),
351            _ => Err(anyhow::Error::msg("not a UUID")),
352        }
353    }
354}
355
356impl TryFrom<&Llsd> for Url {
357    type Error = anyhow::Error;
358
359    fn try_from(llsd: &Llsd) -> Result<Self> {
360        match llsd {
361            Llsd::Uri(uri) => Ok(uri.try_into()?),
362            Llsd::String(llsd) => Ok(Url::parse(llsd.as_str())?),
363            _ => Err(anyhow::Error::msg("not a URL")),
364        }
365    }
366}
367
368mod private {
369    pub trait Sealed {}
370    impl Sealed for usize {}
371    impl Sealed for str {}
372    impl Sealed for String {}
373    impl<T> Sealed for &T where T: ?Sized + Sealed {}
374}
375
376pub trait Index: private::Sealed {
377    fn index_into<'v>(&self, v: &'v Llsd) -> Option<&'v Llsd>;
378    fn index_into_mut<'v>(&self, v: &'v mut Llsd) -> Option<&'v mut Llsd>;
379    fn index_or_insert<'v>(&self, v: &'v mut Llsd) -> &'v mut Llsd;
380}
381
382impl<I> ops::Index<I> for Llsd
383where
384    I: Index,
385{
386    type Output = Llsd;
387    fn index(&self, index: I) -> &Llsd {
388        static NULL: Llsd = Llsd::Undefined;
389        index.index_into(self).unwrap_or(&NULL)
390    }
391}
392
393impl Index for usize {
394    fn index_into<'v>(&self, v: &'v Llsd) -> Option<&'v Llsd> {
395        match v {
396            Llsd::Array(vec) => vec.get(*self),
397            _ => None,
398        }
399    }
400    fn index_into_mut<'v>(&self, v: &'v mut Llsd) -> Option<&'v mut Llsd> {
401        match v {
402            Llsd::Array(vec) => vec.get_mut(*self),
403            _ => None,
404        }
405    }
406    fn index_or_insert<'v>(&self, v: &'v mut Llsd) -> &'v mut Llsd {
407        match v {
408            Llsd::Array(vec) => {
409                let len = vec.len();
410                vec.get_mut(*self).unwrap_or_else(|| {
411                    panic!(
412                        "cannot access index {} of JSON array of length {}",
413                        self, len
414                    )
415                })
416            }
417            _ => panic!("cannot access index {}", self),
418        }
419    }
420}
421
422impl Index for str {
423    fn index_into<'v>(&self, v: &'v Llsd) -> Option<&'v Llsd> {
424        match v {
425            Llsd::Map(map) => map.get(self),
426            _ => None,
427        }
428    }
429    fn index_into_mut<'v>(&self, v: &'v mut Llsd) -> Option<&'v mut Llsd> {
430        match v {
431            Llsd::Map(map) => map.get_mut(self),
432            _ => None,
433        }
434    }
435    fn index_or_insert<'v>(&self, v: &'v mut Llsd) -> &'v mut Llsd {
436        if let Llsd::Undefined = v {
437            *v = Llsd::Map(HashMap::new());
438        }
439        match v {
440            Llsd::Map(map) => map.entry(self.to_owned()).or_insert(Llsd::Undefined),
441            _ => panic!("cannot access key {:?}", self),
442        }
443    }
444}
445
446impl<T> Index for &T
447where
448    T: ?Sized + Index,
449{
450    fn index_into<'v>(&self, v: &'v Llsd) -> Option<&'v Llsd> {
451        (**self).index_into(v)
452    }
453    fn index_into_mut<'v>(&self, v: &'v mut Llsd) -> Option<&'v mut Llsd> {
454        (**self).index_into_mut(v)
455    }
456    fn index_or_insert<'v>(&self, v: &'v mut Llsd) -> &'v mut Llsd {
457        (**self).index_or_insert(v)
458    }
459}
460
461impl Index for String {
462    fn index_into<'v>(&self, v: &'v Llsd) -> Option<&'v Llsd> {
463        self[..].index_into(v)
464    }
465    fn index_into_mut<'v>(&self, v: &'v mut Llsd) -> Option<&'v mut Llsd> {
466        self[..].index_into_mut(v)
467    }
468    fn index_or_insert<'v>(&self, v: &'v mut Llsd) -> &'v mut Llsd {
469        self[..].index_or_insert(v)
470    }
471}
472
473impl<I> ops::IndexMut<I> for Llsd
474where
475    I: Index,
476{
477    fn index_mut(&mut self, index: I) -> &mut Llsd {
478        index.index_or_insert(self)
479    }
480}