praxis-proxy-core 0.4.0

Configuration, error types, and server factory for Praxis
Documentation
// SPDX-License-Identifier: MIT
// Copyright (c) 2024 Praxis Contributors

//! Condition predicates that gate filter execution.

mod request;
mod response;

pub use request::{Condition, ConditionMatch};
pub use response::{ResponseCondition, ResponseConditionMatch};

// -----------------------------------------------------------------------------
// Shared Deserialization Macro
// -----------------------------------------------------------------------------

/// Generates a `Deserialize` impl for a when/unless condition enum.
///
/// Both [`Condition`] and [`ResponseCondition`] follow the same
/// structure: a helper struct with optional `when` and `unless`
/// fields, matched into two variants with identical error arms.
///
/// # Arguments
///
/// * `$cond`   -- The condition enum type (e.g. `Condition`).
/// * `$match_` -- The inner match type (e.g. `ConditionMatch`).
/// * `$label`  -- Human-readable label for error messages (e.g. `"condition"`).
macro_rules! impl_condition_deserialize {
    ($cond:ident, $match_:ty, $label:expr) => {
        #[derive(serde::Deserialize)]
        #[serde(deny_unknown_fields)]
        struct ConditionDeserHelper {
            /// The `when` predicate, if present.
            #[serde(default)]
            when: Option<$match_>,

            /// The `unless` predicate, if present.
            #[serde(default)]
            unless: Option<$match_>,
        }

        impl<'de> serde::Deserialize<'de> for $cond {
            fn deserialize<D: serde::de::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
                let helper = ConditionDeserHelper::deserialize(deserializer)?;
                match (helper.when, helper.unless) {
                    (Some(m), None) => Ok($cond::When(m)),
                    (None, Some(m)) => Ok($cond::Unless(m)),
                    (Some(_), Some(_)) => Err(serde::de::Error::custom(concat!(
                        $label,
                        " must have exactly one of 'when' or 'unless', not both"
                    ))),
                    (None, None) => Err(serde::de::Error::custom(concat!(
                        $label,
                        " must have either 'when' or 'unless'"
                    ))),
                }
            }
        }
    };
}

pub(crate) use impl_condition_deserialize;

/// Generates a `Serialize` impl for a when/unless condition enum.
///
/// Produces a one-entry map (`when: <match>` or `unless: <match>`)
/// that round-trips through the custom [`impl_condition_deserialize`]
/// deserializer.
macro_rules! impl_condition_serialize {
    ($cond:ident, $match_:ty) => {
        impl serde::Serialize for $cond {
            fn serialize<S: serde::ser::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
                use serde::ser::SerializeMap as _;
                let mut map = serializer.serialize_map(Some(1))?;
                match self {
                    $cond::When(m) => map.serialize_entry("when", m)?,
                    $cond::Unless(m) => map.serialize_entry("unless", m)?,
                }
                map.end()
            }
        }
    };
}

pub(crate) use impl_condition_serialize;