#![no_std]
#[cfg(feature = "alloc")]
extern crate alloc;
#[cfg(feature = "alloc")]
use alloc::borrow::Cow;
use core::iter::{Iterator, FusedIterator, DoubleEndedIterator};
#[cfg(feature = "alloc")]
#[inline]
pub fn unescape_postal_address_line(
line: &str,
backslash_escaped: bool,
dollar_escaped: bool,
) -> Cow<str> {
match (backslash_escaped, dollar_escaped) {
(true, true) => Cow::Owned(line.replace("\\5C", "\\").replace("\\24", "$")),
(true, false) => Cow::Owned(line.replace("\\5C", "\\")),
(false, true) => Cow::Owned(line.replace("\\24", "$")),
(false, false) => Cow::Borrowed(line),
}
}
#[cfg(feature = "alloc")]
pub fn escape_postal_address_line(line: &str) -> Cow<str> {
let mut backslash: bool = false;
let mut dollar: bool = false;
for c in line.chars() {
if c == '$' {
dollar = true;
continue;
}
if c == '\\' {
backslash = true;
}
}
match (backslash, dollar) {
(true, true) => Cow::Owned(line.replace("\\", "\\5C").replace("$", "\\24")),
(true, false) => Cow::Owned(line.replace("\\", "\\5C")),
(false, true) => Cow::Owned(line.replace("$", "\\24")),
(false, false) => Cow::Borrowed(line),
}
}
pub struct PostalAddressLineIter<'a> {
input: &'a str,
}
impl <'a> PostalAddressLineIter<'a> {
#[inline]
pub(crate) fn new(input: &'a str) -> Self {
PostalAddressLineIter{ input }
}
}
impl <'a> Iterator for PostalAddressLineIter<'a> {
type Item = (&'a str, bool, bool);
fn next(&mut self) -> Option<Self::Item> {
if self.input.len() == 0 {
return None;
}
let mut backslash_escaped: bool = false;
let mut dollar_escaped: bool = false;
for (i, c) in self.input.char_indices() {
if c == '$' {
let ret = &self.input[0..i];
self.input = &self.input[i+1..];
return Some((ret, backslash_escaped, dollar_escaped));
}
if c == '\\' {
if self.input[i+1..].starts_with("5C") {
backslash_escaped = true;
} else if self.input[i+1..].starts_with("24") {
dollar_escaped = true;
}
continue;
}
}
let ret = self.input;
self.input = &self.input[0..0]; Some((ret, backslash_escaped, dollar_escaped))
}
fn size_hint(&self) -> (usize, Option<usize>) {
if self.input.len() == 0 {
return (0, Some(0));
}
(1, Some(1 + self.input.len()))
}
}
impl <'a> FusedIterator for PostalAddressLineIter<'a> {}
impl <'a> DoubleEndedIterator for PostalAddressLineIter<'a> {
fn next_back(&mut self) -> Option<Self::Item> {
if self.input.len() == 0 {
return None;
}
let mut backslash_escaped: bool = false;
let mut dollar_escaped: bool = false;
for (i, c) in self.input.char_indices().rev() {
if c == '$' {
let ret = &self.input[i+1..];
self.input = &self.input[0..i];
return Some((ret, backslash_escaped, dollar_escaped));
}
if c == '\\' {
if self.input[i+1..].starts_with("5C") {
backslash_escaped = true;
} else if self.input[i+1..].starts_with("24") {
dollar_escaped = true;
}
continue;
}
}
let ret = self.input;
self.input = &self.input[0..0]; Some((ret, backslash_escaped, dollar_escaped))
}
}
#[inline]
pub fn parse_postal_address<'a>(input: &'a str) -> PostalAddressLineIter<'a> {
PostalAddressLineIter::new(input)
}
#[cfg(test)]
mod tests {
extern crate alloc;
use alloc::borrow::Cow;
use super::parse_postal_address;
#[cfg(feature = "alloc")]
use super::{unescape_postal_address_line, escape_postal_address_line};
#[test]
fn iter_postal_addr_1() {
let input = "1234 Main St.$Anytown, CA 12345$USA";
let mut pa = parse_postal_address(input);
assert_eq!(pa.next(), Some(("1234 Main St.", false, false)));
assert_eq!(pa.next(), Some(("Anytown, CA 12345", false, false)));
assert_eq!(pa.next(), Some(("USA", false, false)));
assert_eq!(pa.next(), None);
assert_eq!(pa.next(), None);
}
#[test]
fn iter_postal_addr_2() {
let input = "\\241,000,000 Sweepstakes$PO Box 1000000$Anytown, CA 12345$USA";
let mut pa = parse_postal_address(input);
assert_eq!(pa.next(), Some(("\\241,000,000 Sweepstakes", false, true)));
assert_eq!(pa.next(), Some(("PO Box 1000000", false, false)));
assert_eq!(pa.next(), Some(("Anytown, CA 12345", false, false)));
assert_eq!(pa.next(), Some(("USA", false, false)));
assert_eq!(pa.next(), None);
assert_eq!(pa.next(), None);
}
#[test]
fn iter_postal_addr_3() {
let input = "1\\5C,000\\5C,000 \\24weepstakes$Anytown\\AB, CA 12345\\\\$\\\\USA\\\\5C";
let mut pa = parse_postal_address(input);
assert_eq!(pa.next(), Some(("1\\5C,000\\5C,000 \\24weepstakes", true, true)));
assert_eq!(pa.next(), Some(("Anytown\\AB, CA 12345\\\\", false, false)));
assert_eq!(pa.next(), Some(("\\\\USA\\\\5C", true, false)));
assert_eq!(pa.next(), None);
assert_eq!(pa.next(), None);
}
#[test]
fn rev_iter_postal_addr_1() {
let input = "1234 Main St.$Anytown, CA 12345$USA";
let mut pa = parse_postal_address(input);
assert_eq!(pa.next_back(), Some(("USA", false, false)));
assert_eq!(pa.next(), Some(("1234 Main St.", false, false)));
assert_eq!(pa.next_back(), Some(("Anytown, CA 12345", false, false)));
assert_eq!(pa.next(), None);
assert_eq!(pa.next(), None);
assert_eq!(pa.next_back(), None);
assert_eq!(pa.next_back(), None);
}
#[test]
fn rev_iter_postal_addr_2() {
let input = "\\241,000,000 Sweepstakes$PO Box 1000000$Anytown, CA 12345$USA";
let mut pa = parse_postal_address(input);
assert_eq!(pa.next_back(), Some(("USA", false, false)));
assert_eq!(pa.next_back(), Some(("Anytown, CA 12345", false, false)));
assert_eq!(pa.next_back(), Some(("PO Box 1000000", false, false)));
assert_eq!(pa.next_back(), Some(("\\241,000,000 Sweepstakes", false, true)));
assert_eq!(pa.next(), None);
assert_eq!(pa.next(), None);
assert_eq!(pa.next_back(), None);
assert_eq!(pa.next_back(), None);
}
#[test]
fn rev_iter_postal_addr_3() {
let input = "1\\5C,000\\5C,000 \\24weepstakes$Anytown\\AB, CA 12345\\\\$\\\\USA\\\\5C";
let mut pa = parse_postal_address(input);
assert_eq!(pa.next_back(), Some(("\\\\USA\\\\5C", true, false)));
assert_eq!(pa.next_back(), Some(("Anytown\\AB, CA 12345\\\\", false, false)));
assert_eq!(pa.next_back(), Some(("1\\5C,000\\5C,000 \\24weepstakes", true, true)));
assert_eq!(pa.next(), None);
assert_eq!(pa.next(), None);
assert_eq!(pa.next_back(), None);
assert_eq!(pa.next_back(), None);
}
#[cfg(feature = "alloc")]
#[test]
fn unescape_1() {
let input = "1\\5C,000\\5C,000 \\24weepstakes";
let pa = unescape_postal_address_line(input, true, true);
assert!(matches!(pa, Cow::Owned(_)));
assert_eq!(pa.as_ref(), "1\\,000\\,000 $weepstakes");
}
#[cfg(feature = "alloc")]
#[test]
fn unescape_2() {
let input = "\\\\USA\\\\5C";
let pa = unescape_postal_address_line(input, true, false);
assert!(matches!(pa, Cow::Owned(_)));
assert_eq!(pa.as_ref(), "\\\\USA\\\\");
}
#[cfg(feature = "alloc")]
#[test]
fn unescape_3() {
let input = "Anytown, CA 12345";
let pa = unescape_postal_address_line(input, false, false);
assert!(matches!(pa, Cow::Borrowed(_)));
assert_eq!(pa.as_ref(), "Anytown, CA 12345");
}
#[cfg(feature = "alloc")]
#[test]
fn escape_1() {
let input = "1\\,000\\,000 $weepstakes";
let pa = escape_postal_address_line(input);
assert!(matches!(pa, Cow::Owned(_)));
assert_eq!(pa.as_ref(), "1\\5C,000\\5C,000 \\24weepstakes");
}
#[cfg(feature = "alloc")]
#[test]
fn escape_2() {
let input = "\\\\USA\\\\";
let pa = escape_postal_address_line(input);
assert!(matches!(pa, Cow::Owned(_)));
assert_eq!(pa.as_ref(), "\\5C\\5CUSA\\5C\\5C");
}
#[cfg(feature = "alloc")]
#[test]
fn escape_3() {
let input = "Anytown, CA 12345";
let pa = escape_postal_address_line(input);
assert!(matches!(pa, Cow::Borrowed(_)));
assert_eq!(pa.as_ref(), "Anytown, CA 12345");
}
}