We have some config settings that can "do the thing", or "not do it". Examples I have encountered so far are:
proxy.socks_port, default 9150
proxy.dns_port, default is don't
logging.journald, default is don't
In these cases we support a sentinel value meaning "do not do this thing". For ports, 0; for journald, "". This is necessary because when parsing and deserializing TOML, there is no way to explicitly specify "None". So to override a nontrivial default it is necessary to set the value to something.
I think we are more or less coherent about this at least in these three cases, at the serde (and therefore TOML) layer, but maybe not correct. The Builder contains Option<Option<T>>. I think this deserializes at least from TOML the same way Option<T>, but probably we should change this to just Option<T>.
And, our public and internal Rust APIs are confused:
Should we use builer(setter(strip_option)), so that eg we have ProxyConfig::socks_port(port: u16)? (Some but not all of the above do that.) That expects the caller to pass 0 to mean None which is anomalous. I propose the answer "don't use strip_option.
In the validated version of the configuration, what should the type be, and how do we represent the "none" value? Should we use Option<NonZeruU16> for the port, say? (I say "no", because those NonZero types are a pain to work with.) ISTM that we should use Option<T> (eg, Option<u16>) but normalise it so that we never have Some(<sentinel>).
A question arises: is the sentinel value always going to be T::Default ?
This is part of #458 (closed). Noticed while pursuing "test that example config is exhaustive", part of #457 (closed).