use std::str;
use super::parser::{self, Parser};
pub type Port = u16;
#[derive(Clone, Debug, PartialEq)]
pub struct PortSet(Vec<Port>);
impl PortSet {
pub fn try_from_iter(iter: impl Iterator<Item = Port>) -> Option<PortSet> {
let mut ports = vec![];
for port in iter {
if !ports.contains(&port) {
ports.push(port)
}
}
if ports.is_empty() {
None
} else {
Some(Self(ports))
}
}
pub fn as_slice(&self) -> &[Port] {
self.0.as_slice()
}
pub fn into_inner(self) -> Vec<Port> {
self.0
}
}
impl<'a> TryFrom<parser::Pair<'a, parser::Rule>> for PortSet {
type Error = CannotConvertToPortSetError;
fn try_from(
pair: parser::Pair<'a, parser::Rule>,
) -> Result<Self, Self::Error> {
use parser::Rule;
if matches!(pair.as_rule(), Rule::port_set) {
let mut ports = vec![];
for p in pair.into_inner() {
match p.as_rule() {
Rule::port => ports.push(
p.as_str()
.parse()
.map_err(|_| CannotConvertToPortSetError)?,
),
Rule::port_range => {
let mut limits = p.into_inner();
let start: Port = limits
.next()
.expect("grammar guarantees start port")
.as_str()
.parse()
.map_err(|_| CannotConvertToPortSetError)?;
let end = limits
.next()
.expect("grammar guarantees end port")
.as_str()
.parse()
.map_err(|_| CannotConvertToPortSetError)?;
if start <= end {
ports.extend(start..=end);
} else {
ports.extend((end..=start).rev());
}
}
_ => unreachable!("grammar guarantees port or port_range"),
}
}
Ok(
Self::try_from_iter(ports.into_iter())
.expect("grammar guarantees at least one port"),
)
} else {
Err(CannotConvertToPortSetError)
}
}
}
#[derive(Debug, PartialEq, thiserror::Error)]
#[error("cannot convert to port set")]
pub struct CannotConvertToPortSetError;
impl str::FromStr for PortSet {
type Err = PortSetParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
parser::ConnectionExprLanguage::parse(parser::Rule::port_set_expr, s)
.map_err(|_| PortSetParseError)?
.next()
.expect("grammar guaranteed post_set_expr")
.into_inner()
.next()
.expect("grammar guarantees post_set")
.try_into()
.map_err(|_| PortSetParseError)
}
}
#[derive(Debug, PartialEq, thiserror::Error)]
#[error("cannot parse port set expression")]
pub struct PortSetParseError;
#[cfg(test)]
mod test {
use super::*;
#[test]
fn port_set_parsing() {
assert_eq!("1".parse(), Ok(PortSet(vec![1])));
assert_eq!("65535".parse(), Ok(PortSet(vec![65535])));
assert_eq!("1,2".parse(), Ok(PortSet(vec![1, 2])));
assert_eq!("1-3".parse(), Ok(PortSet(vec![1, 2, 3])));
assert_eq!("3-1".parse(), Ok(PortSet(vec![3, 2, 1])));
assert_eq!("1,1-2,5,2-4".parse(), Ok(PortSet(vec![1, 2, 5, 3, 4])));
assert_eq!("".parse::<PortSet>(), Err(PortSetParseError));
assert_eq!(" 1".parse::<PortSet>(), Err(PortSetParseError));
assert_eq!("1 ".parse::<PortSet>(), Err(PortSetParseError));
assert_eq!(",".parse::<PortSet>(), Err(PortSetParseError));
assert_eq!(",1".parse::<PortSet>(), Err(PortSetParseError));
assert_eq!("1,".parse::<PortSet>(), Err(PortSetParseError));
assert_eq!("-1".parse::<PortSet>(), Err(PortSetParseError));
assert_eq!("1-".parse::<PortSet>(), Err(PortSetParseError));
assert_eq!("1,,2".parse::<PortSet>(), Err(PortSetParseError));
assert_eq!("1--2".parse::<PortSet>(), Err(PortSetParseError));
assert_eq!("1-2-3".parse::<PortSet>(), Err(PortSetParseError));
assert_eq!("65536".parse::<PortSet>(), Err(PortSetParseError));
}
}