use crate::*;
pub enum RuleFnDef<D, S> {
Dynamic(D),
Static(S),
}
pub type IgnoredCallback = RuleFnDef<Box<dyn FnMut(&[u8]) + Send + 'static>, fn(&[u8])>;
pub type WriteIgnoredCallback<W> = RuleFnDef<
Box<dyn FnMut(&mut std::io::BufWriter<W>, &[u8]) -> std::io::Result<()> + Send + 'static>,
fn(&mut std::io::BufWriter<W>, &[u8]) -> std::io::Result<()>,
>;
pub struct PeekedBlocksDef<'a, BR> {
inner: &'a [BR],
}
pub trait PeekAs<'a, T> {
type Peeked: 'a;
fn peek_as(&'a self) -> Option<&'a Self::Peeked>;
}
impl<'a, BR> PeekedBlocksDef<'a, BR> {
pub fn new(inner: &'a [BR]) -> Self {
Self { inner }
}
pub fn len(&self) -> usize {
self.inner.len()
}
pub fn is_empty(&self) -> bool {
self.inner.is_empty()
}
pub fn nth(&self, index: usize) -> Option<PeekedBlockDef<'a, BR>> {
self.inner.get(index).map(PeekedBlockDef::new)
}
pub fn first(&self) -> Option<PeekedBlockDef<'a, BR>> {
self.nth(0)
}
pub fn has<T>(&self) -> bool
where
BR: PeekAs<'a, T>,
{
self.inner.iter().any(|block| block.peek_as().is_some())
}
pub fn get_as<T>(&self) -> Option<&'a <BR as PeekAs<'a, T>>::Peeked>
where
BR: PeekAs<'a, T>,
{
self.inner.iter().find_map(|block| block.peek_as())
}
pub fn get<T>(&self) -> Option<&'a <BR as PeekAs<'a, T>>::Peeked>
where
BR: PeekAs<'a, T>,
{
self.get_as::<T>()
}
pub fn find<T, F>(&self, mut predicate: F) -> Option<&'a <BR as PeekAs<'a, T>>::Peeked>
where
BR: PeekAs<'a, T>,
F: FnMut(&<BR as PeekAs<'a, T>>::Peeked) -> bool,
{
self.inner
.iter()
.filter_map(|block| block.peek_as())
.find(|block| predicate(block))
}
pub fn iter_as<T>(&self) -> impl Iterator<Item = &'a <BR as PeekAs<'a, T>>::Peeked> + 'a
where
BR: PeekAs<'a, T>,
{
self.inner.iter().filter_map(|block| block.peek_as())
}
pub fn iter(&self) -> impl Iterator<Item = PeekedBlockDef<'a, BR>> + 'a {
self.inner.iter().map(PeekedBlockDef::new)
}
pub fn as_slice(&self) -> &'a [BR] {
self.inner
}
}
pub struct PeekedBlockDef<'a, BR> {
inner: &'a BR,
}
impl<'a, BR> PeekedBlockDef<'a, BR> {
pub fn new(inner: &'a BR) -> Self {
Self { inner }
}
pub fn as_referred(&self) -> &'a BR {
self.inner
}
pub fn as_type<T>(&self) -> Option<&'a <BR as PeekAs<'a, T>>::Peeked>
where
BR: PeekAs<'a, T>,
{
self.inner.peek_as()
}
}
impl<'a, BR> std::ops::Deref for PeekedBlockDef<'a, BR> {
type Target = BR;
fn deref(&self) -> &Self::Target {
self.inner
}
}
impl<'a, BR> IntoIterator for PeekedBlocksDef<'a, BR> {
type Item = PeekedBlockDef<'a, BR>;
type IntoIter = std::iter::Map<std::slice::Iter<'a, BR>, fn(&'a BR) -> PeekedBlockDef<'a, BR>>;
fn into_iter(self) -> Self::IntoIter {
self.inner.iter().map(PeekedBlockDef::new)
}
}
pub type PrefilterCallback<BR> = RuleFnDef<
Box<dyn Fn(PeekedBlocksDef<'_, BR>) -> bool + Send + 'static>,
fn(PeekedBlocksDef<'_, BR>) -> bool,
>;
pub type PayloadFilterCallback =
RuleFnDef<Box<dyn Fn(&[u8]) -> bool + Send + 'static>, fn(&[u8]) -> bool>;
pub type FilterCallback<B, P, Inner> = RuleFnDef<
Box<dyn Fn(&PacketDef<B, P, Inner>) -> bool + Send + 'static>,
fn(&PacketDef<B, P, Inner>) -> bool,
>;
#[allow(missing_docs)]
#[enum_ids::enum_ids(display)]
pub enum RuleDef<B: BlockDef, BR: BlockReferredDef<B>, P: PayloadDef<Inner>, Inner: PayloadInnerDef>
{
Ignored(IgnoredCallback),
Prefilter(PrefilterCallback<BR>),
FilterPayload(PayloadFilterCallback),
FilterPacket(FilterCallback<B, P, Inner>),
}
pub struct RulesDef<
B: BlockDef,
BR: BlockReferredDef<B>,
P: PayloadDef<Inner>,
Inner: PayloadInnerDef,
> {
pub rules: Vec<RuleDef<B, BR, P, Inner>>,
}
impl<B: BlockDef, BR: BlockReferredDef<B>, P: PayloadDef<Inner>, Inner: PayloadInnerDef> Default
for RulesDef<B, BR, P, Inner>
{
fn default() -> Self {
Self { rules: Vec::new() }
}
}
impl<B: BlockDef, BR: BlockReferredDef<B>, P: PayloadDef<Inner>, Inner: PayloadInnerDef>
RulesDef<B, BR, P, Inner>
{
pub fn add_rule(&mut self, rule: RuleDef<B, BR, P, Inner>) -> Result<(), Error> {
match &rule {
RuleDef::Prefilter(..) => {
if self
.rules
.iter()
.any(|r| matches!(r, RuleDef::Prefilter(..)))
{
return Err(Error::RuleDuplicate);
}
}
RuleDef::FilterPayload(..) => {
if self
.rules
.iter()
.any(|r| matches!(r, RuleDef::FilterPayload(..)))
{
return Err(Error::RuleDuplicate);
}
}
RuleDef::FilterPacket(..) => {
if self
.rules
.iter()
.any(|r| matches!(r, RuleDef::FilterPacket(..)))
{
return Err(Error::RuleDuplicate);
}
}
RuleDef::Ignored(..) => {
if self.rules.iter().any(|r| matches!(r, RuleDef::Ignored(..))) {
return Err(Error::RuleDuplicate);
}
}
};
self.rules.push(rule);
Ok(())
}
pub fn remove_rule(&mut self, rule: RuleDefId) {
self.rules
.retain(|r| r.id().to_string() != rule.to_string());
}
pub fn ignore(&mut self, buffer: &[u8]) -> Result<(), Error> {
for rule in self.rules.iter_mut() {
match rule {
RuleDef::Ignored(cb) => match cb {
RuleFnDef::Static(cb) => cb(buffer),
RuleFnDef::Dynamic(cb) => cb(buffer),
},
_ignored => {}
}
}
Ok(())
}
pub fn prefilter(&self, blocks: &[BR]) -> bool {
let Some(cb) = self.rules.iter().find_map(|r| {
if let RuleDef::Prefilter(cb) = r {
Some(cb)
} else {
None
}
}) else {
return true;
};
match cb {
RuleFnDef::Static(cb) => cb(PeekedBlocksDef::new(blocks)),
RuleFnDef::Dynamic(cb) => cb(PeekedBlocksDef::new(blocks)),
}
}
pub fn filter_payload(&self, buffer: &[u8]) -> bool {
let Some(cb) = self.rules.iter().find_map(|r| {
if let RuleDef::FilterPayload(cb) = r {
Some(cb)
} else {
None
}
}) else {
return true;
};
match cb {
RuleFnDef::Static(cb) => cb(buffer),
RuleFnDef::Dynamic(cb) => cb(buffer),
}
}
pub fn has_payload_filter(&self) -> bool {
self.rules
.iter()
.any(|rule| matches!(rule, RuleDef::FilterPayload(..)))
}
pub fn filter_packet(&self, packet: &PacketDef<B, P, Inner>) -> bool {
let Some(cb) = self.rules.iter().find_map(|r| {
if let RuleDef::FilterPacket(cb) = r {
Some(cb)
} else {
None
}
}) else {
return true;
};
match cb {
RuleFnDef::Static(cb) => cb(packet),
RuleFnDef::Dynamic(cb) => cb(packet),
}
}
}