flams_ontology/uris/
name.rs

1use flams_utils::gc::{TArcInterned, TArcInterner};
2use lazy_static::lazy_static;
3use parking_lot::Mutex;
4use smallvec::SmallVec;
5use std::convert::Infallible;
6use std::fmt::{Debug, Display};
7use std::num::NonZeroUsize;
8use std::str::FromStr;
9use triomphe::Arc;
10
11#[cfg(feature = "wasm")]
12#[cfg_attr(feature = "wasm", wasm_bindgen::prelude::wasm_bindgen(typescript_custom_section))]
13const TS_NAME: &str = "export type Name = string;";
14
15lazy_static! {
16    pub(super) static ref NAMES: Arc<Mutex<TArcInterner<str, 4, 100_000>>> =
17        Arc::new(Mutex::new(TArcInterner::default()));
18}
19
20#[derive(Clone, PartialEq, Eq, Hash)]
21pub struct NameStep(pub(super) TArcInterned<str>);
22impl Ord for NameStep {
23    #[inline]
24    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
25        self.0.as_ref().cmp(other.0.as_ref())
26    }
27}
28impl PartialOrd for NameStep {
29    #[inline]
30    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
31        Some(self.cmp(other))
32    }
33}
34impl AsRef<str> for NameStep {
35    #[inline]
36    fn as_ref(&self) -> &str {
37        self.0.as_ref()
38    }
39}
40
41impl Display for NameStep {
42    #[inline]
43    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
44        Display::fmt(self.as_ref(), f)
45    }
46}
47impl Debug for NameStep {
48    #[inline]
49    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
50        Display::fmt(self.as_ref(), f)
51    }
52}
53
54#[derive(Clone, PartialEq, Eq, Hash)]
55pub struct Name(pub(super) SmallVec<NameStep, 4>);
56impl Name {
57    #[inline]
58    #[must_use]
59    pub fn steps(&self) -> &[NameStep] {
60        &self.0
61    }
62    #[inline]
63    #[must_use]
64    pub const fn is_simple(&self) -> bool {
65        self.0.len() == 1
66    }
67    #[inline]
68    #[must_use]
69    pub fn len(&self) -> NonZeroUsize {
70        NonZeroUsize::new(self.0.len()).unwrap_or_else(|| unreachable!())
71    }
72
73    #[inline]
74    #[must_use]
75    pub fn last_name(&self) -> &NameStep {
76        self.0.last().unwrap_or_else(|| unreachable!())
77    }
78
79    #[must_use]
80    pub fn with_last_name(mut self,s:NameStep) -> Self {
81        if self.0.len() == 1 {
82            Self(smallvec::smallvec![s])
83        } else {
84            self.0.pop();
85            self.0.push(s);
86            self
87        }
88    }
89    #[inline]
90    #[must_use]
91    pub fn first_name(&self) -> &NameStep {
92        self.0.first().unwrap_or_else(|| unreachable!())
93    }
94}
95impl Display for Name {
96    #[inline]
97    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
98        let mut steps = self.steps().iter();
99        let Some(first) = steps.next() else {
100            unreachable!()
101        };
102        Display::fmt(first, f)?;
103        for step in steps {
104            write!(f, "/{step}")?;
105        }
106        Ok(())
107    }
108}
109impl Debug for Name {
110    #[inline]
111    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
112        Display::fmt(self, f)
113    }
114}
115
116pub const INVALID_CHARS: [char;3] = ['\\','{','}'];
117
118#[derive(Debug,thiserror::Error)]
119#[error("Invalid URI character")]
120pub struct InvalidURICharacter;
121
122impl FromStr for Name {
123    type Err = InvalidURICharacter;
124    fn from_str(s: &str) -> Result<Self, Self::Err> {
125        if s.contains(INVALID_CHARS) || s.is_empty() {
126            return Err(InvalidURICharacter);
127        }
128        let steps = s
129            .split('/')
130            .map(|s| NameStep(NAMES.lock().get_or_intern(s)));
131        Ok(Self(steps.collect()))
132    }
133}
134
135impl From<NameStep> for Name {
136    #[inline]
137    fn from(step: NameStep) -> Self {
138        Self(SmallVec::from([step]))
139    }
140}
141
142/*impl<A: AsRef<str>> TryFrom<A> for Name {
143    type Error = InvalidURICharacter;
144    #[inline]
145    fn try_from(s: A) -> Result<Self,InvalidURICharacter> {
146        s.as_ref().parse()
147    }
148}*/
149
150#[cfg(feature = "serde")]
151mod serde_impl {
152    use super::{InvalidURICharacter, Name, NameStep};
153    use crate::uris::serialize;
154    serialize!(as NameStep);
155    impl<'de> serde::Deserialize<'de> for NameStep {
156        fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
157            let s = String::deserialize(deserializer)?;
158            Ok(Self(super::NAMES.lock().get_or_intern(s.as_str())))
159        }
160    }
161    serialize!(Name);
162    impl<'de> serde::Deserialize<'de> for Name {
163        fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
164            let s = String::deserialize(deserializer)?;
165            s.parse().map_err(|e:InvalidURICharacter| serde::de::Error::custom(e.to_string()))
166        }
167    }
168}