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}