-
Notifications
You must be signed in to change notification settings - Fork 17
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
How to serialize as a commented out line? #11
Comments
This seems tricky, since I don't believe serde has any built-in concept of comments when serializing, and doesn't have a good way of interweaving metadata like a "this value is unchanged from default" flag to cross abstraction boundaries. It's not pretty but your best bet might be something like: use std::collections::HashMap;
trait DefaultValue<T: PartialEq> {
const VALUE: T;
const NAME: &'static str;
}
fn serialize_or_default<D: DefaultValue<T>, T: PartialEq + std::string::ToString, S: serde::Serializer>(value: &T, mut s: S) -> Result<S::Ok, S::Error> {
let name = if value == &D::VALUE {
format!(";{}", D::NAME)
} else {
D::NAME.to_owned()
};
serde::Serialize::serialize(
&std::iter::once((name, value.to_string())).collect::<HashMap<_, _>>(),
s
)
}
struct DefaultSetting; impl DefaultValue<u32> for DefaultSetting { const NAME: &'static str = "setting"; const VALUE: u32 = 100; }
#[derive(Deserialize, Serialize, Debug)]
struct Data {
#[serde(flatten, serialize_with = "serialize_or_default::<DefaultSetting, _, _>")]
setting: u32,
}
assert_eq!(serde_ini::to_string(&Data {
setting: 100
}).unwrap(), ";setting=100\r\n");
assert_eq!(serde_ini::to_string(&Data {
setting: 99
}).unwrap(), "setting=99\r\n"); Not very optimal because |
Very interesting approach, thanks @arcnmx! It seems to be working good when serializing. Yet, when deserializing, the Unwrapping creates a "can only flatten structs and maps" panic when adding an I have not yet fully grasped struct flattening in serde, but from quickly skimming through the docs, it seems to me as if I'd need some custom |
Oh, sorry, I assumed flatten was a serialization-only attribute... You'd maybe just be better off using #[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
#[serde(into = "DataWithDefaults")]
struct Data {
setting: u32,
}
impl Default for Data {
fn default() -> Self {
Data {
setting: 100,
}
}
}
#[derive(Serialize, Deserialize)]
#[serde(transparent)]
struct DataWithDefaults(HashMap<String, String>);
fn convert(d: &Data) -> HashMap<&'static str, String> {
// this could possibly be done lazy/messy using serde::de::IntoDeserializer or even like: serde_ini::from_str(&serde_ini::to_string(&d).unwrap()).unwrap()
// consider an ordered map if that's important?
std::iter::once(
("setting", d.setting.to_string())
).collect()
}
impl From<Data> for DataWithDefaults {
fn from(d: Data) -> Self {
let d = convert(&d);
let defaults = convert(&Data::default());
DataWithDefaults(
d.into_iter()
.map(|(k, v)| (
if defaults.get(&k) == Some(&v) { format!(";{}", k) } else { k.into() },
v
)).collect()
)
}
}
assert_eq!(serde_ini::to_string(&Data {
setting: 100
}).unwrap(), ";setting=100\r\n");
assert_eq!(serde_ini::to_string(&Data {
setting: 99
}).unwrap(), "setting=99\r\n");
assert_eq!(serde_ini::from_str::<Data>("setting=100\r\n").unwrap(), Data {
setting: 100
}); Part of the issue really just is that serde isn't very good at formatting data for human consumption. This works but is hacky, and if you want to do things like start adding like docs/comments to the file it gets messier. Making let mut writer = serde_ini::Writer::new(...);
for item in serde_ini::Parser::from_str(&serde_ini::to_string(data)?) {
// match on item, check for keys with defaults, insert comments, etc. here
writer.write(item.expect("we generated invalid ini?"))?
}) A generic |
Hi @arcnmx , thanks again for your kind help and all the example code! Actually, I went for the [
("setting1", d.setting1.to_string()),
("setting2", d.setting2.to_string()),
// ...
("settingn", d.settingn.to_string()),
]
.iter()
.cloned()
.collect() block at the end, as I was not able to turn your idea to use I admit that having a more powerful/generic Thus I'm closing this issue. |
Hi,
I'm still pretty new to serde's concepts, so it might be that there is a very easy solution to my issue.
I want to be able to serialize some lines in my ini files as comments, for example if they contain a default value.
I.e. I might have some
setting=100
which should be serialized as;setting=100
, if100
happens to be the default value for this setting. These "commented out" default values are pretty common in configuration files as a form of self-documentation.I see that the parser already has a
Item::Comment
variant that the writer seems to be able to serialize, but I can't find anyserialize_comment
(or similar) method anywhere and the writer's API is not public.Any hints how to achieve my goal would bei highly appreciated. I don't mind if it involves a custom
serialize_with
attribute and function or a manuallyimpl Serialize/r for T
.The text was updated successfully, but these errors were encountered: