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 */