1#![no_std]
44
45#[cfg(feature = "alloc")]
46extern crate alloc;
47#[cfg(feature = "alloc")]
48use alloc::borrow::Cow;
49use core::iter::{Iterator, FusedIterator, DoubleEndedIterator};
50
51#[cfg(feature = "alloc")]
57#[inline]
58pub fn unescape_postal_address_line(
59 line: &str,
60 backslash_escaped: bool,
61 dollar_escaped: bool,
62) -> Cow<str> {
63 match (backslash_escaped, dollar_escaped) {
66 (true, true) => Cow::Owned(line.replace("\\5C", "\\").replace("\\24", "$")),
67 (true, false) => Cow::Owned(line.replace("\\5C", "\\")),
68 (false, true) => Cow::Owned(line.replace("\\24", "$")),
69 (false, false) => Cow::Borrowed(line),
70 }
71}
72
73#[cfg(feature = "alloc")]
79pub fn escape_postal_address_line(line: &str) -> Cow<str> {
80 let mut backslash: bool = false;
82 let mut dollar: bool = false;
83 for c in line.chars() {
84 if c == '$' {
85 dollar = true;
86 continue;
87 }
88 if c == '\\' {
89 backslash = true;
90 }
91 }
92 match (backslash, dollar) {
95 (true, true) => Cow::Owned(line.replace("\\", "\\5C").replace("$", "\\24")),
96 (true, false) => Cow::Owned(line.replace("\\", "\\5C")),
97 (false, true) => Cow::Owned(line.replace("$", "\\24")),
98 (false, false) => Cow::Borrowed(line),
99 }
100}
101
102pub struct PostalAddressLineIter<'a> {
108 input: &'a str,
109}
110
111impl <'a> PostalAddressLineIter<'a> {
112
113 #[inline]
115 pub(crate) fn new(input: &'a str) -> Self {
116 PostalAddressLineIter{ input }
117 }
118
119}
120
121impl <'a> Iterator for PostalAddressLineIter<'a> {
122
123 type Item = (&'a str, bool, bool);
129
130 fn next(&mut self) -> Option<Self::Item> {
136 if self.input.len() == 0 {
137 return None;
138 }
139 let mut backslash_escaped: bool = false;
140 let mut dollar_escaped: bool = false;
141 for (i, c) in self.input.char_indices() {
142 if c == '$' {
143 let ret = &self.input[0..i];
144 self.input = &self.input[i+1..];
145 return Some((ret, backslash_escaped, dollar_escaped));
146 }
147 if c == '\\' {
148 if self.input[i+1..].starts_with("5C") {
149 backslash_escaped = true;
150 } else if self.input[i+1..].starts_with("24") {
151 dollar_escaped = true;
152 }
153 continue;
154 }
155 }
156 let ret = self.input;
157 self.input = &self.input[0..0]; Some((ret, backslash_escaped, dollar_escaped))
159 }
160
161 fn size_hint(&self) -> (usize, Option<usize>) {
162 if self.input.len() == 0 {
163 return (0, Some(0));
164 }
165 (1, Some(1 + self.input.len()))
166 }
167
168}
169
170impl <'a> FusedIterator for PostalAddressLineIter<'a> {}
171
172impl <'a> DoubleEndedIterator for PostalAddressLineIter<'a> {
173
174 fn next_back(&mut self) -> Option<Self::Item> {
175 if self.input.len() == 0 {
176 return None;
177 }
178 let mut backslash_escaped: bool = false;
179 let mut dollar_escaped: bool = false;
180 for (i, c) in self.input.char_indices().rev() {
181 if c == '$' {
182 let ret = &self.input[i+1..];
183 self.input = &self.input[0..i];
184 return Some((ret, backslash_escaped, dollar_escaped));
185 }
186 if c == '\\' {
187 if self.input[i+1..].starts_with("5C") {
188 backslash_escaped = true;
189 } else if self.input[i+1..].starts_with("24") {
190 dollar_escaped = true;
191 }
192 continue;
193 }
194 }
195 let ret = self.input;
196 self.input = &self.input[0..0]; Some((ret, backslash_escaped, dollar_escaped))
198 }
199
200}
201
202#[inline]
225pub fn parse_postal_address<'a>(input: &'a str) -> PostalAddressLineIter<'a> {
226 PostalAddressLineIter::new(input)
227}
228
229#[cfg(test)]
230mod tests {
231
232 extern crate alloc;
233 use alloc::borrow::Cow;
234 use super::parse_postal_address;
235 #[cfg(feature = "alloc")]
236 use super::{unescape_postal_address_line, escape_postal_address_line};
237
238 #[test]
239 fn iter_postal_addr_1() {
240 let input = "1234 Main St.$Anytown, CA 12345$USA";
241 let mut pa = parse_postal_address(input);
242 assert_eq!(pa.next(), Some(("1234 Main St.", false, false)));
243 assert_eq!(pa.next(), Some(("Anytown, CA 12345", false, false)));
244 assert_eq!(pa.next(), Some(("USA", false, false)));
245 assert_eq!(pa.next(), None);
246 assert_eq!(pa.next(), None);
247 }
248
249 #[test]
250 fn iter_postal_addr_2() {
251 let input = "\\241,000,000 Sweepstakes$PO Box 1000000$Anytown, CA 12345$USA";
252 let mut pa = parse_postal_address(input);
253 assert_eq!(pa.next(), Some(("\\241,000,000 Sweepstakes", false, true)));
254 assert_eq!(pa.next(), Some(("PO Box 1000000", false, false)));
255 assert_eq!(pa.next(), Some(("Anytown, CA 12345", false, false)));
256 assert_eq!(pa.next(), Some(("USA", false, false)));
257 assert_eq!(pa.next(), None);
258 assert_eq!(pa.next(), None);
259 }
260
261 #[test]
262 fn iter_postal_addr_3() {
263 let input = "1\\5C,000\\5C,000 \\24weepstakes$Anytown\\AB, CA 12345\\\\$\\\\USA\\\\5C";
264 let mut pa = parse_postal_address(input);
265 assert_eq!(pa.next(), Some(("1\\5C,000\\5C,000 \\24weepstakes", true, true)));
266 assert_eq!(pa.next(), Some(("Anytown\\AB, CA 12345\\\\", false, false)));
267 assert_eq!(pa.next(), Some(("\\\\USA\\\\5C", true, false)));
268 assert_eq!(pa.next(), None);
269 assert_eq!(pa.next(), None);
270 }
271
272 #[test]
273 fn rev_iter_postal_addr_1() {
274 let input = "1234 Main St.$Anytown, CA 12345$USA";
275 let mut pa = parse_postal_address(input);
276 assert_eq!(pa.next_back(), Some(("USA", false, false)));
277 assert_eq!(pa.next(), Some(("1234 Main St.", false, false)));
278 assert_eq!(pa.next_back(), Some(("Anytown, CA 12345", false, false)));
279 assert_eq!(pa.next(), None);
280 assert_eq!(pa.next(), None);
281 assert_eq!(pa.next_back(), None);
282 assert_eq!(pa.next_back(), None);
283 }
284
285 #[test]
286 fn rev_iter_postal_addr_2() {
287 let input = "\\241,000,000 Sweepstakes$PO Box 1000000$Anytown, CA 12345$USA";
288 let mut pa = parse_postal_address(input);
289 assert_eq!(pa.next_back(), Some(("USA", false, false)));
290 assert_eq!(pa.next_back(), Some(("Anytown, CA 12345", false, false)));
291 assert_eq!(pa.next_back(), Some(("PO Box 1000000", false, false)));
292 assert_eq!(pa.next_back(), Some(("\\241,000,000 Sweepstakes", false, true)));
293 assert_eq!(pa.next(), None);
294 assert_eq!(pa.next(), None);
295 assert_eq!(pa.next_back(), None);
296 assert_eq!(pa.next_back(), None);
297 }
298
299 #[test]
300 fn rev_iter_postal_addr_3() {
301 let input = "1\\5C,000\\5C,000 \\24weepstakes$Anytown\\AB, CA 12345\\\\$\\\\USA\\\\5C";
302 let mut pa = parse_postal_address(input);
303 assert_eq!(pa.next_back(), Some(("\\\\USA\\\\5C", true, false)));
304 assert_eq!(pa.next_back(), Some(("Anytown\\AB, CA 12345\\\\", false, false)));
305 assert_eq!(pa.next_back(), Some(("1\\5C,000\\5C,000 \\24weepstakes", true, true)));
306 assert_eq!(pa.next(), None);
307 assert_eq!(pa.next(), None);
308 assert_eq!(pa.next_back(), None);
309 assert_eq!(pa.next_back(), None);
310 }
311
312 #[cfg(feature = "alloc")]
313 #[test]
314 fn unescape_1() {
315 let input = "1\\5C,000\\5C,000 \\24weepstakes";
316 let pa = unescape_postal_address_line(input, true, true);
317 assert!(matches!(pa, Cow::Owned(_)));
318 assert_eq!(pa.as_ref(), "1\\,000\\,000 $weepstakes");
319 }
320
321 #[cfg(feature = "alloc")]
322 #[test]
323 fn unescape_2() {
324 let input = "\\\\USA\\\\5C";
325 let pa = unescape_postal_address_line(input, true, false);
326 assert!(matches!(pa, Cow::Owned(_)));
327 assert_eq!(pa.as_ref(), "\\\\USA\\\\");
328 }
329
330 #[cfg(feature = "alloc")]
331 #[test]
332 fn unescape_3() {
333 let input = "Anytown, CA 12345";
334 let pa = unescape_postal_address_line(input, false, false);
335 assert!(matches!(pa, Cow::Borrowed(_)));
336 assert_eq!(pa.as_ref(), "Anytown, CA 12345");
337 }
338
339 #[cfg(feature = "alloc")]
340 #[test]
341 fn escape_1() {
342 let input = "1\\,000\\,000 $weepstakes";
343 let pa = escape_postal_address_line(input);
344 assert!(matches!(pa, Cow::Owned(_)));
345 assert_eq!(pa.as_ref(), "1\\5C,000\\5C,000 \\24weepstakes");
346 }
347
348 #[cfg(feature = "alloc")]
349 #[test]
350 fn escape_2() {
351 let input = "\\\\USA\\\\";
352 let pa = escape_postal_address_line(input);
353 assert!(matches!(pa, Cow::Owned(_)));
354 assert_eq!(pa.as_ref(), "\\5C\\5CUSA\\5C\\5C");
355 }
356
357 #[cfg(feature = "alloc")]
358 #[test]
359 fn escape_3() {
360 let input = "Anytown, CA 12345";
361 let pa = escape_postal_address_line(input);
362 assert!(matches!(pa, Cow::Borrowed(_)));
363 assert_eq!(pa.as_ref(), "Anytown, CA 12345");
364 }
365
366}