Skip to main content

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 derive;
11pub mod notation;
12pub mod rpc;
13pub mod xml;
14
15#[cfg(feature = "derive")]
16pub use llsd_rs_derive::{LlsdFrom, LlsdFromTo, LlsdInto};
17
18#[derive(Debug, Default, Clone, PartialEq, Eq)]
19pub enum Uri {
20    #[default]
21    Empty,
22    Url(Url),
23    String(String, url::ParseError),
24}
25
26impl Uri {
27    pub fn new() -> Self {
28        Uri::Empty
29    }
30
31    pub fn parse(uri: &str) -> Self {
32        let uri = uri.trim();
33        if uri.is_empty() {
34            return Uri::Empty;
35        }
36        match Url::parse(uri) {
37            Ok(url) => Uri::Url(url),
38            Err(e) => Uri::String(uri.to_string(), e),
39        }
40    }
41
42    pub fn as_str(&self) -> &str {
43        match self {
44            Uri::Url(url) => url.as_str(),
45            Uri::String(s, _) => s,
46            Uri::Empty => "",
47        }
48    }
49
50    pub fn is_empty(&self) -> bool {
51        matches!(self, Uri::Empty)
52    }
53
54    pub fn is_url(&self) -> bool {
55        matches!(self, Uri::Url(_))
56    }
57
58    pub fn error(&self) -> Option<url::ParseError> {
59        match self {
60            Uri::String(_, e) => Some(*e),
61            _ => None,
62        }
63    }
64}
65
66impl From<Url> for Uri {
67    fn from(uri: Url) -> Self {
68        Uri::Url(uri)
69    }
70}
71
72impl From<&str> for Uri {
73    fn from(uri: &str) -> Self {
74        Self::parse(uri)
75    }
76}
77
78impl From<String> for Uri {
79    fn from(uri: String) -> Self {
80        Self::parse(&uri)
81    }
82}
83
84impl From<&Uri> for String {
85    fn from(uri: &Uri) -> Self {
86        match uri {
87            Uri::Url(url) => url.to_string(),
88            Uri::String(s, _) => s.clone(),
89            Uri::Empty => String::new(),
90        }
91    }
92}
93
94impl<'a> From<&'a Uri> for &'a str {
95    fn from(uri: &'a Uri) -> Self {
96        match uri {
97            Uri::Url(url) => url.as_str(),
98            Uri::String(s, _) => s,
99            Uri::Empty => "",
100        }
101    }
102}
103
104impl TryFrom<&Uri> for Url {
105    type Error = url::ParseError;
106
107    fn try_from(uri: &Uri) -> core::result::Result<Self, Self::Error> {
108        match uri {
109            Uri::Url(url) => Ok(url.clone()),
110            Uri::String(_, e) => Err(*e),
111            Uri::Empty => Err(url::ParseError::EmptyHost),
112        }
113    }
114}
115
116#[derive(Debug, Default, Clone, EnumAsInner, PartialEq)]
117pub enum Llsd {
118    #[default]
119    Undefined,
120    Boolean(bool),
121    Integer(i32),
122    Real(f64),
123    String(String),
124    Uri(Uri),
125    Uuid(Uuid),
126    Date(DateTime<Utc>),
127    Binary(Vec<u8>),
128    Array(Vec<Llsd>),
129    Map(HashMap<String, Llsd>),
130}
131
132impl Llsd {
133    pub fn new() -> Self {
134        Llsd::Undefined
135    }
136
137    pub fn array() -> Self {
138        Llsd::Array(Vec::new())
139    }
140
141    pub fn map() -> Self {
142        Llsd::Map(HashMap::new())
143    }
144
145    pub fn clear(&mut self) {
146        *self = Llsd::Undefined;
147    }
148
149    pub fn push<T: Into<Llsd>>(mut self, llsd: T) -> Result<Self> {
150        match &mut self {
151            Llsd::Array(array) => array.push(llsd.into()),
152            Llsd::Undefined => {
153                self = Llsd::Array(vec![llsd.into()]);
154            }
155            _ => return Err(anyhow::Error::msg("not an array")),
156        }
157        Ok(self)
158    }
159
160    pub fn insert<K: Into<String>, T: Into<Llsd>>(mut self, key: K, llsd: T) -> Result<Self> {
161        match &mut self {
162            Llsd::Map(map) => {
163                map.insert(key.into(), llsd.into());
164            }
165            Llsd::Undefined => {
166                let mut map = HashMap::new();
167                map.insert(key.into(), llsd.into());
168                self = Llsd::Map(map);
169            }
170            _ => return Err(anyhow::Error::msg("not a map")),
171        }
172        Ok(self)
173    }
174
175    pub fn get(&self, index: impl Index) -> Option<&Llsd> {
176        index.index_into(self)
177    }
178
179    pub fn get_mut(&mut self, index: impl Index) -> Option<&mut Llsd> {
180        index.index_into_mut(self)
181    }
182
183    pub fn contains(&self, index: impl Index) -> bool {
184        self.get(index).is_some()
185    }
186
187    pub fn len(&self) -> usize {
188        match self {
189            Llsd::Array(a) => a.len(),
190            Llsd::Map(m) => m.len(),
191            _ => 0,
192        }
193    }
194
195    pub fn is_empty(&self) -> bool {
196        self.len() == 0
197    }
198
199    pub fn pointer(&self, pointer: &str) -> Option<&Llsd> {
200        if pointer.is_empty() {
201            return Some(self);
202        }
203        if !pointer.starts_with('/') {
204            return None;
205        }
206        pointer
207            .split('/')
208            .skip(1)
209            .map(|x| x.replace("~1", "/").replace("~0", "~"))
210            .try_fold(self, |target, token| match target {
211                Llsd::Array(array) => token.parse::<usize>().ok().and_then(|x| array.get(x)),
212                Llsd::Map(map) => map.get(&token),
213                _ => None,
214            })
215    }
216
217    pub fn pointer_mut(&mut self, pointer: &str) -> Option<&mut Llsd> {
218        if pointer.is_empty() {
219            return Some(self);
220        }
221        if !pointer.starts_with('/') {
222            return None;
223        }
224        pointer
225            .split('/')
226            .skip(1)
227            .map(|x| x.replace("~1", "/").replace("~0", "~"))
228            .try_fold(self, |target, token| match target {
229                Llsd::Array(array) => token.parse::<usize>().ok().and_then(|x| array.get_mut(x)),
230                Llsd::Map(map) => map.get_mut(&token),
231                _ => None,
232            })
233    }
234
235    pub fn take(&mut self) -> Self {
236        std::mem::replace(self, Llsd::Undefined)
237    }
238}
239
240impl From<bool> for Llsd {
241    fn from(llsd: bool) -> Self {
242        Llsd::Boolean(llsd)
243    }
244}
245
246impl From<&bool> for Llsd {
247    fn from(v: &bool) -> Self {
248        Llsd::Boolean(*v)
249    }
250}
251
252macro_rules! impl_from_int {
253    ($($t:ty),*) => {
254            $(
255            impl From<$t> for Llsd {
256                fn from(llsd: $t) -> Self {
257                    Llsd::Integer(llsd as i32)
258                }
259            }
260            impl TryFrom<&Llsd> for $t {
261                type Error = anyhow::Error;
262
263                fn try_from(llsd: &Llsd) -> Result<Self> {
264                    match llsd {
265                        Llsd::Integer(value) => Ok(*value as $t),
266                        Llsd::Real(value) => Ok(*value as $t),
267                        Llsd::Boolean(value) => Ok(if *value { 1 } else { 0 } as $t),
268                        Llsd::String(value) => {
269                            value.parse::<$t>().map_err(|_| anyhow::Error::msg("Invalid integer"))
270                        }
271                        _ => Err(anyhow::Error::msg("Expected LLSD Integer")),
272                    }
273                }
274            }
275		)*
276	};
277}
278
279impl_from_int!(u8, u16, u32, u64, i8, i16, i32, i64);
280
281macro_rules! impl_from_real {
282    ($($t:ty),*) => {
283        $(
284            impl From<$t> for Llsd {
285                fn from(llsd: $t) -> Self {
286                    Llsd::Real(llsd as f64)
287                }
288            }
289            impl From<&$t> for Llsd {
290                fn from(llsd: &$t) -> Self {
291                    Llsd::Real(*llsd as f64)
292                }
293            }
294            impl TryFrom<&Llsd> for $t {
295                type Error = anyhow::Error;
296
297                fn try_from(llsd: &Llsd) -> Result<Self> {
298                    match llsd {
299                        Llsd::Real(value) => Ok(*value as $t),
300                        Llsd::Integer(value) => Ok(*value as $t),
301                        Llsd::Boolean(value) => Ok(if *value { 1.0 } else { 0.0 } as $t),
302                        Llsd::String(value) => {
303                            value.parse::<$t>().map_err(|_| anyhow::Error::msg("Invalid real"))
304                        }
305                        _ => Err(anyhow::Error::msg("Expected LLSD Real")),
306                    }
307                }
308            }
309        )*
310    };
311}
312
313impl_from_real!(f32, f64);
314
315impl From<&str> for Llsd {
316    fn from(llsd: &str) -> Self {
317        Llsd::String(llsd.to_string())
318    }
319}
320
321impl From<String> for Llsd {
322    fn from(llsd: String) -> Self {
323        Llsd::String(llsd)
324    }
325}
326
327impl From<&String> for Llsd {
328    fn from(v: &String) -> Self {
329        Llsd::String(v.clone())
330    }
331}
332
333impl From<Uuid> for Llsd {
334    fn from(llsd: Uuid) -> Self {
335        Llsd::Uuid(llsd)
336    }
337}
338
339impl From<&Uuid> for Llsd {
340    fn from(v: &Uuid) -> Self {
341        Llsd::Uuid(*v)
342    }
343}
344
345impl From<Url> for Llsd {
346    fn from(llsd: Url) -> Self {
347        Llsd::Uri(llsd.into())
348    }
349}
350
351impl From<&Url> for Llsd {
352    fn from(v: &Url) -> Self {
353        Llsd::Uri(v.clone().into())
354    }
355}
356
357impl From<DateTime<Utc>> for Llsd {
358    fn from(llsd: DateTime<Utc>) -> Self {
359        Llsd::Date(llsd)
360    }
361}
362
363impl From<&DateTime<Utc>> for Llsd {
364    fn from(v: &DateTime<Utc>) -> Self {
365        Llsd::Date(*v)
366    }
367}
368
369impl From<DateTime<FixedOffset>> for Llsd {
370    fn from(llsd: DateTime<FixedOffset>) -> Self {
371        Llsd::Date(llsd.with_timezone(&Utc))
372    }
373}
374
375impl From<&DateTime<FixedOffset>> for Llsd {
376    fn from(v: &DateTime<FixedOffset>) -> Self {
377        Llsd::Date(v.with_timezone(&Utc))
378    }
379}
380
381impl From<&[u8]> for Llsd {
382    fn from(llsd: &[u8]) -> Self {
383        Llsd::Binary(Vec::from(llsd))
384    }
385}
386
387impl<const N: usize> From<[u8; N]> for Llsd {
388    fn from(llsd: [u8; N]) -> Self {
389        Llsd::Binary(llsd.to_vec())
390    }
391}
392
393impl<T: Into<Llsd>> From<Vec<T>> for Llsd {
394    fn from(llsd: Vec<T>) -> Self {
395        Llsd::Array(llsd.into_iter().map(Into::into).collect())
396    }
397}
398
399impl<K: Into<String>, V: Into<Llsd>> From<HashMap<K, V>> for Llsd {
400    fn from(llsd: HashMap<K, V>) -> Self {
401        Llsd::Map(
402            llsd.into_iter()
403                .map(|(k, v)| (k.into(), v.into()))
404                .collect(),
405        )
406    }
407}
408
409// Tuple support (2..=4) explicit implementations -------------------------------------------
410impl<A: Into<Llsd>, B: Into<Llsd>> From<(A, B)> for Llsd {
411    fn from(t: (A, B)) -> Self {
412        let (a, b) = t;
413        Llsd::Array(vec![a.into(), b.into()])
414    }
415}
416impl<A, B> TryFrom<&Llsd> for (A, B)
417where
418    for<'x> A: TryFrom<&'x Llsd, Error = anyhow::Error>,
419    for<'x> B: TryFrom<&'x Llsd, Error = anyhow::Error>,
420{
421    type Error = anyhow::Error;
422    fn try_from(v: &Llsd) -> Result<Self> {
423        if let Llsd::Array(a) = v {
424            if a.len() == 2 {
425                Ok((A::try_from(&a[0])?, B::try_from(&a[1])?))
426            } else {
427                Err(anyhow::Error::msg("Expected array of length 2"))
428            }
429        } else {
430            Err(anyhow::Error::msg("Expected LLSD Array"))
431        }
432    }
433}
434
435impl<A: Into<Llsd>, B: Into<Llsd>, C: Into<Llsd>> From<(A, B, C)> for Llsd {
436    fn from(t: (A, B, C)) -> Self {
437        let (a, b, c) = t;
438        Llsd::Array(vec![a.into(), b.into(), c.into()])
439    }
440}
441impl<A, B, C> TryFrom<&Llsd> for (A, B, C)
442where
443    for<'x> A: TryFrom<&'x Llsd, Error = anyhow::Error>,
444    for<'x> B: TryFrom<&'x Llsd, Error = anyhow::Error>,
445    for<'x> C: TryFrom<&'x Llsd, Error = anyhow::Error>,
446{
447    type Error = anyhow::Error;
448    fn try_from(v: &Llsd) -> Result<Self> {
449        if let Llsd::Array(a) = v {
450            if a.len() == 3 {
451                Ok((
452                    A::try_from(&a[0])?,
453                    B::try_from(&a[1])?,
454                    C::try_from(&a[2])?,
455                ))
456            } else {
457                Err(anyhow::Error::msg("Expected array of length 3"))
458            }
459        } else {
460            Err(anyhow::Error::msg("Expected LLSD Array"))
461        }
462    }
463}
464
465impl<A: Into<Llsd>, B: Into<Llsd>, C: Into<Llsd>, D: Into<Llsd>> From<(A, B, C, D)> for Llsd {
466    fn from(t: (A, B, C, D)) -> Self {
467        let (a, b, c, d) = t;
468        Llsd::Array(vec![a.into(), b.into(), c.into(), d.into()])
469    }
470}
471impl<A, B, C, D> TryFrom<&Llsd> for (A, B, C, D)
472where
473    for<'x> A: TryFrom<&'x Llsd, Error = anyhow::Error>,
474    for<'x> B: TryFrom<&'x Llsd, Error = anyhow::Error>,
475    for<'x> C: TryFrom<&'x Llsd, Error = anyhow::Error>,
476    for<'x> D: TryFrom<&'x Llsd, Error = anyhow::Error>,
477{
478    type Error = anyhow::Error;
479    fn try_from(v: &Llsd) -> Result<Self> {
480        if let Llsd::Array(a) = v {
481            if a.len() == 4 {
482                Ok((
483                    A::try_from(&a[0])?,
484                    B::try_from(&a[1])?,
485                    C::try_from(&a[2])?,
486                    D::try_from(&a[3])?,
487                ))
488            } else {
489                Err(anyhow::Error::msg("Expected array of length 4"))
490            }
491        } else {
492            Err(anyhow::Error::msg("Expected LLSD Array"))
493        }
494    }
495}
496
497impl<K: Into<String>, V: Into<Llsd>> FromIterator<(K, V)> for Llsd {
498    fn from_iter<I: IntoIterator<Item = (K, V)>>(iter: I) -> Self {
499        Llsd::Map(
500            iter.into_iter()
501                .map(|(k, v)| (k.into(), v.into()))
502                .collect(),
503        )
504    }
505}
506
507impl TryFrom<&Llsd> for Uuid {
508    type Error = anyhow::Error;
509
510    fn try_from(llsd: &Llsd) -> Result<Self> {
511        match llsd {
512            Llsd::Uuid(llsd) => Ok(*llsd),
513            Llsd::String(llsd) => Ok(Uuid::parse_str(llsd.as_str())?),
514            _ => Err(anyhow::Error::msg("not a UUID")),
515        }
516    }
517}
518
519impl TryFrom<&Llsd> for Url {
520    type Error = anyhow::Error;
521
522    fn try_from(llsd: &Llsd) -> Result<Self> {
523        match llsd {
524            Llsd::Uri(uri) => Ok(uri.try_into()?),
525            Llsd::String(llsd) => Ok(Url::parse(llsd.as_str())?),
526            _ => Err(anyhow::Error::msg("not a URL")),
527        }
528    }
529}
530
531mod private {
532    pub trait Sealed {}
533    impl Sealed for usize {}
534    impl Sealed for str {}
535    impl Sealed for String {}
536    impl<T> Sealed for &T where T: ?Sized + Sealed {}
537}
538
539pub trait Index: private::Sealed {
540    fn index_into<'v>(&self, v: &'v Llsd) -> Option<&'v Llsd>;
541    fn index_into_mut<'v>(&self, v: &'v mut Llsd) -> Option<&'v mut Llsd>;
542    fn index_or_insert<'v>(&self, v: &'v mut Llsd) -> &'v mut Llsd;
543}
544
545impl<I> ops::Index<I> for Llsd
546where
547    I: Index,
548{
549    type Output = Llsd;
550    fn index(&self, index: I) -> &Llsd {
551        static NULL: Llsd = Llsd::Undefined;
552        index.index_into(self).unwrap_or(&NULL)
553    }
554}
555
556impl Index for usize {
557    fn index_into<'v>(&self, v: &'v Llsd) -> Option<&'v Llsd> {
558        match v {
559            Llsd::Array(vec) => vec.get(*self),
560            _ => None,
561        }
562    }
563    fn index_into_mut<'v>(&self, v: &'v mut Llsd) -> Option<&'v mut Llsd> {
564        match v {
565            Llsd::Array(vec) => vec.get_mut(*self),
566            _ => None,
567        }
568    }
569    fn index_or_insert<'v>(&self, v: &'v mut Llsd) -> &'v mut Llsd {
570        match v {
571            Llsd::Array(vec) => {
572                let len = vec.len();
573                vec.get_mut(*self).unwrap_or_else(|| {
574                    panic!(
575                        "cannot access index {} of JSON array of length {}",
576                        self, len
577                    )
578                })
579            }
580            _ => panic!("cannot access index {}", self),
581        }
582    }
583}
584
585impl Index for str {
586    fn index_into<'v>(&self, v: &'v Llsd) -> Option<&'v Llsd> {
587        match v {
588            Llsd::Map(map) => map.get(self),
589            _ => None,
590        }
591    }
592    fn index_into_mut<'v>(&self, v: &'v mut Llsd) -> Option<&'v mut Llsd> {
593        match v {
594            Llsd::Map(map) => map.get_mut(self),
595            _ => None,
596        }
597    }
598    fn index_or_insert<'v>(&self, v: &'v mut Llsd) -> &'v mut Llsd {
599        if let Llsd::Undefined = v {
600            *v = Llsd::Map(HashMap::new());
601        }
602        match v {
603            Llsd::Map(map) => map.entry(self.to_owned()).or_insert(Llsd::Undefined),
604            _ => panic!("cannot access key {:?}", self),
605        }
606    }
607}
608
609impl<T> Index for &T
610where
611    T: ?Sized + Index,
612{
613    fn index_into<'v>(&self, v: &'v Llsd) -> Option<&'v Llsd> {
614        (**self).index_into(v)
615    }
616    fn index_into_mut<'v>(&self, v: &'v mut Llsd) -> Option<&'v mut Llsd> {
617        (**self).index_into_mut(v)
618    }
619    fn index_or_insert<'v>(&self, v: &'v mut Llsd) -> &'v mut Llsd {
620        (**self).index_or_insert(v)
621    }
622}
623
624impl Index for String {
625    fn index_into<'v>(&self, v: &'v Llsd) -> Option<&'v Llsd> {
626        self[..].index_into(v)
627    }
628    fn index_into_mut<'v>(&self, v: &'v mut Llsd) -> Option<&'v mut Llsd> {
629        self[..].index_into_mut(v)
630    }
631    fn index_or_insert<'v>(&self, v: &'v mut Llsd) -> &'v mut Llsd {
632        self[..].index_or_insert(v)
633    }
634}
635
636impl<I> ops::IndexMut<I> for Llsd
637where
638    I: Index,
639{
640    fn index_mut(&mut self, index: I) -> &mut Llsd {
641        index.index_or_insert(self)
642    }
643}
644
645impl TryFrom<&Llsd> for bool {
646    type Error = anyhow::Error;
647
648    fn try_from(llsd: &Llsd) -> anyhow::Result<Self> {
649        if let Some(value) = llsd.as_boolean() {
650            Ok(*value)
651        } else {
652            Err(anyhow::Error::msg("Expected LLSD Boolean"))
653        }
654    }
655}
656
657impl TryFrom<&Llsd> for String {
658    type Error = anyhow::Error;
659
660    fn try_from(llsd: &Llsd) -> anyhow::Result<Self> {
661        if let Some(value) = llsd.as_string() {
662            Ok(value.clone())
663        } else {
664            Err(anyhow::Error::msg("Expected LLSD String"))
665        }
666    }
667}
668
669impl<T> TryFrom<&Llsd> for Vec<T>
670where
671    T: for<'a> TryFrom<&'a Llsd, Error = anyhow::Error>,
672{
673    type Error = anyhow::Error;
674
675    fn try_from(llsd: &Llsd) -> anyhow::Result<Self> {
676        if let Some(array) = llsd.as_array() {
677            array.iter().map(|item| T::try_from(item)).collect()
678        } else {
679            Err(anyhow::Error::msg("Expected LLSD Array"))
680        }
681    }
682}
683
684impl<V> TryFrom<&Llsd> for HashMap<String, V>
685where
686    V: for<'a> TryFrom<&'a Llsd, Error = anyhow::Error>,
687{
688    type Error = anyhow::Error;
689
690    fn try_from(llsd: &Llsd) -> anyhow::Result<Self> {
691        if let Some(map) = llsd.as_map() {
692            map.iter()
693                .map(|(k, v)| Ok((k.clone(), V::try_from(v)?)))
694                .collect()
695        } else {
696            Err(anyhow::Error::msg("Expected LLSD Map"))
697        }
698    }
699}