1use std::cmp::Ordering;
2use std::fmt;
3
4use crate::entry::{
5 maybe_msgid_msgctxt_eot_split, mo_entry_to_string,
6 EntryCmpByOptions, MsgidEotMsgctxt, POEntry, Translated,
7};
8use crate::traits::Merge;
9
10#[derive(Default, Clone, Debug, PartialEq)]
27pub struct MOEntry {
28 pub msgid: String,
30 pub msgstr: Option<String>,
32 pub msgid_plural: Option<String>,
34 pub msgstr_plural: Vec<String>,
36 pub msgctxt: Option<String>,
38}
39
40impl MOEntry {
41 pub fn new(
42 msgid: String,
43 msgstr: Option<String>,
44 msgid_plural: Option<String>,
45 msgstr_plural: Vec<String>,
46 msgctxt: Option<String>,
47 ) -> MOEntry {
48 MOEntry {
49 msgid,
50 msgstr,
51 msgid_plural,
52 msgstr_plural,
53 msgctxt,
54 }
55 }
56
57 pub fn to_string_with_wrapwidth(
59 &self,
60 wrapwidth: usize,
61 ) -> String {
62 mo_entry_to_string(self, wrapwidth, "")
63 }
64
65 pub fn cmp_by(
70 &self,
71 other: &Self,
72 options: &EntryCmpByOptions,
73 ) -> Ordering {
74 let placeholder = &"\0".to_string();
75
76 if options.by_msgctxt {
77 let msgctxt = self
78 .msgctxt
79 .as_ref()
80 .unwrap_or(placeholder)
81 .to_string();
82 let other_msgctxt = other
83 .msgctxt
84 .as_ref()
85 .unwrap_or(placeholder)
86 .to_string();
87 if msgctxt > other_msgctxt {
88 return Ordering::Greater;
89 } else if msgctxt < other_msgctxt {
90 return Ordering::Less;
91 }
92 }
93
94 if options.by_msgid_plural {
95 let msgid_plural = self
96 .msgid_plural
97 .as_ref()
98 .unwrap_or(placeholder)
99 .to_string();
100 let other_msgid_plural = other
101 .msgid_plural
102 .as_ref()
103 .unwrap_or(placeholder)
104 .to_string();
105 if msgid_plural > other_msgid_plural {
106 return Ordering::Greater;
107 } else if msgid_plural < other_msgid_plural {
108 return Ordering::Less;
109 }
110 }
111
112 if options.by_msgstr_plural {
113 let mut msgstr_plural = self.msgstr_plural.clone();
114 msgstr_plural.sort();
115 let mut other_msgstr_plural = other.msgstr_plural.clone();
116 other_msgstr_plural.sort();
117 if msgstr_plural > other_msgstr_plural {
118 return Ordering::Greater;
119 } else if msgstr_plural < other_msgstr_plural {
120 return Ordering::Less;
121 }
122 }
123
124 if options.by_msgid {
125 if self.msgid > other.msgid {
126 return Ordering::Greater;
127 } else if self.msgid < other.msgid {
128 return Ordering::Less;
129 }
130 }
131
132 if options.by_msgstr {
133 let msgstr = self
134 .msgstr
135 .as_ref()
136 .unwrap_or(placeholder)
137 .to_string();
138 let other_msgstr = other
139 .msgstr
140 .as_ref()
141 .unwrap_or(placeholder)
142 .to_string();
143 if msgstr > other_msgstr {
144 return Ordering::Greater;
145 } else if msgstr < other_msgstr {
146 return Ordering::Less;
147 }
148 }
149
150 Ordering::Equal
151 }
152}
153
154impl MsgidEotMsgctxt for MOEntry {
155 fn msgid_eot_msgctxt(&self) -> String {
156 maybe_msgid_msgctxt_eot_split(&self.msgid, &self.msgctxt)
157 .to_string()
158 }
159}
160
161impl Translated for MOEntry {
162 fn translated(&self) -> bool {
169 if let Some(msgstr) = &self.msgstr {
170 return !msgstr.is_empty();
171 }
172
173 if self.msgstr_plural.is_empty() {
174 return false;
175 } else {
176 for msgstr_plural in &self.msgstr_plural {
177 if !msgstr_plural.is_empty() {
178 return true;
179 }
180 }
181 }
182
183 false
184 }
185}
186
187impl Merge for MOEntry {
188 fn merge(&mut self, other: Self) {
189 self.msgid = other.msgid;
190 self.msgstr = other.msgstr;
191 self.msgid_plural = other.msgid_plural;
192 self.msgstr_plural = other.msgstr_plural;
193 self.msgctxt = other.msgctxt;
194 }
195}
196
197impl fmt::Display for MOEntry {
198 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
199 write!(f, "{}", self.to_string_with_wrapwidth(78))
200 }
201}
202
203impl From<&str> for MOEntry {
204 fn from(s: &str) -> Self {
206 MOEntry::new(s.to_string(), None, None, vec![], None)
207 }
208}
209
210impl From<&POEntry> for MOEntry {
211 fn from(entry: &POEntry) -> Self {
216 MOEntry {
217 msgid: entry.msgid.clone(),
218 msgstr: entry.msgstr.clone(),
219 msgid_plural: entry.msgid_plural.clone(),
220 msgstr_plural: entry.msgstr_plural.clone(),
221 msgctxt: entry.msgctxt.clone(),
222 }
223 }
224}
225
226#[cfg(test)]
227mod tests {
228 use super::*;
229
230 #[test]
231 fn constructor() {
232 let moentry = MOEntry::new(
233 "msgid".to_string(),
234 Some("msgstr".to_string()),
235 None,
236 vec![],
237 None,
238 );
239
240 assert_eq!(moentry.msgid, "msgid");
241 assert_eq!(moentry.msgstr, Some("msgstr".to_string()));
242 assert_eq!(moentry.msgid_plural, None);
243 assert_eq!(moentry.msgstr_plural, vec![] as Vec<String>);
244 assert_eq!(moentry.msgctxt, None);
245 }
246
247 #[test]
248 fn moentry_translated() {
249 let moentry = MOEntry::new(
251 "msgid".to_string(),
252 Some("".to_string()),
253 None,
254 vec![],
255 None,
256 );
257 assert_eq!(moentry.translated(), false);
258
259 let moentry = MOEntry::new(
260 "msgid".to_string(),
261 Some("msgstr".to_string()),
262 None,
263 vec![],
264 None,
265 );
266 assert_eq!(moentry.translated(), true);
267
268 let moentry = MOEntry::new(
270 "msgid".to_string(),
271 None,
272 None,
273 vec![],
274 None,
275 );
276 assert_eq!(moentry.translated(), false);
277
278 let moentry = MOEntry::new(
280 "msgid".to_string(),
281 None,
282 None,
283 vec!["".to_string()],
284 None,
285 );
286 assert_eq!(moentry.translated(), false);
287 }
288
289 #[test]
290 fn moentry_merge() {
291 let mut moentry = MOEntry::new(
292 "msgid".to_string(),
293 Some("msgstr".to_string()),
294 Some("msgid_plural".to_string()),
295 vec!["msgstr_plural".to_string()],
296 Some("msgctxt".to_string()),
297 );
298 let other = MOEntry::new(
299 "other_msgid".to_string(),
300 Some("other_msgstr".to_string()),
301 Some("other_msgid_plural".to_string()),
302 vec!["other_msgstr_plural".to_string()],
303 Some("other_msgctxt".to_string()),
304 );
305
306 moentry.merge(other);
307
308 assert_eq!(moentry.msgid, "other_msgid");
309 assert_eq!(moentry.msgstr, Some("other_msgstr".to_string()));
310 assert_eq!(
311 moentry.msgid_plural,
312 Some("other_msgid_plural".to_string())
313 );
314 assert_eq!(
315 moentry.msgstr_plural,
316 vec!["other_msgstr_plural".to_string()],
317 );
318 assert_eq!(
319 moentry.msgctxt,
320 Some("other_msgctxt".to_string())
321 );
322 }
323
324 #[test]
325 fn moentry_to_string() {
326 let moentry = MOEntry::new(
328 "msgid".to_string(),
329 Some("msgstr".to_string()),
330 Some("msgid_plural".to_string()),
331 vec!["msgstr_plural".to_string()],
332 Some("msgctxt".to_string()),
333 );
334
335 let expected = r#"msgctxt "msgctxt"
336msgid "msgid"
337msgid_plural "msgid_plural"
338msgstr[0] "msgstr_plural"
339"#
340 .to_string();
341
342 assert_eq!(moentry.to_string(), expected);
343
344 let moentry = MOEntry::new(
346 "msgid".to_string(),
347 Some("msgstr".to_string()),
348 None,
349 vec![],
350 Some("msgctxt".to_string()),
351 );
352
353 let expected = r#"msgctxt "msgctxt"
354msgid "msgid"
355msgstr "msgstr"
356"#
357 .to_string();
358
359 assert_eq!(moentry.to_string(), expected);
360 }
361
362 #[test]
363 fn moentry_from_poentry() {
364 let msgstr_plural = vec!["msgstr_plural".to_string()];
365
366 let mut poentry = POEntry::new(0);
367 poentry.msgid = "msgid".to_string();
368 poentry.msgstr = Some("msgstr".to_string());
369 poentry.msgid_plural = Some("msgid_plural".to_string());
370 poentry.msgstr_plural = msgstr_plural.clone();
371 poentry.msgctxt = Some("msgctxt".to_string());
372
373 let moentry = MOEntry::from(&poentry);
374
375 assert_eq!(moentry.msgid, "msgid");
376 assert_eq!(moentry.msgstr, Some("msgstr".to_string()));
377 assert_eq!(
378 moentry.msgid_plural,
379 Some("msgid_plural".to_string())
380 );
381 assert_eq!(moentry.msgstr_plural, msgstr_plural);
382 assert_eq!(moentry.msgctxt, Some("msgctxt".to_string()));
383 }
384}