Move List's path enumeration into list.go, position into node.go and sortedNames into sorted.go
This commit is contained in:
-10
@@ -20,16 +20,6 @@ const (
|
||||
DataTypeBoolean DataType = datatype.Boolean
|
||||
)
|
||||
|
||||
// position is where a JSON value sits, which decides whether it may carry a datatype or
|
||||
// be null.
|
||||
type position int
|
||||
|
||||
const (
|
||||
inFormat position = iota // rendered by a format, so neither
|
||||
atTop // a category or an inline template, whose fields may be columns
|
||||
inColumn // a column, or a choice item standing in for one
|
||||
)
|
||||
|
||||
// datatypeOf reads a template's "datatype" (default DataTypeString).
|
||||
func datatypeOf(m map[string]any, pos position) (DataType, error) {
|
||||
v, ok := m["datatype"]
|
||||
|
||||
-98
@@ -12,7 +12,6 @@ import (
|
||||
|
||||
"github.com/larvit/fejkdata/internal/datafiles"
|
||||
"github.com/larvit/fejkdata/internal/drawstate"
|
||||
"github.com/larvit/fejkdata/internal/invariant"
|
||||
)
|
||||
|
||||
// MaxRepeat caps a repeat, and caps the renders nested repeats multiply to along
|
||||
@@ -122,103 +121,6 @@ func (f *Generator) List() []string {
|
||||
return out
|
||||
}
|
||||
|
||||
// paths lists the dot paths addressable from n, relative to it, where "" is n
|
||||
// itself. A folder has no value of its own, so it contributes only its children's.
|
||||
// With intoRepeats, it also lists the paths below a level carrying a repeat, so a path
|
||||
// there meets the repeat's refusal.
|
||||
func paths(n node, intoRepeats bool) []string {
|
||||
switch n := n.(type) {
|
||||
case *folder:
|
||||
var out []string
|
||||
for _, name := range sortedNames(n.children) {
|
||||
out = appendUnder(out, name, paths(n.children[name], intoRepeats))
|
||||
}
|
||||
for _, name := range sortedNames(n.unloaded) {
|
||||
out = appendUnder(out, name, n.unloaded[name].paths)
|
||||
}
|
||||
return out
|
||||
case *template:
|
||||
out := []string{""}
|
||||
if n.repeat > 1 && !intoRepeats {
|
||||
return out
|
||||
}
|
||||
for _, name := range sortedNames(n.fields) {
|
||||
out = appendUnder(out, name, paths(n.fields[name], intoRepeats))
|
||||
}
|
||||
return out
|
||||
case *table:
|
||||
return tablePaths(n, intoRepeats)
|
||||
case *tableColumn, *tableRow, *nullItem:
|
||||
return []string{""}
|
||||
case *choice:
|
||||
shared := n.shared
|
||||
if !intoRepeats {
|
||||
shared = sharedPaths(n.items, false)
|
||||
}
|
||||
return append([]string{""}, sortedNames(shared)...)
|
||||
default:
|
||||
panic(invariant.Broken("paths has no case for node %T", n))
|
||||
}
|
||||
}
|
||||
|
||||
func appendUnder(out []string, name string, ps []string) []string {
|
||||
for _, p := range ps {
|
||||
out = append(out, join(name, p))
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// tablePaths is a table's columns, then each table linked to it under its name: the
|
||||
// direct descents, a step at a time.
|
||||
func tablePaths(t *table, intoRepeats bool) []string {
|
||||
out := append([]string{""}, t.rows.Header()...)
|
||||
sort.Strings(out[1:])
|
||||
for _, c := range t.rows.Children() {
|
||||
out = appendUnder(out, c.Segment(), paths(c.Owner(), intoRepeats))
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// sharedPaths is the sub-paths every item carries — the only ones a path may step
|
||||
// through a choice to reach. It intersects, bailing as soon as the set
|
||||
// is empty, which is immediate for a choice of plain strings.
|
||||
func sharedPaths(items []node, intoRepeats bool) map[string]bool {
|
||||
shared := subPaths(items[0], intoRepeats)
|
||||
for _, it := range items[1:] {
|
||||
if len(shared) == 0 {
|
||||
return nil
|
||||
}
|
||||
next := subPaths(it, intoRepeats)
|
||||
for p := range shared {
|
||||
if !next[p] {
|
||||
delete(shared, p)
|
||||
}
|
||||
}
|
||||
}
|
||||
return shared
|
||||
}
|
||||
|
||||
// subPaths is paths(n, intoRepeats) as a set, without the empty path that means n itself.
|
||||
func subPaths(n node, intoRepeats bool) map[string]bool {
|
||||
out := map[string]bool{}
|
||||
for _, p := range paths(n, intoRepeats) {
|
||||
if p != "" {
|
||||
out[p] = true
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func join(prefix, name string) string {
|
||||
switch {
|
||||
case prefix == "":
|
||||
return name
|
||||
case name == "":
|
||||
return prefix
|
||||
}
|
||||
return prefix + "." + name
|
||||
}
|
||||
|
||||
var randomBytes = crand.Read
|
||||
|
||||
// drawSeed is the seed WithSeed gave, or one read from the system's entropy.
|
||||
|
||||
@@ -3,7 +3,6 @@ package fejkdata
|
||||
import (
|
||||
"fmt"
|
||||
"slices"
|
||||
"sort"
|
||||
|
||||
"github.com/larvit/fejkdata/internal/invariant"
|
||||
)
|
||||
@@ -88,15 +87,6 @@ func namedNodes(m map[string]node) []namedNode {
|
||||
return out
|
||||
}
|
||||
|
||||
func sortedNames[V any](m map[string]V) []string {
|
||||
names := make([]string, 0, len(m))
|
||||
for name := range m {
|
||||
names = append(names, name)
|
||||
}
|
||||
sort.Strings(names)
|
||||
return names
|
||||
}
|
||||
|
||||
// renderEdge is a child a node renders into, labelled by what reaches it (a field
|
||||
// name, reference, or choice index) for a readable cycle report.
|
||||
type renderEdge struct {
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
package fejkdata
|
||||
|
||||
import (
|
||||
"sort"
|
||||
|
||||
"github.com/larvit/fejkdata/internal/invariant"
|
||||
)
|
||||
|
||||
// paths lists the dot paths addressable from n, relative to it, where "" is n
|
||||
// itself. A folder has no value of its own, so it contributes only its children's.
|
||||
// With intoRepeats, it also lists the paths below a level carrying a repeat, so a path
|
||||
// there meets the repeat's refusal.
|
||||
func paths(n node, intoRepeats bool) []string {
|
||||
switch n := n.(type) {
|
||||
case *folder:
|
||||
var out []string
|
||||
for _, name := range sortedNames(n.children) {
|
||||
out = appendUnder(out, name, paths(n.children[name], intoRepeats))
|
||||
}
|
||||
for _, name := range sortedNames(n.unloaded) {
|
||||
out = appendUnder(out, name, n.unloaded[name].paths)
|
||||
}
|
||||
return out
|
||||
case *template:
|
||||
out := []string{""}
|
||||
if n.repeat > 1 && !intoRepeats {
|
||||
return out
|
||||
}
|
||||
for _, name := range sortedNames(n.fields) {
|
||||
out = appendUnder(out, name, paths(n.fields[name], intoRepeats))
|
||||
}
|
||||
return out
|
||||
case *table:
|
||||
return tablePaths(n, intoRepeats)
|
||||
case *tableColumn, *tableRow, *nullItem:
|
||||
return []string{""}
|
||||
case *choice:
|
||||
shared := n.shared
|
||||
if !intoRepeats {
|
||||
shared = sharedPaths(n.items, false)
|
||||
}
|
||||
return append([]string{""}, sortedNames(shared)...)
|
||||
default:
|
||||
panic(invariant.Broken("paths has no case for node %T", n))
|
||||
}
|
||||
}
|
||||
|
||||
func appendUnder(out []string, name string, ps []string) []string {
|
||||
for _, p := range ps {
|
||||
out = append(out, join(name, p))
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// tablePaths is a table's columns, then each table linked to it under its name: the
|
||||
// direct descents, a step at a time.
|
||||
func tablePaths(t *table, intoRepeats bool) []string {
|
||||
out := append([]string{""}, t.rows.Header()...)
|
||||
sort.Strings(out[1:])
|
||||
for _, c := range t.rows.Children() {
|
||||
out = appendUnder(out, c.Segment(), paths(c.Owner(), intoRepeats))
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// sharedPaths is the sub-paths every item carries — the only ones a path may step
|
||||
// through a choice to reach. It intersects, bailing as soon as the set
|
||||
// is empty, which is immediate for a choice of plain strings.
|
||||
func sharedPaths(items []node, intoRepeats bool) map[string]bool {
|
||||
shared := subPaths(items[0], intoRepeats)
|
||||
for _, it := range items[1:] {
|
||||
if len(shared) == 0 {
|
||||
return nil
|
||||
}
|
||||
next := subPaths(it, intoRepeats)
|
||||
for p := range shared {
|
||||
if !next[p] {
|
||||
delete(shared, p)
|
||||
}
|
||||
}
|
||||
}
|
||||
return shared
|
||||
}
|
||||
|
||||
// subPaths is paths(n, intoRepeats) as a set, without the empty path that means n itself.
|
||||
func subPaths(n node, intoRepeats bool) map[string]bool {
|
||||
out := map[string]bool{}
|
||||
for _, p := range paths(n, intoRepeats) {
|
||||
if p != "" {
|
||||
out[p] = true
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func join(prefix, name string) string {
|
||||
switch {
|
||||
case prefix == "":
|
||||
return name
|
||||
case name == "":
|
||||
return prefix
|
||||
}
|
||||
return prefix + "." + name
|
||||
}
|
||||
@@ -163,6 +163,16 @@ func rowShape(raw any) (format string, fields []string, weighted, isRow bool) {
|
||||
return format, fields, weighted, true
|
||||
}
|
||||
|
||||
// position is where a JSON value sits, which decides whether it may carry a datatype or
|
||||
// be null.
|
||||
type position int
|
||||
|
||||
const (
|
||||
inFormat position = iota // rendered by a format, so neither
|
||||
atTop // a category or an inline template, whose fields may be columns
|
||||
inColumn // a column, or a choice item standing in for one
|
||||
)
|
||||
|
||||
// compileAt compiles a node that is no choice's item. Only a choice's items carry a
|
||||
// weight, so one here would be inert whatever its type.
|
||||
func compileAt(v any, pos position) (node, error) {
|
||||
|
||||
Reference in New Issue
Block a user