use proc_macro2::{Ident, TokenStream};
use quote::quote;
use prost_protovalidate_types::FieldMaskRules;
pub(crate) fn generate(
rules: &FieldMaskRules,
field_ident: &Ident,
proto_name: &str,
) -> Vec<TokenStream> {
let mut checks = Vec::new();
if let Some(ref expected) = rules.r#const {
let expected_paths = &expected.paths;
checks.push(quote! {
if let ::core::option::Option::Some(ref _fm) = self.#field_ident {
let _expected: &[&str] = &[#(#expected_paths),*];
if _fm.paths.len() != _expected.len()
|| !_fm.paths.iter().zip(_expected.iter()).all(|(a, b)| a == b)
{
violations.push(::prost_protovalidate::Violation::new(
#proto_name, "field_mask.const", "must equal paths",
));
}
}
});
}
if !rules.r#in.is_empty() {
let allowed = &rules.r#in;
checks.push(quote! {
if let ::core::option::Option::Some(ref _fm) = self.#field_ident {
let _allowed: &[&str] = &[#(#allowed),*];
if !_fm.paths.iter().all(|_p| {
_allowed.iter().any(|_a| {
_p == _a || _p.starts_with(&::std::format!("{_a}."))
})
}) {
violations.push(::prost_protovalidate::Violation::new(
#proto_name, "field_mask.in", "must only contain allowed paths",
));
}
}
});
}
if !rules.not_in.is_empty() {
let blocked = &rules.not_in;
checks.push(quote! {
if let ::core::option::Option::Some(ref _fm) = self.#field_ident {
let _blocked: &[&str] = &[#(#blocked),*];
if _fm.paths.iter().any(|_p| {
_blocked.iter().any(|_b| {
_p == _b || _p.starts_with(&::std::format!("{_b}."))
})
}) {
violations.push(::prost_protovalidate::Violation::new(
#proto_name, "field_mask.not_in", "must not contain forbidden paths",
));
}
}
});
}
checks
}