use crate::util::{self, Base64Error};
pub fn strip_suffix<'a>(s: &'a str, suffix: &str) -> &'a str {
debug_assert!(s.ends_with(suffix));
&s[..(s.len() - suffix.len())]
}
const CRLF: &str = "\r\n";
pub fn strip_fws(input: &str) -> Option<&str> {
if let Some(s) = strip_wsp(input) {
s.strip_prefix(CRLF).and_then(strip_wsp).or(Some(s))
} else {
input.strip_prefix(CRLF).and_then(strip_wsp)
}
}
pub fn rstrip_fws(input: &str) -> Option<&str> {
let s = rstrip_wsp(input)?;
match s.strip_suffix(CRLF) {
Some(s) => rstrip_wsp(s).or(Some(s)),
None => Some(s),
}
}
pub fn trim_surrounding_fws(s: &str) -> &str {
let s = strip_fws(s).unwrap_or(s);
rstrip_fws(s).unwrap_or(s)
}
fn strip_wsp(input: &str) -> Option<&str> {
input
.strip_prefix(is_wsp)
.map(|s| s.trim_start_matches(is_wsp))
}
fn rstrip_wsp(input: &str) -> Option<&str> {
input
.strip_suffix(is_wsp)
.map(|s| s.trim_end_matches(is_wsp))
}
pub fn is_wsp(c: char) -> bool {
matches!(c, ' ' | '\t')
}
pub fn parse_colon_separated_tvalue(value: &str) -> impl Iterator<Item = &str> {
value.split(':').map(trim_surrounding_fws)
}
pub fn parse_base64_tvalue(value: &str) -> Result<Vec<u8>, Base64Error> {
let value: String = value
.chars()
.filter(|c| !matches!(c, ' ' | '\t' | '\r' | '\n'))
.collect();
util::decode_base64(&value)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn strip_fws_ok() {
assert_eq!(strip_fws(""), None);
assert_eq!(strip_fws("x"), None);
assert_eq!(strip_fws(" x"), Some("x"));
assert_eq!(strip_fws("\r\n"), None);
assert_eq!(strip_fws(" \r\n"), Some("\r\n"));
assert_eq!(strip_fws(" \r\nx"), Some("\r\nx"));
assert_eq!(strip_fws(" \r\n "), Some(""));
assert_eq!(strip_fws(" \r\n x"), Some("x"));
assert_eq!(strip_fws("\r\nx"), None);
assert_eq!(strip_fws("\r\n x"), Some("x"));
}
#[test]
fn rstrip_fws_ok() {
assert_eq!(rstrip_fws(""), None);
assert_eq!(rstrip_fws("x"), None);
assert_eq!(rstrip_fws("x "), Some("x"));
assert_eq!(rstrip_fws("\r\n"), None);
assert_eq!(rstrip_fws("x\r\n "), Some("x"));
assert_eq!(rstrip_fws("x \r\n "), Some("x"));
}
#[test]
fn parse_colon_separated_tvalue_ok() {
let parse_tval = |value| parse_colon_separated_tvalue(value).collect::<Vec<_>>();
assert_eq!(
parse_tval("ab:\r\n\tc\r\n\td\r\n\t:e"),
["ab", "c\r\n\td", "e"]
);
assert_eq!(parse_tval(""), [""]);
}
#[test]
fn parse_base64_tvalue_ok() {
assert_eq!(parse_base64_tvalue("").unwrap(), []);
assert_eq!(parse_base64_tvalue("TQ==").unwrap(), b"M");
}
}