use super::response::ResponseLine;
use nom::{
bytes::complete::take_while1,
character::complete::{anychar, char as chr, digit1},
combinator::map_res,
error::ErrorKind,
};
use std::{borrow::Cow, fmt};
type NomErr<'a> = nom::Err<(&'a str, ErrorKind)>;
#[derive(Debug, Clone)]
pub struct ParseError(pub String);
impl From<NomErr<'_>> for ParseError {
fn from(err: NomErr<'_>) -> Self {
ParseError(err.to_string())
}
}
impl std::error::Error for ParseError {}
impl fmt::Display for ParseError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
write!(f, "ParseError({})", self.0)
}
}
pub fn response_line(line: &str) -> Result<ResponseLine, ParseError> {
let parser = map_res(digit1, |code: &str| code.parse::<u16>());
let (rest, code) = parser(line)?;
let (rest, ch) = anychar(rest)?;
if ![' ', '-', '+'].contains(&ch) {
return Err(ParseError(format!(
"Unexpected response character '{}'. Expected ' ', '-' or '+'.",
ch
)));
}
Ok(ResponseLine {
has_more: ['-', '+'].contains(&ch),
is_multiline: ch == '+',
code,
value: rest.to_owned(),
})
}
pub fn key_value(line: &str) -> Result<(Cow<'_, str>, Vec<Cow<'_, str>>), ParseError> {
let (rest, identifier) = take_while1(|ch| ch != '=')(line)?;
let (rest, _) = chr('=')(rest)?;
let lines = rest.split('\n');
let parts = lines
.filter(|s| !s.is_empty())
.map(|line| {
line.split('"').filter(|part| !part.trim().is_empty()).map(Cow::from)
})
.flatten()
.collect();
Ok((identifier.trim().into(), parts))
}
#[cfg(test)]
mod test {
#[test]
fn key_value() {
let (key, values) = super::key_value("greeting=\"hello\" \"world 🌎\"").unwrap();
assert_eq!(key, "greeting");
assert_eq!(values, &["hello", "world 🌎"]);
}
}