use core::fmt;
use bytes::Bytes;
use thiserror::Error;
use super::parser::{quoted_text, record, records, token, NoTail as _};
pub fn parse_header(input: &[u8], delimiter: u8) -> Result<Vec<FieldValue>, InvalidValue> {
records(delimiter)(input)
.no_tail()
.map_err(|_| InvalidValue("header", Bytes::copy_from_slice(input)))
}
pub fn parse_field(input: &[u8]) -> Result<FieldValue, InvalidValue> {
record()(input)
.no_tail()
.map_err(|_| InvalidValue("field", Bytes::copy_from_slice(input)))
}
pub fn parse_token(input: &[u8]) -> Result<Token, InvalidValue> {
token()(input)
.no_tail()
.map_err(|_| InvalidValue("token", Bytes::copy_from_slice(input)))
}
pub fn parse_quoted_string(input: &[u8]) -> Result<QuotedText, InvalidValue> {
quoted_text(input)
.no_tail()
.map_err(|_| InvalidValue("quoted string", Bytes::copy_from_slice(input)))
}
pub fn combine_values<'a, I>(iter: I, delimiter: u8) -> Vec<u8>
where
I: IntoIterator<Item = &'a FieldValue>,
{
iter.into_iter()
.map(|v| v.as_bytes().to_vec())
.collect::<Vec<_>>()
.join([delimiter, b' '].as_slice())
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct FieldValue(Entry);
impl FieldValue {
pub fn as_bytes(&self) -> &[u8] {
match &self.0 {
Entry::Token(token) => token.as_bytes(),
Entry::QuotedText(quoted) => quoted.as_bytes(),
}
}
pub fn token(&self) -> Option<&Token> {
match &self.0 {
Entry::Token(token) => Some(token),
_ => None,
}
}
pub fn quoted_string(&self) -> Option<&QuotedText> {
match &self.0 {
Entry::QuotedText(quoted) => Some(quoted),
_ => None,
}
}
pub fn into_token(self) -> Option<Token> {
match self.0 {
Entry::Token(token) => Some(token),
_ => None,
}
}
pub fn into_quoted_string(self) -> Option<QuotedText> {
match self.0 {
Entry::QuotedText(quoted) => Some(quoted),
_ => None,
}
}
pub fn entry(&self) -> &Entry {
&self.0
}
pub fn into_entry(self) -> Entry {
self.0
}
}
impl From<Entry> for FieldValue {
fn from(entry: Entry) -> Self {
Self(entry)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum Entry {
Token(Token),
QuotedText(QuotedText),
}
#[derive(Debug, Clone)]
pub struct FieldKey(Token);
impl FieldKey {
pub const fn new(token: Token) -> Self {
Self(token)
}
pub fn as_bytes(&self) -> &[u8] {
self.0.as_bytes()
}
pub fn into_bytes(self) -> Bytes {
self.0.into_bytes()
}
pub fn into_token(self) -> Token {
self.0
}
}
impl PartialEq for FieldKey {
fn eq(&self, other: &Self) -> bool {
self.0.eq_ignore_ascii_case(&other.0)
}
}
impl Eq for FieldKey {}
impl PartialOrd for FieldKey {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(::std::cmp::Ord::cmp(self, other))
}
}
impl Ord for FieldKey {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.0.ord_ignore_ascii_case(&other.0)
}
}
impl From<Token> for FieldKey {
fn from(token: Token) -> Self {
Self(token)
}
}
#[derive(Debug, Error)]
pub struct InvalidValue(&'static str, Bytes);
impl fmt::Display for InvalidValue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"Invalid {}: {}",
self.0,
String::from_utf8_lossy(&self.1)
)
}
}
impl InvalidValue {
pub fn value(&self) -> &[u8] {
&self.1
}
}
#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Default)]
pub struct Token(Bytes);
impl Token {
pub(super) fn new(bytes: Bytes) -> Self {
Self(bytes)
}
pub fn as_bytes(&self) -> &[u8] {
&self.0
}
pub fn into_bytes(self) -> Bytes {
self.0
}
pub fn from_static(s: &'static str) -> Self {
parse_token(s.as_bytes()).unwrap()
}
pub fn parse(b: &[u8]) -> Result<Self, InvalidValue> {
parse_token(b)
}
pub const fn from_static_unchecked(s: &'static str) -> Self {
Self(Bytes::from_static(s.as_bytes()))
}
pub fn eq_ignore_ascii_case(&self, other: &Self) -> bool {
self.0.eq_ignore_ascii_case(&other.0)
}
pub fn ord_ignore_ascii_case(&self, other: &Self) -> std::cmp::Ordering {
self.0
.to_ascii_lowercase()
.cmp(&other.0.to_ascii_lowercase())
}
}
impl fmt::Debug for Token {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("Token")
.field(&String::from_utf8_lossy(&self.0))
.finish()
}
}
#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Default)]
pub struct QuotedText(Bytes);
impl QuotedText {
pub(super) fn new(bytes: Bytes) -> Self {
Self(bytes)
}
pub fn as_bytes(&self) -> &[u8] {
&self.0
}
}
impl fmt::Debug for QuotedText {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("QuotedText")
.field(&String::from_utf8_lossy(&self.0))
.finish()
}
}