use std::cell::RefCell;
#[cfg(feature = "json")]
use std::io::Write;
use std::rc::Rc;
#[cfg(feature = "json")]
use qubit_budget::json::JsonDecodeSession;
#[cfg(feature = "json")]
use qubit_budget::json::JsonEncodeLimits;
#[cfg(feature = "json")]
use qubit_budget::json::JsonEncodeSession;
#[cfg(feature = "json")]
use qubit_json::decode::JsonDecoder;
#[cfg(feature = "json")]
use qubit_json::encode::JsonEncoder;
use qubit_utils::Transient;
#[cfg(feature = "json")]
use qubit_value::ValueWireEncodePreflight;
use serde::Deserialize;
use serde::Deserializer;
use serde::Serialize;
use serde::Serializer;
use serde::de;
use serde::de::DeserializeSeed;
use serde::ser::Error as SerError;
use super::metadata_filter_builder::MetadataFilterBuilder;
#[cfg(feature = "json")]
use super::wire::METADATA_FILTER_WIRE_VERSION_V1;
use super::wire::MetadataFilterWireV1Ref;
use super::wire::MetadataFilterWireV1Seed;
#[cfg(feature = "schema")]
use crate::Condition;
use crate::FilterExpression;
#[cfg(feature = "json")]
use crate::FilterExpressionView;
use crate::FilterLimits;
use crate::FilterMatchOptions;
use crate::Metadata;
#[cfg(feature = "schema")]
use crate::MetadataResult;
#[cfg(feature = "json")]
use crate::metadata_limits::MetadataLimits;
#[derive(Debug, Clone, PartialEq)]
pub struct MetadataFilter {
expression: FilterExpression,
options: FilterMatchOptions,
limits: Transient<FilterLimits>,
}
impl MetadataFilter {
#[inline]
#[must_use = "the constructed all-matching filter should be used"]
pub fn all() -> Self {
Self::new(
FilterExpression::match_all(),
FilterMatchOptions::default(),
FilterLimits::MAX,
)
}
#[inline]
#[must_use = "the constructed no-match filter should be used"]
pub fn none() -> Self {
Self::new(
FilterExpression::match_none(),
FilterMatchOptions::default(),
FilterLimits::MAX,
)
}
#[inline]
#[must_use]
pub const fn builder() -> MetadataFilterBuilder {
MetadataFilterBuilder::new()
}
pub fn deserialize_with_filter_limits<'de, D>(
deserializer: D,
receiver_limits: FilterLimits,
) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let error_slot = Rc::new(RefCell::new(None));
let wire = MetadataFilterWireV1Seed::new(receiver_limits, Rc::clone(&error_slot))
.deserialize(deserializer)
.map_err(|error| {
error_slot
.borrow_mut()
.take()
.map_or_else(|| de::Error::custom(error), de::Error::custom)
})?;
wire.into_filter(receiver_limits).map_err(de::Error::custom)
}
#[cfg(feature = "json")]
#[inline]
pub fn decode_json_slice(input: &[u8]) -> Result<Self, crate::MetadataWireDecodeError> {
Self::decode_json_slice_with_limits(input, MetadataLimits::default(), FilterLimits::MAX)
}
#[cfg(feature = "json")]
pub fn decode_json_slice_with_limits(
input: &[u8],
limits: MetadataLimits,
receiver_filter_limits: FilterLimits,
) -> Result<Self, crate::MetadataWireDecodeError> {
limits
.validate()
.map_err(crate::MetadataWireDecodeError::InvalidLimits)?;
let mut decoder = JsonDecoder::new(JsonDecodeSession::from_limits(limits.json_decode()));
let error_slot = Rc::new(RefCell::new(None));
let wire = decoder
.decode_seed_utf8(
MetadataFilterWireV1Seed::new(receiver_filter_limits, Rc::clone(&error_slot)),
input,
)
.map_err(|error| {
error_slot.borrow_mut().take().map_or_else(
|| Into::<crate::MetadataWireDecodeError>::into(error),
crate::MetadataWireDecodeError::Filter,
)
})?;
if wire.version() != METADATA_FILTER_WIRE_VERSION_V1 {
return Err(crate::MetadataWireDecodeError::UnsupportedVersion {
expected: METADATA_FILTER_WIRE_VERSION_V1,
actual: wire.version(),
});
}
wire.into_filter(receiver_filter_limits)
.map_err(crate::MetadataWireDecodeError::Filter)
}
#[cfg(feature = "json")]
pub fn to_json_vec(&self) -> Result<Vec<u8>, crate::MetadataWireEncodeError> {
self.to_json_vec_with_limits(crate::metadata_limits::default_json_encode_limits())
}
#[cfg(feature = "json")]
pub fn to_json_vec_with_limits(&self, limits: JsonEncodeLimits) -> Result<Vec<u8>, crate::MetadataWireEncodeError> {
self.preflight_wire_values(limits)?;
let session = JsonEncodeSession::from_limits(limits);
JsonEncoder::new(session).to_vec(self).map_err(Into::into)
}
#[cfg(feature = "json")]
pub fn to_json_writer<W>(&self, writer: W) -> Result<(), crate::MetadataWireEncodeError>
where
W: Write,
{
self.to_json_writer_with_limits(writer, crate::metadata_limits::default_json_encode_limits())
}
#[cfg(feature = "json")]
pub fn to_json_writer_with_limits<W>(
&self,
writer: W,
limits: JsonEncodeLimits,
) -> Result<(), crate::MetadataWireEncodeError>
where
W: Write,
{
self.preflight_wire_values(limits)?;
let session = JsonEncodeSession::from_limits(limits);
JsonEncoder::new(session)
.write_buffered(writer, self)
.map_err(Into::into)
}
#[inline]
pub(crate) const fn new(expression: FilterExpression, options: FilterMatchOptions, limits: FilterLimits) -> Self {
Self {
expression,
options,
limits: Transient::new(limits),
}
}
#[inline]
#[must_use = "the filter expression should be inspected"]
pub const fn expression(&self) -> &FilterExpression {
&self.expression
}
#[inline]
#[must_use]
pub const fn options(&self) -> FilterMatchOptions {
self.options
}
#[inline]
#[must_use = "the filter limits should be inspected"]
pub const fn limits(&self) -> FilterLimits {
*self.limits.get()
}
#[cfg(feature = "json")]
fn preflight_wire_values(&self, limits: JsonEncodeLimits) -> Result<(), crate::MetadataWireEncodeError> {
fn visit(
expression: &FilterExpression,
checker: &mut ValueWireEncodePreflight,
) -> Result<(), crate::MetadataWireEncodeError> {
match expression.view() {
FilterExpressionView::Condition(condition) => condition.visit_operands(&mut |value| {
checker.check_value(value).map_err(crate::MetadataWireEncodeError::from)
}),
FilterExpressionView::And(children) | FilterExpressionView::Or(children) => {
for child in children {
visit(child, checker)?;
}
Ok(())
}
FilterExpressionView::Not(inner) => visit(inner, checker),
FilterExpressionView::True | FilterExpressionView::False => Ok(()),
}
}
let mut checker = ValueWireEncodePreflight::new(limits);
visit(&self.expression, &mut checker)
}
#[inline]
#[must_use]
pub fn matches(&self, metadata: &Metadata) -> bool {
self.expression.evaluate(metadata, self.options).is_match()
}
#[cfg(feature = "schema")]
#[inline]
pub(crate) fn visit_conditions<F>(&self, mut visitor: F) -> MetadataResult<()>
where
F: FnMut(&Condition) -> MetadataResult<()>,
{
self.expression.visit_conditions(&mut visitor)
}
}
impl Serialize for MetadataFilter {
#[inline]
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
MetadataFilterWireV1Ref::try_from(self)
.map_err(<S::Error as SerError>::custom)?
.serialize(serializer)
}
}
impl<'de> Deserialize<'de> for MetadataFilter {
#[inline]
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
Self::deserialize_with_filter_limits(deserializer, FilterLimits::MAX)
}
}