Skip to content

Decide and implement policy on Option in config

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).