globetrotter_model/
json.rs1use crate::{Language, TemplateEngine, diagnostics::Spanned};
2use indexmap::IndexMap;
3use serde::{Deserialize, Serialize};
4
5#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
6pub enum Translation {
7 #[serde(rename = "literal")]
8 Literal(String),
9 #[serde(rename = "template")]
10 Template(String),
11}
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize, Serialize)]
14#[derive(Default)]
15pub enum Version {
16 #[serde(rename = "1", alias = "v1")]
17 #[default]
18 V1,
19 }
22
23
24#[derive(thiserror::Error, Debug)]
25pub enum Error {
26 #[error("missing translation of key {key:?} for language {language:?}")]
27 MissingKey {
28 key: Spanned<String>,
29 language: Language,
30 },
31 #[error(transparent)]
32 Json(#[from] serde_json::Error),
33}
34
35#[derive(Debug, Clone, Deserialize, Serialize)]
36pub struct Translations {
37 #[serde(default)]
38 pub version: Version,
39 pub template_engine: Option<TemplateEngine>,
40 pub language: Language,
41 pub translations: IndexMap<String, Translation>,
42}
43
44impl crate::Translations {
45 pub fn write_translations_json(
52 &self,
53 language: Language,
54 template_engine: Option<TemplateEngine>,
55 strict: bool,
56 writer: impl std::io::Write,
57 ) -> Result<Translations, Error> {
58 let translations = self.translations_json(language, strict, template_engine)?;
59 serde_json::to_writer_pretty(writer, &translations)?;
60 Ok(translations)
61 }
62
63 pub fn translations_json(
70 &self,
71 language: Language,
72 strict: bool,
73 template_engine: Option<TemplateEngine>,
74 ) -> Result<Translations, Error> {
75 let translations = self
76 .0
77 .iter()
78 .map(
79 |(key, translation)| match translation.language.get(&language) {
80 Some(t) => {
81 let value = if translation.is_template() {
82 Translation::Template(t.as_ref().clone())
83 } else {
84 Translation::Literal(t.as_ref().clone())
85 };
86 Ok((key.clone().into_inner(), value))
87 }
88 None if strict => Err(Error::MissingKey {
89 key: key.clone(),
90 language,
91 }),
92 None => Ok((
93 key.clone().into_inner(),
94 Translation::Literal(format!("missing translation {key} for {language:?}")),
95 )),
96 },
97 )
98 .collect::<Result<IndexMap<_, _>, _>>()?;
99 Ok(Translations {
100 version: Version::V1,
101 template_engine,
102 translations,
103 language,
104 })
105 }
106}