cdx_core/extensions/semantic/
glossary.rs1use serde::{Deserialize, Serialize};
4
5#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
7#[serde(rename_all = "camelCase")]
8pub struct Glossary {
9 pub terms: Vec<GlossaryTerm>,
11}
12
13impl Glossary {
14 #[must_use]
16 pub fn new() -> Self {
17 Self { terms: Vec::new() }
18 }
19
20 pub fn add_term(&mut self, term: GlossaryTerm) {
22 self.terms.push(term);
23 }
24
25 #[must_use]
27 pub fn get(&self, id: &str) -> Option<&GlossaryTerm> {
28 self.terms.iter().find(|t| t.id == id)
29 }
30
31 #[must_use]
33 pub fn find_by_text(&self, text: &str) -> Option<&GlossaryTerm> {
34 let lower = text.to_lowercase();
35 self.terms.iter().find(|t| {
36 t.term.to_lowercase() == lower || t.aliases.iter().any(|a| a.to_lowercase() == lower)
37 })
38 }
39
40 #[must_use]
42 pub fn len(&self) -> usize {
43 self.terms.len()
44 }
45
46 #[must_use]
48 pub fn is_empty(&self) -> bool {
49 self.terms.is_empty()
50 }
51}
52
53impl Default for Glossary {
54 fn default() -> Self {
55 Self::new()
56 }
57}
58
59#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
61#[serde(rename_all = "camelCase")]
62pub struct GlossaryTerm {
63 pub id: String,
65
66 pub term: String,
68
69 pub definition: String,
71
72 #[serde(default, skip_serializing_if = "Vec::is_empty")]
74 pub aliases: Vec<String>,
75
76 #[serde(default, skip_serializing_if = "Vec::is_empty")]
78 pub see_also: Vec<String>,
79
80 #[serde(default, skip_serializing_if = "Option::is_none")]
82 pub category: Option<String>,
83
84 #[serde(default, skip_serializing_if = "Option::is_none")]
86 pub pronunciation: Option<String>,
87
88 #[serde(default, skip_serializing_if = "Option::is_none")]
90 pub etymology: Option<String>,
91
92 #[serde(default, skip_serializing_if = "Option::is_none")]
94 pub usage: Option<String>,
95}
96
97impl GlossaryTerm {
98 #[must_use]
100 pub fn new(
101 id: impl Into<String>,
102 term: impl Into<String>,
103 definition: impl Into<String>,
104 ) -> Self {
105 Self {
106 id: id.into(),
107 term: term.into(),
108 definition: definition.into(),
109 aliases: Vec::new(),
110 see_also: Vec::new(),
111 category: None,
112 pronunciation: None,
113 etymology: None,
114 usage: None,
115 }
116 }
117
118 #[must_use]
120 pub fn with_alias(mut self, alias: impl Into<String>) -> Self {
121 self.aliases.push(alias.into());
122 self
123 }
124
125 #[must_use]
127 pub fn with_see_also(mut self, term_id: impl Into<String>) -> Self {
128 self.see_also.push(term_id.into());
129 self
130 }
131
132 #[must_use]
134 pub fn with_category(mut self, category: impl Into<String>) -> Self {
135 self.category = Some(category.into());
136 self
137 }
138
139 #[must_use]
141 pub fn with_pronunciation(mut self, pronunciation: impl Into<String>) -> Self {
142 self.pronunciation = Some(pronunciation.into());
143 self
144 }
145}
146
147#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
149pub struct GlossaryRef {
150 #[serde(rename = "ref", alias = "termId")]
152 pub term_id: String,
153
154 #[serde(default, skip_serializing_if = "Option::is_none")]
156 pub display: Option<String>,
157}
158
159impl GlossaryRef {
160 #[must_use]
162 pub fn new(term_id: impl Into<String>) -> Self {
163 Self {
164 term_id: term_id.into(),
165 display: None,
166 }
167 }
168
169 #[must_use]
171 pub fn with_display(mut self, display: impl Into<String>) -> Self {
172 self.display = Some(display.into());
173 self
174 }
175}