feat(harmony_macros): add yaml macro to validate YAML input

- Introduced a new `yaml` macro in `harmony_macros` that validates if the provided YAML string is valid by attempting to deserialize it using `serde_yaml`.
- Added dependencies on `serde`, `serde_yaml` for handling YAML deserialization.
- Included dev dependencies with features enabled for deriving types from `serde`.
This commit is contained in:
2025-01-22 10:21:08 -05:00
parent 2041ce63d7
commit d8c762e9df
5 changed files with 861 additions and 33 deletions

View File

@@ -9,4 +9,9 @@ proc-macro = true
[dependencies]
harmony_types = { path = "../harmony_types" }
quote = "1.0.37"
serde = "1.0.217"
serde_yaml = "0.9.34"
syn = "2.0.90"
[dev-dependencies]
serde = { version = "1.0.217", features = ["derive"] }

View File

@@ -2,7 +2,9 @@ extern crate proc_macro;
use proc_macro::TokenStream;
use quote::quote;
use syn::{LitStr, parse_macro_input};
use serde_yaml::Value;
use syn::LitStr;
use syn::parse_macro_input;
#[proc_macro]
pub fn ip(input: TokenStream) -> TokenStream {
@@ -76,3 +78,41 @@ fn parse_mac_address(mac: &str) -> Result<[u8; 6], String> {
Ok(bytes)
}
/// Verify that input is valid yaml by trying to deserialize it using
/// serde_yaml::from_str::<serde_yaml::value::Value>(input)
///
/// panics: If yaml is not valid
#[proc_macro]
pub fn yaml(input: TokenStream) -> TokenStream {
// TODO, accept a second argument that is the type to be deserialized to and validate at
// compile-time that deserialization is possible
//
// It does not seem to be doable with the way macros are designed : we may pass an ident to the
// macro, but the macro only has access to the ident, not the type itself.
//
// I also tried to create a restricted version of this macro, but this time serde got in the
// way : my use case is to make sure that the yaml I am given can deserialize strictly (with
// deny_unknown_attributes option on) to k8s-openapi types. But the k8s-openapi types are not
// annotated with deny_unknown_attributes and there does not seem to be a way to tell serde to
// deserialize in "strict mode" when calling the deserialization function itself. I tried
// wrapping the types in something like :
//
// ```rust
// #[derive(Deserialize, Debug)]
// #[serde(deny_unknown_fields)]
// struct Strict<T>(pub T);
// ```
//
// But it still does actually deserialize T strictly. I gave up for now at this point. Will
// find a solution some day!
let yaml = parse_macro_input!(input as LitStr);
serde_yaml::from_str::<Value>(yaml.value().as_str()).expect("Should be valid yaml");
quote! {
serde_yaml::from_str(#yaml)
}
.into()
}