1mod api;
4mod borrowed;
5mod merge;
6mod parse;
7mod scan;
8mod serialize;
9mod text;
10
11pub use api::{
12 ApiError, CatalogMessage, CatalogMessageExtra, CatalogMessageKey, CatalogOrigin, CatalogStats,
13 CatalogUpdateInput, CatalogUpdateResult, Diagnostic, DiagnosticSeverity, EffectiveTranslation,
14 EffectiveTranslationRef, ExtractedMessage, ExtractedPluralMessage, ExtractedSingularMessage,
15 NormalizedParsedCatalog, ObsoleteStrategy, OrderBy, ParseCatalogOptions, ParsedCatalog,
16 PlaceholderCommentMode, PluralEncoding, PluralSource, SourceExtractedMessage, TranslationShape,
17 UpdateCatalogFileOptions, UpdateCatalogOptions, parse_catalog, update_catalog,
18 update_catalog_file,
19};
20pub use borrowed::{
21 BorrowedHeader, BorrowedMsgStr, BorrowedPoFile, BorrowedPoItem, parse_po_borrowed,
22};
23pub use merge::{ExtractedMessage as MergeExtractedMessage, merge_catalog};
24pub use parse::parse_po;
25pub use serialize::stringify_po;
26pub use text::{escape_string, extract_quoted, extract_quoted_cow, unescape_string};
27
28use core::{fmt, ops::Index};
29
30#[derive(Debug, Clone, PartialEq, Eq, Default)]
31pub struct PoFile {
32 pub comments: Vec<String>,
33 pub extracted_comments: Vec<String>,
34 pub headers: Vec<Header>,
35 pub items: Vec<PoItem>,
36}
37
38#[derive(Debug, Clone, PartialEq, Eq, Default)]
39pub struct Header {
40 pub key: String,
41 pub value: String,
42}
43
44#[derive(Debug, Clone, PartialEq, Eq, Default)]
45pub struct PoItem {
46 pub msgid: String,
47 pub msgctxt: Option<String>,
48 pub references: Vec<String>,
49 pub msgid_plural: Option<String>,
50 pub msgstr: MsgStr,
51 pub comments: Vec<String>,
52 pub extracted_comments: Vec<String>,
53 pub flags: Vec<String>,
54 pub metadata: Vec<(String, String)>,
55 pub obsolete: bool,
56 pub nplurals: usize,
57}
58
59impl PoItem {
60 pub fn new(nplurals: usize) -> Self {
61 Self {
62 nplurals,
63 ..Self::default()
64 }
65 }
66
67 pub(crate) fn clear_for_reuse(&mut self, nplurals: usize) {
68 self.msgid.clear();
69 self.msgctxt = None;
70 self.references.clear();
71 self.msgid_plural = None;
72 self.msgstr = MsgStr::None;
73 self.comments.clear();
74 self.extracted_comments.clear();
75 self.flags.clear();
76 self.metadata.clear();
77 self.obsolete = false;
78 self.nplurals = nplurals;
79 }
80}
81
82#[derive(Debug, Clone, PartialEq, Eq, Default)]
83pub enum MsgStr {
84 #[default]
85 None,
86 Singular(String),
87 Plural(Vec<String>),
88}
89
90impl MsgStr {
91 pub fn is_empty(&self) -> bool {
92 matches!(self, Self::None)
93 }
94
95 pub fn len(&self) -> usize {
96 match self {
97 Self::None => 0,
98 Self::Singular(_) => 1,
99 Self::Plural(values) => values.len(),
100 }
101 }
102
103 pub fn first(&self) -> Option<&String> {
104 match self {
105 Self::None => None,
106 Self::Singular(value) => Some(value),
107 Self::Plural(values) => values.first(),
108 }
109 }
110
111 pub fn first_str(&self) -> Option<&str> {
112 self.first().map(String::as_str)
113 }
114
115 pub fn iter(&self) -> MsgStrIter<'_> {
116 match self {
117 Self::None => MsgStrIter::empty(),
118 Self::Singular(value) => MsgStrIter::single(value),
119 Self::Plural(values) => MsgStrIter::many(values.iter()),
120 }
121 }
122
123 pub fn into_vec(self) -> Vec<String> {
124 match self {
125 Self::None => Vec::new(),
126 Self::Singular(value) => vec![value],
127 Self::Plural(values) => values,
128 }
129 }
130}
131
132impl From<String> for MsgStr {
133 fn from(value: String) -> Self {
134 Self::Singular(value)
135 }
136}
137
138impl From<Vec<String>> for MsgStr {
139 fn from(values: Vec<String>) -> Self {
140 match values.len() {
141 0 => Self::None,
142 1 => Self::Singular(values.into_iter().next().expect("single msgstr value")),
143 _ => Self::Plural(values),
144 }
145 }
146}
147
148impl Index<usize> for MsgStr {
149 type Output = String;
150
151 fn index(&self, index: usize) -> &Self::Output {
152 match self {
153 Self::None => panic!("msgstr index out of bounds: no translations present"),
154 Self::Singular(value) if index == 0 => value,
155 Self::Singular(_) => panic!("msgstr index out of bounds: singular translation"),
156 Self::Plural(values) => &values[index],
157 }
158 }
159}
160
161pub struct MsgStrIter<'a> {
162 inner: MsgStrIterInner<'a>,
163}
164
165enum MsgStrIterInner<'a> {
166 Empty,
167 Single(Option<&'a String>),
168 Many(std::slice::Iter<'a, String>),
169}
170
171impl<'a> MsgStrIter<'a> {
172 fn empty() -> Self {
173 Self {
174 inner: MsgStrIterInner::Empty,
175 }
176 }
177
178 fn single(value: &'a String) -> Self {
179 Self {
180 inner: MsgStrIterInner::Single(Some(value)),
181 }
182 }
183
184 fn many(iter: std::slice::Iter<'a, String>) -> Self {
185 Self {
186 inner: MsgStrIterInner::Many(iter),
187 }
188 }
189}
190
191impl<'a> Iterator for MsgStrIter<'a> {
192 type Item = &'a String;
193
194 fn next(&mut self) -> Option<Self::Item> {
195 match &mut self.inner {
196 MsgStrIterInner::Empty => None,
197 MsgStrIterInner::Single(value) => value.take(),
198 MsgStrIterInner::Many(iter) => iter.next(),
199 }
200 }
201}
202
203#[derive(Debug, Clone, PartialEq, Eq)]
204pub struct SerializeOptions {
205 pub fold_length: usize,
206 pub compact_multiline: bool,
207}
208
209impl Default for SerializeOptions {
210 fn default() -> Self {
211 Self {
212 fold_length: 80,
213 compact_multiline: true,
214 }
215 }
216}
217
218#[derive(Debug, Clone, PartialEq, Eq)]
219pub struct ParseError {
220 message: String,
221}
222
223impl ParseError {
224 pub fn new(message: impl Into<String>) -> Self {
225 Self {
226 message: message.into(),
227 }
228 }
229}
230
231impl fmt::Display for ParseError {
232 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
233 f.write_str(&self.message)
234 }
235}
236
237impl std::error::Error for ParseError {}