serde-rs / serde

Serialization framework for Rust
https://serde.rs/
Apache License 2.0
8.81k stars 747 forks source link

Make it easier to implement `deny_unknown_fields` for flattened structs #2749

Open palant opened 1 month ago

palant commented 1 month ago

I’ve written a (somewhat specialized) custom Deserialize implementation that would support both deny_unknown_fields and flattened structs at the same time. Reason: not having deny_unknown_fields is a major PITA for configuration files, typos happen. At the same time, modularizing code requires flattening configuration parts that different modules are responsible for.

The main problem with my implementation: some hacks are only necessary because Serde APIs make it so hard to implement something like this. Essentially, there are two issues:

  1. AFAICT the only way of accessing the fields handled by a Deserialize implementation is calling it with a fake deserializer that will error out on every call while also recording the fields. Note: I know that an implementation doesn’t have to be truthful here, but that’s good enough for me.
  2. A Deserialize implementation has no way of exposing a dynamic list of fields because the API takes only static slices. From what I can tell, static slices are an implementation detail of the automatically derived implementation and aren’t actually required for things to work correctly.

It seems that both issues can be solved by adding an additional method to the Deserialize trait, something like:

/// Returns `true` if all fields will be handled, otherwise adds a list of fields to the vector.
fn get_supported_fields(list: &mut Vec<&'static str>) -> bool;

Not sure whether this kind of hack is desirable but a default implementation of this method can be provided to keep things backwards compatible. Calling deserialize() with a fake deserializer will show whether deserialize_map() is called (all fields handled), deserialize_struct() (specific fields) or something else. New code can provide a custom implementation for this method, e.g. one that will combine the fields from multiple member structs.