fieldmasker 0.0.1

A utility for selecting and filtering response fields via field masks.
Documentation
use crate::spec::{Kind, Node};
use crate::{FieldMask, FieldMaskError};

pub(crate) fn validate_mask(mask: &FieldMask, spec: &Node) -> Result<(), FieldMaskError> {
    if mask.is_all() {
        return Ok(());
    }
    for p in &mask.paths {
        validate_one(p, spec, p.join("."))?;
    }
    Ok(())
}

pub(crate) fn validate_one(
    path: &[String],
    spec: &Node,
    full: String,
) -> Result<(), FieldMaskError> {
    if path.is_empty() {
        return Ok(());
    }
    match &spec.kind {
        Kind::Object(map) => {
            let key = &path[0];
            if key == "*" {
                return Err(FieldMaskError::UnknownPath(full));
            }
            let Some(child) = map.get(key) else {
                return Err(FieldMaskError::UnknownPath(full));
            };
            validate_one(&path[1..], child, full)
        }
        Kind::Array(elem) | Kind::Map(elem) => {
            // Either "array.field" (do NOT consume a segment) or "array.*.field" (consume the '*')
            if path[0] == "*" {
                validate_one(&path[1..], elem, full)
            } else {
                validate_one(path, elem, full)
            }
        }
        Kind::Scalar => {
            if path.len() > 1 {
                Err(FieldMaskError::UnknownPath(full))
            } else {
                Ok(())
            }
        }
    }
}