mirror of
https://github.com/nushell/nushell.git
synced 2025-08-10 23:57:37 +02:00
Allow missing fields in derived FromValue::from_value
calls (#13206)
# Description In #13031 I added the derive macros for `FromValue` and `IntoValue`. In that implementation, in particular for structs with named fields, it was not possible to omit fields while loading them from a value, when the field is an `Option`. This PR adds extra handling for this behavior, so if a field is an `Option` and that field is missing in the `Value`, then the field becomes `None`. This behavior is also tested in `nu_protocol::value::test_derive::missing_options`. # User-Facing Changes When using structs for options or similar, users can now just emit fields in the record and the derive `from_value` method will be able to understand this, if the struct has an `Option` type for that field. # Tests + Formatting - 🟢 `toolkit fmt` - 🟢 `toolkit clippy` - 🟢 `toolkit test` - 🟢 `toolkit test stdlib` # After Submitting A showcase for this feature would be great, I tried to use the current derive macro in a plugin of mine for a config but without this addition, they are annoying to use. So, when this is done, I would add an example for such plugin configs that may be loaded via `FromValue`.
This commit is contained in:
@ -171,6 +171,62 @@ fn named_fields_struct_incorrect_type() {
|
||||
assert!(res.is_err());
|
||||
}
|
||||
|
||||
#[derive(IntoValue, FromValue, Debug, PartialEq, Default)]
|
||||
struct ALotOfOptions {
|
||||
required: bool,
|
||||
float: Option<f64>,
|
||||
int: Option<i64>,
|
||||
value: Option<Value>,
|
||||
nested: Option<Nestee>,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn missing_options() {
|
||||
let value = Value::test_record(Record::new());
|
||||
let res: Result<ALotOfOptions, _> = ALotOfOptions::from_value(value);
|
||||
assert!(res.is_err());
|
||||
|
||||
let value = Value::test_record(record! {"required" => Value::test_bool(true)});
|
||||
let expected = ALotOfOptions {
|
||||
required: true,
|
||||
..Default::default()
|
||||
};
|
||||
let actual = ALotOfOptions::from_value(value).unwrap();
|
||||
assert_eq!(expected, actual);
|
||||
|
||||
let value = Value::test_record(record! {
|
||||
"required" => Value::test_bool(true),
|
||||
"float" => Value::test_float(std::f64::consts::PI),
|
||||
});
|
||||
let expected = ALotOfOptions {
|
||||
required: true,
|
||||
float: Some(std::f64::consts::PI),
|
||||
..Default::default()
|
||||
};
|
||||
let actual = ALotOfOptions::from_value(value).unwrap();
|
||||
assert_eq!(expected, actual);
|
||||
|
||||
let value = Value::test_record(record! {
|
||||
"required" => Value::test_bool(true),
|
||||
"int" => Value::test_int(12),
|
||||
"nested" => Value::test_record(record! {
|
||||
"u32" => Value::test_int(34),
|
||||
}),
|
||||
});
|
||||
let expected = ALotOfOptions {
|
||||
required: true,
|
||||
int: Some(12),
|
||||
nested: Some(Nestee {
|
||||
u32: 34,
|
||||
some: None,
|
||||
none: None,
|
||||
}),
|
||||
..Default::default()
|
||||
};
|
||||
let actual = ALotOfOptions::from_value(value).unwrap();
|
||||
assert_eq!(expected, actual);
|
||||
}
|
||||
|
||||
#[derive(IntoValue, FromValue, Debug, PartialEq)]
|
||||
struct UnnamedFieldsStruct<T>(u32, String, T)
|
||||
where
|
||||
|
Reference in New Issue
Block a user