use winnow::{
ModalResult, Parser,
combinator::{opt, repeat},
error::{ErrMode, InputError},
token::{literal, one_of, take_while},
};
use zcheapstr::CheapStr;
use crate::{FieldName, InterfaceName, TypeName};
pub fn field_name<'a>(input: &mut &'a [u8]) -> ModalResult<FieldName<'a>, InputError<&'a [u8]>> {
(
one_of(|c: u8| c.is_ascii_alphabetic()),
repeat::<_, _, (), _, _>(
0..,
(
opt(one_of(|c: u8| c == b'_')),
one_of(|c: u8| c.is_ascii_alphanumeric()),
)
.void(),
),
)
.take()
.map(|name| FieldName(CheapStr::from(bytes_to_str(name))))
.parse_next(input)
}
pub fn type_name<'a>(input: &mut &'a [u8]) -> ModalResult<TypeName<'a>, InputError<&'a [u8]>> {
(
one_of(|c: u8| c.is_ascii_uppercase()),
take_while(0.., |c: u8| c.is_ascii_alphanumeric()),
)
.take()
.map(|name| TypeName(CheapStr::from(bytes_to_str(name))))
.parse_next(input)
}
pub fn interface_name<'a>(
input: &mut &'a [u8],
) -> ModalResult<InterfaceName<'a>, InputError<&'a [u8]>> {
(
one_of(|c: u8| c.is_ascii_alphabetic()),
repeat::<_, _, (), _, _>(0.., segment_tail),
repeat::<_, _, (), _, _>(
1..,
(
literal("."),
one_of(|c: u8| c.is_ascii_alphanumeric()),
repeat::<_, _, (), _, _>(0.., segment_tail),
)
.void(),
),
)
.take()
.map(|name| InterfaceName(CheapStr::from(bytes_to_str(name))))
.parse_next(input)
}
pub(crate) fn parses_fully<'a, P, T>(mut parser: P, name: &'a str) -> bool
where
P: FnMut(&mut &'a [u8]) -> Result<T, ErrMode<InputError<&'a [u8]>>>,
{
let mut input = name.as_bytes();
parser(&mut input).is_ok() && input.is_empty()
}
fn segment_tail<'a>(input: &mut &'a [u8]) -> ModalResult<(), InputError<&'a [u8]>> {
(
take_while(0.., |c: u8| c == b'-'),
one_of(|c: u8| c.is_ascii_alphanumeric()),
)
.void()
.parse_next(input)
}
fn bytes_to_str(bytes: &[u8]) -> &str {
core::str::from_utf8(bytes).unwrap()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parse_interface_name() {
let input = b"org.example.test";
let mut input_mut = input.as_slice();
let result = interface_name(&mut input_mut).unwrap();
assert_eq!(result, "org.example.test");
assert!(input_mut.is_empty());
let input = b"com.example.foo.bar";
let mut input_mut = input.as_slice();
let result = interface_name(&mut input_mut).unwrap();
assert_eq!(result, "com.example.foo.bar");
assert!(input_mut.is_empty());
let mut input_mut = b"example".as_slice();
assert!(interface_name(&mut input_mut).is_err());
let mut input_mut = b"1example.test".as_slice();
assert!(interface_name(&mut input_mut).is_err());
let input = b"org.exam-ple.test";
let mut input_mut = input.as_slice();
let result = interface_name(&mut input_mut).unwrap();
assert_eq!(result, "org.exam-ple.test");
assert!(input_mut.is_empty());
let input = b"org.0example";
let mut input_mut = input.as_slice();
let result = interface_name(&mut input_mut).unwrap();
assert_eq!(result, "org.0example");
assert!(input_mut.is_empty());
let mut input_mut = b"org-.example".as_slice();
assert!(!full_interface_name_is_valid(&mut input_mut));
let mut input_mut = b"org.example-".as_slice();
assert!(!full_interface_name_is_valid(&mut input_mut));
let mut input_mut = b"org.-example".as_slice();
assert!(!full_interface_name_is_valid(&mut input_mut));
}
fn full_interface_name_is_valid(input: &mut &[u8]) -> bool {
interface_name(input).is_ok() && input.is_empty()
}
}