Replace locale dirs with a data-folder namespace model: New([]paths), folder dot-paths, last-wins merge

This commit is contained in:
lilleman
2026-06-08 23:52:37 +02:00
parent acf7571334
commit 8db65ac598
46 changed files with 423 additions and 278 deletions
+15 -12
View File
@@ -1,10 +1,12 @@
// Command fakes prints one fake value from a locale directory.
// Command fakes prints one fake value from one or more data directories.
//
// fakes ./locales/sv_SE person # a full person
// fakes ./locales/sv_SE person.last # just the surname (dotted path)
// fakes -seed 42 ./locales/sv_SE address
// fakes ./data/sv_SE person # a full person
// fakes ./data/sv_SE person.last # just the surname (dotted path)
// fakes ./data sv_SE.person # point at the tree, address by folder
// fakes ./data/sv_SE ./mydata person # layer custom data; last dir wins
// fakes -seed 42 ./data/sv_SE address
//
// It is a thin CLI over the fakes library: New(dir) then Fake(path).
// It is a thin CLI over the fakes library: New(dirs) then Fake(path).
package main
import (
@@ -16,11 +18,11 @@ import (
"github.com/Timewave-AB/fakes"
)
const usage = `Usage: fakes [-seed N] <locale-dir> <path>
const usage = `Usage: fakes [-seed N] <data-dir>... <path>
<locale-dir> a locale directory, e.g. ./locales/sv_SE
<path> a category, or a dotted path into one (person, person.last)
-seed N seed for reproducible output`
<data-dir> one or more data directories, e.g. ./data/sv_SE (last wins on clash)
<path> a category, or a dotted path into one (person, person.last)
-seed N seed for reproducible output`
func main() { os.Exit(run(os.Args[1:], os.Stdout, os.Stderr)) }
@@ -34,7 +36,7 @@ func run(args []string, stdout, stderr io.Writer) int {
if err := fs.Parse(args); err != nil {
return 2
}
if fs.NArg() != 2 {
if fs.NArg() < 2 {
fs.Usage()
return 2
}
@@ -46,12 +48,13 @@ func run(args []string, stdout, stderr io.Writer) int {
}
})
f, err := fakes.New(fs.Arg(0), opts...)
dirs, path := fs.Args()[:fs.NArg()-1], fs.Arg(fs.NArg()-1)
f, err := fakes.New(dirs, opts...)
if err != nil {
fmt.Fprintln(stderr, err)
return 1
}
v, err := f.Fake(fs.Arg(1))
v, err := f.Fake(path)
if err != nil {
fmt.Fprintln(stderr, err)
return 1
+20 -5
View File
@@ -6,7 +6,10 @@ import (
"testing"
)
const svSE = "../../locales/sv_SE"
const (
svSE = "../../data/sv_SE"
enUS = "../../data/en_US"
)
// runOut runs the CLI and returns exit code, stdout, stderr.
func runOut(args ...string) (int, string, string) {
@@ -54,8 +57,9 @@ func TestRunSeedDeterministic(t *testing.T) {
}
}
func TestRunUsageOnWrongArgCount(t *testing.T) {
for _, args := range [][]string{{}, {svSE}, {svSE, "person", "extra"}} {
func TestRunUsageOnTooFewArgs(t *testing.T) {
// Need at least one data dir plus a path; fewer is misuse.
for _, args := range [][]string{{}, {svSE}} {
code, _, errb := runOut(args...)
if code != 2 {
t.Errorf("run(%v) = %d, want 2", args, code)
@@ -66,6 +70,17 @@ func TestRunUsageOnWrongArgCount(t *testing.T) {
}
}
func TestRunMultipleDirs(t *testing.T) {
// Several data dirs then a final path: all but the last arg are dirs.
code, out, errb := runOut(enUS, svSE, "person")
if code != 0 {
t.Fatalf("run(multi-dir) = %d, stderr=%q", code, errb)
}
if strings.TrimSpace(out) == "" {
t.Fatal("empty output for multi-dir run")
}
}
func TestRunUnknownCategoryFails(t *testing.T) {
code, _, errb := runOut(svSE, "nope")
if code != 1 {
@@ -76,8 +91,8 @@ func TestRunUnknownCategoryFails(t *testing.T) {
}
}
func TestRunBadLocaleFails(t *testing.T) {
code, _, errb := runOut("../../locales/sv", "person")
func TestRunMissingDirFails(t *testing.T) {
code, _, errb := runOut("../../data/nope", "person")
if code != 1 {
t.Fatalf("run = %d, want 1", code)
}