use bytes::{Buf, BufMut};
use crate::error::CodecError;
use crate::kvp::{KeyValuePair, KvpValue};
use crate::types::{FilterType, Location};
use crate::varint::{MoqtProfile, VarInt, VarIntError};
pub const SUBSCRIPTION_FILTER_PARAMETER: u64 = 0x21;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FilterEnd {
Group(u64),
GroupDelta(u64),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SubscriptionFilter {
pub filter_type: FilterType,
pub start_location: Option<Location>,
pub end_group: Option<FilterEnd>,
}
fn malformed(detail: &'static str) -> CodecError {
CodecError::SubscriptionFilterMalformed { detail }
}
const NO_FILTER_TYPE: &str = "it carries no Filter Type";
const NO_START: &str = "its Filter Type promises a Start Location and the value ends first";
const NO_END: &str = "its Filter Type promises an End Group and the value ends first";
const TRAILING: &str = "bytes follow the filter inside the parameter";
const START_MISSING: &str = "its Filter Type promises a Start Location and none is set";
const START_SURPLUS: &str = "its Filter Type promises no Start Location and one is set";
const END_MISSING: &str = "its Filter Type promises an End Group and none is set";
const END_SURPLUS: &str = "its Filter Type promises no End Group and one is set";
const END_IS_DELTA: &str = "its End Group is a delta where this draft writes the group in full";
const END_IS_ABSOLUTE: &str = "its End Group is written in full where this draft writes a delta";
impl SubscriptionFilter {
fn wants_start(filter_type: FilterType) -> bool {
matches!(filter_type, FilterType::AbsoluteStart | FilterType::AbsoluteRange)
}
fn wants_end(filter_type: FilterType) -> bool {
matches!(filter_type, FilterType::AbsoluteRange)
}
pub fn decode(bytes: &[u8]) -> Result<Self, CodecError> {
Self::decode_with(bytes, FilterEnd::Group, |buf: &mut &[u8]| VarInt::decode(buf))
}
pub fn decode_moqt<P: MoqtProfile>(bytes: &[u8]) -> Result<Self, CodecError> {
Self::decode_with(bytes, FilterEnd::GroupDelta, |buf: &mut &[u8]| {
VarInt::decode_moqt::<P>(buf)
})
}
fn decode_with<F>(
bytes: &[u8],
end: fn(u64) -> FilterEnd,
mut read: F,
) -> Result<Self, CodecError>
where
F: FnMut(&mut &[u8]) -> Result<VarInt, VarIntError>,
{
fn ran_out(err: VarIntError, detail: &'static str) -> CodecError {
match err {
VarIntError::UnexpectedEnd => malformed(detail),
other => CodecError::VarInt(other),
}
}
let mut buf = bytes;
let raw = read(&mut buf).map_err(|e| ran_out(e, NO_FILTER_TYPE))?.into_inner();
let filter_type = FilterType::from_u64(raw).ok_or(CodecError::InvalidFilterType(raw))?;
let start_location = if Self::wants_start(filter_type) {
let group = read(&mut buf).map_err(|e| ran_out(e, NO_START))?;
let object = read(&mut buf).map_err(|e| ran_out(e, NO_START))?;
Some(Location { group, object })
} else {
None
};
let end_group = if Self::wants_end(filter_type) {
Some(end(read(&mut buf).map_err(|e| ran_out(e, NO_END))?.into_inner()))
} else {
None
};
if buf.has_remaining() {
return Err(malformed(TRAILING));
}
Ok(SubscriptionFilter { filter_type, start_location, end_group })
}
pub fn encode(&self, buf: &mut impl BufMut) -> Result<(), CodecError> {
self.encode_with(false, buf, |v, out| {
VarInt::from_u64(v)?.encode(out);
Ok(())
})
}
pub fn encode_moqt<P: MoqtProfile>(&self, buf: &mut impl BufMut) -> Result<(), CodecError> {
self.encode_with(true, buf, |v, out| {
VarInt::from_u64_moqt(v).encode_moqt::<P>(out);
Ok(())
})
}
fn encode_with<W>(
&self,
end_is_delta: bool,
buf: &mut impl BufMut,
write: W,
) -> Result<(), CodecError>
where
W: Fn(u64, &mut Vec<u8>) -> Result<(), CodecError>,
{
let wants_start = Self::wants_start(self.filter_type);
if wants_start && self.start_location.is_none() {
return Err(malformed(START_MISSING));
}
if !wants_start && self.start_location.is_some() {
return Err(malformed(START_SURPLUS));
}
let wants_end = Self::wants_end(self.filter_type);
let end = match (wants_end, self.end_group) {
(true, None) => return Err(malformed(END_MISSING)),
(false, Some(_)) => return Err(malformed(END_SURPLUS)),
(_, value) => value,
};
match end {
Some(FilterEnd::GroupDelta(_)) if !end_is_delta => {
return Err(malformed(END_IS_DELTA));
}
Some(FilterEnd::Group(_)) if end_is_delta => {
return Err(malformed(END_IS_ABSOLUTE));
}
_ => {}
}
let mut out = Vec::new();
write(self.filter_type as u64, &mut out)?;
if let Some(start) = self.start_location {
write(start.group.into_inner(), &mut out)?;
write(start.object.into_inner(), &mut out)?;
}
if let Some(FilterEnd::Group(v) | FilterEnd::GroupDelta(v)) = end {
write(v, &mut out)?;
}
buf.put_slice(&out);
Ok(())
}
pub fn parameter(&self) -> Result<KeyValuePair, CodecError> {
let mut value = Vec::new();
self.encode(&mut value)?;
Ok(KeyValuePair {
key: VarInt::from_u64_moqt(SUBSCRIPTION_FILTER_PARAMETER),
value: KvpValue::Bytes(value),
})
}
pub fn parameter_moqt<P: MoqtProfile>(&self) -> Result<KeyValuePair, CodecError> {
let mut value = Vec::new();
self.encode_moqt::<P>(&mut value)?;
Ok(KeyValuePair {
key: VarInt::from_u64_moqt(SUBSCRIPTION_FILTER_PARAMETER),
value: KvpValue::Bytes(value),
})
}
pub fn from_parameters(parameters: &[KeyValuePair]) -> Option<Result<Self, CodecError>> {
Self::from_parameters_with(parameters, Self::decode)
}
pub fn from_parameters_moqt<P: MoqtProfile>(
parameters: &[KeyValuePair],
) -> Option<Result<Self, CodecError>> {
Self::from_parameters_with(parameters, Self::decode_moqt::<P>)
}
fn from_parameters_with(
parameters: &[KeyValuePair],
decode: fn(&[u8]) -> Result<Self, CodecError>,
) -> Option<Result<Self, CodecError>> {
let parameter =
parameters.iter().find(|p| p.key.into_inner() == SUBSCRIPTION_FILTER_PARAMETER)?;
match ¶meter.value {
KvpValue::Bytes(value) => Some(decode(value)),
KvpValue::Varint(_) => {
Some(Err(malformed("its value is a bare varint where the type defines a filter")))
}
}
}
pub fn last_group(&self) -> Result<Option<u64>, CodecError> {
match self.end_group {
None => Ok(None),
Some(FilterEnd::Group(group)) => Ok(Some(group)),
Some(FilterEnd::GroupDelta(delta)) => {
let start_group =
self.start_location.map(|l| l.group.into_inner()).unwrap_or_default();
start_group
.checked_add(delta)
.map(Some)
.ok_or(CodecError::FilterEndGroupOverflow { start_group, delta })
}
}
}
}