flams_ontology/uris/
base.rs1use 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 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}