use crate::*;
pub enum RuleFnDef<D, S> {
Dynamic(D),
Static(S),
}
pub type IgnoredCallback = RuleFnDef<Box<dyn FnMut(&[u8]) + Send + 'static>, fn(&[u8])>;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum IgnoredAction {
Continue,
Stop,
}
pub type ControlledIgnoredCallback<WorkflowCtx> = RuleFnDef<
Box<dyn FnMut(&[u8], &mut WorkflowCtx) -> Result<IgnoredAction, Error> + Send + 'static>,
fn(&[u8], &mut WorkflowCtx) -> Result<IgnoredAction, Error>,
>;
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,
>;
pub type NextPacketCallback<B, P, Inner, WorkflowCtx> = RuleFnDef<
Box<
dyn FnMut(&NextPacket<B, P, Inner>, &mut WorkflowCtx) -> Result<(), Error> + Send + 'static,
>,
fn(&NextPacket<B, P, Inner>, &mut WorkflowCtx) -> Result<(), Error>,
>;
#[allow(missing_docs)]
#[enum_ids::enum_ids(display)]
pub enum RuleDef<
B: BlockDef,
BR: BlockReferredDef<B>,
P: PayloadDef<Inner>,
Inner: PayloadInnerDef,
WorkflowCtx = (),
> {
Ignored(IgnoredCallback),
IgnoredControl(ControlledIgnoredCallback<WorkflowCtx>),
NextPacket(NextPacketCallback<B, P, Inner, WorkflowCtx>),
Prefilter(PrefilterCallback<BR>),
FilterPayload(PayloadFilterCallback),
FilterPacket(FilterCallback<B, P, Inner>),
}
pub struct RulesDef<
B: BlockDef,
BR: BlockReferredDef<B>,
P: PayloadDef<Inner>,
Inner: PayloadInnerDef,
WorkflowCtx = (),
> {
pub rules: Vec<RuleDef<B, BR, P, Inner, WorkflowCtx>>,
}
impl<
B: BlockDef,
BR: BlockReferredDef<B>,
P: PayloadDef<Inner>,
Inner: PayloadInnerDef,
WorkflowCtx,
> Default for RulesDef<B, BR, P, Inner, WorkflowCtx>
{
fn default() -> Self {
Self { rules: Vec::new() }
}
}
impl<
B: BlockDef,
BR: BlockReferredDef<B>,
P: PayloadDef<Inner>,
Inner: PayloadInnerDef,
WorkflowCtx,
> RulesDef<B, BR, P, Inner, WorkflowCtx>
{
pub fn add_rule(&mut self, rule: RuleDef<B, BR, P, Inner, WorkflowCtx>) -> 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);
}
}
RuleDef::IgnoredControl(..) => {
if self
.rules
.iter()
.any(|r| matches!(r, RuleDef::IgnoredControl(..)))
{
return Err(Error::RuleDuplicate);
}
}
RuleDef::NextPacket(..) => {
if self
.rules
.iter()
.any(|r| matches!(r, RuleDef::NextPacket(..)))
{
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], ctx: &mut WorkflowCtx) -> 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),
},
RuleDef::IgnoredControl(cb) => {
let action = match cb {
RuleFnDef::Static(cb) => cb(buffer, ctx)?,
RuleFnDef::Dynamic(cb) => cb(buffer, ctx)?,
};
if action == IgnoredAction::Stop {
return Err(Error::IgnoredDataRejected);
}
}
_ignored => {}
}
}
Ok(())
}
pub fn next_packet(
&mut self,
next: &NextPacket<B, P, Inner>,
ctx: &mut WorkflowCtx,
) -> Result<(), Error> {
for rule in self.rules.iter_mut() {
if let RuleDef::NextPacket(cb) = rule {
match cb {
RuleFnDef::Static(cb) => cb(next, ctx)?,
RuleFnDef::Dynamic(cb) => cb(next, ctx)?,
}
}
}
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),
}
}
}
#[cfg(test)]
mod tests {
use crate::{
ByteBlock, DefaultProtocolContext, Error, ExtractPayloadFrom, IgnoredAction, IoSlices,
NextPacket, PacketDef, PayloadDef, PayloadHeader, ProtocolSchema, ReadBlockFrom,
ReadBlockFromSlice, ReadFrom, ReadStatus, RuleDef, RuleDefId, RuleFnDef, RulesDef,
TryExtractPayloadFrom, TryExtractPayloadFromBuffered, TryReadFrom, TryReadFromBuffered,
WriteMutTo, WriteTo, WriteVectoredMutTo, WriteVectoredTo, packet::rules::PeekAs,
};
use std::io::Cursor;
use std::sync::{
Arc,
atomic::{AtomicUsize, Ordering},
};
#[derive(Clone)]
struct RuleBlock {
field: u8,
}
impl RuleBlock {
fn new(field: u8) -> Self {
Self { field }
}
}
impl crate::Size for RuleBlock {
fn size(&self) -> u64 {
self.field as u64
}
}
impl WriteTo for RuleBlock {
fn write<T: std::io::Write>(&self, _: &mut T) -> std::io::Result<usize> {
Err(std::io::Error::other("rules test block write stub"))
}
fn write_all<T: std::io::Write>(&self, _: &mut T) -> std::io::Result<()> {
Err(std::io::Error::other("rules test block write_all stub"))
}
}
impl WriteVectoredTo for RuleBlock {
fn slices(&self) -> std::io::Result<IoSlices<'_>> {
Err(std::io::Error::other("rules test block slices stub"))
}
}
impl TryReadFromBuffered for RuleBlock {
fn try_read<T: std::io::BufRead, S: ProtocolSchema>(
_: &mut T,
) -> Result<ReadStatus<Self>, Error> {
Err(Error::Test)
}
}
impl TryReadFrom for RuleBlock {
fn try_read<T: std::io::Read + std::io::Seek, S: ProtocolSchema>(
_: &mut T,
) -> Result<ReadStatus<Self>, Error> {
Err(Error::Test)
}
}
impl ReadFrom for RuleBlock {
fn read<T: std::io::Read, S: ProtocolSchema>(_: &mut T) -> Result<Self, Error> {
Err(Error::Test)
}
}
impl ReadBlockFrom for RuleBlock {
fn read<T: std::io::Read>(_: &mut T, _: bool) -> Result<Self, Error> {
Err(Error::Test)
}
}
impl ReadBlockFromSlice for RuleBlock {
fn read_from_slice<'a>(_: &'a [u8], _: bool) -> Result<Self, Error>
where
Self: 'a + Sized,
{
Err(Error::Test)
}
}
impl crate::BlockDef for RuleBlock {}
impl crate::BlockReferredDef<RuleBlock> for RuleBlock {}
#[derive(Clone)]
struct RulePayload {
_field: u8,
}
impl RulePayload {
fn new(field: u8) -> Self {
Self { _field: field }
}
}
impl ProtocolSchema for RulePayload {
type Context<'a> = DefaultProtocolContext;
}
impl WriteVectoredMutTo for RulePayload {
fn slices(&mut self, _: &mut Self::Context<'_>) -> std::io::Result<IoSlices<'_>> {
Err(std::io::Error::other("rules test payload slices stub"))
}
}
impl WriteMutTo for RulePayload {
fn write<T: std::io::Write>(
&mut self,
_: &mut T,
_: &mut Self::Context<'_>,
) -> std::io::Result<usize> {
Err(std::io::Error::other("rules test payload write stub"))
}
fn write_all<T: std::io::Write>(
&mut self,
_: &mut T,
_: &mut Self::Context<'_>,
) -> std::io::Result<()> {
Err(std::io::Error::other("rules test payload write_all stub"))
}
}
impl crate::PayloadSignature for RulePayload {
fn sig(&self) -> ByteBlock {
ByteBlock::Len4(*b"RULS")
}
}
impl crate::PayloadEncodeReferred for RulePayload {
fn encode(&self, _ctx: &mut Self::Context<'_>) -> std::io::Result<Option<&[u8]>> {
Err(std::io::Error::other(
"rules test payload encode_referred stub",
))
}
}
impl crate::PayloadHooks for RulePayload {
fn after_decode(&mut self) -> std::io::Result<()> {
Err(std::io::Error::other(
"rules test payload after_decode stub",
))
}
fn before_encode(&mut self) -> std::io::Result<()> {
Err(std::io::Error::other(
"rules test payload before_encode stub",
))
}
}
impl crate::PayloadEncode for RulePayload {
fn encode(&self, _ctx: &mut Self::Context<'_>) -> std::io::Result<Vec<u8>> {
Err(std::io::Error::other("rules test payload encode stub"))
}
}
impl crate::PayloadCrc for RulePayload {
fn crc(&self, _: &mut Self::Context<'_>) -> std::io::Result<ByteBlock> {
Err(std::io::Error::other("rules test payload crc stub"))
}
fn crc_size() -> usize {
0
}
}
impl crate::PayloadSize for RulePayload {
fn size(&self, _: &mut Self::Context<'_>) -> std::io::Result<u64> {
Err(std::io::Error::other("rules test payload size stub"))
}
}
impl crate::PayloadInnerDef for RulePayload {}
impl TryExtractPayloadFromBuffered<RulePayload> for RulePayload {
fn try_read<B: std::io::BufRead>(
_: &mut B,
_: &PayloadHeader,
_: &mut <RulePayload as ProtocolSchema>::Context<'_>,
) -> Result<ReadStatus<RulePayload>, Error> {
Err(Error::Test)
}
}
impl TryExtractPayloadFrom<RulePayload> for RulePayload {
fn try_read<B: std::io::Read + std::io::Seek>(
_: &mut B,
_: &PayloadHeader,
_: &mut <RulePayload as ProtocolSchema>::Context<'_>,
) -> Result<ReadStatus<RulePayload>, Error> {
Err(Error::Test)
}
}
impl ExtractPayloadFrom<RulePayload> for RulePayload {
fn read<B: std::io::Read>(
_: &mut B,
_: &PayloadHeader,
_: &mut <RulePayload as ProtocolSchema>::Context<'_>,
) -> Result<RulePayload, Error> {
Err(Error::Test)
}
}
impl PayloadDef<RulePayload> for RulePayload {}
struct BrA;
struct BrB;
enum Referred {
A(BrA),
B(BrB),
}
impl PeekAs<'_, BrA> for Referred {
type Peeked = BrA;
fn peek_as(&self) -> Option<&Self::Peeked> {
match self {
Referred::A(v) => Some(v),
Referred::B(_) => None,
}
}
}
impl PeekAs<'_, BrB> for Referred {
type Peeked = BrB;
fn peek_as(&self) -> Option<&Self::Peeked> {
match self {
Referred::A(_) => None,
Referred::B(v) => Some(v),
}
}
}
#[test]
fn peeked_blocks_helpers_work() {
let arr = vec![Referred::A(BrA), Referred::B(BrB)];
let peeked = crate::PeekedBlocksDef::new(&arr);
assert_eq!(peeked.len(), 2);
assert!(!peeked.is_empty());
assert!(peeked.has::<BrA>());
assert!(peeked.has::<BrB>());
assert!(peeked.get::<BrA>().is_some());
assert!(peeked.get::<BrB>().is_some());
assert!(peeked.find::<BrB, _>(|_| true).is_some());
assert_eq!(peeked.iter_as::<BrA>().count(), 1);
assert_eq!(peeked.iter_as::<BrB>().count(), 1);
assert_eq!(peeked.iter().count(), 2);
assert_eq!(peeked.as_slice().len(), 2);
let first = peeked.first().expect("first");
assert!(first.as_type::<BrA>().is_some());
assert!(first.as_type::<BrB>().is_none());
}
#[test]
fn peeked_block_accessors_and_into_iter_work() {
let arr = vec![Referred::A(BrA), Referred::B(BrB)];
let peeked = crate::PeekedBlocksDef::new(&arr);
let first = peeked.first().expect("first");
assert!(std::ptr::eq(first.as_referred(), &*first));
let collected = peeked.into_iter().count();
assert_eq!(collected, 2);
}
#[test]
fn rules_add_duplicate_and_remove_behaviour() {
let mut rules = RulesDef::<RuleBlock, RuleBlock, RulePayload, RulePayload>::default();
rules
.add_rule(RuleDef::Ignored(RuleFnDef::Static(|_| {})))
.expect("first ignored");
assert!(matches!(
rules.add_rule(RuleDef::Ignored(RuleFnDef::Static(|_| {}))),
Err(Error::RuleDuplicate)
));
rules
.add_rule(RuleDef::IgnoredControl(RuleFnDef::Static(|_, _| {
Ok(IgnoredAction::Continue)
})))
.expect("first ignored control");
assert!(matches!(
rules.add_rule(RuleDef::IgnoredControl(RuleFnDef::Static(|_, _| {
Ok(IgnoredAction::Continue)
}))),
Err(Error::RuleDuplicate)
));
rules
.add_rule(RuleDef::NextPacket(RuleFnDef::Static(|_, _| Ok(()))))
.expect("first next packet");
assert!(matches!(
rules.add_rule(RuleDef::NextPacket(RuleFnDef::Static(|_, _| Ok(())))),
Err(Error::RuleDuplicate)
));
rules
.add_rule(RuleDef::Prefilter(RuleFnDef::Static(|_| true)))
.expect("first prefilter");
assert!(matches!(
rules.add_rule(RuleDef::Prefilter(RuleFnDef::Static(|_| true))),
Err(Error::RuleDuplicate)
));
rules.remove_rule(RuleDefId::Ignored);
rules
.add_rule(RuleDef::Ignored(RuleFnDef::Static(|_| {})))
.expect("ignored can be added again after remove");
rules.remove_rule(RuleDefId::IgnoredControl);
rules
.add_rule(RuleDef::IgnoredControl(RuleFnDef::Static(|_, _| {
Ok(IgnoredAction::Continue)
})))
.expect("ignored control can be added again after remove");
rules.remove_rule(RuleDefId::NextPacket);
rules
.add_rule(RuleDef::NextPacket(RuleFnDef::Static(|_, _| Ok(()))))
.expect("next packet can be added again after remove");
rules
.add_rule(RuleDef::FilterPayload(RuleFnDef::Static(|_| true)))
.expect("first payload filter");
assert!(matches!(
rules.add_rule(RuleDef::FilterPayload(RuleFnDef::Static(|_| true))),
Err(Error::RuleDuplicate)
));
rules
.add_rule(RuleDef::FilterPacket(RuleFnDef::Static(|_| true)))
.expect("first packet filter");
assert!(matches!(
rules.add_rule(RuleDef::FilterPacket(RuleFnDef::Static(|_| true))),
Err(Error::RuleDuplicate)
));
}
#[test]
fn rules_callbacks_filter_and_ignore_paths() {
let mut rules = RulesDef::<RuleBlock, RuleBlock, RulePayload, RulePayload>::default();
let ignored_calls = Arc::new(AtomicUsize::new(0));
let ignored_calls_c = ignored_calls.clone();
rules
.add_rule(RuleDef::Ignored(RuleFnDef::Dynamic(Box::new(move |_| {
ignored_calls_c.fetch_add(1, Ordering::SeqCst);
}))))
.expect("ignored rule");
rules
.add_rule(RuleDef::Prefilter(RuleFnDef::Static(|blocks| {
blocks.len() == 1
})))
.expect("prefilter rule");
rules
.add_rule(RuleDef::FilterPayload(RuleFnDef::Static(|payload| {
payload == [1, 2, 3]
})))
.expect("payload rule");
rules
.add_rule(RuleDef::FilterPacket(RuleFnDef::Static(|packet| {
packet.payload.is_none()
})))
.expect("packet rule");
rules.ignore(&[9, 9], &mut ()).expect("ignore callback");
assert_eq!(ignored_calls.load(Ordering::SeqCst), 1);
let blocks_a = vec![RuleBlock::new(1)];
assert!(rules.prefilter(&blocks_a));
assert!(!rules.prefilter(&[]));
assert!(rules.has_payload_filter());
assert!(rules.filter_payload(&[1, 2, 3]));
assert!(!rules.filter_payload(&[7, 8]));
let packet_no_payload = PacketDef::<RuleBlock, RulePayload, RulePayload>::default();
let packet_with_payload = PacketDef::<RuleBlock, RulePayload, RulePayload>::new(
vec![],
Some(RulePayload::new(1)),
);
assert!(rules.filter_packet(&packet_no_payload));
assert!(!rules.filter_packet(&packet_with_payload));
}
#[test]
fn rules_ignore_static_callback_path_is_called() {
static IGNORED_STATIC_CALLS: AtomicUsize = AtomicUsize::new(0);
fn ignored_static_cb(_: &[u8]) {
IGNORED_STATIC_CALLS.fetch_add(1, Ordering::SeqCst);
}
IGNORED_STATIC_CALLS.store(0, Ordering::SeqCst);
let mut rules = RulesDef::<RuleBlock, RuleBlock, RulePayload, RulePayload>::default();
rules
.add_rule(RuleDef::Ignored(RuleFnDef::Static(ignored_static_cb)))
.expect("ignored static rule");
rules
.ignore(&[1, 2, 3], &mut ())
.expect("ignore static callback");
assert_eq!(IGNORED_STATIC_CALLS.load(Ordering::SeqCst), 1);
}
#[test]
fn rules_ignored_control_can_continue_or_stop_with_context() {
struct IgnoreCtx {
total: usize,
limit: usize,
next_results: usize,
}
struct ControlledPayload;
impl ProtocolSchema for ControlledPayload {
type Context<'a> = IgnoreCtx;
}
impl WriteVectoredMutTo for ControlledPayload {
fn slices(&mut self, _: &mut Self::Context<'_>) -> std::io::Result<IoSlices<'_>> {
Err(std::io::Error::other("controlled payload slices test stub"))
}
}
impl WriteMutTo for ControlledPayload {
fn write<T: std::io::Write>(
&mut self,
_: &mut T,
_: &mut Self::Context<'_>,
) -> std::io::Result<usize> {
Err(std::io::Error::other("controlled payload write test stub"))
}
fn write_all<T: std::io::Write>(
&mut self,
_: &mut T,
_: &mut Self::Context<'_>,
) -> std::io::Result<()> {
Err(std::io::Error::other(
"controlled payload write_all test stub",
))
}
}
impl crate::PayloadSignature for ControlledPayload {
fn sig(&self) -> ByteBlock {
ByteBlock::Len4(*b"CTRL")
}
}
impl crate::PayloadEncodeReferred for ControlledPayload {
fn encode(&self, _: &mut Self::Context<'_>) -> std::io::Result<Option<&[u8]>> {
Err(std::io::Error::other(
"controlled payload encode_referred test stub",
))
}
}
impl crate::PayloadHooks for ControlledPayload {}
impl crate::PayloadEncode for ControlledPayload {
fn encode(&self, _: &mut Self::Context<'_>) -> std::io::Result<Vec<u8>> {
Err(std::io::Error::other("controlled payload encode test stub"))
}
}
impl crate::PayloadCrc for ControlledPayload {
fn crc(&self, _: &mut Self::Context<'_>) -> std::io::Result<ByteBlock> {
Err(std::io::Error::other("controlled payload crc test stub"))
}
fn crc_size() -> usize {
0
}
}
impl crate::PayloadSize for ControlledPayload {
fn size(&self, _: &mut Self::Context<'_>) -> std::io::Result<u64> {
Err(std::io::Error::other("controlled payload size test stub"))
}
}
impl crate::PayloadInnerDef for ControlledPayload {}
impl TryExtractPayloadFromBuffered<ControlledPayload> for ControlledPayload {
fn try_read<B: std::io::BufRead>(
_: &mut B,
_: &PayloadHeader,
_: &mut <ControlledPayload as ProtocolSchema>::Context<'_>,
) -> Result<ReadStatus<ControlledPayload>, Error> {
Err(Error::Test)
}
}
impl TryExtractPayloadFrom<ControlledPayload> for ControlledPayload {
fn try_read<B: std::io::Read + std::io::Seek>(
_: &mut B,
_: &PayloadHeader,
_: &mut <ControlledPayload as ProtocolSchema>::Context<'_>,
) -> Result<ReadStatus<ControlledPayload>, Error> {
Err(Error::Test)
}
}
impl ExtractPayloadFrom<ControlledPayload> for ControlledPayload {
fn read<B: std::io::Read>(
_: &mut B,
_: &PayloadHeader,
_: &mut <ControlledPayload as ProtocolSchema>::Context<'_>,
) -> Result<ControlledPayload, Error> {
Err(Error::Test)
}
}
impl PayloadDef<ControlledPayload> for ControlledPayload {}
let mut rules = RulesDef::<
RuleBlock,
RuleBlock,
ControlledPayload,
ControlledPayload,
IgnoreCtx,
>::default();
rules
.add_rule(RuleDef::IgnoredControl(RuleFnDef::Static(|bytes, ctx| {
ctx.total += bytes.len();
if ctx.total > ctx.limit {
Ok(IgnoredAction::Stop)
} else {
Ok(IgnoredAction::Continue)
}
})))
.expect("ignored control rule");
rules
.add_rule(RuleDef::NextPacket(RuleFnDef::Static(|_, ctx| {
ctx.next_results += 1;
Ok(())
})))
.expect("next packet rule");
let mut ctx = IgnoreCtx {
total: 0,
limit: 4,
next_results: 0,
};
rules.ignore(&[1, 2], &mut ctx).expect("first chunk");
assert_eq!(ctx.total, 2);
assert!(matches!(
rules.ignore(&[3, 4, 5], &mut ctx),
Err(Error::IgnoredDataRejected)
));
assert_eq!(ctx.total, 5);
rules
.next_packet(&NextPacket::NotFound, &mut ctx)
.expect("next packet callback");
assert_eq!(ctx.next_results, 1);
}
#[test]
fn trait_required_stub_methods_return_explicit_errors() {
let mut buffered = Cursor::new(Vec::<u8>::new());
let mut stream = Cursor::new(Vec::<u8>::new());
let payload_header = PayloadHeader {
sig: ByteBlock::Len4([0, 0, 0, 0]),
crc: ByteBlock::Len4([0, 0, 0, 0]),
len: 0,
};
let block = RuleBlock::new(1);
assert_eq!(<RuleBlock as crate::Size>::size(&block), 1);
assert!(block.write(&mut Vec::new()).is_err());
assert!(block.write_all(&mut Vec::new()).is_err());
assert!(block.slices().is_err());
assert!(matches!(
<RuleBlock as TryReadFromBuffered>::try_read::<_, ()>(&mut buffered),
Err(Error::Test)
));
assert!(matches!(
<RuleBlock as TryReadFrom>::try_read::<_, ()>(&mut stream),
Err(Error::Test)
));
assert!(matches!(
<RuleBlock as ReadFrom>::read::<_, ()>(&mut buffered),
Err(Error::Test)
));
assert!(matches!(
<RuleBlock as ReadBlockFrom>::read(&mut buffered, false),
Err(Error::Test)
));
assert!(matches!(
<RuleBlock as ReadBlockFromSlice>::read_from_slice(&[], false),
Err(Error::Test)
));
let mut payload = RulePayload::new(1);
assert_eq!(
<RulePayload as crate::PayloadSignature>::sig(&payload).as_slice(),
b"RULS"
);
assert!(<RulePayload as crate::PayloadHooks>::before_encode(&mut payload).is_err());
assert!(<RulePayload as crate::PayloadHooks>::after_decode(&mut payload).is_err());
assert!(<RulePayload as crate::PayloadEncode>::encode(&payload, &mut ()).is_err());
assert!(<RulePayload as crate::PayloadEncodeReferred>::encode(&payload, &mut ()).is_err());
assert!(<RulePayload as crate::PayloadCrc>::crc(&payload, &mut ()).is_err());
assert!(<RulePayload as crate::PayloadSize>::size(&payload, &mut ()).is_err());
assert!(payload.write(&mut Vec::new(), &mut ()).is_err());
assert!(payload.write_all(&mut Vec::new(), &mut ()).is_err());
assert!(payload.slices(&mut ()).is_err());
assert!(matches!(
<RulePayload as TryExtractPayloadFromBuffered<RulePayload>>::try_read(
&mut buffered,
&payload_header,
&mut ()
),
Err(Error::Test)
));
assert!(matches!(
<RulePayload as TryExtractPayloadFrom<RulePayload>>::try_read(
&mut stream,
&payload_header,
&mut ()
),
Err(Error::Test)
));
assert!(matches!(
<RulePayload as ExtractPayloadFrom<RulePayload>>::read(
&mut buffered,
&payload_header,
&mut ()
),
Err(Error::Test)
));
}
}