1use std::fmt::Display;
7use std::{collections::HashMap, hash::Hash};
8
9use derive_more::From;
10use itertools::Itertools;
11use ustr::Ustr;
12use uuid::Uuid;
13
14#[cfg(feature = "serde")]
15use serde::{self, Deserialize, Serialize};
16#[cfg(feature = "serde-wasm")]
17use tsify::Tsify;
18
19use super::column::{Column, IndexedHashColumn, Mapping, MutMapping};
20
21#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, From)]
28pub enum NameSegment {
29 Uuid(Uuid),
31
32 Text(Ustr),
34}
35
36impl From<&str> for NameSegment {
37 fn from(name: &str) -> Self {
38 Self::Text(name.into())
39 }
40}
41
42impl Display for NameSegment {
43 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
44 match self {
45 Self::Uuid(uiid) => uiid.as_braced().fmt(f),
46 Self::Text(name) => name.fmt(f),
47 }
48 }
49}
50
51impl NameSegment {
52 pub fn serialize_string(&self) -> String {
54 match self {
55 Self::Uuid(uiid) => uiid.to_string(),
56 Self::Text(name) => format!("`{name}`"),
57 }
58 }
59
60 pub fn deserialize_str(input: &str) -> Result<Self, String> {
62 let mut chars = input.chars();
63 if chars.next() == Some('`') && chars.next_back() == Some('`') {
64 Ok(Self::Text(chars.as_str().into()))
65 } else {
66 let uuid = Uuid::parse_str(input).map_err(|err| format!("Invalid UUID: {err}"))?;
67 Ok(Self::Uuid(uuid))
68 }
69 }
70}
71
72#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, From)]
101#[cfg_attr(feature = "serde-wasm", derive(Tsify))]
102#[cfg_attr(feature = "serde-wasm", tsify(into_wasm_abi, from_wasm_abi))]
103pub struct QualifiedName(
104 #[cfg_attr(feature = "serde-wasm", tsify(type = "string"))] Vec<NameSegment>,
105);
106
107pub fn name(x: impl Into<QualifiedName>) -> QualifiedName {
109 x.into()
110}
111
112impl<const N: usize> From<[NameSegment; N]> for QualifiedName {
113 fn from(segments: [NameSegment; N]) -> Self {
114 Vec::from(segments).into()
115 }
116}
117
118impl<const N: usize> From<[Uuid; N]> for QualifiedName {
119 fn from(segments: [Uuid; N]) -> Self {
120 segments.map(NameSegment::Uuid).into()
121 }
122}
123
124impl<const N: usize> From<[&str; N]> for QualifiedName {
125 fn from(segments: [&str; N]) -> Self {
126 segments.map(NameSegment::from).into()
127 }
128}
129
130impl From<Uuid> for QualifiedName {
131 fn from(id: Uuid) -> Self {
132 Self::single(id.into())
133 }
134}
135
136impl From<Ustr> for QualifiedName {
137 fn from(name: Ustr) -> Self {
138 Self::single(name.into())
139 }
140}
141
142impl From<&str> for QualifiedName {
143 fn from(name: &str) -> Self {
144 Self::single(name.into())
145 }
146}
147
148impl Display for QualifiedName {
149 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
150 fmt_qualified(
151 f,
152 &self.0,
153 |segment| matches!(segment, NameSegment::Text(name) if name.contains(char::is_whitespace)),
154 )
155 }
156}
157
158fn fmt_qualified<T: Display>(
159 f: &mut std::fmt::Formatter<'_>,
160 segments: &[T],
161 mut show_quotes: impl FnMut(&T) -> bool,
162) -> std::fmt::Result {
163 let n = segments.len();
164 for (i, segment) in segments.iter().enumerate() {
165 if i > 0 {
166 write!(f, ".")?;
167 }
168 let quote = n > 1 && show_quotes(segment);
169 if quote {
170 write!(f, "`")?;
171 }
172 write!(f, "{segment}")?;
173 if quote {
174 write!(f, "`")?;
175 }
176 }
177 Ok(())
178}
179
180#[cfg(feature = "serde")]
181impl Serialize for QualifiedName {
182 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
183 where
184 S: serde::Serializer,
185 {
186 serializer.serialize_str(self.serialize_string().as_str())
187 }
188}
189
190#[cfg(feature = "serde")]
191impl<'de> Deserialize<'de> for QualifiedName {
192 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
193 where
194 D: serde::Deserializer<'de>,
195 {
196 deserializer.deserialize_str(QualifiedNameVisitor)
197 }
198}
199
200#[cfg(feature = "serde")]
201struct QualifiedNameVisitor;
202
203#[cfg(feature = "serde")]
204impl<'de> serde::de::Visitor<'de> for QualifiedNameVisitor {
205 type Value = QualifiedName;
206
207 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
208 write!(formatter, "a qualified name as a dot-separated string")
209 }
210
211 fn visit_str<E>(self, input: &str) -> Result<Self::Value, E>
212 where
213 E: serde::de::Error,
214 {
215 QualifiedName::deserialize_str(input).map_err(E::custom)
216 }
217}
218
219impl QualifiedName {
220 pub fn single(segment: NameSegment) -> Self {
222 Self(vec![segment])
223 }
224
225 pub fn segments(&self) -> impl Iterator<Item = &NameSegment> {
227 self.0.iter()
228 }
229
230 pub fn only(&self) -> Option<NameSegment> {
232 if self.0.len() == 1 {
233 Some(self.0[0])
234 } else {
235 None
236 }
237 }
238
239 pub fn serialize_string(&self) -> String {
241 self.segments().map(|segment| segment.serialize_string()).join(".")
242 }
243
244 pub fn deserialize_str(input: &str) -> Result<Self, String> {
246 let segments: Result<Vec<_>, _> =
247 input.split(".").map(NameSegment::deserialize_str).collect();
248 Ok(segments?.into())
249 }
250}
251
252#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, From)]
254#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
255#[cfg_attr(feature = "serde", serde(untagged))]
256#[cfg_attr(feature = "serde-wasm", derive(Tsify))]
257#[cfg_attr(feature = "serde-wasm", tsify(into_wasm_abi, from_wasm_abi))]
258pub enum LabelSegment {
259 Text(Ustr),
261
262 Index(usize),
264}
265
266impl From<&str> for LabelSegment {
267 fn from(label: &str) -> Self {
268 Self::Text(label.into())
269 }
270}
271
272impl Display for LabelSegment {
273 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
274 match self {
275 Self::Text(label) => label.fmt(f),
276 Self::Index(index) => index.fmt(f),
277 }
278 }
279}
280
281#[derive(Clone, Debug, PartialEq, Eq, From)]
283#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
284#[cfg_attr(feature = "serde-wasm", derive(Tsify))]
285#[cfg_attr(feature = "serde-wasm", tsify(into_wasm_abi, from_wasm_abi))]
286pub struct QualifiedLabel(Vec<LabelSegment>);
287
288pub fn label(x: impl Into<QualifiedLabel>) -> QualifiedLabel {
290 x.into()
291}
292
293impl<const N: usize> From<[LabelSegment; N]> for QualifiedLabel {
294 fn from(segments: [LabelSegment; N]) -> Self {
295 Vec::from(segments).into()
296 }
297}
298
299impl<const N: usize> From<[&str; N]> for QualifiedLabel {
300 fn from(segments: [&str; N]) -> Self {
301 segments.map(LabelSegment::from).into()
302 }
303}
304
305impl From<Ustr> for QualifiedLabel {
306 fn from(label: Ustr) -> Self {
307 Self::single(label.into())
308 }
309}
310
311impl From<&str> for QualifiedLabel {
312 fn from(label: &str) -> Self {
313 Self::single(label.into())
314 }
315}
316
317impl From<usize> for QualifiedLabel {
318 fn from(value: usize) -> Self {
319 Self::single(value.into())
320 }
321}
322
323impl Display for QualifiedLabel {
324 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
325 fmt_qualified(
326 f,
327 &self.0,
328 |segment| matches!(segment, LabelSegment::Text(label) if label.contains(char::is_whitespace)),
329 )
330 }
331}
332
333impl QualifiedLabel {
334 pub fn single(segment: LabelSegment) -> Self {
336 Self(vec![segment])
337 }
338
339 pub fn segments(&self) -> impl Iterator<Item = &LabelSegment> {
341 self.0.iter()
342 }
343}
344
345#[derive(Clone, Debug)]
347pub struct Namespace {
348 inner: HashMap<NameSegment, Namespace>,
349 uuid_labels: Option<IndexedHashColumn<Uuid, LabelSegment>>,
350}
351
352#[derive(Clone, Debug, PartialEq, Eq)]
354#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
355#[cfg_attr(feature = "serde", serde(tag = "tag", content = "content"))]
356#[cfg_attr(feature = "serde-wasm", derive(Tsify))]
357#[cfg_attr(feature = "serde-wasm", tsify(into_wasm_abi, from_wasm_abi))]
358pub enum NameLookup {
359 Unique(QualifiedName),
361
362 Arbitrary(QualifiedName),
364
365 None,
367}
368
369impl Namespace {
370 pub fn new_for_text() -> Self {
372 Self {
373 inner: Default::default(),
374 uuid_labels: None,
375 }
376 }
377
378 pub fn new_for_uuid() -> Self {
380 Self {
381 inner: Default::default(),
382 uuid_labels: Some(Default::default()),
383 }
384 }
385
386 pub fn add_inner(&mut self, name: NameSegment, inner: Self) {
388 assert!(
389 self.inner.insert(name, inner).is_none(),
390 "Inner namespace already exists for segment: {name}"
391 );
392 }
393
394 pub fn set_label(&mut self, uuid: Uuid, label: LabelSegment) {
396 self.uuid_labels.as_mut().expect("Should be a UUID namespace").set(uuid, label);
397 }
398
399 pub fn label(&self, name: &QualifiedName) -> Option<QualifiedLabel> {
401 let mut namespace = Some(self);
402 let labels: Option<Vec<_>> = name
403 .segments()
404 .map(|segment| {
405 let maybe_label = match segment {
406 NameSegment::Uuid(uuid) => namespace
407 .and_then(|ns| ns.uuid_labels.as_ref())
408 .and_then(|ul| ul.apply_to_ref(uuid)),
409 NameSegment::Text(name) => Some(LabelSegment::Text(*name)),
410 };
411 namespace = namespace.and_then(|ns| ns.inner.get(segment));
412 maybe_label
413 })
414 .collect();
415 Some(labels?.into())
416 }
417
418 pub fn name_with_label(&self, label: &QualifiedLabel) -> NameLookup {
420 let mut namespace = Some(self);
421 let mut ambiguous = false;
422 let names: Option<Vec<_>> = label
423 .segments()
424 .map(|segment| {
425 let maybe_uuid_labels = namespace.and_then(|ns| ns.uuid_labels.as_ref());
426 let maybe_name = match (maybe_uuid_labels, segment) {
427 (Some(uuid_labels), _) => {
428 let mut uuids = uuid_labels.preimage(segment);
429 let maybe_uuid = uuids.next();
430 if uuids.next().is_some() {
431 ambiguous = true;
432 }
433 maybe_uuid.map(NameSegment::Uuid)
434 }
435 (None, LabelSegment::Text(text)) => Some(NameSegment::Text(*text)),
436 (None, LabelSegment::Index(_)) => None,
437 };
438 namespace = namespace
439 .and_then(|ns| maybe_name.as_ref().and_then(|name| ns.inner.get(name)));
440 maybe_name
441 })
442 .collect();
443 match names {
444 Some(names) if !ambiguous => NameLookup::Unique(names.into()),
445 Some(names) => NameLookup::Arbitrary(names.into()),
446 None => NameLookup::None,
447 }
448 }
449}
450
451#[cfg(test)]
452mod tests {
453 use super::*;
454 use uuid::uuid;
455
456 const UUID1: Uuid = uuid!("67e55044-10b1-426f-9247-bb680e5fe0c8");
457 const UUID2: Uuid = uuid!("f81d4fae-7dec-11d0-a765-00a0c91e6bf6");
458
459 #[test]
460 fn display_name() {
461 let string = name(UUID1).to_string();
462 assert_eq!(string.chars().next_tuple(), Some(('{', '6', '7', 'e')));
463 assert_eq!(string.chars().next_back(), Some('}'));
464
465 assert_eq!(name("foo").to_string(), "foo");
466 assert_eq!(name("foo bar").to_string(), "foo bar");
467 assert_eq!(name(["foo bar", "baz"]).to_string(), "`foo bar`.baz");
468 }
469
470 #[test]
471 fn serialize_name() {
472 let qual_name = name(UUID1);
473 let serialized = qual_name.serialize_string();
474 assert_eq!(serialized.chars().next_tuple(), Some(('6', '7', 'e')));
475 assert_eq!(QualifiedName::deserialize_str(&serialized), Ok(qual_name));
476
477 let qual_name = name(["foo", "bar", "baz"].map(NameSegment::from));
478 let serialized = qual_name.serialize_string();
479 assert_eq!(serialized, "`foo`.`bar`.`baz`");
480 assert_eq!(QualifiedName::deserialize_str(&serialized), Ok(qual_name));
481 }
482
483 #[test]
484 fn display_label() {
485 assert_eq!(label("foo").to_string(), "foo");
486 assert_eq!(label("foo bar").to_string(), "foo bar");
487 assert_eq!(label(2).to_string(), "2");
488
489 assert_eq!(label([LabelSegment::from("foo"), LabelSegment::from(1)]).to_string(), "foo.1");
490 assert_eq!(label(["foo bar", "baz"]).to_string(), "`foo bar`.baz");
491 }
492
493 #[test]
494 fn namespaces() {
495 let mut child = Namespace::new_for_uuid();
496 child.set_label(UUID1, "bar".into());
497 child.set_label(UUID2, "baz".into());
498 let mut root = Namespace::new_for_uuid();
499 root.add_inner(UUID1.into(), child);
500 root.add_inner(UUID2.into(), Namespace::new_for_text());
501 root.set_label(UUID1, "foo".into());
502 root.set_label(UUID2, "textual".into());
503
504 let (qual_name, qual_label) = (name([UUID1, UUID2]), label(["foo", "baz"]));
505 assert_eq!(root.label(&qual_name), Some(qual_label.clone()));
506 assert_eq!(root.name_with_label(&qual_label), NameLookup::Unique(qual_name));
507
508 let qual_name =
509 name([NameSegment::Uuid(UUID1), NameSegment::Uuid(UUID1), NameSegment::from("biz")]);
510 let qual_label = label(["foo", "bar", "biz"]);
511 assert_eq!(root.label(&qual_name), Some(qual_label.clone()));
512 assert_eq!(root.name_with_label(&qual_label), NameLookup::Unique(qual_name));
513
514 assert_eq!(root.label(&name([UUID2, UUID1])), None);
515 assert_eq!(root.name_with_label(&label(["bar", "foo"])), NameLookup::None);
516
517 let mut ambiguous = Namespace::new_for_uuid();
518 ambiguous.set_label(UUID1, "foo".into());
519 ambiguous.set_label(UUID2, "foo".into());
520 assert!(matches!(ambiguous.name_with_label(&label("foo")), NameLookup::Arbitrary(_)));
521 }
522}