Skip to main content

json_gettext/common/
json_get_text_builder.rs

1use std::{collections::HashMap, fs::File, path::Path};
2
3use serde::Serialize;
4use serde_json::{Map, Value};
5
6use super::{Context, IntoKey, JSONGetText};
7use crate::{JSONGetTextBuildError, JSONGetTextValue};
8
9/// To build a JSONGetText instance, this struct can help you do that step by step.
10#[derive(Debug, Clone)]
11pub struct JSONGetTextBuilder<'a> {
12    default_key:       crate::Key,
13    context:           Context<'a>,
14    allow_extra_texts: bool,
15}
16
17impl<'a> JSONGetTextBuilder<'a> {
18    /// Create a new `JSONGetTextBuilder` instance. You need to decide your default key at this stage.
19    #[inline]
20    pub fn new<K: IntoKey>(default_key: K) -> JSONGetTextBuilder<'a> {
21        JSONGetTextBuilder {
22            default_key:       default_key.into_key(),
23            context:           HashMap::new(),
24            allow_extra_texts: false,
25        }
26    }
27
28    /// Set whether a non-default key may contain texts that the default key does not define.
29    /// When enabled, such texts are dropped while building instead of causing a `TextInKeyNotInDefaultKey` error.
30    #[inline]
31    pub fn allow_extra_texts(&mut self, allow: bool) -> &mut Self {
32        self.allow_extra_texts = allow;
33
34        self
35    }
36
37    /// Add a JSON string to the context for a specific key. The JSON string must represent a map object (key-value).
38    pub fn add_json<K: IntoKey, J: AsRef<str> + ?Sized>(
39        &mut self,
40        key: K,
41        json: &'a J,
42    ) -> Result<&mut Self, JSONGetTextBuildError> {
43        let key = key.into_key();
44
45        if self.context.contains_key(&key) {
46            return Err(JSONGetTextBuildError::DuplicatedKey(key));
47        }
48
49        let map: HashMap<String, JSONGetTextValue<'a>> = serde_json::from_str(json.as_ref())?;
50
51        self.context.insert(key, map);
52
53        Ok(self)
54    }
55
56    /// Add a JSON string to the context for a specific key. The JSON string must represent a map object (key-value).
57    pub fn add_json_owned<K: IntoKey, J: AsRef<str>>(
58        &mut self,
59        key: K,
60        json: J,
61    ) -> Result<&mut Self, JSONGetTextBuildError> {
62        let key = key.into_key();
63
64        if self.context.contains_key(&key) {
65            return Err(JSONGetTextBuildError::DuplicatedKey(key));
66        }
67
68        let value: Map<String, Value> = serde_json::from_str(json.as_ref())?;
69
70        let mut map: HashMap<String, JSONGetTextValue<'static>> =
71            HashMap::with_capacity(value.len());
72
73        for (k, v) in value {
74            map.insert(k, JSONGetTextValue::from_json_value(v));
75        }
76
77        self.context.insert(key, map);
78
79        Ok(self)
80    }
81
82    /// Add a JSON file to the context for a specific key. The JSON file must represent a map object (key-value).
83    pub fn add_json_file<K: IntoKey, P: AsRef<Path>>(
84        &mut self,
85        key: K,
86        path: P,
87    ) -> Result<&mut Self, JSONGetTextBuildError> {
88        let key = key.into_key();
89
90        if self.context.contains_key(&key) {
91            return Err(JSONGetTextBuildError::DuplicatedKey(key));
92        }
93
94        let path = path.as_ref();
95
96        let value: Map<String, Value> = serde_json::from_reader(File::open(path)?)?;
97
98        let mut map: HashMap<String, JSONGetTextValue<'static>> =
99            HashMap::with_capacity(value.len());
100
101        for (k, v) in value {
102            map.insert(k, JSONGetTextValue::from_json_value(v));
103        }
104
105        self.context.insert(key, map);
106
107        Ok(self)
108    }
109
110    /// Add any serializable value to the context for a specific key. The value must represent a map object (key-value).
111    pub fn add_serialize<K: IntoKey, S: Serialize>(
112        &mut self,
113        key: K,
114        value: S,
115    ) -> Result<&mut Self, JSONGetTextBuildError> {
116        let key = key.into_key();
117
118        if self.context.contains_key(&key) {
119            return Err(JSONGetTextBuildError::DuplicatedKey(key));
120        }
121
122        let value: Value = serde_json::to_value(value)?;
123
124        match value {
125            Value::Object(value) => {
126                let mut map: HashMap<String, JSONGetTextValue<'static>> =
127                    HashMap::with_capacity(value.len());
128
129                for (k, v) in value {
130                    map.insert(k, JSONGetTextValue::from_json_value(v));
131                }
132
133                self.context.insert(key, map);
134
135                Ok(self)
136            },
137            _ => Err(JSONGetTextBuildError::NotObject),
138        }
139    }
140
141    /// Add a map to the context.
142    pub fn add_map<K: IntoKey>(
143        &mut self,
144        key: K,
145        map: HashMap<String, JSONGetTextValue<'a>>,
146    ) -> Result<&mut Self, JSONGetTextBuildError> {
147        let key = key.into_key();
148
149        if self.context.contains_key(&key) {
150            return Err(JSONGetTextBuildError::DuplicatedKey(key));
151        }
152
153        self.context.insert(key, map);
154
155        Ok(self)
156    }
157
158    /// Build a `JSONGetText` instance.
159    pub fn build(self) -> Result<JSONGetText<'a>, JSONGetTextBuildError> {
160        JSONGetText::from_context_with_default_key(
161            self.default_key,
162            self.context,
163            self.allow_extra_texts,
164        )
165    }
166}
167
168impl<'a> From<crate::Key> for JSONGetTextBuilder<'a> {
169    #[inline]
170    fn from(v: crate::Key) -> JSONGetTextBuilder<'a> {
171        JSONGetTextBuilder::new(v)
172    }
173}