flams_ontology/uris/
base.rs

1use crate::uris::errors::URIParseError;
2use crate::uris::macros::debugdisplay;
3use crate::uris::{sealed, URIOrRefTrait, URIRef, URIRefTrait, URITrait, URI};
4use either::Either;
5use flams_utils::gc::{TArcInterned, TArcInterner};
6use lazy_static::lazy_static;
7use parking_lot::Mutex;
8use std::fmt::Display;
9use std::str::{FromStr, Split};
10use triomphe::Arc;
11
12lazy_static! {
13    static ref BASE_URIS: Arc<Mutex<TArcInterner<str, 4, 100>>> =
14        Arc::new(Mutex::new(TArcInterner::default()));
15}
16
17#[derive(Clone, PartialEq, Eq, Hash)]
18pub struct BaseURI(TArcInterned<str>);
19impl BaseURI {
20    #[must_use]
21    #[inline]
22    pub fn new_unchecked(s: &str) -> Self {
23        Self(BASE_URIS.lock().get_or_intern(s))
24    }
25    /// ### Errors
26    /// Returns an error if the string is not a valid URL, or has a query or fragment.
27    pub fn new_checked(s: &str) -> Result<Self, url::ParseError> {
28        use url::{ParseError, Url};
29        let url = Url::parse(s)?;
30        if url.cannot_be_a_base() {
31            return Err(ParseError::RelativeUrlWithoutBase);
32        }
33        if url.query().is_some() || url.fragment().is_some() {
34            return Err(ParseError::RelativeUrlWithoutBase);
35        }
36        Ok(Self::new_unchecked(s))
37    }
38    fn new_checked_partially(s: &str) -> Result<Self, url::ParseError> {
39        use url::{ParseError, Url};
40        let url = Url::parse(s)?;
41        if url.cannot_be_a_base() {
42            return Err(ParseError::RelativeUrlWithoutBase);
43        }
44        Ok(Self::new_unchecked(s))
45    }
46
47    pub(super) fn pre_parse(s: &str) -> Result<Either<Self, (Self, Split<char>)>, URIParseError> {
48        #[inline]
49        fn do_base(s: &str) -> Result<BaseURI, URIParseError> {
50            Ok(BaseURI::new_checked_partially(s)?)
51        }
52
53        let Some((base, rest)) = s.split_once('?') else {
54            return do_base(s).map(Either::Left);
55        };
56        let base = do_base(base)?;
57        Ok(if rest.is_empty() {
58            Either::Left(base)
59        } else {
60            Either::Right((base, rest.split('&')))
61        })
62    }
63}
64impl AsRef<str> for BaseURI {
65    #[inline]
66    fn as_ref(&self) -> &str {
67        self.0.as_ref()
68    }
69}
70impl Display for BaseURI {
71    #[inline]
72    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
73        f.write_str(self.as_ref())
74    }
75}
76debugdisplay!(BaseURI);
77impl URIOrRefTrait for BaseURI {
78    #[inline]
79    fn base(&self) -> &Self {
80        self
81    }
82    fn as_uri(&self) -> URIRef {
83        URIRef::Base(self)
84    }
85}
86impl URITrait for BaseURI {
87    type Ref<'a> = &'a Self;
88}
89impl<'a> URIRefTrait<'a> for &'a BaseURI {
90    type Owned = BaseURI;
91    #[inline]
92    fn owned(self) -> BaseURI {
93        self.clone()
94    }
95}
96
97impl FromStr for BaseURI {
98    type Err = URIParseError;
99    #[inline]
100    fn from_str(s: &str) -> Result<Self, Self::Err> {
101        Self::new_checked(s).map_err(Into::into)
102    }
103}
104
105#[cfg(feature = "serde")]
106mod serde_impl {
107    use super::BaseURI;
108    use crate::uris::serialize;
109    serialize!(as BaseURI);
110    impl<'de> serde::Deserialize<'de> for BaseURI {
111        fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
112            let s = String::deserialize(deserializer)?;
113            Self::new_checked(&s).map_err(serde::de::Error::custom)
114        }
115    }
116}