usecrate::utils; usecrate::{
BareItem, Decimal, Dictionary, FromStr, InnerList, Item, List, ListEntry, Num, Parameters,
SFVResult,
}; use std::iter::Peekable; use std::str::{from_utf8, Chars};
/// Implements parsing logic for each structured field value type. pubtrait ParseValue { /// This method should not be used for parsing input into structured field value. /// Use `Parser::parse_item`, `Parser::parse_list` or `Parsers::parse_dictionary` for that. fn parse(input_chars: &mut Peekable<Chars>) -> SFVResult<Self> where Self: Sized;
}
/// If structured field value of List or Dictionary type is split into multiple lines, /// allows to parse more lines and merge them into already existing structure field value. pubtrait ParseMore { /// If structured field value is split across lines, /// parses and merges next line into a single structured field value. /// # Examples /// ``` /// # use sfv::{Parser, SerializeValue, ParseMore}; /// /// let mut list_field = Parser::parse_list("11, (12 13)".as_bytes()).unwrap(); /// list_field.parse_more("\"foo\", \"bar\"".as_bytes()).unwrap(); /// /// assert_eq!(list_field.serialize_value().unwrap(), "11, (12 13), \"foo\", \"bar\""); fn parse_more(&mutself, input_bytes: &[u8]) -> SFVResult<()> where Self: Sized;
}
impl ParseValue for Item { fn parse(input_chars: &mut Peekable<Chars>) -> SFVResult<Item> { // https://httpwg.org/specs/rfc8941.html#parse-item let bare_item = Parser::parse_bare_item(input_chars)?; let params = Parser::parse_parameters(input_chars)?;
Ok(Item { bare_item, params })
}
}
impl ParseValue for List { fn parse(input_chars: &mut Peekable<Chars>) -> SFVResult<List> { // https://httpwg.org/specs/rfc8941.html#parse-list // List represents an array of (item_or_inner_list, parameters)
letmut members = vec![];
while input_chars.peek().is_some() {
members.push(Parser::parse_list_entry(input_chars)?);
utils::consume_ows_chars(input_chars);
if input_chars.peek().is_none() { return Ok(members);
}
iflet Some(c) = input_chars.next() { if c != ',' { return Err("parse_list: trailing characters after list member");
}
}
utils::consume_ows_chars(input_chars);
if input_chars.peek().is_none() { return Err("parse_list: trailing comma");
}
}
while input_chars.peek().is_some() { let this_key = Parser::parse_key(input_chars)?;
iflet Some('=') = input_chars.peek() {
input_chars.next(); let member = Parser::parse_list_entry(input_chars)?;
dict.insert(this_key, member);
} else { let value = true; let params = Parser::parse_parameters(input_chars)?; let member = Item {
bare_item: BareItem::Boolean(value),
params,
};
dict.insert(this_key, member.into());
}
utils::consume_ows_chars(input_chars);
if input_chars.peek().is_none() { return Ok(dict);
}
iflet Some(c) = input_chars.next() { if c != ',' { return Err("parse_dict: trailing characters after dictionary member");
}
}
impl ParseMore for List { fn parse_more(&mutself, input_bytes: &[u8]) -> SFVResult<()> { let parsed_list = Parser::parse_list(input_bytes)?; self.extend(parsed_list);
Ok(())
}
}
impl ParseMore for Dictionary { fn parse_more(&mutself, input_bytes: &[u8]) -> SFVResult<()> { let parsed_dict = Parser::parse_dictionary(input_bytes)?; self.extend(parsed_dict);
Ok(())
}
}
/// Exposes methods for parsing input into structured field value. pubstruct Parser;
impl Parser { /// Parses input into structured field value of Dictionary type pubfn parse_dictionary(input_bytes: &[u8]) -> SFVResult<Dictionary> { Self::parse::<Dictionary>(input_bytes)
}
/// Parses input into structured field value of List type pubfn parse_list(input_bytes: &[u8]) -> SFVResult<List> { Self::parse::<List>(input_bytes)
}
/// Parses input into structured field value of Item type pubfn parse_item(input_bytes: &[u8]) -> SFVResult<Item> { Self::parse::<Item>(input_bytes)
}
// Generic parse method for checking input before parsing // and handling trailing text error fn parse<T: ParseValue>(input_bytes: &[u8]) -> SFVResult<T> { // https://httpwg.org/specs/rfc8941.html#text-parse if !input_bytes.is_ascii() { return Err("parse: non-ascii characters in input");
}
letmut input_chars = from_utf8(input_bytes)
.map_err(|_| "parse: conversion from bytes to str failed")?
.chars()
.peekable();
utils::consume_sp_chars(&mut input_chars);
let output = T::parse(&mut input_chars)?;
utils::consume_sp_chars(&mut input_chars);
if input_chars.next().is_some() { return Err("parse: trailing characters after parsed value");
};
Ok(output)
}
match input_chars.peek() {
Some(c) if !c.is_ascii_digit() => { return Err("parse_number: input number does not start with a digit")
}
None => return Err("parse_number: input number lacks a digit"),
_ => (),
}
// Get number from input as a string and identify whether it's a decimal or integer let (is_integer, input_number) = Self::extract_digits(input_chars)?;
// Parse input_number from string into integer if is_integer { let output_number = input_number
.parse::<i64>()
.map_err(|_err| "parse_number: parsing i64 failed")?
* sign;
let (min_int, max_int) = (-999_999_999_999_999_i64, 999_999_999_999_999_i64); if !(min_int <= output_number && output_number <= max_int) { return Err("parse_number: integer number is out of range");
}
return Ok(Num::Integer(output_number));
}
// Parse input_number from string into decimal let chars_after_dot = input_number
.find('.')
.map(|dot_pos| input_number.len() - dot_pos - 1);
let param_name = Self::parse_key(input_chars)?; let param_value = match input_chars.peek() {
Some('=') => {
input_chars.next(); Self::parse_bare_item(input_chars)?
}
_ => BareItem::Boolean(true),
};
params.insert(param_name, param_value);
}
// If parameters already contains a name param_name (comparing character-for-character), overwrite its value. // Note that when duplicate Parameter keys are encountered, this has the effect of ignoring all but the last instance.
Ok(params)
}
pub(crate) fn parse_key(input_chars: &mut Peekable<Chars>) -> SFVResult<String> { match input_chars.peek() {
Some(c) if c == &'*' || c.is_ascii_lowercase() => (),
_ => return Err("parse_key: first character is not lcalpha or '*'"),
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.