flams_router_base/
uris.rs

1use flams_math_archives::{
2    MathArchive,
3    backend::{GlobalBackend, LocalBackend},
4};
5use ftml_uris::{ArchiveId, ArchiveUri};
6
7pub fn get_uri(a: &ArchiveId) -> Option<ArchiveUri> {
8    GlobalBackend.with_archive(a, |a| a.map(|a| a.uri().clone()))
9}
10
11/*
12macro_rules! charstr {
13    ($c:ident) => {
14        const_str::concat!($c::SEPARATOR)
15    };
16}
17
18#[derive(Copy, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
19pub enum URIKind {
20    Full,
21    Rel,
22    Archive,
23    Path,
24    Document,
25    DocumentElement,
26    Module,
27    Declaration,
28}
29
30#[derive(Clone)]
31pub enum SymURIComponents {
32    Uri(SymbolUri),
33    Comps {
34        a: ArchiveId,
35        p: Option<String>,
36        m: String,
37        s: String,
38    },
39}
40
41impl SymURIComponents {
42    pub fn into_args<
43        R,
44        F: FnOnce(
45            Option<SymbolUri>,
46            Option<ArchiveId>,
47            Option<String>,
48            Option<Language>,
49            Option<String>,
50            Option<String>,
51        ) -> R,
52    >(
53        self,
54        f: F,
55    ) -> R {
56        match self {
57            Self::Uri(uri) => f(Some(uri), None, None, None, None, None),
58            Self::Comps { a, p, m, s } => f(None, Some(a), p, None, Some(m), Some(s)),
59        }
60    }
61}
62
63impl
64    TryFrom<(
65        Option<SymbolUri>,
66        Option<ArchiveId>,
67        Option<String>,
68        Option<String>,
69        Option<String>,
70    )> for SymURIComponents
71{
72    type Error = ();
73    fn try_from(
74        (uri, a, p, m, s): (
75            Option<SymbolUri>,
76            Option<ArchiveId>,
77            Option<String>,
78            Option<String>,
79            Option<String>,
80        ),
81    ) -> Result<Self, ()> {
82        if let Some(uri) = uri {
83            return if a.is_none() && p.is_none() && m.is_none() && s.is_none() {
84                Ok(Self::Uri(uri))
85            } else {
86                Err(())
87            };
88        }
89        a.map_or_else(
90            || Err(()),
91            |a| match (m, s) {
92                (Some(m), Some(s)) => Ok(Self::Comps { a, p, m, s }),
93                _ => Err(()),
94            },
95        )
96    }
97}
98
99#[derive(Clone)]
100pub enum DocURIComponents {
101    Uri(DocumentUri),
102    RelPath(ArchiveId, String),
103    Comps {
104        a: ArchiveId,
105        p: Option<String>,
106        l: Option<Language>,
107        d: String,
108    },
109}
110impl DocURIComponents {
111    pub fn into_args<
112        R,
113        F: FnOnce(
114            Option<DocumentUri>,
115            Option<String>,
116            Option<ArchiveId>,
117            Option<String>,
118            Option<Language>,
119            Option<String>,
120        ) -> R,
121    >(
122        self,
123        f: F,
124    ) -> R {
125        match self {
126            Self::Uri(uri) => f(Some(uri), None, None, None, None, None),
127            Self::RelPath(a, rp) => f(None, Some(rp), Some(a), None, None, None),
128            Self::Comps { a, p, l, d } => f(None, None, Some(a), p, l, Some(d)),
129        }
130    }
131}
132
133impl
134    TryFrom<(
135        Option<DocumentUri>,
136        Option<String>,
137        Option<ArchiveId>,
138        Option<String>,
139        Option<Language>,
140        Option<String>,
141    )> for DocURIComponents
142{
143    type Error = ();
144    fn try_from(
145        (uri, rp, a, p, l, d): (
146            Option<DocumentUri>,
147            Option<String>,
148            Option<ArchiveId>,
149            Option<String>,
150            Option<Language>,
151            Option<String>,
152        ),
153    ) -> Result<Self, ()> {
154        if let Some(uri) = uri {
155            return if rp.is_none() && a.is_none() && p.is_none() && l.is_none() && d.is_none() {
156                Ok(Self::Uri(uri))
157            } else {
158                Err(())
159            };
160        }
161        a.map_or_else(
162            || Err(()),
163            |a| {
164                if let Some(rp) = rp {
165                    if p.is_none() && l.is_none() && d.is_none() {
166                        Ok(Self::RelPath(a, rp))
167                    } else {
168                        Err(())
169                    }
170                } else if let Some(d) = d {
171                    Ok(Self::Comps { a, p, l, d })
172                } else {
173                    Err(())
174                }
175            },
176        )
177    }
178}
179
180#[derive(Clone)]
181pub enum URIComponents {
182    Uri(URI),
183    RelPath(ArchiveId, String),
184    DocComps {
185        a: ArchiveId,
186        p: Option<String>,
187        l: Option<Language>,
188        d: String,
189    },
190    ElemComps {
191        a: ArchiveId,
192        p: Option<String>,
193        l: Option<Language>,
194        d: String,
195        e: String,
196    },
197    ModComps {
198        a: ArchiveId,
199        p: Option<String>,
200        m: String,
201    },
202    SymComps {
203        a: ArchiveId,
204        p: Option<String>,
205        m: String,
206        s: String,
207    },
208}
209impl From<DocURIComponents> for URIComponents {
210    fn from(value: DocURIComponents) -> Self {
211        match value {
212            DocURIComponents::Uri(u) => Self::Uri(URI::Narrative(u.into())),
213            DocURIComponents::RelPath(a, rp) => Self::RelPath(a, rp),
214            DocURIComponents::Comps { a, p, l, d } => Self::DocComps { a, p, l, d },
215        }
216    }
217}
218
219impl URIComponents {
220    pub fn into_args<
221        R,
222        F: FnOnce(
223            Option<URI>,
224            Option<String>,
225            Option<ArchiveId>,
226            Option<String>,
227            Option<Language>,
228            Option<String>,
229            Option<String>,
230            Option<String>,
231            Option<String>,
232            Option<URI>,
233        ) -> R,
234    >(
235        self,
236        f: F,
237    ) -> R {
238        match self {
239            Self::Uri(uri) => f(
240                Some(uri),
241                None,
242                None,
243                None,
244                None,
245                None,
246                None,
247                None,
248                None,
249                None,
250            ),
251            Self::RelPath(a, rp) => f(
252                None,
253                Some(rp),
254                Some(a),
255                None,
256                None,
257                None,
258                None,
259                None,
260                None,
261                None,
262            ),
263            Self::DocComps { a, p, l, d } => {
264                f(None, None, Some(a), p, l, Some(d), None, None, None, None)
265            }
266            Self::ElemComps { a, p, l, d, e } => f(
267                None,
268                None,
269                Some(a),
270                p,
271                l,
272                Some(d),
273                Some(e),
274                None,
275                None,
276                None,
277            ),
278            Self::ModComps { a, p, m } => f(
279                None,
280                None,
281                Some(a),
282                p,
283                None,
284                None,
285                None,
286                Some(m),
287                None,
288                None,
289            ),
290            Self::SymComps { a, p, m, s } => f(
291                None,
292                None,
293                Some(a),
294                p,
295                None,
296                None,
297                None,
298                Some(m),
299                Some(s),
300                None,
301            ),
302        }
303    }
304}
305
306#[allow(clippy::many_single_char_names)]
307impl
308    TryFrom<(
309        Option<URI>,
310        Option<String>,
311        Option<ArchiveId>,
312        Option<String>,
313        Option<Language>,
314        Option<String>,
315        Option<String>,
316        Option<String>,
317        Option<String>,
318    )> for URIComponents
319{
320    type Error = ();
321    fn try_from(
322        (uri, rp, a, p, l, d, e, m, s): (
323            Option<URI>,
324            Option<String>,
325            Option<ArchiveId>,
326            Option<String>,
327            Option<Language>,
328            Option<String>,
329            Option<String>,
330            Option<String>,
331            Option<String>,
332        ),
333    ) -> Result<Self, ()> {
334        if let Some(uri) = uri {
335            return if rp.is_none()
336                && a.is_none()
337                && p.is_none()
338                && l.is_none()
339                && d.is_none()
340                && e.is_none()
341                && m.is_none()
342                && s.is_none()
343            {
344                Ok(Self::Uri(uri))
345            } else {
346                Err(())
347            };
348        }
349        a.map_or_else(
350            || Err(()),
351            |a| {
352                if let Some(rp) = rp {
353                    if p.is_none() && l.is_none() && d.is_none() && m.is_none() && s.is_none() {
354                        Ok(Self::RelPath(a, rp))
355                    } else {
356                        Err(())
357                    }
358                } else if let Some(d) = d {
359                    if e.is_none() && m.is_none() && s.is_none() {
360                        Ok(Self::DocComps { a, p, l, d })
361                    } else if let Some(e) = e {
362                        if m.is_none() && s.is_none() {
363                            Ok(Self::ElemComps { a, p, l, d, e })
364                        } else {
365                            Err(())
366                        }
367                    } else {
368                        Err(())
369                    }
370                } else if let Some(m) = m {
371                    if d.is_none() && e.is_none() && s.is_none() && l.is_none() {
372                        Ok(Self::ModComps { a, p, m })
373                    } else if let Some(s) = s {
374                        if d.is_none() && e.is_none() && l.is_none() {
375                            Ok(Self::SymComps { a, p, m, s })
376                        } else {
377                            Err(())
378                        }
379                    } else {
380                        Err(())
381                    }
382                } else {
383                    Err(())
384                }
385            },
386        )
387    }
388}
389
390pub use
391pub trait URIComponentsTrait {
392    fn get(&self, key: &str) -> Option<&str>;
393    fn get_string(&self, key: &str) -> Option<String>;
394
395    fn kind(&self) -> Option<URIKind>;
396    fn as_doc(&self) -> Option<DocURIComponents> {
397        if let Some(uri) = self.get("uri") {
398            return DocumentUri::from_str(uri).ok().map(DocURIComponents::Uri);
399        }
400        let a = self.get(charstr!(ArchiveUri)).map(ArchiveId::new)?;
401        if let Some(rp) = self.get_string("rp") {
402            if self.get(charstr!(DocumentUri)).is_none()
403                && self.get(charstr!(DocumentElementUri)).is_none()
404                && self.get(charstr!(ModuleUri)).is_none()
405                && self.get(charstr!(SymbolUri)).is_none()
406            {
407                Some(DocURIComponents::RelPath(a, rp))
408            } else {
409                None
410            }
411        } else if self.get(charstr!(DocumentElementUri)).is_none()
412            && self.get(charstr!(ModuleUri)).is_none()
413            && self.get(charstr!(SymbolUri)).is_none()
414        {
415            let p = self.get_string(charstr!(PathURI));
416            let l = self.get("l").and_then(|s| Language::from_str(s).ok());
417            let d = self.get_string("d")?;
418            Some(DocURIComponents::Comps { a, p, l, d })
419        } else {
420            None
421        }
422    }
423    fn as_comps(&self) -> Option<URIComponents> {
424        if let Some(uri) = self.get("uri") {
425            return URI::from_str(uri).ok().map(URIComponents::Uri);
426        }
427        let a = self.get(charstr!(ArchiveUri)).map(ArchiveId::new)?;
428        if let Some(rp) = self.get_string("rp") {
429            return if self.get(charstr!(DocumentUri)).is_none()
430                && self.get(charstr!(DocumentElementUri)).is_none()
431                && self.get(charstr!(ModuleUri)).is_none()
432                && self.get(charstr!(SymbolUri)).is_none()
433            {
434                Some(URIComponents::RelPath(a, rp))
435            } else {
436                None
437            };
438        }
439        if let Some(e) = self.get(charstr!(DocumentElementUri)) {
440            let d = self.get(charstr!(DocumentUri))?;
441            return if self.get(charstr!(ModuleUri)).is_none()
442                && self.get(charstr!(SymbolUri)).is_none()
443            {
444                Some(URIComponents::ElemComps {
445                    a,
446                    p: self.get(charstr!(PathURI)).map(ToString::to_string),
447                    l: self.get("l").and_then(|s| Language::from_str(s).ok()),
448                    d: d.to_string(),
449                    e: e.to_string(),
450                })
451            } else {
452                None
453            };
454        }
455        if let Some(d) = self.get(charstr!(DocumentUri)) {
456            return if self.get(charstr!(ModuleUri)).is_none()
457                && self.get(charstr!(SymbolUri)).is_none()
458            {
459                Some(URIComponents::DocComps {
460                    a,
461                    p: self.get(charstr!(PathURI)).map(ToString::to_string),
462                    l: self.get("l").and_then(|s| Language::from_str(s).ok()),
463                    d: d.to_string(),
464                })
465            } else {
466                None
467            };
468        }
469        if let Some(s) = self.get(charstr!(SymbolUri)) {
470            let m = self.get(charstr!(ModuleUri))?;
471            return if self.get(charstr!(DocumentUri)).is_none()
472                && self.get(charstr!(DocumentElementUri)).is_none()
473            {
474                Some(URIComponents::SymComps {
475                    a,
476                    p: self.get(charstr!(PathURI)).map(ToString::to_string),
477                    m: m.to_string(),
478                    s: s.to_string(),
479                })
480            } else {
481                None
482            };
483        }
484        if let Some(m) = self.get(charstr!(ModuleUri)) {
485            return if self.get(charstr!(DocumentUri)).is_none()
486                && self.get(charstr!(DocumentElementUri)).is_none()
487            {
488                Some(URIComponents::ModComps {
489                    a,
490                    p: self.get(charstr!(PathURI)).map(ToString::to_string),
491                    m: m.to_string(),
492                })
493            } else {
494                None
495            };
496        }
497        None
498    }
499
500    #[cfg(feature = "ssr")]
501    fn parse(&self) -> Option<URI> {
502        if let Some(uri) = self.get("uri") {
503            return URI::from_str(uri).ok();
504        }
505        let a = ArchiveId::new(self.get(charstr!(ArchiveUri))?);
506        if let Some(rp) = self.get("rp") {
507            return from_archive_relpath(&a, rp).map(|r| URI::Narrative(r.into()));
508        }
509        todo!()
510    }
511}
512
513impl URIComponentsTrait for leptos_router::params::ParamsMap {
514    #[inline]
515    fn get(&self, key: &str) -> Option<&str> {
516        self.get_str(key)
517    }
518    #[inline]
519    fn get_string(&self, key: &str) -> Option<String> {
520        self.get(key)
521    }
522    fn kind(&self) -> Option<URIKind> {
523        if self.get("uri").is_some() {
524            return Some(URIKind::Full);
525        }
526        if self.get("rp").is_some() {
527            return Some(URIKind::Rel);
528        }
529        self.get(charstr!(ArchiveUri))?;
530        if self.get(charstr!(DocumentUri)).is_some() {
531            if self.get(charstr!(ModuleUri)).is_some() || self.get(charstr!(SymbolUri)).is_some() {
532                return None;
533            }
534            if self.get(charstr!(DocumentElementUri)).is_some() {
535                Some(URIKind::DocumentElement)
536            } else {
537                Some(URIKind::Document)
538            }
539        } else if self.get(charstr!(ModuleUri)).is_some() {
540            if self.get(charstr!(DocumentElementUri)).is_some() {
541                return None;
542            }
543            if self.get(charstr!(SymbolUri)).is_some() {
544                Some(URIKind::Declaration)
545            } else {
546                Some(URIKind::Module)
547            }
548        } else if self.get(charstr!(PathURI)).is_some() {
549            Some(URIKind::Path)
550        } else {
551            Some(URIKind::Archive)
552        }
553    }
554}
555
556#[cfg(feature = "ssr")]
557mod ssr {
558    use super::{DocURIComponents, SymURIComponents, URIComponents};
559    use flams_ontology::{
560        languages::Language,
561        uris::{
562            ArchiveId, DocumentElementUri, DocumentUri, ModuleUri, Name, SymbolUri, URI,
563            UriRefTrait,
564        },
565    };
566    use flams_system::backend::{Backend, GlobalBackend};
567    use std::str::FromStr;
568
569    impl SymURIComponents {
570        #[must_use]
571        pub fn parse(self) -> Option<SymbolUri> {
572            match self {
573                Self::Uri(uri) => Some(uri),
574                Self::Comps { a, p, m, s } => get_sym_uri(&a, p, &m, &s),
575            }
576        }
577    }
578
579    impl DocURIComponents {
580        #[must_use]
581        pub fn parse(self) -> Option<DocumentUri> {
582            match self {
583                Self::Uri(uri) => Some(uri),
584                Self::RelPath(a, rp) => from_archive_relpath(&a, &rp),
585                Self::Comps { a, p, l, d } => get_doc_uri(
586                    &a,
587                    p.map(|p| Name::from_str(&p).unwrap_or_else(|_| unreachable!())),
588                    l.unwrap_or_default(),
589                    Name::from_str(&d).unwrap_or_else(|_| unreachable!()),
590                ),
591            }
592        }
593    }
594
595    impl URIComponents {
596        #[must_use]
597        pub fn parse(self) -> Option<URI> {
598            match self {
599                Self::Uri(uri) => Some(uri),
600                Self::RelPath(a, rp) => {
601                    from_archive_relpath(&a, &rp).map(|d| URI::Narrative(d.into()))
602                }
603                Self::DocComps { a, p, l, d } => get_doc_uri(
604                    &a,
605                    p.map(|p| Name::from_str(&p).unwrap_or_else(|_| unreachable!())),
606                    l.unwrap_or_default(),
607                    Name::from_str(&d).unwrap_or_else(|_| unreachable!()),
608                )
609                .map(|d| URI::Narrative(d.into())),
610                Self::ElemComps { a, p, l, d, e } => {
611                    get_elem_uri(&a, p, l, &d, &e).map(|e| URI::Narrative(e.into()))
612                }
613                Self::ModComps { a, p, m } => {
614                    get_mod_uri(&a, p, &m).map(|m| URI::Content(m.into()))
615                }
616                Self::SymComps { a, p, m, s } => {
617                    get_sym_uri(&a, p, &m, &s).map(|s| URI::Content(s.into()))
618                }
619            }
620        }
621    }
622
623    #[must_use]
624    pub fn from_archive_relpath(a: &ArchiveId, rp: &str) -> Option<DocumentUri> {
625        let (p, n) = if let Some((p, n)) = rp.rsplit_once('/') {
626            (
627                Some(Name::from_str(p).unwrap_or_else(|_| unreachable!())),
628                n,
629            )
630        } else {
631            (None, rp)
632        };
633        let (n, l) = if let Some((n, l)) = n.rsplit_once('.') {
634            Language::from_str(l).map_or_else(
635                |()| {
636                    n.rsplit_once('.').map_or_else(
637                        || {
638                            (
639                                Name::from_str(n).unwrap_or_else(|_| unreachable!()),
640                                Language::default(),
641                            )
642                        },
643                        |(n, l)| {
644                            (
645                                Name::from_str(n).unwrap_or_else(|_| unreachable!()),
646                                Language::from_str(l).unwrap_or_default(),
647                            )
648                        },
649                    )
650                },
651                |l| (Name::from_str(n).unwrap_or_else(|_| unreachable!()), l),
652            )
653        } else {
654            (
655                Name::from_str(n).unwrap_or_else(|_| unreachable!()),
656                Language::default(),
657            )
658        };
659        get_doc_uri(a, p, l, n)
660    }
661
662    #[must_use]
663    pub fn get_doc_uri(
664        a: &ArchiveId,
665        p: Option<Name>,
666        l: Language,
667        d: Name,
668    ) -> Option<DocumentUri> {
669        let a = GlobalBackend::get().with_archive(a, |a| a.map(|a| a.uri().owned()))?;
670        let p = if let Some(p) = p { a % p } else { a.into() };
671        Some(p & (d, l))
672    }
673
674    #[must_use]
675    #[allow(clippy::many_single_char_names)]
676    pub fn get_elem_uri(
677        a: &ArchiveId,
678        p: Option<String>,
679        l: Option<Language>,
680        d: &str,
681        e: &str,
682    ) -> Option<DocumentElementUri> {
683        get_doc_uri(
684            a,
685            p.map(|p| Name::from_str(&p).ok())?,
686            l.unwrap_or_default(),
687            Name::from_str(d).ok()?,
688        )
689        .and_then(|d| (d & e).ok())
690    }
691
692    #[must_use]
693    #[allow(clippy::many_single_char_names)]
694    pub fn get_mod_uri(a: &ArchiveId, p: Option<String>, m: &str) -> Option<ModuleUri> {
695        let a = GlobalBackend::get().with_archive(a, |a| a.map(|a| a.uri().owned()))?;
696        let p = if let Some(p) = p {
697            a % Name::from_str(&p).ok()?
698        } else {
699            a.into()
700        };
701        (p | m).ok()
702    }
703
704    #[must_use]
705    #[allow(clippy::many_single_char_names)]
706    pub fn get_sym_uri(a: &ArchiveId, p: Option<String>, m: &str, s: &str) -> Option<SymbolUri> {
707        get_mod_uri(a, p, m).and_then(|m| (m | s).ok())
708    }
709}
710
711#[cfg(feature = "ssr")]
712pub use ssr::*;
713 */