use core::mem;
use crate::parser::str::find_split_hole;
use crate::template::error::Error;
use crate::template::parser::validate as validate_parser;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(super) struct ExprBody<'a>(&'a str);
impl<'a> ExprBody<'a> {
#[inline]
#[must_use]
pub(super) fn new(s: &'a str) -> Self {
debug_assert!(
!s.is_empty(),
"[precondition] valid expression body is not empty"
);
Self(s)
}
#[must_use]
pub(super) fn decompose(&self) -> (Operator, VarListStr<'a>) {
debug_assert!(
!self.0.is_empty(),
"[precondition] valid expression body is not empty"
);
let first = self.0.as_bytes()[0];
if first.is_ascii_alphanumeric() || (first == b'_') || (first == b'%') {
(Operator::String, VarListStr::new(self.0))
} else {
let op = Operator::from_byte(first).unwrap_or_else(|| {
unreachable!(
"[precondition] valid expression has (optional) \
valid operator, but got a byte {first:#02x?}"
)
});
(op, VarListStr::new(&self.0[1..]))
}
}
#[inline]
#[must_use]
pub(super) fn as_str(&self) -> &'a str {
self.0
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct VarName<'a>(&'a str);
impl<'a> VarName<'a> {
#[inline]
#[must_use]
pub(super) fn from_trusted(s: &'a str) -> Self {
Self(s)
}
#[inline]
pub fn new(s: &'a str) -> Result<Self, Error> {
match validate_parser::validate_varname(s, 0) {
Ok(_) => Ok(Self::from_trusted(s)),
Err(e) => Err(e),
}
}
#[inline]
#[must_use]
pub fn as_str(&self) -> &'a str {
self.0
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct VarSpec<'a> {
name: VarName<'a>,
modifier: Modifier,
}
impl<'a> VarSpec<'a> {
#[inline]
#[must_use]
pub(super) fn name(&self) -> VarName<'a> {
self.name
}
#[inline]
#[must_use]
pub(super) fn modifier(&self) -> Modifier {
self.modifier
}
#[must_use]
pub(super) fn parse_trusted(s: &'a str) -> Self {
if let Some(varname) = s.strip_suffix('*') {
return Self {
name: VarName::from_trusted(varname),
modifier: Modifier::Explode,
};
}
match find_split_hole(s, b':') {
Some((varname, max_len)) => {
let max_len: u16 = max_len
.parse()
.expect("[precondition] the input should be valid `varspec`");
Self {
name: VarName::from_trusted(varname),
modifier: Modifier::MaxLen(max_len),
}
}
None => Self {
name: VarName(s),
modifier: Modifier::None,
},
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(super) struct VarListStr<'a>(&'a str);
impl<'a> VarListStr<'a> {
#[inline]
#[must_use]
pub(super) fn new(s: &'a str) -> Self {
Self(s)
}
}
impl<'a> IntoIterator for VarListStr<'a> {
type IntoIter = VarListIter<'a>;
type Item = (usize, VarSpec<'a>);
#[inline]
fn into_iter(self) -> Self::IntoIter {
VarListIter { rest: self.0 }
}
}
#[derive(Debug, Clone)]
pub(super) struct VarListIter<'a> {
rest: &'a str,
}
impl<'a> Iterator for VarListIter<'a> {
type Item = (usize, VarSpec<'a>);
fn next(&mut self) -> Option<Self::Item> {
match find_split_hole(self.rest, b',') {
Some((prefix, new_rest)) => {
self.rest = new_rest;
Some((prefix.len(), VarSpec::parse_trusted(prefix)))
}
None => {
if self.rest.is_empty() {
None
} else {
Some((
self.rest.len(),
VarSpec::parse_trusted(mem::take(&mut self.rest)),
))
}
}
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub(super) enum Modifier {
None,
MaxLen(u16),
Explode,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub(super) enum MaybeOperator {
Operator(Operator),
Reserved(OperatorReservedForFuture),
}
impl MaybeOperator {
pub(super) fn from_byte(b: u8) -> Option<Self> {
match b {
b'+' => Some(Self::Operator(Operator::Reserved)),
b'#' => Some(Self::Operator(Operator::Fragment)),
b'.' => Some(Self::Operator(Operator::Label)),
b'/' => Some(Self::Operator(Operator::PathSegments)),
b';' => Some(Self::Operator(Operator::PathParams)),
b'?' => Some(Self::Operator(Operator::FormQuery)),
b'&' => Some(Self::Operator(Operator::FormQueryCont)),
b'=' => Some(Self::Reserved(OperatorReservedForFuture::Equals)),
b',' => Some(Self::Reserved(OperatorReservedForFuture::Comma)),
b'!' => Some(Self::Reserved(OperatorReservedForFuture::Exclamation)),
b'@' => Some(Self::Reserved(OperatorReservedForFuture::AtSign)),
b'|' => Some(Self::Reserved(OperatorReservedForFuture::Pipe)),
_ => None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub(super) enum Operator {
String,
Reserved,
Fragment,
Label,
PathSegments,
PathParams,
FormQuery,
FormQueryCont,
}
impl Operator {
#[must_use]
pub(super) fn from_byte(b: u8) -> Option<Self> {
match b {
b'+' => Some(Self::Reserved),
b'#' => Some(Self::Fragment),
b'.' => Some(Self::Label),
b'/' => Some(Self::PathSegments),
b';' => Some(Self::PathParams),
b'?' => Some(Self::FormQuery),
b'&' => Some(Self::FormQueryCont),
_ => None,
}
}
#[inline]
#[must_use]
pub(super) const fn len(self) -> usize {
if matches!(self, Self::String) {
0
} else {
1
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub(super) enum OperatorReservedForFuture {
Equals,
Comma,
Exclamation,
AtSign,
Pipe,
}