flams_ontology/uris/
archives.rs

1use crate::uris::errors::URIParseError;
2use crate::uris::macros::debugdisplay;
3use crate::uris::{
4    BaseURI, Name, PathURIRef, PathURITrait, URIOrRefTrait, URIRef, URIRefTrait, URITrait, URI,
5};
6use const_format::concatcp;
7use either::Either;
8use flams_utils::gc::{TArcInterned, TArcInterner};
9use lazy_static::lazy_static;
10use parking_lot::Mutex;
11use std::convert::Infallible;
12use std::fmt::Display;
13use std::str::{FromStr, Split};
14use triomphe::Arc;
15
16#[cfg(feature = "wasm")]
17#[cfg_attr(feature = "wasm", wasm_bindgen::prelude::wasm_bindgen(typescript_custom_section))]
18const TS_ARCHIVE_ID: &str = "export type ArchiveId = string;";
19
20lazy_static! {
21    static ref ARCHIVE_IDS: Arc<Mutex<TArcInterner<str, 4, 100>>> =
22        Arc::new(Mutex::new(TArcInterner::default()));
23
24    static ref NO_ARCHIVE_ID:ArchiveId = ArchiveId::new("no/archive");
25    static ref NO_ARCHIVE_URI:ArchiveURI = "http://unknown.source?a=no/archive".parse().unwrap_or_else(|_| unreachable!());
26}
27
28#[derive(Clone, PartialEq, Eq, Hash)]
29pub struct ArchiveId(TArcInterned<str>);
30impl ArchiveId {
31    #[must_use]
32    pub fn last_name(&self) -> &str {
33        let s = self.as_ref();
34        s.rsplit_once('/').map_or(s, |(_, s)| s)
35    }
36
37    #[inline]#[must_use]
38    pub fn no_archive() -> Self { NO_ARCHIVE_ID.clone() }
39
40    #[must_use]
41    pub fn steps(&self) -> std::str::Split<char> {
42        self.as_ref().split('/')
43    }
44
45    #[must_use]
46    pub fn is_meta(&self) -> bool {
47        self.last_name().eq_ignore_ascii_case("meta-inf")
48    }
49
50    #[must_use]
51    pub fn new(s: &str) -> Self {
52        Self(ARCHIVE_IDS.lock().get_or_intern(s))
53    }
54}
55
56impl Ord for ArchiveId {
57    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
58        use std::cmp::Ordering::*;
59        let mut left = self.steps();
60        let mut right = other.steps();
61        loop {
62            match (left.next(), right.next()) {
63                (None, None) => return Equal,
64                (None, _) => return Less,
65                (_, None) => return Greater,
66                (Some(l), Some(r)) => match l.cmp(r) {
67                    Equal => (),
68                    _ if self.is_meta() && left.next().is_none() && right.next().is_none() => {
69                        return Less
70                    }
71                    _ if other.is_meta() && left.next().is_none() && right.next().is_none() => {
72                        return Greater
73                    }
74                    o => return o,
75                },
76            }
77        }
78    }
79}
80impl PartialOrd for ArchiveId {
81    #[inline]
82    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
83        Some(self.cmp(other))
84    }
85}
86
87impl FromStr for ArchiveId {
88    type Err = Infallible;
89    fn from_str(s: &str) -> Result<Self, Self::Err> {
90        Ok(Self(ARCHIVE_IDS.lock().get_or_intern(s)))
91    }
92}
93impl AsRef<str> for ArchiveId {
94    #[inline]
95    fn as_ref(&self) -> &str {
96        self.0.as_ref()
97    }
98}
99
100impl Display for ArchiveId {
101    #[inline]
102    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
103        f.write_str(self.as_ref())
104    }
105}
106debugdisplay!(ArchiveId);
107#[derive(Clone, PartialEq, Eq, Hash)]
108pub struct ArchiveURI {
109    pub(super) base: super::BaseURI,
110    pub(super) archive: ArchiveId,
111}
112impl Display for ArchiveURI {
113    #[inline]
114    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
115        write!(f, "{}?{}={}", self.base, Self::SEPARATOR, self.archive)
116    }
117}
118debugdisplay!(ArchiveURI);
119
120impl ArchiveURI {
121    #[must_use]
122    pub fn no_archive() -> Self { NO_ARCHIVE_URI.clone() }
123    #[must_use]
124    #[allow(clippy::missing_const_for_fn)]
125    pub fn new(base: BaseURI, archive: ArchiveId) -> Self {
126        Self { base, archive }
127    }
128    pub const SEPARATOR: char = 'a';
129    pub(super) fn pre_parse<R>(
130        s: &str,
131        uri_kind: &'static str,
132        f: impl FnOnce(Self, Split<char>) -> Result<R, URIParseError>,
133    ) -> Result<R, URIParseError> {
134        let Either::Right((base, mut split)) = BaseURI::pre_parse(s)? else {
135            return Err(URIParseError::MissingPartFor {
136                uri_kind,
137                part: "archive id",
138                original: s.to_string(),
139            });
140        };
141        let Some(archive) = split.next() else {
142            unreachable!()
143        };
144        if !archive.starts_with(concatcp!(ArchiveURI::SEPARATOR, "=")) {
145            return Err(URIParseError::MissingPartFor {
146                uri_kind,
147                part: "archive id",
148                original: s.to_string(),
149            });
150        }
151        let archive = Self {
152            base,
153            archive: ArchiveId::new(&archive[2..]),
154        };
155        f(archive, split)
156    }
157}
158impl FromStr for ArchiveURI {
159    type Err = URIParseError;
160    fn from_str(s: &str) -> Result<Self, Self::Err> {
161        Self::pre_parse(s, "archive uri", |a, mut split| {
162            if split.next().is_some() {
163                return Err(URIParseError::TooManyPartsFor {
164                    uri_kind: "archive uri",
165                    original: s.to_string(),
166                });
167            }
168            Ok(a)
169        })
170    }
171}
172impl URIOrRefTrait for ArchiveURI {
173    #[inline]
174    fn base(&self) -> &BaseURI {
175        &self.base
176    }
177    fn as_uri(&self) -> URIRef {
178        URIRef::Archive(self.archive_uri())
179    }
180}
181impl URITrait for ArchiveURI {
182    type Ref<'a> = ArchiveURIRef<'a>;
183}
184
185pub trait ArchiveURITrait: URIOrRefTrait {
186    fn archive_uri(&self) -> ArchiveURIRef;
187
188    #[inline]
189    fn archive_id(&self) -> &ArchiveId {
190        self.archive_uri().archive
191    }
192}
193impl ArchiveURITrait for ArchiveURI {
194    #[inline]
195    fn archive_uri(&self) -> ArchiveURIRef {
196        ArchiveURIRef {
197            base: &self.base,
198            archive: &self.archive,
199        }
200    }
201    #[inline]
202    fn archive_id(&self) -> &ArchiveId {
203        &self.archive
204    }
205}
206
207impl PathURITrait for ArchiveURI {
208    fn as_path(&self) -> PathURIRef {
209        PathURIRef {
210            archive: self.archive_uri(),
211            path: None,
212        }
213    }
214    #[inline]
215    fn path(&self) -> Option<&Name> {
216        None
217    }
218}
219
220#[derive(Clone, Copy, PartialEq, Eq, Hash)]
221pub struct ArchiveURIRef<'a> {
222    pub(super) base: &'a super::BaseURI,
223    pub(super) archive: &'a ArchiveId,
224}
225impl<'a> ArchiveURIRef<'a> {
226    #[inline]
227    #[must_use]
228    pub const fn new(base: &'a BaseURI, archive: &'a ArchiveId) -> Self {
229        Self { base, archive }
230    }
231    #[inline]
232    #[must_use]
233    pub const fn id(&self) -> &ArchiveId {
234        self.archive
235    }
236}
237
238impl<'a> URIOrRefTrait for ArchiveURIRef<'a> {
239    #[inline]
240    fn base(&self) -> &'a BaseURI {
241        self.base
242    }
243    #[inline]
244    fn as_uri(&self) -> URIRef {
245        URIRef::<'a>::Archive(*self)
246    }
247}
248impl<'a> URIRefTrait<'a> for ArchiveURIRef<'a> {
249    type Owned = ArchiveURI;
250    #[inline]
251    fn owned(self) -> ArchiveURI {
252        ArchiveURI {
253            base: self.base.clone(),
254            archive: self.archive.clone(),
255        }
256    }
257}
258
259impl Display for ArchiveURIRef<'_> {
260    #[inline]
261    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
262        write!(
263            f,
264            "{}?{}={}",
265            self.base,
266            ArchiveURI::SEPARATOR,
267            self.archive
268        )
269    }
270}
271debugdisplay!(ArchiveURIRef<'_>);
272impl<'a> PathURITrait for ArchiveURIRef<'a> {
273    #[inline]
274    fn as_path(&self) -> PathURIRef<'a> {
275        PathURIRef {
276            archive: *self,
277            path: Option::<&'a Name>::None,
278        }
279    }
280}
281
282impl ArchiveURITrait for ArchiveURIRef<'_> {
283    #[inline]
284    fn archive_uri(&self) -> Self {
285        *self
286    }
287}
288
289#[cfg(feature = "serde")]
290mod serde_impl {
291    use super::{ArchiveId, ArchiveURI};
292    use crate::uris::{serialize, ArchiveURIRef};
293
294    serialize!(as+DE ArchiveId);
295    serialize!(DE ArchiveURI);
296    serialize!(ArchiveURIRef<'_>);
297}