#![cfg_attr(feature = "zonefile", doc = "[zonefile][crate::zonefile]")]
#![cfg_attr(not(feature = "zonefile"), doc = "zonefile")]
#![allow(clippy::manual_range_contains)] #![allow(unused_imports)]
use crate::base::charstr::{CharStr, CharStrBuilder};
use crate::base::name::{Name, ToName};
use crate::base::wire::{Compose, Composer};
use core::convert::{TryFrom, TryInto};
use core::iter::Peekable;
use core::marker::PhantomData;
use core::{fmt, str};
use octseq::str::Str;
use octseq::{
EmptyBuilder, FreezeBuilder, FromBuilder, OctetsBuilder, ShortBuf,
Truncate,
};
#[cfg(feature = "std")]
use std::error;
use super::Ttl;
pub trait Scan<S: Scanner>: Sized {
fn scan(scanner: &mut S) -> Result<Self, S::Error>;
}
macro_rules! impl_scan_unsigned {
( $type:ident) => {
impl<S: Scanner> Scan<S> for $type {
fn scan(scanner: &mut S) -> Result<Self, S::Error> {
let mut res: $type = 0;
scanner.scan_symbols(|ch| {
res = res.checked_mul(10).ok_or_else(|| {
S::Error::custom("decimal number overflow")
})?;
res += ch.into_digit(10).map_err(|_| {
S::Error::custom("expected decimal number")
})? as $type;
Ok(())
})?;
Ok(res)
}
}
};
}
impl_scan_unsigned!(u8);
impl_scan_unsigned!(u16);
impl_scan_unsigned!(u32);
impl_scan_unsigned!(u64);
impl_scan_unsigned!(u128);
impl<S: Scanner> Scan<S> for Ttl {
fn scan(scanner: &mut S) -> Result<Self, <S as Scanner>::Error> {
let mut res: u32 = 0;
scanner.scan_symbols(|ch| {
res = res
.checked_mul(10)
.ok_or_else(|| S::Error::custom("decimal number overflow"))?;
res += ch
.into_digit(10)
.map_err(|_| S::Error::custom("expected decimal number"))?;
Ok(())
})?;
Ok(Ttl::from_secs(res))
}
}
pub trait Scanner {
type Octets: AsRef<[u8]>;
type OctetsBuilder: OctetsBuilder
+ AsRef<[u8]>
+ AsMut<[u8]>
+ Truncate
+ FreezeBuilder<Octets = Self::Octets>;
type Name: ToName;
type Error: ScannerError;
fn has_space(&self) -> bool;
fn continues(&mut self) -> bool;
fn scan_symbols<F>(&mut self, op: F) -> Result<(), Self::Error>
where
F: FnMut(Symbol) -> Result<(), Self::Error>;
fn scan_entry_symbols<F>(&mut self, op: F) -> Result<(), Self::Error>
where
F: FnMut(EntrySymbol) -> Result<(), Self::Error>;
fn convert_token<C: ConvertSymbols<Symbol, Self::Error>>(
&mut self,
convert: C,
) -> Result<Self::Octets, Self::Error>;
fn convert_entry<C: ConvertSymbols<EntrySymbol, Self::Error>>(
&mut self,
convert: C,
) -> Result<Self::Octets, Self::Error>;
fn scan_octets(&mut self) -> Result<Self::Octets, Self::Error>;
fn scan_svcb_octets(&mut self) -> Result<Self::Octets, Self::Error> {
Err(Self::Error::custom(
"Scanning SVCB octets is only implemented by some Scanners",
))
}
fn scan_ascii_str<F, T>(&mut self, op: F) -> Result<T, Self::Error>
where
F: FnOnce(&str) -> Result<T, Self::Error>;
fn scan_name(&mut self) -> Result<Self::Name, Self::Error>;
fn scan_charstr(&mut self) -> Result<CharStr<Self::Octets>, Self::Error>;
fn scan_string(&mut self) -> Result<Str<Self::Octets>, Self::Error>;
fn scan_charstr_entry(&mut self) -> Result<Self::Octets, Self::Error>;
fn scan_opt_unknown_marker(&mut self) -> Result<bool, Self::Error>;
fn octets_builder(&mut self) -> Result<Self::OctetsBuilder, Self::Error>;
}
macro_rules! declare_error_trait {
(ScannerError: Sized $(+ $($supertrait:ident)::+)*) => {
pub trait ScannerError: Sized $(+ $($supertrait)::+)* {
fn custom(msg: &'static str) -> Self;
fn end_of_entry() -> Self;
fn short_buf() -> Self;
fn trailing_tokens() -> Self;
}
}
}
#[cfg(feature = "std")]
declare_error_trait!(ScannerError: Sized + error::Error);
#[cfg(not(feature = "std"))]
declare_error_trait!(ScannerError: Sized + fmt::Debug + fmt::Display);
#[cfg(feature = "std")]
impl ScannerError for std::io::Error {
fn custom(msg: &'static str) -> Self {
std::io::Error::other(msg)
}
fn end_of_entry() -> Self {
std::io::Error::new(
std::io::ErrorKind::UnexpectedEof,
"unexpected end of entry",
)
}
fn short_buf() -> Self {
std::io::Error::other(ShortBuf)
}
fn trailing_tokens() -> Self {
std::io::Error::other("trailing data")
}
}
pub trait ConvertSymbols<Sym, Error> {
fn process_symbol(&mut self, symbol: Sym)
-> Result<Option<&[u8]>, Error>;
fn process_tail(&mut self) -> Result<Option<&[u8]>, Error>;
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Symbol {
Char(char),
SimpleEscape(u8),
DecimalEscape(u8),
}
impl Symbol {
pub fn from_chars<C: Iterator<Item = char>>(
chars: &mut C,
) -> Result<Option<Self>, SymbolCharsError> {
#[inline]
fn bad_escape() -> SymbolCharsError {
SymbolCharsError(SymbolCharsEnum::BadEscape)
}
#[inline]
fn short_input() -> SymbolCharsError {
SymbolCharsError(SymbolCharsEnum::ShortInput)
}
let ch = match chars.next() {
Some(ch) => ch,
None => return Ok(None),
};
if ch != '\\' {
return Ok(Some(Symbol::Char(ch)));
}
match chars.next() {
Some(ch) if ch.is_ascii_digit() => {
let ch = ch.to_digit(10).unwrap() * 100;
let ch2 = match chars.next() {
Some(ch) => match ch.to_digit(10) {
Some(ch) => ch * 10,
None => return Err(bad_escape()),
},
None => return Err(short_input()),
};
let ch3 = match chars.next() {
Some(ch) => match ch.to_digit(10) {
Some(ch) => ch,
None => return Err(bad_escape()),
},
None => return Err(short_input()),
};
let res = ch + ch2 + ch3;
if res > 255 {
return Err(bad_escape());
}
Ok(Some(Symbol::DecimalEscape(res as u8)))
}
Some(ch) => {
let ch = u8::try_from(ch).map_err(|_| bad_escape())?;
if ch < 0x20 || ch > 0x7e {
Err(bad_escape())
} else {
Ok(Some(Symbol::SimpleEscape(ch)))
}
}
None => Err(short_input()),
}
}
pub fn from_slice_index(
octets: &[u8],
pos: usize,
) -> Result<Option<(Symbol, usize)>, SymbolOctetsError> {
#[inline]
fn bad_utf8() -> SymbolOctetsError {
SymbolOctetsError(SymbolOctetsEnum::BadUtf8)
}
#[inline]
fn bad_escape() -> SymbolOctetsError {
SymbolOctetsError(SymbolOctetsEnum::BadEscape)
}
#[inline]
fn short_input() -> SymbolOctetsError {
SymbolOctetsError(SymbolOctetsEnum::ShortInput)
}
let c1 = match octets.get(pos) {
Some(c1) => *c1,
None => return Ok(None),
};
let pos = pos + 1;
if c1 == b'\\' {
let c2 = match octets.get(pos) {
Some(c2) => *c2,
None => return Err(short_input()),
};
let pos = pos + 1;
if c2.is_ascii_control() {
return Err(bad_escape());
} else if !c2.is_ascii_digit() {
return Ok(Some((Symbol::SimpleEscape(c2), pos)));
}
let c3 = match octets.get(pos) {
Some(c) if c.is_ascii_digit() => *c,
Some(_) => return Err(bad_escape()),
None => return Err(short_input()),
};
let pos = pos + 1;
let c4 = match octets.get(pos) {
Some(c) if c.is_ascii_digit() => *c,
Some(_) => return Err(bad_escape()),
None => return Err(short_input()),
};
let pos = pos + 1;
Ok(Some((
Symbol::DecimalEscape(
u8::try_from(
(u32::from(c2 - b'0') * 100)
+ (u32::from(c3 - b'0') * 10)
+ (u32::from(c4 - b'0')),
)
.map_err(|_| bad_escape())?,
),
pos,
)))
} else {
if c1 < 128 {
return Ok(Some((Symbol::Char(c1.into()), pos)));
}
if c1 & 0b0100_0000 == 0 {
return Err(bad_utf8());
}
let c2 = match octets.get(pos) {
Some(c2) => *c2,
None => return Err(short_input()),
};
let pos = pos + 1;
if c2 & 0b1100_0000 != 0b1000_0000 {
return Err(bad_utf8());
}
if c1 & 0b0010_0000 == 0 {
return Ok(Some((
Symbol::Char(
(u32::from(c2 & 0b0011_1111)
| (u32::from(c1 & 0b0001_1111) << 6))
.try_into()
.map_err(|_| bad_utf8())?,
),
pos,
)));
}
let c3 = match octets.get(pos) {
Some(c3) => *c3,
None => return Err(short_input()),
};
let pos = pos + 1;
if c3 & 0b1100_0000 != 0b1000_0000 {
return Err(bad_utf8());
}
if c1 & 0b0001_0000 == 0 {
return Ok(Some((
Symbol::Char(
(u32::from(c3 & 0b0011_1111)
| (u32::from(c2 & 0b0011_1111) << 6)
| (u32::from(c1 & 0b0001_1111) << 12))
.try_into()
.map_err(|_| bad_utf8())?,
),
pos,
)));
}
let c4 = match octets.get(pos) {
Some(c4) => *c4,
None => return Err(short_input()),
};
let pos = pos + 1;
if c4 & 0b1100_0000 != 0b1000_0000 {
return Err(bad_utf8());
}
Ok(Some((
Symbol::Char(
(u32::from(c4 & 0b0011_1111)
| (u32::from(c3 & 0b0011_1111) << 6)
| (u32::from(c2 & 0b0011_1111) << 12)
| (u32::from(c1 & 0b0000_1111) << 18))
.try_into()
.map_err(|_| bad_utf8())?,
),
pos,
)))
}
}
#[must_use]
pub fn from_octet(ch: u8) -> Self {
if ch == b' ' || ch == b'"' || ch == b'\\' || ch == b';' {
Symbol::SimpleEscape(ch)
} else if !(0x20..0x7F).contains(&ch) {
Symbol::DecimalEscape(ch)
} else {
Symbol::Char(ch as char)
}
}
#[must_use]
pub fn quoted_from_octet(ch: u8) -> Self {
if ch == b'"' || ch == b'\\' {
Symbol::SimpleEscape(ch)
} else if !(0x20..0x7F).contains(&ch) {
Symbol::DecimalEscape(ch)
} else {
Symbol::Char(ch as char)
}
}
#[must_use]
pub fn display_from_octet(ch: u8) -> Self {
if ch == b'\\' {
Symbol::SimpleEscape(ch)
} else if !(0x20..0x7F).contains(&ch) {
Symbol::DecimalEscape(ch)
} else {
Symbol::Char(ch as char)
}
}
pub fn into_octet(self) -> Result<u8, BadSymbol> {
match self {
Symbol::Char(ch) => {
if ch.is_ascii() && ch >= '\u{20}' && ch <= '\u{7E}' {
Ok(ch as u8)
} else {
Err(BadSymbol(BadSymbolEnum::NonAscii))
}
}
Symbol::SimpleEscape(ch) | Symbol::DecimalEscape(ch) => Ok(ch),
}
}
pub fn into_ascii(self) -> Result<u8, BadSymbol> {
match self {
Symbol::Char(ch) => {
if ch.is_ascii() && ch >= '\u{20}' && ch <= '\u{7E}' {
Ok(ch as u8)
} else {
Err(BadSymbol(BadSymbolEnum::NonAscii))
}
}
Symbol::SimpleEscape(ch) | Symbol::DecimalEscape(ch) => {
if ch >= 0x20 && ch <= 0x7E {
Ok(ch)
} else {
Err(BadSymbol(BadSymbolEnum::NonAscii))
}
}
}
}
pub fn into_char(self) -> Result<char, BadSymbol> {
match self {
Symbol::Char(ch) => Ok(ch),
Symbol::SimpleEscape(ch) if ch >= 0x20 && ch < 0x7F => {
Ok(ch.into())
}
_ => Err(BadSymbol(BadSymbolEnum::NonUtf8)),
}
}
pub fn into_digit(self, base: u32) -> Result<u32, BadSymbol> {
if let Symbol::Char(ch) = self {
match ch.to_digit(base) {
Some(ch) => Ok(ch),
None => Err(BadSymbol(BadSymbolEnum::NonDigit)),
}
} else {
Err(BadSymbol(BadSymbolEnum::Escape))
}
}
#[must_use]
pub fn is_word_char(self) -> bool {
match self {
Symbol::Char(ch) => {
ch != ' '
&& ch != '\t'
&& ch != '\r'
&& ch != '\n'
&& ch != '('
&& ch != ')'
&& ch != ';'
&& ch != '"'
}
_ => true,
}
}
}
impl From<char> for Symbol {
fn from(ch: char) -> Symbol {
Symbol::Char(ch)
}
}
impl fmt::Display for Symbol {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
Symbol::Char(ch) => write!(f, "{}", ch),
Symbol::SimpleEscape(ch) => write!(f, "\\{}", ch as char),
Symbol::DecimalEscape(ch) => write!(f, "\\{:03}", ch),
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum EntrySymbol {
Symbol(Symbol),
EndOfToken,
}
impl From<Symbol> for EntrySymbol {
fn from(symbol: Symbol) -> Self {
EntrySymbol::Symbol(symbol)
}
}
#[derive(Clone, Debug)]
pub struct Symbols<Chars> {
chars: Result<Chars, SymbolCharsError>,
}
impl<Chars> Symbols<Chars> {
pub fn new(chars: Chars) -> Self {
Symbols { chars: Ok(chars) }
}
pub fn ok(self) -> Result<(), SymbolCharsError> {
self.chars.map(|_| ())
}
pub fn with<F, T, E>(chars: Chars, op: F) -> Result<T, E>
where
F: FnOnce(&mut Self) -> Result<T, E>,
E: From<SymbolCharsError>,
{
let mut symbols = Self::new(chars);
let res = op(&mut symbols)?;
symbols.ok()?;
Ok(res)
}
}
impl<Chars: Iterator<Item = char>> Iterator for Symbols<Chars> {
type Item = Symbol;
fn next(&mut self) -> Option<Self::Item> {
self.chars = {
let chars = match self.chars.as_mut() {
Ok(chars) => chars,
Err(_) => return None,
};
match Symbol::from_chars(chars) {
Ok(res) => return res,
Err(err) => Err(err),
}
};
None
}
}
pub struct IterScanner<Iter: Iterator, Octets> {
iter: Peekable<Iter>,
marker: PhantomData<Octets>,
}
impl<Iter: Iterator, Octets> IterScanner<Iter, Octets> {
pub fn new<I: IntoIterator<IntoIter = Iter>>(iter: I) -> Self {
IterScanner {
iter: iter.into_iter().peekable(),
marker: PhantomData,
}
}
pub fn is_exhausted(&mut self) -> bool {
self.iter.peek().is_none()
}
}
impl<Iter, Item, Octets> Scanner for IterScanner<Iter, Octets>
where
Item: AsRef<str>,
Iter: Iterator<Item = Item>,
Octets: FromBuilder,
<Octets as FromBuilder>::Builder: EmptyBuilder + Composer,
{
type Octets = Octets;
type OctetsBuilder = <Octets as FromBuilder>::Builder;
type Name = Name<Octets>;
type Error = StrError;
fn has_space(&self) -> bool {
false
}
fn continues(&mut self) -> bool {
self.iter.peek().is_some()
}
fn scan_symbols<F>(&mut self, mut op: F) -> Result<(), Self::Error>
where
F: FnMut(Symbol) -> Result<(), Self::Error>,
{
let token = match self.iter.next() {
Some(token) => token,
None => return Err(StrError::end_of_entry()),
};
for sym in Symbols::new(token.as_ref().chars()) {
op(sym)?;
}
Ok(())
}
fn scan_entry_symbols<F>(&mut self, mut op: F) -> Result<(), Self::Error>
where
F: FnMut(EntrySymbol) -> Result<(), Self::Error>,
{
for token in &mut self.iter {
for sym in Symbols::new(token.as_ref().chars()) {
op(sym.into())?;
}
op(EntrySymbol::EndOfToken)?;
}
Ok(())
}
fn convert_token<C: ConvertSymbols<Symbol, Self::Error>>(
&mut self,
mut convert: C,
) -> Result<Self::Octets, Self::Error> {
let token = match self.iter.next() {
Some(token) => token,
None => return Err(StrError::end_of_entry()),
};
let mut res = <Octets as FromBuilder>::Builder::empty();
for sym in Symbols::new(token.as_ref().chars()) {
if let Some(data) = convert.process_symbol(sym)? {
res.append_slice(data).map_err(Into::into)?;
}
}
if let Some(data) = convert.process_tail()? {
res.append_slice(data).map_err(Into::into)?;
}
Ok(<Octets as FromBuilder>::from_builder(res))
}
fn convert_entry<C: ConvertSymbols<EntrySymbol, Self::Error>>(
&mut self,
mut convert: C,
) -> Result<Self::Octets, Self::Error> {
let mut res = <Octets as FromBuilder>::Builder::empty();
for token in &mut self.iter {
for sym in Symbols::new(token.as_ref().chars()) {
if let Some(data) = convert.process_symbol(sym.into())? {
res.append_slice(data).map_err(Into::into)?;
}
}
}
if let Some(data) = convert.process_tail()? {
res.append_slice(data).map_err(Into::into)?;
}
Ok(<Octets as FromBuilder>::from_builder(res))
}
fn scan_octets(&mut self) -> Result<Self::Octets, Self::Error> {
let token = match self.iter.next() {
Some(token) => token,
None => return Err(StrError::end_of_entry()),
};
let mut res = <Octets as FromBuilder>::Builder::empty();
for sym in Symbols::new(token.as_ref().chars()) {
match sym.into_octet() {
Ok(ch) => res.append_slice(&[ch]).map_err(Into::into)?,
Err(_) => return Err(StrError::custom("bad symbol")),
}
}
Ok(<Octets as FromBuilder>::from_builder(res))
}
fn scan_ascii_str<F, T>(&mut self, op: F) -> Result<T, Self::Error>
where
F: FnOnce(&str) -> Result<T, Self::Error>,
{
let res = self.scan_string()?;
if res.is_ascii() {
op(&res)
} else {
Err(StrError::custom("non-ASCII characters"))
}
}
fn scan_name(&mut self) -> Result<Self::Name, Self::Error> {
let token = match self.iter.next() {
Some(token) => token,
None => return Err(StrError::end_of_entry()),
};
Name::from_symbols(Symbols::new(token.as_ref().chars()))
.map_err(|_| StrError::custom("invalid domain name"))
}
fn scan_charstr(&mut self) -> Result<CharStr<Self::Octets>, Self::Error> {
let token = match self.iter.next() {
Some(token) => token,
None => return Err(StrError::end_of_entry()),
};
let mut res =
CharStrBuilder::<<Octets as FromBuilder>::Builder>::new();
for sym in Symbols::new(token.as_ref().chars()) {
match sym.into_octet() {
Ok(ch) => res.append_slice(&[ch])?,
Err(_) => return Err(StrError::custom("bad symbol")),
}
}
Ok(res.finish())
}
fn scan_string(&mut self) -> Result<Str<Self::Octets>, Self::Error> {
let token = match self.iter.next() {
Some(token) => token,
None => return Err(StrError::end_of_entry()),
};
let mut res = <Octets as FromBuilder>::Builder::empty();
let mut buf = [0u8; 4];
for sym in Symbols::new(token.as_ref().chars()) {
match sym.into_char() {
Ok(ch) => res
.append_slice(ch.encode_utf8(&mut buf).as_bytes())
.map_err(Into::into)?,
Err(_) => return Err(StrError::custom("bad symbol")),
}
}
Ok(Str::from_utf8(<Octets as FromBuilder>::from_builder(res))
.unwrap())
}
fn scan_charstr_entry(&mut self) -> Result<Self::Octets, Self::Error> {
let mut res = <Octets as FromBuilder>::Builder::empty();
while self.iter.peek().is_some() {
self.scan_charstr()?.compose(&mut res).map_err(Into::into)?;
}
Ok(<Octets as FromBuilder>::from_builder(res))
}
fn scan_opt_unknown_marker(&mut self) -> Result<bool, Self::Error> {
match self.iter.peek() {
Some(token) if token.as_ref() == "\\#" => Ok(true),
_ => Ok(false),
}
}
fn octets_builder(&mut self) -> Result<Self::OctetsBuilder, Self::Error> {
Ok(<Octets as FromBuilder>::Builder::empty())
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct SymbolCharsError(SymbolCharsEnum);
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum SymbolCharsEnum {
BadEscape,
ShortInput,
}
impl SymbolCharsError {
pub(crate) const fn bad_escape() -> Self {
Self(SymbolCharsEnum::BadEscape)
}
pub(crate) const fn short_input() -> Self {
Self(SymbolCharsEnum::ShortInput)
}
#[must_use]
pub fn as_str(self) -> &'static str {
match self.0 {
SymbolCharsEnum::BadEscape => "illegal escape sequence",
SymbolCharsEnum::ShortInput => "unexpected end of input",
}
}
}
impl fmt::Display for SymbolCharsError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
#[cfg(feature = "std")]
impl std::error::Error for SymbolCharsError {}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct SymbolOctetsError(SymbolOctetsEnum);
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum SymbolOctetsEnum {
BadUtf8,
BadEscape,
ShortInput,
}
impl SymbolOctetsError {
#[must_use]
pub fn as_str(self) -> &'static str {
match self.0 {
SymbolOctetsEnum::BadUtf8 => "illegal UTF-8 sequence",
SymbolOctetsEnum::BadEscape => "illegal escape sequence",
SymbolOctetsEnum::ShortInput => "unexpected end of data",
}
}
}
impl fmt::Display for SymbolOctetsError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
#[cfg(feature = "std")]
impl std::error::Error for SymbolOctetsError {}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct BadSymbol(BadSymbolEnum);
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum BadSymbolEnum {
NonAscii,
NonUtf8,
NonDigit,
Escape,
}
impl BadSymbol {
pub(crate) fn non_ascii() -> Self {
Self(BadSymbolEnum::NonAscii)
}
#[must_use]
pub fn as_str(self) -> &'static str {
match self.0 {
BadSymbolEnum::NonAscii => "non-ASCII symbol",
BadSymbolEnum::NonUtf8 => "invalid UTF-8 sequence",
BadSymbolEnum::NonDigit => "expected digit",
BadSymbolEnum::Escape => "unexpected escape sequence",
}
}
}
impl fmt::Display for BadSymbol {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
#[cfg(feature = "std")]
impl std::error::Error for BadSymbol {}
#[cfg(feature = "std")]
impl From<BadSymbol> for std::io::Error {
fn from(err: BadSymbol) -> Self {
std::io::Error::other(err)
}
}
#[derive(Debug)]
pub struct StrError(&'static str);
impl ScannerError for StrError {
fn custom(msg: &'static str) -> Self {
StrError(msg)
}
fn end_of_entry() -> Self {
Self::custom("unexpected end of entry")
}
fn short_buf() -> Self {
Self::custom("short buffer")
}
fn trailing_tokens() -> Self {
Self::custom("trailing data")
}
}
impl From<ShortBuf> for StrError {
fn from(_: ShortBuf) -> Self {
Self::short_buf()
}
}
impl fmt::Display for StrError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.0)
}
}
#[cfg(feature = "std")]
impl std::error::Error for StrError {}
#[cfg(test)]
#[cfg(feature = "std")]
mod test {
use super::*;
#[test]
fn symbol_from_slice_index() {
let mut buf = [0u8; 4];
for ch in '\0'..char::MAX {
if ch == '\\' {
continue;
}
let slice = ch.encode_utf8(&mut buf).as_bytes();
assert_eq!(
Symbol::from_slice_index(slice, 0),
Ok(Some((Symbol::Char(ch), ch.len_utf8()))),
"char '{}'",
ch,
);
}
for ch in '0'..'\x7f' {
if ch.is_ascii_digit() {
continue;
}
assert_eq!(
Symbol::from_slice_index(format!("\\{}", ch).as_bytes(), 0),
Ok(Some((Symbol::SimpleEscape(ch as u8), 2))),
"sequence \"\\{}\"",
ch
);
}
for ch in 0..256 {
assert_eq!(
Symbol::from_slice_index(
format!("\\{:03}", ch).as_bytes(),
0
),
Ok(Some((Symbol::DecimalEscape(ch as u8), 4))),
"sequence \"\\{:03}\"",
ch
);
}
}
}