use crate::diagnostic::Field;
use crate::error::{FaultCode, ParseError};
pub(crate) const CONTROLS: [char; 3] = ['\r', '\n', '\0'];
pub(crate) fn refuse(field: Field, value: &str, refused: &[char]) -> Result<(), ParseError> {
match value.find(refused) {
Some(pos) => Err(ParseError::malformed(
field,
FaultCode::InvalidChar,
Some(pos),
)),
None => Ok(()),
}
}
pub(crate) fn refuse_controls(field: Field, value: &str) -> Result<(), ParseError> {
refuse(field, value, &CONTROLS)
}
pub(crate) fn only(
field: Field,
value: &str,
allowed: impl Fn(char) -> bool,
) -> Result<(), ParseError> {
if value.is_empty() {
return Err(ParseError::empty(field));
}
match value.find(|c| !allowed(c)) {
Some(pos) => Err(ParseError::malformed(
field,
FaultCode::InvalidChar,
Some(pos),
)),
None => Ok(()),
}
}
pub(crate) const URI_REFUSED: [char; 5] = ['<', '>', '\r', '\n', '\0'];
pub(crate) fn checked_uri(field: Field, uri: sip_uri::Uri) -> Result<sip_uri::Uri, ParseError> {
use sip_uri::UriParse;
let wire = uri.to_string();
refuse(field, &wire, &URI_REFUSED)?;
match sip_uri::Uri::parse_strict(&wire) {
Ok(back) if back == uri => Ok(uri),
_ => Err(ParseError::malformed(
field,
FaultCode::Unrepresentable,
None,
)),
}
}
pub(crate) fn checked_token(field: Field, value: &str) -> Result<String, ParseError> {
only(field, value, crate::is_token_char)?;
Ok(value.to_owned())
}
#[cfg(feature = "serde")]
pub(crate) fn reads_back<T: std::fmt::Display + PartialEq>(
value: T,
parse: impl FnOnce(&str) -> Result<T, ParseError>,
) -> Result<T, ParseError> {
let wire = value.to_string();
refuse_controls(Field::Value, &wire)?;
if parse(&wire)? == value {
Ok(value)
} else {
Err(ParseError::malformed(
Field::Value,
FaultCode::Unrepresentable,
None,
))
}
}