use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Glossary {
pub terms: Vec<GlossaryTerm>,
}
impl Glossary {
#[must_use]
pub fn new() -> Self {
Self { terms: Vec::new() }
}
pub fn add_term(&mut self, term: GlossaryTerm) {
self.terms.push(term);
}
#[must_use]
pub fn get(&self, id: &str) -> Option<&GlossaryTerm> {
self.terms.iter().find(|t| t.id == id)
}
#[must_use]
pub fn find_by_text(&self, text: &str) -> Option<&GlossaryTerm> {
let lower = text.to_lowercase();
self.terms.iter().find(|t| {
t.term.to_lowercase() == lower || t.aliases.iter().any(|a| a.to_lowercase() == lower)
})
}
#[must_use]
pub fn len(&self) -> usize {
self.terms.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.terms.is_empty()
}
}
impl Default for Glossary {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct GlossaryTerm {
pub id: String,
pub term: String,
pub definition: String,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub aliases: Vec<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub see_also: Vec<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub category: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub pronunciation: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub etymology: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub usage: Option<String>,
}
impl GlossaryTerm {
#[must_use]
pub fn new(
id: impl Into<String>,
term: impl Into<String>,
definition: impl Into<String>,
) -> Self {
Self {
id: id.into(),
term: term.into(),
definition: definition.into(),
aliases: Vec::new(),
see_also: Vec::new(),
category: None,
pronunciation: None,
etymology: None,
usage: None,
}
}
#[must_use]
pub fn with_alias(mut self, alias: impl Into<String>) -> Self {
self.aliases.push(alias.into());
self
}
#[must_use]
pub fn with_see_also(mut self, term_id: impl Into<String>) -> Self {
self.see_also.push(term_id.into());
self
}
#[must_use]
pub fn with_category(mut self, category: impl Into<String>) -> Self {
self.category = Some(category.into());
self
}
#[must_use]
pub fn with_pronunciation(mut self, pronunciation: impl Into<String>) -> Self {
self.pronunciation = Some(pronunciation.into());
self
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct GlossaryRef {
#[serde(rename = "ref", alias = "termId")]
pub term_id: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub display: Option<String>,
}
impl GlossaryRef {
#[must_use]
pub fn new(term_id: impl Into<String>) -> Self {
Self {
term_id: term_id.into(),
display: None,
}
}
#[must_use]
pub fn with_display(mut self, display: impl Into<String>) -> Self {
self.display = Some(display.into());
self
}
}