fieldmasker 0.0.1

A utility for selecting and filtering response fields via field masks.
Documentation
use crate::ser::selective::Selective;
use crate::spec::MaskSpec;
use crate::trie::MaskTrie;
use crate::validate::validate_mask;
use crate::{FieldMask, FieldMaskError};
use serde::ser::Error;
use serde::{Serialize, Serializer};

/// A wrapper that applies a [`FieldMask`] when serializing a value.
///
/// `Masked<T>` implements `Serialize` (when `T: Serialize + MaskSpec`) and will
/// selectively serialize only the fields permitted by the mask.
///
/// # Examples
/// ```
/// # use serde::Serialize;
/// # use fieldmasker::{FieldMask, Masked, MaskSpec, spec::Node};
/// #[derive(Serialize, MaskSpec)]
/// struct Item { id: u32, title: String, body: String }
///
/// let item = Item { id: 1, title: "t".into(), body: "b".into() };
/// let mask = FieldMask::parse("id,title").unwrap();
/// let json = serde_json::to_string(&Masked::new(item, mask)).unwrap();
/// assert!(json.contains("id"));
/// assert!(json.contains("title"));
/// assert!(!json.contains("body"));
/// ```
#[derive(Clone, Debug)]
pub struct Masked<T> {
    value: T,
    mask: FieldMask,
}

impl<T> Masked<T> {
    /// Creates a new masked wrapper for `value` with the given `mask`.
    pub fn new(value: T, mask: FieldMask) -> Self {
        Self { value, mask }
    }

    /// Consumes the wrapper and returns the inner value.
    pub fn into_inner(self) -> T {
        self.value
    }

    /// Returns a reference to the associated mask.
    pub fn mask(&self) -> &FieldMask {
        &self.mask
    }

    /// Convenience method: delegates to [`FieldMask::contains_exact`] for `T`.
    pub fn contains_exact(&self, path: &str) -> Result<bool, FieldMaskError>
    where
        T: MaskSpec,
    {
        self.mask.contains_exact::<T>(path)
    }

    /// Convenience method: delegates to [`FieldMask::intersects`] for `T`.
    pub fn intersects(&self, path: &str) -> Result<bool, FieldMaskError>
    where
        T: MaskSpec,
    {
        self.mask.intersects::<T>(path)
    }
}

impl<T> Serialize for Masked<T>
where
    T: Serialize + MaskSpec,
{
    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
        if self.mask.is_all() || self.mask.is_empty() {
            return self.value.serialize(serializer);
        }
        validate_mask(&self.mask, T::mask_spec()).map_err(|e| Error::custom(e.to_string()))?;
        let trie = MaskTrie::new(&self.mask);
        let gate = Selective {
            ser: serializer,
            trie: &trie,
            spec: T::mask_spec(),
            include_all: trie.terminal,
        };
        self.value
            .serialize(gate)
            .map_err(|e| Error::custom(e.to_string()))
    }
}