1use std::collections::BTreeMap;
4
5use crate::KwId;
6
7#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
9pub struct Keyword {
10 pub namespace: Option<String>,
12 pub name: String,
14}
15
16impl Keyword {
17 #[must_use]
19 pub fn new(namespace: Option<&str>, name: &str) -> Self {
20 Self {
21 namespace: namespace.map(str::to_owned),
22 name: name.to_owned(),
23 }
24 }
25
26 #[must_use]
30 pub fn parse(text: &str) -> Self {
31 match text.split_once('/') {
32 Some((namespace, name)) if !namespace.is_empty() && !name.is_empty() => {
33 Self::new(Some(namespace), name)
34 }
35 _ => Self::new(None, text),
36 }
37 }
38}
39
40impl std::fmt::Display for Keyword {
41 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
42 match &self.namespace {
43 Some(namespace) => write!(f, ":{namespace}/{}", self.name),
44 None => write!(f, ":{}", self.name),
45 }
46 }
47}
48
49#[derive(Clone, Debug, Default)]
51pub struct KeywordInterner {
52 by_keyword: BTreeMap<Keyword, KwId>,
53 by_id: BTreeMap<KwId, Keyword>,
54 next: KwId,
55}
56
57impl KeywordInterner {
58 pub fn intern(&mut self, keyword: Keyword) -> KwId {
60 if let Some(id) = self.by_keyword.get(&keyword) {
61 return *id;
62 }
63 let id = self.next;
64 self.next += 1;
65 self.by_id.insert(id, keyword.clone());
66 self.by_keyword.insert(keyword, id);
67 id
68 }
69
70 #[must_use]
72 pub fn get(&self, keyword: &Keyword) -> Option<KwId> {
73 self.by_keyword.get(keyword).copied()
74 }
75
76 #[must_use]
78 pub fn resolve(&self, id: KwId) -> Option<&Keyword> {
79 self.by_id.get(&id)
80 }
81
82 pub fn iter(&self) -> impl Iterator<Item = (KwId, &Keyword)> {
85 self.by_id.iter().map(|(id, keyword)| (*id, keyword))
86 }
87
88 #[must_use]
90 pub fn len(&self) -> usize {
91 self.by_id.len()
92 }
93
94 #[must_use]
96 pub fn is_empty(&self) -> bool {
97 self.by_id.is_empty()
98 }
99}