Require every choice variant to carry a path, and list only what Fake accepts

This commit is contained in:
lilleman
2026-08-27 23:07:03 +02:00
committed by lilleman-tw
parent 7d5d03e393
commit af5b8c9abd
3 changed files with 90 additions and 35 deletions
+21 -4
View File
@@ -28,9 +28,11 @@ func (f *Fakes) Fake(path string) (string, error) {
return render(f.rand, n), nil
}
// descend walks named fields, resolving choices it meets along the way. It is
// the one render-side step that can fail, because the path comes from the
// caller and may name a field that does not exist.
// descend walks named fields to the node a path names. It is the one render-side
// step that can fail, because the path comes from the caller and may name a field
// that does not exist. A choice consumes no segment, so every variant must carry
// the rest of the path before one is picked at random — a path that resolves at
// all resolves on every call. A nil session validates without picking.
func descend(s *session, n node, segments []string) (node, error) {
if len(segments) == 0 {
return n, nil
@@ -49,7 +51,22 @@ func descend(s *session, n node, segments []string) (node, error) {
}
return descend(s, child, segments[1:])
case *choice:
return descend(s, pick(s, n), segments) // a choice consumes no path segment
for i, item := range n.items {
_, err := descend(nil, item, segments)
if err == nil {
continue
}
if len(n.items) == 1 { // a single-variant choice is a transparent wrapper
return nil, err
}
return nil, fmt.Errorf("variant %d of a %d-way choice: %w", i+1, len(n.items), err)
}
if s == nil {
return n, nil
}
return descend(s, pick(s, n), segments)
case literal:
return nil, fmt.Errorf("a plain string has no field %q", segments[0])
default:
return nil, fmt.Errorf("cannot descend into %T at %q", n, segments[0])
}