Repository: sxyazi/yazi
File: yazi-codegen/src/lib.rs (Rust)

Code before:
use proc_macro::TokenStream;
use quote::quote;
use syn::{Data, DeriveInput, Fields, parse_macro_input};

#[proc_macro_derive(DeserializeOver)]
pub fn deserialize_over(input: TokenStream) -> TokenStream {
	let DeriveInput { ident, .. } = parse_macro_input!(input as DeriveInput);

	quote! {
		impl #ident {
			pub(crate) fn deserialize_over(self, input: &str) -> Result<Self, toml::de::Error> {
				crate::error_with_input(self.deserialize_over_with(crate::parse_recoverable(input)?), input)
			}
		}
	}
	.into()
}

#[proc_macro_derive(DeserializeOver1)]
pub fn deserialize_over1(input: TokenStream) -> TokenStream {
	let DeriveInput { ident, data, .. } = parse_macro_input!(input as DeriveInput);

	let assignments = match data {
		Data::Struct(struct_) => match struct_.fields {
			Fields::Named(fields) => {
				let mut assignments = Vec::with_capacity(fields.named.len());

				for field in fields.named {
					let field_ident = &field.ident;
					let field_name = field_ident.as_ref().unwrap().to_string();

					assignments.push(quote! {
						if let Some(value) = table.remove(#field_name) {
							let span = value.span();
							let table = match value.into_inner() {
								toml::de::DeValue::Table(table) => table,
								_ => return Err(serde::de::Error::custom(format!("expected top-level `{}` to be a TOML table", #field_name))),

Review comment (bug):
`crate::parse_recoverable(input)?` short-circuits before `error_with_input(...)` runs, so TOML *parse* errors won't have `set_input(Some(input))` applied. If the goal is to keep span + source context on parse failures, consider ensuring parse errors also go through `error_with_input` (or have `parse_recoverable` set the input on the returned `toml::de::Error`).
```suggestion
				crate::error_with_input(
					crate::parse_recoverable(input).and_then(|table| self.deserialize_over_with(table)),
					input,
				)
```

Code after:
use proc_macro::TokenStream;
use quote::quote;
use syn::{Data, DeriveInput, Fields, parse_macro_input};

#[proc_macro_derive(DeserializeOver)]
pub fn deserialize_over(input: TokenStream) -> TokenStream {
	let DeriveInput { ident, .. } = parse_macro_input!(input as DeriveInput);

	quote! {
		impl #ident {
			pub(crate) fn deserialize_over(self, input: &str) -> Result<Self, toml::de::Error> {
				crate::error_with_input(self.deserialize_over_with(toml::de::DeTable::parse(input)?), input)
			}
		}
	}
	.into()
}

#[proc_macro_derive(DeserializeOver1)]
pub fn deserialize_over1(input: TokenStream) -> TokenStream {
	let DeriveInput { ident, data, .. } = parse_macro_input!(input as DeriveInput);

	let assignments = match data {
		Data::Struct(struct_) => match struct_.fields {
			Fields::Named(fields) => {
				let mut assignments = Vec::with_capacity(fields.named.len());

				for field in fields.named {
					let field_ident = &field.ident;
					let field_name = field_ident.as_ref().unwrap().to_string();

					assignments.push(quote! {
						if let Some(value) = table.remove(#field_name) {
							if !matches!(value.get_ref(), toml::de::DeValue::Table(_)) {
								_ = toml::Table::deserialize(value.into_deserializer())?;
								return Err(serde::de::Error::custom(format!("expected top-level `{}` to be a TOML table", #field_name)));
							}