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