flams_ontology/uris/
name.rs1use 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#[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}