Simplify config API types and accessors, and config handling code

For each FooConfig:

  • Make FooConfigBuilder Serialize and Deserialize;
  • Make the validated configuration no longer Serialize and Deserialize
  • Make FooConfig the builder type;
  • Have the validated configuration be a hidden type that is created when passing the FooConfig to a constructor. Make FooConfig::validate pub, while having FooConfig::build be private. Sadly this is not going to be possible, since the validated type must be convyed through the various API layers so that the builders can work.

Improve cases where BigConfig contains a LittleConfig.

  • Automatically generate the code to convert and validate LittleConfig to LittleConfigValidated and put it into BigConfigValidated.little. (This is necessary for the above changes because BigConfigValidated.little must be LittleConfigValidated, but the builder must contain a LittleConfig not a LittleConfigValidated.)
  • Instead of providing a setter method on BigConfig that expects to consume a LittleConfig, provide an accessor method impl BigConfig { pub fn little(&mut self) -> &mut LittleConfig; }. [1]

This should considerably reduce the amount of code in our tree, although it requires new feature(s) in derive_builder.

[1] Rationale: this allows the caller to set a sub-field big.little.wombat by writing bit.little().wombat(Wombat) rather than making an entirely new LittleConfig. The latter is clumsy and also risks accidental overwrites if different parts of the calling code want to modify different fields of big.little. (If we move fields out of little, API could be preserved by making LittleConfig a newtype with appropriate handwritten accessors which redirect the field accessors.)

This is part of #285 (closed).

Edited by Ian Jackson