The PartialDiscovery object owns two data fields that are
lazily evaluated: the set of undecided revisions, and the
children cache. That laziness is known to be essential for
performance.
In the previous version, we were using Option<T>, which led
us to methods such as ensure_undecided() followed by calls to
self.undecided.as_ref().unwrap(), as it was the simplest way
to avoid reference sharing problems, but that wasn't
satisfying. Human readers knew that panicking was indeed
impossible, but that wasn't enforced by the compiler.
We had something similar yet less pervasive with the early
release of target_heads.
The reviewer, Kevin Cox, then suggested to use a code pattern
known as typestate: different types to represent the successive
stages, with the second one always having an undecided set.
This is what we are doing here. Now we have two state types:
OnlyCommon and WithUndecided.
Only the first has the target_heads member; only the second
has the undecided member.
It makes the code a bit longer, because we have to make
PartialDiscovery a wrapper enum for identical consumption
within hg-cpython and reexpose the public interface.
But it makes the inner code more focused, clearer and
better checked by the compiler. A few further simplifications
will be made in following changesets thanks to this.
A key point that we didn't know how to solve
in the first version was the in-place mutation of that wrapper
enum, provided by the mutate_undecided() method.
This is partial because we don't address the children cache.
We'll do that in a follow-up also.
Also some methods and doc-comments have been kept on the inner
structs for readability of this changesets, but they will
be factorized on the wrapper enum in a next move