1use std::fmt::Display;
4use std::{collections::HashMap, hash::Hash};
5
6use derive_more::From;
7use itertools::Itertools;
8use ustr::Ustr;
9use uuid::Uuid;
10
11#[cfg(feature = "serde")]
12use serde::{self, Deserialize, Serialize};
13#[cfg(feature = "serde-wasm")]
14use tsify::Tsify;
15
16use super::column::{Column, IndexedHashColumn, Mapping, MutMapping};
17use super::pretty::*;
18
19#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, From)]
25#[cfg_attr(feature = "serde-wasm", derive(Tsify))]
26#[cfg_attr(
27 feature = "serde-wasm",
28 tsify(into_wasm_abi, from_wasm_abi, type = "string")
29)]
30pub enum NameSegment {
31 Uuid(Uuid),
33
34 Text(Ustr),
36}
37
38impl From<&str> for NameSegment {
39 fn from(name: &str) -> Self {
40 Self::Text(name.into())
41 }
42}
43
44impl Display for NameSegment {
45 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
46 match self {
47 Self::Uuid(uiid) => uiid.as_braced().fmt(f),
48 Self::Text(name) => name.fmt(f),
49 }
50 }
51}
52
53pub fn name_seg(s: impl Into<Ustr>) -> NameSegment {
56 NameSegment::Text(s.into())
57}
58
59#[cfg(feature = "serde")]
60impl Serialize for NameSegment {
61 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
62 where
63 S: serde::Serializer,
64 {
65 serializer.serialize_str(self.serialize_string().as_str())
66 }
67}
68
69#[cfg(feature = "serde")]
70impl<'de> Deserialize<'de> for NameSegment {
71 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
72 where
73 D: serde::Deserializer<'de>,
74 {
75 deserializer.deserialize_str(NameSegmentVisitor)
76 }
77}
78
79#[cfg(feature = "serde")]
80struct NameSegmentVisitor;
81
82#[cfg(feature = "serde")]
83impl<'de> serde::de::Visitor<'de> for NameSegmentVisitor {
84 type Value = NameSegment;
85
86 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
87 write!(formatter, "a name segment as a string")
88 }
89
90 fn visit_str<E>(self, input: &str) -> Result<Self::Value, E>
91 where
92 E: serde::de::Error,
93 {
94 NameSegment::deserialize_str(input).map_err(E::custom)
95 }
96}
97
98impl NameSegment {
99 pub fn serialize_string(&self) -> String {
101 match self {
102 Self::Uuid(uiid) => uiid.to_string(),
103 Self::Text(name) => format!("`{name}`"),
104 }
105 }
106
107 pub fn deserialize_str(input: &str) -> Result<Self, String> {
109 let mut chars = input.chars();
110 if chars.next() == Some('`') && chars.next_back() == Some('`') {
111 Ok(Self::Text(chars.as_str().into()))
112 } else {
113 let uuid = Uuid::parse_str(input).map_err(|err| format!("Invalid UUID: {err}"))?;
114 Ok(Self::Uuid(uuid))
115 }
116 }
117}
118
119#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, From)]
147#[cfg_attr(feature = "serde-wasm", derive(Tsify))]
148#[cfg_attr(
149 feature = "serde-wasm",
150 tsify(into_wasm_abi, from_wasm_abi, type = "string")
151)]
152pub struct QualifiedName(Vec<NameSegment>);
153
154pub fn name(x: impl Into<QualifiedName>) -> QualifiedName {
156 x.into()
157}
158
159impl<const N: usize> From<[NameSegment; N]> for QualifiedName {
160 fn from(segments: [NameSegment; N]) -> Self {
161 Vec::from(segments).into()
162 }
163}
164
165impl<const N: usize> From<[Uuid; N]> for QualifiedName {
166 fn from(segments: [Uuid; N]) -> Self {
167 segments.map(NameSegment::Uuid).into()
168 }
169}
170
171impl<const N: usize> From<[&str; N]> for QualifiedName {
172 fn from(segments: [&str; N]) -> Self {
173 segments.map(NameSegment::from).into()
174 }
175}
176
177impl From<Uuid> for QualifiedName {
178 fn from(id: Uuid) -> Self {
179 Self::single(id.into())
180 }
181}
182
183impl From<Ustr> for QualifiedName {
184 fn from(name: Ustr) -> Self {
185 Self::single(name.into())
186 }
187}
188
189impl From<&str> for QualifiedName {
190 fn from(name: &str) -> Self {
191 Self::single(name.into())
192 }
193}
194
195impl ToDoc for QualifiedName {
196 fn to_doc<'a>(&self) -> D<'a> {
197 t(format!("{self}"))
198 }
199}
200
201impl Display for QualifiedName {
202 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
203 fmt_qualified(
204 f,
205 &self.0,
206 |segment| matches!(segment, NameSegment::Text(name) if name.contains(char::is_whitespace)),
207 )
208 }
209}
210
211fn fmt_qualified<T: Display>(
212 f: &mut std::fmt::Formatter<'_>,
213 segments: &[T],
214 mut show_quotes: impl FnMut(&T) -> bool,
215) -> std::fmt::Result {
216 let n = segments.len();
217 for (i, segment) in segments.iter().enumerate() {
218 if i > 0 {
219 write!(f, ".")?;
220 }
221 let quote = n > 1 && show_quotes(segment);
222 if quote {
223 write!(f, "`")?;
224 }
225 write!(f, "{segment}")?;
226 if quote {
227 write!(f, "`")?;
228 }
229 }
230 Ok(())
231}
232
233#[cfg(feature = "serde")]
234impl Serialize for QualifiedName {
235 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
236 where
237 S: serde::Serializer,
238 {
239 serializer.serialize_str(self.serialize_string().as_str())
240 }
241}
242
243#[cfg(feature = "serde")]
244impl<'de> Deserialize<'de> for QualifiedName {
245 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
246 where
247 D: serde::Deserializer<'de>,
248 {
249 deserializer.deserialize_str(QualifiedNameVisitor)
250 }
251}
252
253#[cfg(feature = "serde")]
254struct QualifiedNameVisitor;
255
256#[cfg(feature = "serde")]
257impl<'de> serde::de::Visitor<'de> for QualifiedNameVisitor {
258 type Value = QualifiedName;
259
260 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
261 write!(formatter, "a qualified name as a dot-separated string")
262 }
263
264 fn visit_str<E>(self, input: &str) -> Result<Self::Value, E>
265 where
266 E: serde::de::Error,
267 {
268 QualifiedName::deserialize_str(input).map_err(E::custom)
269 }
270}
271
272impl QualifiedName {
273 pub fn single(segment: NameSegment) -> Self {
275 Self(vec![segment])
276 }
277
278 pub fn as_slice(&self) -> &[NameSegment] {
280 self.0.as_slice()
281 }
282
283 pub fn segments(&self) -> impl Iterator<Item = &NameSegment> {
285 self.0.iter()
286 }
287
288 pub fn only(&self) -> Option<NameSegment> {
290 if self.0.len() == 1 {
291 Some(self.0[0])
292 } else {
293 None
294 }
295 }
296
297 pub fn snoc(&self, segment: NameSegment) -> Self {
299 let mut segments = self.0.clone();
300 segments.push(segment);
301 Self(segments)
302 }
303
304 pub fn serialize_string(&self) -> String {
306 self.segments().map(|segment| segment.serialize_string()).join(".")
307 }
308
309 pub fn deserialize_str(input: &str) -> Result<Self, String> {
311 let segments: Result<Vec<_>, _> =
312 input.split(".").map(NameSegment::deserialize_str).collect();
313 Ok(segments?.into())
314 }
315}
316
317#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, From)]
319#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
320#[cfg_attr(feature = "serde", serde(untagged))]
321#[cfg_attr(feature = "serde-wasm", derive(Tsify))]
322#[cfg_attr(feature = "serde-wasm", tsify(into_wasm_abi, from_wasm_abi))]
323pub enum LabelSegment {
324 Text(Ustr),
326
327 Index(usize),
329}
330
331pub fn label_seg(s: impl Into<Ustr>) -> LabelSegment {
334 LabelSegment::Text(s.into())
335}
336
337impl From<&str> for LabelSegment {
338 fn from(label: &str) -> Self {
339 Self::Text(label.into())
340 }
341}
342
343impl Display for LabelSegment {
344 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
345 match self {
346 Self::Text(label) => label.fmt(f),
347 Self::Index(index) => index.fmt(f),
348 }
349 }
350}
351
352#[derive(Clone, Debug, PartialEq, Eq, From)]
354#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
355#[cfg_attr(feature = "serde-wasm", derive(Tsify))]
356#[cfg_attr(feature = "serde-wasm", tsify(into_wasm_abi, from_wasm_abi))]
357pub struct QualifiedLabel(Vec<LabelSegment>);
358
359pub fn label(x: impl Into<QualifiedLabel>) -> QualifiedLabel {
361 x.into()
362}
363
364impl<const N: usize> From<[LabelSegment; N]> for QualifiedLabel {
365 fn from(segments: [LabelSegment; N]) -> Self {
366 Vec::from(segments).into()
367 }
368}
369
370impl<const N: usize> From<[&str; N]> for QualifiedLabel {
371 fn from(segments: [&str; N]) -> Self {
372 segments.map(LabelSegment::from).into()
373 }
374}
375
376impl From<Ustr> for QualifiedLabel {
377 fn from(label: Ustr) -> Self {
378 Self::single(label.into())
379 }
380}
381
382impl From<&str> for QualifiedLabel {
383 fn from(label: &str) -> Self {
384 Self::single(label.into())
385 }
386}
387
388impl From<usize> for QualifiedLabel {
389 fn from(value: usize) -> Self {
390 Self::single(value.into())
391 }
392}
393
394impl ToDoc for QualifiedLabel {
395 fn to_doc<'a>(&self) -> D<'a> {
396 t(format!("{self}"))
397 }
398}
399
400impl Display for QualifiedLabel {
401 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
402 fmt_qualified(
403 f,
404 &self.0,
405 |segment| matches!(segment, LabelSegment::Text(label) if label.contains(char::is_whitespace)),
406 )
407 }
408}
409
410impl QualifiedLabel {
411 pub fn single(segment: LabelSegment) -> Self {
413 Self(vec![segment])
414 }
415
416 pub fn segments(&self) -> impl Iterator<Item = &LabelSegment> {
418 self.0.iter()
419 }
420
421 pub fn snoc(&self, segment: LabelSegment) -> Self {
423 let mut segments = self.0.clone();
424 segments.push(segment);
425 Self(segments)
426 }
427}
428
429#[derive(Clone, Debug)]
431pub struct Namespace {
432 inner: HashMap<NameSegment, Namespace>,
433 uuid_labels: Option<IndexedHashColumn<Uuid, LabelSegment>>,
434}
435
436#[derive(Clone, Debug, PartialEq, Eq)]
438#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
439#[cfg_attr(feature = "serde", serde(tag = "tag", content = "content"))]
440#[cfg_attr(feature = "serde-wasm", derive(Tsify))]
441#[cfg_attr(feature = "serde-wasm", tsify(into_wasm_abi, from_wasm_abi))]
442pub enum NameLookup {
443 Unique(QualifiedName),
445
446 Arbitrary(QualifiedName),
448
449 None,
451}
452
453impl Namespace {
454 pub fn new_for_text() -> Self {
456 Self {
457 inner: Default::default(),
458 uuid_labels: None,
459 }
460 }
461
462 pub fn new_for_uuid() -> Self {
464 Self {
465 inner: Default::default(),
466 uuid_labels: Some(Default::default()),
467 }
468 }
469
470 pub fn add_inner(&mut self, name: NameSegment, inner: Self) {
472 assert!(
473 self.inner.insert(name, inner).is_none(),
474 "Inner namespace already exists for segment: {name}"
475 );
476 }
477
478 pub fn set_label(&mut self, uuid: Uuid, label: LabelSegment) {
480 let uuid_labels = self.uuid_labels.as_mut().expect("Should be a UUID namespace");
481 if let LabelSegment::Text(s) = &label
482 && s.is_empty()
483 {
484 uuid_labels.unset(&uuid);
486 } else {
487 uuid_labels.set(uuid, label);
488 }
489 }
490
491 pub fn label(&self, name: &QualifiedName) -> Option<QualifiedLabel> {
493 let mut namespace = Some(self);
494 let labels: Option<Vec<_>> = name
495 .segments()
496 .map(|segment| {
497 let maybe_label = match segment {
498 NameSegment::Uuid(uuid) => namespace
499 .and_then(|ns| ns.uuid_labels.as_ref())
500 .and_then(|ul| ul.apply_to_ref(uuid)),
501 NameSegment::Text(name) => Some(LabelSegment::Text(*name)),
502 };
503 namespace = namespace.and_then(|ns| ns.inner.get(segment));
504 maybe_label
505 })
506 .collect();
507 Some(labels?.into())
508 }
509
510 pub fn label_string(&self, name: &QualifiedName) -> String {
517 let mut namespace = Some(self);
518 let labels = name.segments().map(|segment| {
519 let label = match segment {
520 NameSegment::Uuid(uuid) => namespace
521 .and_then(|ns| ns.uuid_labels.as_ref())
522 .and_then(|ul| ul.apply_to_ref(uuid))
523 .unwrap_or_else(|| LabelSegment::Text(uuid.braced().to_string().into())),
524 NameSegment::Text(name) => LabelSegment::Text(*name),
525 };
526 namespace = namespace.and_then(|ns| ns.inner.get(segment));
527 label
528 });
529 QualifiedLabel(labels.collect()).to_string()
530 }
531
532 pub fn name_with_label(&self, label: &QualifiedLabel) -> NameLookup {
534 let mut namespace = Some(self);
535 let mut ambiguous = false;
536 let names: Option<Vec<_>> = label
537 .segments()
538 .map(|segment| {
539 let maybe_uuid_labels = namespace.and_then(|ns| ns.uuid_labels.as_ref());
540 let maybe_name = match (maybe_uuid_labels, segment) {
541 (Some(uuid_labels), _) => {
542 let mut uuids = uuid_labels.preimage(segment);
543 let maybe_uuid = uuids.next();
544 if uuids.next().is_some() {
545 ambiguous = true;
546 }
547 maybe_uuid.map(NameSegment::Uuid)
548 }
549 (None, LabelSegment::Text(text)) => Some(NameSegment::Text(*text)),
550 (None, LabelSegment::Index(_)) => None,
551 };
552 namespace = namespace
553 .and_then(|ns| maybe_name.as_ref().and_then(|name| ns.inner.get(name)));
554 maybe_name
555 })
556 .collect();
557 match names {
558 Some(names) if !ambiguous => NameLookup::Unique(names.into()),
559 Some(names) => NameLookup::Arbitrary(names.into()),
560 None => NameLookup::None,
561 }
562 }
563}
564
565#[cfg(test)]
566mod tests {
567 use super::*;
568 use uuid::uuid;
569
570 const UUID1: Uuid = uuid!("67e55044-10b1-426f-9247-bb680e5fe0c8");
571 const UUID2: Uuid = uuid!("f81d4fae-7dec-11d0-a765-00a0c91e6bf6");
572
573 #[test]
574 fn display_name() {
575 let string = name(UUID1).to_string();
576 assert_eq!(string.chars().next_tuple(), Some(('{', '6', '7', 'e')));
577 assert_eq!(string.chars().next_back(), Some('}'));
578
579 assert_eq!(name("foo").to_string(), "foo");
580 assert_eq!(name("foo bar").to_string(), "foo bar");
581 assert_eq!(name(["foo bar", "baz"]).to_string(), "`foo bar`.baz");
582 }
583
584 #[test]
585 fn serialize_name() {
586 let qual_name = name(UUID1);
587 let serialized = qual_name.serialize_string();
588 assert_eq!(serialized.chars().next_tuple(), Some(('6', '7', 'e')));
589 assert_eq!(QualifiedName::deserialize_str(&serialized), Ok(qual_name));
590
591 let qual_name = name(["foo", "bar", "baz"].map(NameSegment::from));
592 let serialized = qual_name.serialize_string();
593 assert_eq!(serialized, "`foo`.`bar`.`baz`");
594 assert_eq!(QualifiedName::deserialize_str(&serialized), Ok(qual_name));
595 }
596
597 #[test]
598 fn display_label() {
599 assert_eq!(label("foo").to_string(), "foo");
600 assert_eq!(label("foo bar").to_string(), "foo bar");
601 assert_eq!(label(2).to_string(), "2");
602
603 assert_eq!(label([LabelSegment::from("foo"), LabelSegment::from(1)]).to_string(), "foo.1");
604 assert_eq!(label(["foo bar", "baz"]).to_string(), "`foo bar`.baz");
605 }
606
607 #[test]
608 fn namespaces() {
609 let mut child1 = Namespace::new_for_uuid();
610 child1.set_label(UUID1, "bar".into());
611 child1.set_label(UUID2, "baz".into());
612 let mut root = Namespace::new_for_uuid();
613 root.add_inner(UUID1.into(), child1);
614 root.add_inner(UUID2.into(), Namespace::new_for_text());
615 root.set_label(UUID1, "foo".into());
616 root.set_label(UUID2, "textual".into());
617
618 let (qual_name, qual_label) = (name([UUID1, UUID2]), label(["foo", "baz"]));
619 assert_eq!(root.label(&qual_name), Some(qual_label.clone()));
620 assert_eq!(root.label_string(&qual_name), "foo.baz");
621 assert_eq!(root.name_with_label(&qual_label), NameLookup::Unique(qual_name));
622
623 let qual_name =
624 name([NameSegment::Uuid(UUID1), NameSegment::Uuid(UUID1), NameSegment::from("biz")]);
625 let qual_label = label(["foo", "bar", "biz"]);
626 assert_eq!(root.label(&qual_name), Some(qual_label.clone()));
627 assert_eq!(root.name_with_label(&qual_label), NameLookup::Unique(qual_name));
628
629 let qual_name = name([UUID2, UUID1]);
630 assert_eq!(root.label(&qual_name), None);
631 assert_eq!(root.label_string(&qual_name), format!("textual.{{{UUID1}}}"));
632 assert_eq!(root.name_with_label(&label(["bar", "foo"])), NameLookup::None);
633
634 let mut ambiguous = Namespace::new_for_uuid();
635 ambiguous.set_label(UUID1, "foo".into());
636 ambiguous.set_label(UUID2, "foo".into());
637 assert!(matches!(ambiguous.name_with_label(&label("foo")), NameLookup::Arbitrary(_)));
638 }
639}