globetrotter_model/
lib.rs

1#![allow(warnings)]
2
3pub mod diagnostics;
4pub mod ext;
5pub mod json;
6pub mod language;
7pub mod toml;
8pub mod validation;
9
10use diagnostics::{DisplayRepr, FileId, Spanned};
11pub use indexmap::IndexMap;
12pub use language::Language;
13
14use serde::{Deserialize, Serialize};
15
16#[derive(
17    Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, strum::Display,
18)]
19pub enum TemplateEngine {
20    #[serde(rename = "handlebars")]
21    Handlebars,
22    #[serde(rename = "golang", alias = "go")]
23    Golang,
24    #[serde(rename = "mustache")]
25    Mustache,
26    #[serde(rename = "jinja2")]
27    Jinja2,
28    Other(String),
29}
30
31impl std::str::FromStr for TemplateEngine {
32    type Err = ::strum::ParseError;
33
34    fn from_str(s: &str) -> Result<Self, Self::Err> {
35        s.parse()
36    }
37}
38
39#[derive(
40    Copy,
41    Clone,
42    Debug,
43    PartialEq,
44    Eq,
45    PartialOrd,
46    Ord,
47    Hash,
48    strum::Display,
49    strum::EnumString,
50    strum::VariantNames,
51    strum::IntoStaticStr,
52    strum::EnumCount,
53    strum::EnumIter,
54    Serialize,
55    Deserialize,
56)]
57pub enum ArgumentType {
58    #[serde(rename = "any")]
59    #[strum(to_string = "any", serialize = "any")]
60    Any,
61    #[serde(rename = "string")]
62    #[strum(to_string = "string", serialize = "string")]
63    String,
64    #[serde(rename = "number")]
65    #[strum(to_string = "number", serialize = "number")]
66    Number,
67    #[serde(rename = "isodatetime")]
68    #[strum(to_string = "isodatetime", serialize = "isodatetime")]
69    Iso8601DateTimeString,
70    // i8,
71    // u8,
72    // i16,
73    // u16,
74    // i32,
75    // u32,
76    // i64,
77    // u64,
78    // i128,
79    // u128,
80    // isize,
81    // usize,
82}
83
84impl ArgumentType {
85    #[must_use]
86    pub fn display(&self) -> DisplayRepr<'_, Self> {
87        DisplayRepr(self)
88    }
89}
90
91pub type Arguments = IndexMap<String, ArgumentType>;
92pub type LanguageTranslations = IndexMap<Language, Spanned<String>>;
93
94#[derive(Clone, Debug, PartialEq, Eq, Serialize)]
95pub struct Translation {
96    #[serde(flatten)]
97    pub language: LanguageTranslations,
98    #[serde(skip_serializing_if = "Arguments::is_empty")]
99    pub arguments: Arguments,
100    #[serde(skip)]
101    pub file_id: FileId,
102}
103
104impl std::fmt::Display for Translation {
105    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
106        f.debug_struct("Translation")
107            .field(
108                "arguments",
109                &self
110                    .arguments
111                    .iter()
112                    .map(|(k, v)| (k, v.display()))
113                    .collect::<IndexMap<_, _>>(),
114            )
115            .field(
116                "language",
117                &self
118                    .language
119                    .iter()
120                    .map(|(k, v)| (k, v.display()))
121                    .collect::<IndexMap<_, _>>(),
122            )
123            .field("file_id", &self.file_id)
124            .finish()
125    }
126}
127
128impl Translation {
129    #[must_use]
130    pub fn is_empty(&self) -> bool {
131        self.arguments.is_empty() && self.language.is_empty()
132    }
133
134    #[must_use]
135    pub fn is_template(&self) -> bool {
136        !self.arguments.is_empty()
137    }
138}
139
140#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize)]
141pub struct Translations(pub IndexMap<Spanned<String>, Translation>);
142
143impl Translations {
144    #[cfg(not(feature = "rayon"))]
145    pub fn sort(&mut self) {
146        self.0.sort_keys();
147        for (_key, translation) in self.0.iter_mut() {
148            translation.arguments.sort_keys();
149            translation.language.sort_keys();
150        }
151    }
152
153    #[cfg(feature = "rayon")]
154    pub fn sort(&mut self) {
155        use rayon::prelude::*;
156        self.0.par_sort_keys();
157        for translation in self.0.values_mut() {
158            translation.arguments.par_sort_keys();
159            translation.language.par_sort_keys();
160        }
161    }
162
163    #[must_use]
164    pub fn iter(&self) -> indexmap::map::Iter<'_, Spanned<String>, Translation> {
165        self.0.iter()
166    }
167
168    #[must_use]
169    pub fn len(&self) -> usize {
170        self.0.len()
171    }
172
173    #[must_use]
174    pub fn is_empty(&self) -> bool {
175        self.0.is_empty()
176    }
177}
178
179impl FromIterator<(Spanned<String>, Translation)> for Translations {
180    fn from_iter<T: IntoIterator<Item = (Spanned<String>, Translation)>>(iter: T) -> Self {
181        Self(iter.into_iter().collect())
182    }
183}
184
185impl<'a> IntoIterator for &'a Translations {
186    type Item = (&'a Spanned<String>, &'a Translation);
187    type IntoIter = indexmap::map::Iter<'a, Spanned<String>, Translation>;
188
189    fn into_iter(self) -> Self::IntoIter {
190        self.0.iter()
191    }
192}
193
194impl IntoIterator for Translations {
195    type Item = (Spanned<String>, Translation);
196    type IntoIter = indexmap::map::IntoIter<Spanned<String>, Translation>;
197
198    fn into_iter(self) -> Self::IntoIter {
199        self.0.into_iter()
200    }
201}
202
203#[cfg(test)]
204mod tests {
205    static INIT: std::sync::Once = std::sync::Once::new();
206
207    /// Initialize test
208    ///
209    /// This ensures `color_eyre` is setup once.
210    pub fn init() {
211        INIT.call_once(|| {
212            color_eyre::install().ok();
213        });
214    }
215}