use crate::errors::OscError;
use alloc::string::{String, ToString};
use alloc::vec::Vec;
use core::fmt::{Display, Formatter};
use nom::branch::alt;
use nom::bytes::complete::{is_a, is_not, tag, take, take_while1, take_while_m_n};
use nom::character::complete::{char, satisfy};
use nom::combinator::{all_consuming, complete, opt, recognize, verify};
use nom::error::{ErrorKind, ParseError};
use nom::multi::{many1, separated_list1};
use nom::sequence::{delimited, pair, separated_pair};
use nom::{IResult, Parser};
use std::collections::HashSet;
use std::iter::FromIterator;
#[derive(Clone, Debug)]
pub struct OscAddress(String);
impl OscAddress {
pub fn new(address: String) -> Result<Self, OscError> {
match verify_address(&address) {
Ok(_) => Ok(OscAddress(address)),
Err(e) => Err(e),
}
}
}
impl Display for OscAddress {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
write!(f, "{}", self.0)
}
}
#[derive(Clone, Debug)]
pub struct Matcher {
pub pattern: String,
pattern_parts: Vec<AddressPatternComponent>,
}
impl Matcher {
pub fn new(pattern: &str) -> Result<Self, OscError> {
verify_address_pattern(pattern)?;
let mut match_fn = all_consuming(many1(map_address_pattern_component));
let (_, pattern_parts) =
match_fn(pattern).map_err(|err| OscError::BadAddressPattern(err.to_string()))?;
Ok(Matcher {
pattern: pattern.into(),
pattern_parts,
})
}
pub fn match_address(&self, address: &OscAddress) -> bool {
if address.0 == self.pattern {
return true;
}
let mut remainder = address.0.as_str();
let mut iter = self.pattern_parts.iter().peekable();
while let Some(part) = iter.next() {
let result = match part {
AddressPatternComponent::Tag(s) => match_literally(remainder, s),
AddressPatternComponent::WildcardSingle => match_wildcard_single(remainder),
AddressPatternComponent::Wildcard(l) => {
match_wildcard(remainder, *l, iter.peek().copied())
}
AddressPatternComponent::CharacterClass(cc) => match_character_class(remainder, cc),
AddressPatternComponent::Choice(s) => match_choice(remainder, s),
};
remainder = match result {
Ok((i, _)) => i,
Err(_) => return false, };
}
remainder.is_empty()
}
}
fn is_address_character(x: char) -> bool {
if !x.is_ascii() || x.is_ascii_control() {
return false;
}
![' ', '#', '*', ',', '/', '?', '[', ']', '{', '}'].contains(&x)
}
fn pattern_choice(input: &str) -> IResult<&str, Vec<&str>> {
delimited(
char('{'),
separated_list1(tag(","), take_while1(is_address_character)),
char('}'),
)(input)
}
fn pattern_character_class(input: &str) -> IResult<&str, &str> {
let inner = pair(
recognize(opt(tag("!"))),
recognize(many1(verify(
alt((
separated_pair(
satisfy(is_address_character),
char('-'),
satisfy(is_address_character),
),
satisfy(is_address_character).map(|c| ('\0', c)),
)),
|(o1, o2): &(char, char)| o1 < o2,
))),
);
delimited(char('['), recognize(inner), char(']'))(input)
}
#[derive(Clone, Debug)]
struct CharacterClass {
pub negated: bool,
pub characters: String,
}
fn expand_character_range(first: char, second: char) -> String {
let start = first as u8;
let end = second as u8;
let range = start..=end;
range
.into_iter()
.map(char::from)
.filter(|c| is_address_character(*c))
.collect()
}
impl CharacterClass {
pub fn new(s: &str) -> Self {
let mut input = s;
let negated;
match char::<_, nom::error::Error<&str>>('!')(input) {
Ok((i, _)) => {
negated = true;
input = i;
}
Err(_) => negated = false,
}
let characters = complete(many1(alt((
char::<_, nom::error::Error<&str>>('!').map(|_| String::from("")),
separated_pair(
satisfy(is_address_character),
char('-'),
satisfy(is_address_character),
)
.map(|(first, second)| expand_character_range(first, second)),
satisfy(is_address_character).map(|x| x.to_string()),
char('-').map(|_| String::from("-")),
))))(input);
match characters {
Ok((_, o)) => CharacterClass {
negated,
characters: HashSet::<char>::from_iter(o.concat().chars())
.iter()
.collect(),
},
_ => {
panic!("Invalid character class formatting {}", s)
}
}
}
}
#[derive(Clone, Debug)]
enum AddressPatternComponent {
Tag(String),
Wildcard(usize),
WildcardSingle,
CharacterClass(CharacterClass),
Choice(Vec<String>),
}
fn map_address_pattern_component(input: &str) -> IResult<&str, AddressPatternComponent> {
alt((
take_while1(is_address_character)
.map(|s: &str| AddressPatternComponent::Tag(String::from(s))),
char('/').map(|c: char| AddressPatternComponent::Tag(c.to_string())),
tag("?").map(|_| AddressPatternComponent::WildcardSingle),
is_a("*?").map(|x: &str| AddressPatternComponent::Wildcard(x.matches('?').count())),
pattern_choice.map(|choices: Vec<&str>| {
AddressPatternComponent::Choice(choices.iter().map(|x| x.to_string()).collect())
}),
pattern_character_class
.map(|s: &str| AddressPatternComponent::CharacterClass(CharacterClass::new(s))),
))(input)
}
fn match_literally<'a>(input: &'a str, pattern: &str) -> IResult<&'a str, &'a str> {
tag(pattern)(input)
}
fn match_wildcard_single(input: &str) -> IResult<&str, &str> {
take_while_m_n(1, 1, is_address_character)(input)
}
fn match_character_class<'a>(
input: &'a str,
character_class: &'a CharacterClass,
) -> IResult<&'a str, &'a str> {
if character_class.negated {
is_not(character_class.characters.as_str())(input)
} else {
is_a(character_class.characters.as_str())(input)
}
}
fn match_choice<'a>(input: &'a str, choices: &[String]) -> IResult<&'a str, &'a str> {
for choice in choices {
if let Ok((i, o)) = tag::<_, _, nom::error::Error<&str>>(choice.as_str())(input) {
return Ok((i, o));
}
}
Err(nom::Err::Error(nom::error::Error::from_error_kind(
input,
ErrorKind::Tag,
)))
}
fn match_wildcard<'a>(
input: &'a str,
minimum_length: usize,
next: Option<&AddressPatternComponent>,
) -> IResult<&'a str, &'a str> {
let next = next.filter(|&part| match part {
AddressPatternComponent::Tag(s) => s != "/",
_ => true,
});
match next {
None => verify(take_while1(is_address_character), |s: &str| {
s.len() >= minimum_length
})(input),
Some(component) => {
let address_part = match input.split_once('/') {
Some((p, _)) => p,
None => input,
};
let mut longest: usize = 0;
for i in 0..address_part.len() {
let (_, substring) = input.split_at(i);
let result: IResult<_, _, nom::error::Error<&str>> = match component {
AddressPatternComponent::Tag(s) => match_literally(substring, s.as_str()),
AddressPatternComponent::CharacterClass(cc) => {
match_character_class(substring, cc)
}
AddressPatternComponent::Choice(s) => match_choice(substring, s),
AddressPatternComponent::WildcardSingle => {
panic!("Single wildcard ('?') must not follow wildcard ('*')")
}
AddressPatternComponent::Wildcard(_) => {
panic!("Double wildcards must be condensed into one")
}
};
if result.is_ok() {
longest = i
}
}
verify(take(longest), |s: &str| s.len() >= minimum_length)(input)
}
}
}
pub fn verify_address(input: &str) -> Result<(), OscError> {
match all_consuming::<_, _, nom::error::Error<&str>, _>(many1(pair(
tag("/"),
take_while1(is_address_character),
)))(input)
{
Ok(_) => Ok(()),
Err(_) => Err(OscError::BadAddress("Invalid address".to_string())),
}
}
fn address_pattern_part_parser(input: &str) -> IResult<&str, Vec<&str>> {
many1::<_, _, nom::error::Error<&str>, _>(alt((
take_while1(is_address_character),
tag("?"),
tag("*"),
recognize(pattern_choice),
pattern_character_class,
)))(input)
}
pub fn verify_address_pattern(input: &str) -> Result<(), OscError> {
match all_consuming(many1(
pair(tag("/"), address_pattern_part_parser.map(|x| x.concat())),
))(input)
{
Ok(_) => Ok(()),
Err(_) => Err(OscError::BadAddress("Invalid address pattern".to_string())),
}
}