Fallible Settings (#42938)

Also tidies up error notifications so that in the case of syntax errors
we don't see noise about the migration failing as well.

Release Notes:

- Invalid values in settings files will no longer prevent the rest of
the file from being parsed.
This commit is contained in:
Conrad Irwin
2025-11-21 08:28:17 -07:00
committed by GitHub
parent a8d7f06b47
commit 2ac13b9489
19 changed files with 509 additions and 494 deletions
+51 -1
View File
@@ -1,6 +1,9 @@
use proc_macro::TokenStream;
use quote::quote;
use syn::{Data, DeriveInput, Fields, parse_macro_input};
use syn::{
Data, DeriveInput, Field, Fields, ItemEnum, ItemStruct, Type, parse_macro_input, parse_quote,
};
/// Derives the `MergeFrom` trait for a struct.
///
@@ -100,3 +103,50 @@ pub fn derive_register_setting(input: TokenStream) -> TokenStream {
}
.into()
}
// Adds serde attributes to each field with type Option<T>:
// #serde(default, skip_serializing_if = "Option::is_none", deserialize_with = "settings::deserialize_fallible")
#[proc_macro_attribute]
pub fn with_fallible_options(_args: TokenStream, input: TokenStream) -> TokenStream {
fn apply_on_fields(fields: &mut Fields) {
match fields {
Fields::Unit => {}
Fields::Named(fields) => {
for field in &mut fields.named {
add_if_option(field)
}
}
Fields::Unnamed(fields) => {
for field in &mut fields.unnamed {
add_if_option(field)
}
}
}
}
fn add_if_option(field: &mut Field) {
match &field.ty {
Type::Path(syn::TypePath { qself: None, path })
if path.leading_colon.is_none()
&& path.segments.len() == 1
&& path.segments[0].ident == "Option" => {}
_ => return,
}
let attr = parse_quote!(
#[serde(default, skip_serializing_if = "Option::is_none", deserialize_with="crate::fallible_options::deserialize")]
);
field.attrs.push(attr);
}
if let Ok(mut input) = syn::parse::<ItemStruct>(input.clone()) {
apply_on_fields(&mut input.fields);
quote!(#input).into()
} else if let Ok(mut input) = syn::parse::<ItemEnum>(input) {
for variant in &mut input.variants {
apply_on_fields(&mut variant.fields);
}
quote!(#input).into()
} else {
panic!("with_fallible_options can only be applied to struct or enum definitions.");
}
}