1//! Write MO files.
2
3use std::{
4 fs::File,
5 io::{BufWriter, Write},
6 path::Path,
7};
8
9use crate::{catalog::Catalog, message::MessageView};
10
11fn original_repr_len(message: &dyn MessageView) -> usize {
12 let mut result: usize = 0usize;
13 let ctxt: &str = message.msgctxt();
14 if !ctxt.is_empty() {
15 result += ctxt.len() + 1;
16 }
17 result += message.msgid().len();
18 if message.is_plural() {
19 result += 1 + message.msgid_plural().unwrap().len();
20 }
21 result
22}
23
24fn write_original_repr(
25 writer: &mut BufWriter<std::fs::File>,
26 message: &dyn MessageView,
27) -> Result<(), std::io::Error> {
28 let ctxt: &str = message.msgctxt();
29 if !ctxt.is_empty() {
30 writer.write_all(buf:ctxt.as_bytes())?;
31 writer.write_all(&[4u8])?;
32 }
33 writer.write_all(buf:message.msgid().as_bytes())?;
34 if message.is_plural() {
35 writer.write_all(&[0u8])?;
36 writer.write_all(buf:message.msgid_plural().unwrap().as_bytes())?;
37 }
38 Ok(())
39}
40
41fn translated_repr_len(message: &dyn MessageView) -> usize {
42 if let true = message.is_singular() {
43 message.msgstr().unwrap().len()
44 } else {
45 messageimpl Iterator
46 .msgstr_plural()
47 .unwrap()
48 .iter()
49 .map(|x: &String| x.len() + 1)
50 .sum::<usize>()
51 - 1
52 }
53}
54
55fn write_translated_repr(
56 writer: &mut BufWriter<std::fs::File>,
57 message: &dyn MessageView,
58) -> Result<(), std::io::Error> {
59 if message.is_singular() {
60 writer.write_all(buf:message.msgstr().unwrap().as_bytes())?;
61 } else {
62 writer.write_all(buf:message.msgstr_plural().unwrap().join(sep:"\u{0000}").as_bytes())?;
63 }
64 Ok(())
65}
66
67/// Saves a catalog to a binary MO file.
68pub fn write(catalog: &Catalog, path: &Path) -> Result<(), std::io::Error> {
69 let file = File::create(path)?;
70 let mut writer = BufWriter::new(file);
71
72 let metadata = catalog.metadata.export_for_mo();
73
74 // Header
75 let magic_number: u32 = 0x950412de;
76 let format_ver: u32 = 0x00000000;
77 let num_strings = catalog.count() + 1;
78 let orig_table_offset = 28;
79 let trans_table_offset = orig_table_offset + 8 * num_strings;
80 writer.write_all(&magic_number.to_ne_bytes())?;
81 writer.write_all(&format_ver.to_ne_bytes())?;
82 writer.write_all(&(num_strings as u32).to_ne_bytes())?;
83 writer.write_all(&(orig_table_offset as u32).to_ne_bytes())?;
84 writer.write_all(&(trans_table_offset as u32).to_ne_bytes())?;
85 writer.write_all(&[0u8; 4])?;
86 writer.write_all(&[0u8; 4])?;
87
88 // O table
89 let mut offset = trans_table_offset + 8 * num_strings;
90 writer.write_all(&0u32.to_ne_bytes())?;
91 writer.write_all(&(offset as u32).to_ne_bytes())?;
92 offset += 1;
93 for &index in catalog.map.values() {
94 let length = original_repr_len(catalog.messages[index].as_ref().unwrap());
95 writer.write_all(&(length as u32).to_ne_bytes())?;
96 writer.write_all(&(offset as u32).to_ne_bytes())?;
97 offset += length + 1;
98 }
99
100 // T table
101 writer.write_all(&(metadata.len() as u32).to_ne_bytes())?;
102 writer.write_all(&(offset as u32).to_ne_bytes())?;
103 offset += metadata.len() + 1;
104 for &index in catalog.map.values() {
105 let length = translated_repr_len(catalog.messages[index].as_ref().unwrap());
106 writer.write_all(&(length as u32).to_ne_bytes())?;
107 writer.write_all(&(offset as u32).to_ne_bytes())?;
108 offset += length + 1;
109 }
110
111 // O strings
112 writer.write_all(&[0u8])?;
113 for &index in catalog.map.values() {
114 write_original_repr(&mut writer, catalog.messages[index].as_ref().unwrap())?;
115 writer.write_all(&[0u8])?;
116 }
117
118 // T strings
119 writer.write_all(metadata.as_bytes())?;
120 writer.write_all(&[0u8])?;
121 for &index in catalog.map.values() {
122 write_translated_repr(&mut writer, catalog.messages[index].as_ref().unwrap())?;
123 writer.write_all(&[0u8])?;
124 }
125
126 writer.flush()?;
127 Ok(())
128}
129