docs, config and tools testing

This commit is contained in:
Arpad Ryszka 2025-12-30 16:52:10 +01:00
parent 60acf6e820
commit 6e3e3dd5aa
56 changed files with 1357 additions and 121 deletions

View File

@ -78,17 +78,12 @@ func unescapeConfig(s string) string {
return []rune{r}, false
},
func(escaped bool) []rune {
if escaped {
return []rune{'\\'}
}
return nil
},
nil,
),
)
w.Write([]byte(s))
w.Flush()
return b.String()
}

View File

@ -114,7 +114,7 @@ func TestConfig(t *testing.T) {
)
t.Run(
"discard in previous same doc",
"discard in previous and same doc",
testExec(
testCase{
impl: fm,
@ -126,12 +126,18 @@ func TestConfig(t *testing.T) {
),
)
// check the syntax for tests
// - white space ignored
// - comment line
// - comment at the end of an entry
// - invalid key
// check the code for tests
t.Run(
"discard multiple values",
testExec(
testCase{
impl: fm,
mergeConf: []string{"one=bar\none=baz\nsecond-var=baz", "one\nsecond-var=qux\nsecond-var"},
command: "foo",
},
"",
";",
),
)
t.Run("white space ignored", testExec(testCase{impl: fm, conf: "one = bar ", command: "foo"}, "", "bar;"))
t.Run(
@ -148,4 +154,77 @@ func TestConfig(t *testing.T) {
)
t.Run("invald key", testExec(testCase{impl: fm, conf: "one two = bar", command: "foo"}, "parse failed", ""))
t.Run(
"config file",
testExec(
testCase{
impl: fm,
mergeConfTyped: []Config{ConfigFile("./internal/tests/testconfig.ini")},
command: "foo",
},
"",
"42;",
),
)
t.Run(
"config file error",
testExec(
testCase{
impl: fm,
mergeConfTyped: []Config{ConfigFile("./internal/tests/testconfig-noexist.ini")},
command: "foo",
},
"file",
),
)
t.Run(
"config file error optional",
testExec(
testCase{
impl: fm,
mergeConfTyped: []Config{OptionalConfig(ConfigFile("./internal/tests/testconfig-noexist.ini"))},
command: "foo",
},
"",
";",
),
)
t.Run(
"config from option",
testExec(
testCase{
impl: fm,
mergeConfTyped: []Config{ConfigFromOption()},
command: "foo --second-var bar --config ./internal/tests/testconfig.ini",
},
"",
"42;bar",
),
)
t.Run(
"config from option shadowed",
testExec(
testCase{
impl: func(struct{ Config int }) {},
mergeConfTyped: []Config{ConfigFromOption()},
command: "foo --config ./internal/tests/testconfig.ini",
},
"option reserved",
),
)
t.Run(
"dangling backslash",
testExec(
testCase{
impl: fm,
conf: "one = foo\\",
},
"parse failed",
),
)
}

View File

@ -9,13 +9,13 @@ import "code.squareroundforest.org/arpio/docreflect"
func init() {
docreflect.Register("code.squareroundforest.org/arpio/wand/tools", "")
docreflect.Register("code.squareroundforest.org/arpio/wand/tools.Docreflect", "\nfunc(out, packageName, gopaths)")
docreflect.Register("code.squareroundforest.org/arpio/wand/tools.Exec", "\nfunc(o, stdin, args)")
docreflect.Register("code.squareroundforest.org/arpio/wand/tools.Exec", "\nfunc(o, stdin, stdout, args)")
docreflect.Register("code.squareroundforest.org/arpio/wand/tools.ExecOptions", "")
docreflect.Register("code.squareroundforest.org/arpio/wand/tools.ExecOptions.CacheDir", "")
docreflect.Register("code.squareroundforest.org/arpio/wand/tools.ExecOptions.ClearCache", "")
docreflect.Register("code.squareroundforest.org/arpio/wand/tools.ExecOptions.Import", "")
docreflect.Register("code.squareroundforest.org/arpio/wand/tools.ExecOptions.InlineImport", "")
docreflect.Register("code.squareroundforest.org/arpio/wand/tools.ExecOptions.NoCache", "")
docreflect.Register("code.squareroundforest.org/arpio/wand/tools.ExecOptions.ReplaceModule", "")
docreflect.Register("code.squareroundforest.org/arpio/wand/tools.Man", "\nfunc(out, o, commandDir)")
docreflect.Register("code.squareroundforest.org/arpio/wand/tools.ManOptions", "")
docreflect.Register("code.squareroundforest.org/arpio/wand/tools.ManOptions.DateString", "")
@ -25,11 +25,12 @@ func init() {
docreflect.Register("code.squareroundforest.org/arpio/wand/tools.MarkdownOptions.Level", "")
docreflect.Register("code.squareroundforest.org/arpio/wand/tools.commandReader", "\nfunc(in)")
docreflect.Register("code.squareroundforest.org/arpio/wand/tools.execCommandDir", "\nfunc(out, commandDir, env)")
docreflect.Register("code.squareroundforest.org/arpio/wand/tools.execInput", "\nfunc(o, stdin, stdout, stderr, args)")
docreflect.Register("code.squareroundforest.org/arpio/wand/tools.execInternal", "\nfunc(command, args)")
docreflect.Register("code.squareroundforest.org/arpio/wand/tools.execTransparent", "\nfunc(command, args)")
docreflect.Register("code.squareroundforest.org/arpio/wand/tools.execWand", "\nfunc(o, args)")
docreflect.Register("code.squareroundforest.org/arpio/wand/tools.execWand", "\nfunc(o, stdin, stdout, stderr, args)")
docreflect.Register("code.squareroundforest.org/arpio/wand/tools.execc", "\nfunc(stdin, stdout, stderr, command, args, env)")
docreflect.Register("code.squareroundforest.org/arpio/wand/tools.hash", "\nfunc(expression, imports, inlineImports)")
docreflect.Register("code.squareroundforest.org/arpio/wand/tools.printGoFile", "\nfunc(fn, expression, imports, inlineImports)")
docreflect.Register("code.squareroundforest.org/arpio/wand/tools.readExec", "\nfunc(o, stdin)")
docreflect.Register("code.squareroundforest.org/arpio/wand/tools.hash", "\nfunc(expression, imports)")
docreflect.Register("code.squareroundforest.org/arpio/wand/tools.printGoFile", "\nfunc(fn, expression, imports)")
docreflect.Register("code.squareroundforest.org/arpio/wand/tools.readExec", "\nfunc(o, stdin, stdout, stderr)")
docreflect.Register("code.squareroundforest.org/arpio/wand/tools.selfPkg", "")
}

View File

@ -18,5 +18,6 @@ func init() {
docreflect.Register("code.squareroundforest.org/arpio/wand/internal/tests/testlib.Options.Bar", "Bars, any number.\n")
docreflect.Register("code.squareroundforest.org/arpio/wand/internal/tests/testlib.Options.Duration", "Duration is another option.\n")
docreflect.Register("code.squareroundforest.org/arpio/wand/internal/tests/testlib.Options.Foo", "Foo is an option.\n")
docreflect.Register("code.squareroundforest.org/arpio/wand/internal/tests/testlib.Options.Some", "Some is an option of any type.\n")
docreflect.Register("code.squareroundforest.org/arpio/wand/internal/tests/testlib.Options.Time", "Time is the third option here.\n")
}

View File

@ -4,13 +4,17 @@ import (
"errors"
"fmt"
"io"
"path/filepath"
"path"
"strconv"
"time"
)
func exec(stdin io.Reader, stdout, stderr io.Writer, exit func(int), cmd Cmd, conf Config, env, args []string) {
_, cmd.name = filepath.Split(args[0])
generate := getenv(env, "_wandgenerate")
if cmd.name == "" || generate == "" {
_, cmd.name = path.Split(args[0])
}
if err := validateCommand(cmd, conf); err != nil {
fmt.Fprintf(stderr, "program error: %v\n", err)
exit(1)

2
go.mod
View File

@ -8,7 +8,7 @@ require (
code.squareroundforest.org/arpio/notation v0.0.0-20251101123932-5f5c05ee0239
code.squareroundforest.org/arpio/textedit v0.0.0-20251207224821-c75c3965789f
code.squareroundforest.org/arpio/textfmt v0.0.0-20251207234108-fed32c8bbe18
code.squareroundforest.org/arpio/treerack v0.0.0-20251031193114-4f1c219052ae
code.squareroundforest.org/arpio/treerack v0.0.0-20251210201822-5e069c4ca4e7
github.com/iancoleman/strcase v0.3.0
golang.org/x/term v0.37.0
)

2
go.sum
View File

@ -12,6 +12,8 @@ code.squareroundforest.org/arpio/textfmt v0.0.0-20251207234108-fed32c8bbe18 h1:2
code.squareroundforest.org/arpio/textfmt v0.0.0-20251207234108-fed32c8bbe18/go.mod h1:+0G3gufMAP8SCEIrDT1D/DaVOSfjS8EwPTBs5vfxqQg=
code.squareroundforest.org/arpio/treerack v0.0.0-20251031193114-4f1c219052ae h1:D28IunhepRhRSp3U2z84e3WtxbYMRzi/FwEEZg54ULM=
code.squareroundforest.org/arpio/treerack v0.0.0-20251031193114-4f1c219052ae/go.mod h1:9XhPcVt1Y1M609z02lHvEcp00dwPD9NUCoVxS2TpcH8=
code.squareroundforest.org/arpio/treerack v0.0.0-20251210201822-5e069c4ca4e7 h1:5dXQ/w5nvo6k46BUvUNvpA9nnCvIOxPgVBJurIHnIYA=
code.squareroundforest.org/arpio/treerack v0.0.0-20251210201822-5e069c4ca4e7/go.mod h1:9XhPcVt1Y1M609z02lHvEcp00dwPD9NUCoVxS2TpcH8=
github.com/iancoleman/strcase v0.3.0 h1:nTXanmYxhfFAMjZL34Ov6gkzEsSJZ5DbhxWjvSASxEI=
github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ=

91
help.go
View File

@ -546,8 +546,8 @@ func docOptions(cmd Cmd, conf Config, titleLevel, wrapWidth, hintStyle int) []En
}
}
func docListSubcommands(cmd Cmd, fullCommand []string, wrapWidth int) []Entry {
subcommandsTitle := Title(1, "Subcommands:")
func docListSubcommands(cmd Cmd, fullCommand []string, level, wrapWidth int) []Entry {
subcommandsTitle := Title(level+1, "Subcommands:")
var (
items []DefinitionItem
@ -600,11 +600,11 @@ func docListSubcommands(cmd Cmd, fullCommand []string, wrapWidth int) []Entry {
}
}
func docFullSubcommands(cmd Cmd, conf Config) []Entry {
func docFullSubcommands(cmd Cmd, conf Config, level int) []Entry {
var e []Entry
e = append(
e,
Title(1, "Subcommands:"),
Title(level+1, "Subcommands:"),
Paragraph(Text(docSubcommandHint)),
)
@ -643,7 +643,7 @@ func docFullSubcommands(cmd Cmd, conf Config) []Entry {
command = fmt.Sprintf("%s (default)", command)
}
e = append(e, Indent(Title(2, command), 2, 0))
e = append(e, Indent(Title(level+2, command), 2, 0))
if sc.command.version != "" {
e = append(e, Indent(Paragraph(Text("Show version.")), 4, 0))
continue
@ -656,7 +656,7 @@ func docFullSubcommands(cmd Cmd, conf Config) []Entry {
e = append(e, synopsis...)
if docs := docs(sc.command); len(docs) > 0 {
e = append(e, Indent(Title(3, "Description:"), 4, 0))
e = append(e, Indent(Title(level+3, "Description:"), 4, 0))
for i := range docs {
docs[i] = Indent(docs[i], 4, 0)
}
@ -675,7 +675,7 @@ func docFullSubcommands(cmd Cmd, conf Config) []Entry {
return e
}
func docEnv(cmd Cmd) []Entry {
func docEnv(cmd Cmd, level int) []Entry {
// env will not work if the app has a non-standard name:
if !commandNameExpression.MatchString(cmd.name) {
return nil
@ -686,13 +686,13 @@ func docEnv(cmd Cmd) []Entry {
return nil
}
e := []Entry{Title(1, "Environment variables:")}
e := []Entry{Title(level+1, "Environment variables:")}
p := paragraphs(envDocs)
for _, pi := range p {
e = append(e, Indent(Paragraph(Text(pi)), 4, 0))
}
e = append(e, Indent(Title(2, "Example environment variable:"), 4, 0))
e = append(e, Indent(Title(level+2, "Example environment variable:"), 4, 0))
option := options[0]
name := strcase.ToScreamingSnake(fmt.Sprintf("%s-%s", cmd.name, option.Name()))
value := "42"
@ -704,7 +704,7 @@ func docEnv(cmd Cmd) []Entry {
return e
}
func docConfig(cmd Cmd, conf Config) []Entry {
func docConfig(cmd Cmd, conf Config, level int) []Entry {
options := allNonHelpOptions(cmd)
if len(options) == 0 {
return nil
@ -715,7 +715,7 @@ func docConfig(cmd Cmd, conf Config) []Entry {
return nil
}
e := []Entry{Title(1, "Configuration:")}
e := []Entry{Title(level+1, "Configuration:")}
p := paragraphs(configDocs)
for _, pi := range p {
e = append(e, Indent(Paragraph(Text(pi)), 4, 0))
@ -742,7 +742,7 @@ func docConfig(cmd Cmd, conf Config) []Entry {
e = append(
e,
Indent(Title(2, "Configuration files:"), 4, 0),
Indent(Title(level+2, "Configuration files:"), 4, 0),
Indent(List(items...), 8, 0),
)
@ -762,7 +762,7 @@ func docConfig(cmd Cmd, conf Config) []Entry {
e = append(
e,
Indent(Title(2, "Example configuration entry:"), 4, 0),
Indent(Title(level+2, "Example configuration entry:"), 4, 0),
Indent(CodeBlock(exampleCode), 4, 0),
)
@ -773,7 +773,7 @@ func docConfig(cmd Cmd, conf Config) []Entry {
e = append(
e,
Indent(Title(2, "Example for discarding an inherited entry:"), 4, 0),
Indent(Title(level+2, "Example for discarding an inherited entry:"), 4, 0),
Indent(CodeBlock(discardExample), 4, 0),
)
@ -802,10 +802,19 @@ func helpOptions(cmd Cmd, conf Config, width int) []Entry {
}
func helpSubcommands(cmd Cmd, fullCommand []string, width int) []Entry {
return docListSubcommands(cmd, fullCommand, width)
return docListSubcommands(cmd, fullCommand, 0, width)
}
func manTitle(cmd Cmd, date time.Time, version string) []Entry {
if version == "" {
for _, sc := range cmd.subcommands {
if sc.version != "" {
version = sc.version
break
}
}
}
return []Entry{
Title(
0,
@ -863,30 +872,30 @@ func manSubcommands(cmd Cmd, conf Config) []Entry {
}
if hasSubcommands {
return docFullSubcommands(cmd, conf)
return docFullSubcommands(cmd, conf, 0)
}
return docListSubcommands(cmd, []string{cmd.name}, 0)
return docListSubcommands(cmd, []string{cmd.name}, 0, 0)
}
func manEnv(cmd Cmd) []Entry {
return docEnv(cmd)
return docEnv(cmd, 0)
}
func manConfig(cmd Cmd, conf Config) []Entry {
return docConfig(cmd, conf)
return docConfig(cmd, conf, 0)
}
func markdownTitle(cmd Cmd) []Entry {
func markdownTitle(cmd Cmd, level int) []Entry {
txt := docCommandNameText(cmd, []string{cmd.name})
return []Entry{Title(0, strings.Join(txt, " "))}
return []Entry{Title(level, strings.Join(txt, " "))}
}
func markdownSynopsis(cmd Cmd) []Entry {
return docSynopsis(cmd, []string{cmd.name}, 1, markdownWrapWidth)
func markdownSynopsis(cmd Cmd, level int) []Entry {
return docSynopsis(cmd, []string{cmd.name}, level+1, markdownWrapWidth)
}
func markdownDocs(cmd Cmd) []Entry {
func markdownDocs(cmd Cmd, level int) []Entry {
paragraphs := docs(cmd)
if len(paragraphs) == 0 {
return nil
@ -896,11 +905,11 @@ func markdownDocs(cmd Cmd) []Entry {
paragraphs[i] = Wrap(paragraphs[i], markdownWrapWidth)
}
return append([]Entry{Title(1, "Description:")}, paragraphs...)
return append([]Entry{Title(level+1, "Description:")}, paragraphs...)
}
func markdownOptions(cmd Cmd, conf Config) []Entry {
e := docOptions(cmd, conf, 1, 0, allRelevantHints)
func markdownOptions(cmd Cmd, conf Config, level int) []Entry {
e := docOptions(cmd, conf, level+1, 0, allRelevantHints)
for i := 1; i < len(e); i++ {
e[i] = Wrap(e[i], markdownWrapWidth)
}
@ -908,7 +917,7 @@ func markdownOptions(cmd Cmd, conf Config) []Entry {
return e
}
func markdownSubcommands(cmd Cmd, conf Config) []Entry {
func markdownSubcommands(cmd Cmd, conf Config, level int) []Entry {
var hasSubcommands bool
for _, sc := range cmd.subcommands {
if sc.helpFor != nil || sc.version != "" {
@ -921,9 +930,9 @@ func markdownSubcommands(cmd Cmd, conf Config) []Entry {
var e []Entry
if hasSubcommands {
e = docFullSubcommands(cmd, conf)
e = docFullSubcommands(cmd, conf, level)
} else {
e = docListSubcommands(cmd, []string{cmd.name}, 0)
e = docListSubcommands(cmd, []string{cmd.name}, level, 0)
}
for i := range e {
@ -933,8 +942,8 @@ func markdownSubcommands(cmd Cmd, conf Config) []Entry {
return e
}
func markdownEnv(cmd Cmd) []Entry {
e := docEnv(cmd)
func markdownEnv(cmd Cmd, level int) []Entry {
e := docEnv(cmd, level)
for i := range e {
e[i] = Wrap(e[i], markdownWrapWidth)
}
@ -942,8 +951,8 @@ func markdownEnv(cmd Cmd) []Entry {
return e
}
func markdownConfig(cmd Cmd, conf Config) []Entry {
e := docConfig(cmd, conf)
func markdownConfig(cmd Cmd, conf Config, level int) []Entry {
e := docConfig(cmd, conf, level)
for i := range e {
e[i] = Wrap(e[i], markdownWrapWidth)
}
@ -977,13 +986,13 @@ func generateMan(out io.Writer, cmd Cmd, conf Config, date time.Time, version st
func generateMarkdown(out io.Writer, cmd Cmd, conf Config, level int) error {
var e []Entry
e = append(e, markdownTitle(cmd)...)
e = append(e, markdownSynopsis(cmd)...)
e = append(e, markdownDocs(cmd)...)
e = append(e, markdownOptions(cmd, conf)...)
e = append(e, markdownSubcommands(cmd, conf)...)
e = append(e, markdownEnv(cmd)...)
e = append(e, markdownConfig(cmd, conf)...)
e = append(e, markdownTitle(cmd, level)...)
e = append(e, markdownSynopsis(cmd, level)...)
e = append(e, markdownDocs(cmd, level)...)
e = append(e, markdownOptions(cmd, conf, level)...)
e = append(e, markdownSubcommands(cmd, conf, level)...)
e = append(e, markdownEnv(cmd, level)...)
e = append(e, markdownConfig(cmd, conf, level)...)
return Markdown(out, Doc(e...))
}

View File

@ -257,6 +257,7 @@ Options:
--bar string [*]: Bars, any number.
--duration duration: Duration is another option.
--foo int: Foo is an option.
--some any: Some is an option of any type.
--time time: Time is the third option here.
--help: Show help.
@ -285,6 +286,7 @@ Options:
--bar string [*]: Bars, any number.
--duration duration: Duration is another option.
--foo int: Foo is an option.
--some any: Some is an option of any type.
--time time: Time is the third option here.
--help: Show help.
@ -381,7 +383,8 @@ Subcommands:
Command("foo", func(struct {
Foo bool
Bar string
}) {}),
}) {
}),
"f", "foo", "b", "bar",
),
command: "foo help",
@ -970,6 +973,10 @@ foo
.br
.in 25
.ti 4
--some any:\~\~\~\~\~\~\~\~\~\~Some is an option of any type.
.br
.in 25
.ti 4
--time time:\~\~\~\~\~\~\~\~\~Time is the third option here.
.br
.in 25
@ -1234,7 +1241,8 @@ When both the environment variable and the command line option is defined, the c
impl: ShortForm(Command("foo", func(struct {
Foo bool
Bar int
}) {}), "f", "foo", "b", "bar"),
}) {
}), "f", "foo", "b", "bar"),
command: "foo",
env: "_wandgenerate=man;_wandgeneratedate=2025-12-03;_wandgenerateversion=2025-12-03-abcd123",
},
@ -1732,6 +1740,7 @@ foo <subcommand>
- --bar string \[\*\]: Bars, any number.
- --duration duration: Duration is another option.
- --foo int: Foo is an option.
- --some any: Some is an option of any type.
- --time time: Time is the third option here.
- --config: Configuration file.
- --help: Show help.

View File

@ -619,7 +619,7 @@ func (c *context) read() bool {
}
token, n, err := c.reader.ReadRune()
if err != nil {
if err == io.EOF {
if errors.Is(err, io.EOF) {
if n == 0 {
c.eof = true
return false

View File

@ -0,0 +1 @@
one = 42

View File

@ -0,0 +1,17 @@
package main
import (
. "code.squareroundforest.org/arpio/wand"
"code.squareroundforest.org/arpio/wand/internal/tests/testlib"
)
func main() {
Exec(
Group(
"test",
Default(Command("foo", testlib.Foo)),
Command("bar", testlib.Bar),
Command("baz", testlib.Baz),
),
)
}

View File

@ -18,6 +18,9 @@ type Options struct {
// Time is the third option here.
Time time.Time
// Some is an option of any type.
Some any
}
type OptionWithHelp struct {

View File

@ -0,0 +1,5 @@
package main
import "code.squareroundforest.org/arpio/wand"
func main() {
wand.Exec(func(a string) { println(a) })
}

View File

@ -0,0 +1,18 @@
module 7uxW5Ik-qaesYEi3BWbg9Q
go 1.25.4
require (
code.squareroundforest.org/arpio/bind v0.0.0-20251105181644-3443251be2d5 // indirect
code.squareroundforest.org/arpio/docreflect v0.0.0-20251031192707-01c5ff18fab1 // indirect
code.squareroundforest.org/arpio/html v0.0.0-20251103020946-e262eca50ac9 // indirect
code.squareroundforest.org/arpio/notation v0.0.0-20251101123932-5f5c05ee0239 // indirect
code.squareroundforest.org/arpio/textedit v0.0.0-20251207224821-c75c3965789f // indirect
code.squareroundforest.org/arpio/textfmt v0.0.0-20251207234108-fed32c8bbe18 // indirect
code.squareroundforest.org/arpio/wand v0.0.0-20251210193110-60acf6e82022 // indirect
github.com/iancoleman/strcase v0.3.0 // indirect
golang.org/x/sys v0.38.0 // indirect
golang.org/x/term v0.37.0 // indirect
)
replace code.squareroundforest.org/arpio/wand => /Users/arpio/work/code/wand

View File

@ -0,0 +1,20 @@
code.squareroundforest.org/arpio/bind v0.0.0-20251105181644-3443251be2d5 h1:SIgLIawD6Vv7rAvUobpVshLshdwFEJ0NOUrWpheS088=
code.squareroundforest.org/arpio/bind v0.0.0-20251105181644-3443251be2d5/go.mod h1:tTCmCwFABKNm3PO0Dclsp4zWhNQFTfg9+uSrgoarZFI=
code.squareroundforest.org/arpio/docreflect v0.0.0-20251031192707-01c5ff18fab1 h1:bJi41U5yGQykg6jVlD2AdWiznvx3Jg7ZpzEU85syOXw=
code.squareroundforest.org/arpio/docreflect v0.0.0-20251031192707-01c5ff18fab1/go.mod h1:/3xQI36oJG8qLBxT2fSS61P5/+i1T64fTX9GHRh8XhA=
code.squareroundforest.org/arpio/html v0.0.0-20251103020946-e262eca50ac9 h1:b7voJlwe0jKH568X+O7b/JTAUrHLTSKNSSL+hhV2Q/Q=
code.squareroundforest.org/arpio/html v0.0.0-20251103020946-e262eca50ac9/go.mod h1:hq+2CENEd4bVSZnOdq38FUFOJJnF3OTQRv78qMGkNlE=
code.squareroundforest.org/arpio/notation v0.0.0-20251101123932-5f5c05ee0239 h1:JvLVMuvF2laxXkIZbHC1/0xtKyKndAwIHbIIWkHqTzc=
code.squareroundforest.org/arpio/notation v0.0.0-20251101123932-5f5c05ee0239/go.mod h1:ait4Fvg9o0+bq5hlxi9dAcPL5a+/sr33qsZPNpToMLY=
code.squareroundforest.org/arpio/textedit v0.0.0-20251207224821-c75c3965789f h1:gomu8xTD953IkL3M528qVEuZ2z93C2I6Hr4vyIwE7kI=
code.squareroundforest.org/arpio/textedit v0.0.0-20251207224821-c75c3965789f/go.mod h1:nXdFdxdI69JrkIT97f+AEE4OgplmxbgNFZC5j7gsdqs=
code.squareroundforest.org/arpio/textfmt v0.0.0-20251207234108-fed32c8bbe18 h1:2aa62CYm9ld5SNoFxWzE2wUN0xjVWQ+xieoeFantdg4=
code.squareroundforest.org/arpio/textfmt v0.0.0-20251207234108-fed32c8bbe18/go.mod h1:+0G3gufMAP8SCEIrDT1D/DaVOSfjS8EwPTBs5vfxqQg=
code.squareroundforest.org/arpio/wand v0.0.0-20251210193110-60acf6e82022 h1:9XKwyrmOi2upsYWyjMQOPhO6ebl7AEHSViBVu9U/2jc=
code.squareroundforest.org/arpio/wand v0.0.0-20251210193110-60acf6e82022/go.mod h1:dwEF9zEIbOxbLxytOKSuRQPKmvbhPjDCJcoXM3j8cOU=
github.com/iancoleman/strcase v0.3.0 h1:nTXanmYxhfFAMjZL34Ov6gkzEsSJZ5DbhxWjvSASxEI=
github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU=
golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254=

View File

@ -0,0 +1,6 @@
package main
import "strings"
import "code.squareroundforest.org/arpio/wand"
func main() {
wand.Exec(strings.Split)
}

View File

@ -0,0 +1,18 @@
module Du66PUa6l2PUdTO2P6Mz8w
go 1.25.4
require (
code.squareroundforest.org/arpio/bind v0.0.0-20251105181644-3443251be2d5 // indirect
code.squareroundforest.org/arpio/docreflect v0.0.0-20251031192707-01c5ff18fab1 // indirect
code.squareroundforest.org/arpio/html v0.0.0-20251103020946-e262eca50ac9 // indirect
code.squareroundforest.org/arpio/notation v0.0.0-20251101123932-5f5c05ee0239 // indirect
code.squareroundforest.org/arpio/textedit v0.0.0-20251207224821-c75c3965789f // indirect
code.squareroundforest.org/arpio/textfmt v0.0.0-20251207234108-fed32c8bbe18 // indirect
code.squareroundforest.org/arpio/wand v0.0.0-20251210193110-60acf6e82022 // indirect
github.com/iancoleman/strcase v0.3.0 // indirect
golang.org/x/sys v0.38.0 // indirect
golang.org/x/term v0.37.0 // indirect
)
replace code.squareroundforest.org/arpio/wand => /Users/arpio/work/code/wand

View File

@ -0,0 +1,20 @@
code.squareroundforest.org/arpio/bind v0.0.0-20251105181644-3443251be2d5 h1:SIgLIawD6Vv7rAvUobpVshLshdwFEJ0NOUrWpheS088=
code.squareroundforest.org/arpio/bind v0.0.0-20251105181644-3443251be2d5/go.mod h1:tTCmCwFABKNm3PO0Dclsp4zWhNQFTfg9+uSrgoarZFI=
code.squareroundforest.org/arpio/docreflect v0.0.0-20251031192707-01c5ff18fab1 h1:bJi41U5yGQykg6jVlD2AdWiznvx3Jg7ZpzEU85syOXw=
code.squareroundforest.org/arpio/docreflect v0.0.0-20251031192707-01c5ff18fab1/go.mod h1:/3xQI36oJG8qLBxT2fSS61P5/+i1T64fTX9GHRh8XhA=
code.squareroundforest.org/arpio/html v0.0.0-20251103020946-e262eca50ac9 h1:b7voJlwe0jKH568X+O7b/JTAUrHLTSKNSSL+hhV2Q/Q=
code.squareroundforest.org/arpio/html v0.0.0-20251103020946-e262eca50ac9/go.mod h1:hq+2CENEd4bVSZnOdq38FUFOJJnF3OTQRv78qMGkNlE=
code.squareroundforest.org/arpio/notation v0.0.0-20251101123932-5f5c05ee0239 h1:JvLVMuvF2laxXkIZbHC1/0xtKyKndAwIHbIIWkHqTzc=
code.squareroundforest.org/arpio/notation v0.0.0-20251101123932-5f5c05ee0239/go.mod h1:ait4Fvg9o0+bq5hlxi9dAcPL5a+/sr33qsZPNpToMLY=
code.squareroundforest.org/arpio/textedit v0.0.0-20251207224821-c75c3965789f h1:gomu8xTD953IkL3M528qVEuZ2z93C2I6Hr4vyIwE7kI=
code.squareroundforest.org/arpio/textedit v0.0.0-20251207224821-c75c3965789f/go.mod h1:nXdFdxdI69JrkIT97f+AEE4OgplmxbgNFZC5j7gsdqs=
code.squareroundforest.org/arpio/textfmt v0.0.0-20251207234108-fed32c8bbe18 h1:2aa62CYm9ld5SNoFxWzE2wUN0xjVWQ+xieoeFantdg4=
code.squareroundforest.org/arpio/textfmt v0.0.0-20251207234108-fed32c8bbe18/go.mod h1:+0G3gufMAP8SCEIrDT1D/DaVOSfjS8EwPTBs5vfxqQg=
code.squareroundforest.org/arpio/wand v0.0.0-20251210193110-60acf6e82022 h1:9XKwyrmOi2upsYWyjMQOPhO6ebl7AEHSViBVu9U/2jc=
code.squareroundforest.org/arpio/wand v0.0.0-20251210193110-60acf6e82022/go.mod h1:dwEF9zEIbOxbLxytOKSuRQPKmvbhPjDCJcoXM3j8cOU=
github.com/iancoleman/strcase v0.3.0 h1:nTXanmYxhfFAMjZL34Ov6gkzEsSJZ5DbhxWjvSASxEI=
github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU=
golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254=

View File

@ -0,0 +1,22 @@
package main
import "io"
import "code.squareroundforest.org/arpio/wand"
func main() {
wand.Exec(func(in io.Reader) (out io.Reader) {
out := bytes.NewBuffer(nil)
buf := make([]byte, 1)
for {
n, err := in.Read(buf)
if n == 1 {
buf[0] = buf[0] * 2
out.Write(buf)
}
if err != nil {
return out
}
}
return out
})
}

View File

@ -0,0 +1,18 @@
module GBAVk0TbT71P1fBFC4wZxQ
go 1.25.4
require (
code.squareroundforest.org/arpio/bind v0.0.0-20251105181644-3443251be2d5 // indirect
code.squareroundforest.org/arpio/docreflect v0.0.0-20251031192707-01c5ff18fab1 // indirect
code.squareroundforest.org/arpio/html v0.0.0-20251103020946-e262eca50ac9 // indirect
code.squareroundforest.org/arpio/notation v0.0.0-20251101123932-5f5c05ee0239 // indirect
code.squareroundforest.org/arpio/textedit v0.0.0-20251207224821-c75c3965789f // indirect
code.squareroundforest.org/arpio/textfmt v0.0.0-20251207234108-fed32c8bbe18 // indirect
code.squareroundforest.org/arpio/wand v0.0.0-20251210193110-60acf6e82022 // indirect
github.com/iancoleman/strcase v0.3.0 // indirect
golang.org/x/sys v0.38.0 // indirect
golang.org/x/term v0.37.0 // indirect
)
replace code.squareroundforest.org/arpio/wand => /Users/arpio/work/code/wand

View File

@ -0,0 +1,20 @@
code.squareroundforest.org/arpio/bind v0.0.0-20251105181644-3443251be2d5 h1:SIgLIawD6Vv7rAvUobpVshLshdwFEJ0NOUrWpheS088=
code.squareroundforest.org/arpio/bind v0.0.0-20251105181644-3443251be2d5/go.mod h1:tTCmCwFABKNm3PO0Dclsp4zWhNQFTfg9+uSrgoarZFI=
code.squareroundforest.org/arpio/docreflect v0.0.0-20251031192707-01c5ff18fab1 h1:bJi41U5yGQykg6jVlD2AdWiznvx3Jg7ZpzEU85syOXw=
code.squareroundforest.org/arpio/docreflect v0.0.0-20251031192707-01c5ff18fab1/go.mod h1:/3xQI36oJG8qLBxT2fSS61P5/+i1T64fTX9GHRh8XhA=
code.squareroundforest.org/arpio/html v0.0.0-20251103020946-e262eca50ac9 h1:b7voJlwe0jKH568X+O7b/JTAUrHLTSKNSSL+hhV2Q/Q=
code.squareroundforest.org/arpio/html v0.0.0-20251103020946-e262eca50ac9/go.mod h1:hq+2CENEd4bVSZnOdq38FUFOJJnF3OTQRv78qMGkNlE=
code.squareroundforest.org/arpio/notation v0.0.0-20251101123932-5f5c05ee0239 h1:JvLVMuvF2laxXkIZbHC1/0xtKyKndAwIHbIIWkHqTzc=
code.squareroundforest.org/arpio/notation v0.0.0-20251101123932-5f5c05ee0239/go.mod h1:ait4Fvg9o0+bq5hlxi9dAcPL5a+/sr33qsZPNpToMLY=
code.squareroundforest.org/arpio/textedit v0.0.0-20251207224821-c75c3965789f h1:gomu8xTD953IkL3M528qVEuZ2z93C2I6Hr4vyIwE7kI=
code.squareroundforest.org/arpio/textedit v0.0.0-20251207224821-c75c3965789f/go.mod h1:nXdFdxdI69JrkIT97f+AEE4OgplmxbgNFZC5j7gsdqs=
code.squareroundforest.org/arpio/textfmt v0.0.0-20251207234108-fed32c8bbe18 h1:2aa62CYm9ld5SNoFxWzE2wUN0xjVWQ+xieoeFantdg4=
code.squareroundforest.org/arpio/textfmt v0.0.0-20251207234108-fed32c8bbe18/go.mod h1:+0G3gufMAP8SCEIrDT1D/DaVOSfjS8EwPTBs5vfxqQg=
code.squareroundforest.org/arpio/wand v0.0.0-20251210193110-60acf6e82022 h1:9XKwyrmOi2upsYWyjMQOPhO6ebl7AEHSViBVu9U/2jc=
code.squareroundforest.org/arpio/wand v0.0.0-20251210193110-60acf6e82022/go.mod h1:dwEF9zEIbOxbLxytOKSuRQPKmvbhPjDCJcoXM3j8cOU=
github.com/iancoleman/strcase v0.3.0 h1:nTXanmYxhfFAMjZL34Ov6gkzEsSJZ5DbhxWjvSASxEI=
github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU=
golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254=

View File

@ -0,0 +1,5 @@
package main
import "code.squareroundforest.org/arpio/wand"
func main() {
wand.Exec(func(a any) { println(a) })
}

View File

@ -0,0 +1,18 @@
module Vw9o9PuLOlqrxfbxo88FwA
go 1.25.4
require (
code.squareroundforest.org/arpio/bind v0.0.0-20251105181644-3443251be2d5 // indirect
code.squareroundforest.org/arpio/docreflect v0.0.0-20251031192707-01c5ff18fab1 // indirect
code.squareroundforest.org/arpio/html v0.0.0-20251103020946-e262eca50ac9 // indirect
code.squareroundforest.org/arpio/notation v0.0.0-20251101123932-5f5c05ee0239 // indirect
code.squareroundforest.org/arpio/textedit v0.0.0-20251207224821-c75c3965789f // indirect
code.squareroundforest.org/arpio/textfmt v0.0.0-20251207234108-fed32c8bbe18 // indirect
code.squareroundforest.org/arpio/wand v0.0.0-20251210193110-60acf6e82022 // indirect
github.com/iancoleman/strcase v0.3.0 // indirect
golang.org/x/sys v0.38.0 // indirect
golang.org/x/term v0.37.0 // indirect
)
replace code.squareroundforest.org/arpio/wand => /Users/arpio/work/code/wand

View File

@ -0,0 +1,20 @@
code.squareroundforest.org/arpio/bind v0.0.0-20251105181644-3443251be2d5 h1:SIgLIawD6Vv7rAvUobpVshLshdwFEJ0NOUrWpheS088=
code.squareroundforest.org/arpio/bind v0.0.0-20251105181644-3443251be2d5/go.mod h1:tTCmCwFABKNm3PO0Dclsp4zWhNQFTfg9+uSrgoarZFI=
code.squareroundforest.org/arpio/docreflect v0.0.0-20251031192707-01c5ff18fab1 h1:bJi41U5yGQykg6jVlD2AdWiznvx3Jg7ZpzEU85syOXw=
code.squareroundforest.org/arpio/docreflect v0.0.0-20251031192707-01c5ff18fab1/go.mod h1:/3xQI36oJG8qLBxT2fSS61P5/+i1T64fTX9GHRh8XhA=
code.squareroundforest.org/arpio/html v0.0.0-20251103020946-e262eca50ac9 h1:b7voJlwe0jKH568X+O7b/JTAUrHLTSKNSSL+hhV2Q/Q=
code.squareroundforest.org/arpio/html v0.0.0-20251103020946-e262eca50ac9/go.mod h1:hq+2CENEd4bVSZnOdq38FUFOJJnF3OTQRv78qMGkNlE=
code.squareroundforest.org/arpio/notation v0.0.0-20251101123932-5f5c05ee0239 h1:JvLVMuvF2laxXkIZbHC1/0xtKyKndAwIHbIIWkHqTzc=
code.squareroundforest.org/arpio/notation v0.0.0-20251101123932-5f5c05ee0239/go.mod h1:ait4Fvg9o0+bq5hlxi9dAcPL5a+/sr33qsZPNpToMLY=
code.squareroundforest.org/arpio/textedit v0.0.0-20251207224821-c75c3965789f h1:gomu8xTD953IkL3M528qVEuZ2z93C2I6Hr4vyIwE7kI=
code.squareroundforest.org/arpio/textedit v0.0.0-20251207224821-c75c3965789f/go.mod h1:nXdFdxdI69JrkIT97f+AEE4OgplmxbgNFZC5j7gsdqs=
code.squareroundforest.org/arpio/textfmt v0.0.0-20251207234108-fed32c8bbe18 h1:2aa62CYm9ld5SNoFxWzE2wUN0xjVWQ+xieoeFantdg4=
code.squareroundforest.org/arpio/textfmt v0.0.0-20251207234108-fed32c8bbe18/go.mod h1:+0G3gufMAP8SCEIrDT1D/DaVOSfjS8EwPTBs5vfxqQg=
code.squareroundforest.org/arpio/wand v0.0.0-20251210193110-60acf6e82022 h1:9XKwyrmOi2upsYWyjMQOPhO6ebl7AEHSViBVu9U/2jc=
code.squareroundforest.org/arpio/wand v0.0.0-20251210193110-60acf6e82022/go.mod h1:dwEF9zEIbOxbLxytOKSuRQPKmvbhPjDCJcoXM3j8cOU=
github.com/iancoleman/strcase v0.3.0 h1:nTXanmYxhfFAMjZL34Ov6gkzEsSJZ5DbhxWjvSASxEI=
github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU=
golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254=

View File

@ -0,0 +1,6 @@
package main
import "fmt"
import "code.squareroundforest.org/arpio/wand"
func main() {
wand.Exec(func(a any) { println(a) })
}

View File

@ -0,0 +1,18 @@
module W0w-rWwNmthjuerTOFnMIQ
go 1.25.4
require (
code.squareroundforest.org/arpio/bind v0.0.0-20251105181644-3443251be2d5 // indirect
code.squareroundforest.org/arpio/docreflect v0.0.0-20251031192707-01c5ff18fab1 // indirect
code.squareroundforest.org/arpio/html v0.0.0-20251103020946-e262eca50ac9 // indirect
code.squareroundforest.org/arpio/notation v0.0.0-20251101123932-5f5c05ee0239 // indirect
code.squareroundforest.org/arpio/textedit v0.0.0-20251207224821-c75c3965789f // indirect
code.squareroundforest.org/arpio/textfmt v0.0.0-20251207234108-fed32c8bbe18 // indirect
code.squareroundforest.org/arpio/wand v0.0.0-20251210193110-60acf6e82022 // indirect
github.com/iancoleman/strcase v0.3.0 // indirect
golang.org/x/sys v0.38.0 // indirect
golang.org/x/term v0.37.0 // indirect
)
replace code.squareroundforest.org/arpio/wand => /Users/arpio/work/code/wand

View File

@ -0,0 +1,20 @@
code.squareroundforest.org/arpio/bind v0.0.0-20251105181644-3443251be2d5 h1:SIgLIawD6Vv7rAvUobpVshLshdwFEJ0NOUrWpheS088=
code.squareroundforest.org/arpio/bind v0.0.0-20251105181644-3443251be2d5/go.mod h1:tTCmCwFABKNm3PO0Dclsp4zWhNQFTfg9+uSrgoarZFI=
code.squareroundforest.org/arpio/docreflect v0.0.0-20251031192707-01c5ff18fab1 h1:bJi41U5yGQykg6jVlD2AdWiznvx3Jg7ZpzEU85syOXw=
code.squareroundforest.org/arpio/docreflect v0.0.0-20251031192707-01c5ff18fab1/go.mod h1:/3xQI36oJG8qLBxT2fSS61P5/+i1T64fTX9GHRh8XhA=
code.squareroundforest.org/arpio/html v0.0.0-20251103020946-e262eca50ac9 h1:b7voJlwe0jKH568X+O7b/JTAUrHLTSKNSSL+hhV2Q/Q=
code.squareroundforest.org/arpio/html v0.0.0-20251103020946-e262eca50ac9/go.mod h1:hq+2CENEd4bVSZnOdq38FUFOJJnF3OTQRv78qMGkNlE=
code.squareroundforest.org/arpio/notation v0.0.0-20251101123932-5f5c05ee0239 h1:JvLVMuvF2laxXkIZbHC1/0xtKyKndAwIHbIIWkHqTzc=
code.squareroundforest.org/arpio/notation v0.0.0-20251101123932-5f5c05ee0239/go.mod h1:ait4Fvg9o0+bq5hlxi9dAcPL5a+/sr33qsZPNpToMLY=
code.squareroundforest.org/arpio/textedit v0.0.0-20251207224821-c75c3965789f h1:gomu8xTD953IkL3M528qVEuZ2z93C2I6Hr4vyIwE7kI=
code.squareroundforest.org/arpio/textedit v0.0.0-20251207224821-c75c3965789f/go.mod h1:nXdFdxdI69JrkIT97f+AEE4OgplmxbgNFZC5j7gsdqs=
code.squareroundforest.org/arpio/textfmt v0.0.0-20251207234108-fed32c8bbe18 h1:2aa62CYm9ld5SNoFxWzE2wUN0xjVWQ+xieoeFantdg4=
code.squareroundforest.org/arpio/textfmt v0.0.0-20251207234108-fed32c8bbe18/go.mod h1:+0G3gufMAP8SCEIrDT1D/DaVOSfjS8EwPTBs5vfxqQg=
code.squareroundforest.org/arpio/wand v0.0.0-20251210193110-60acf6e82022 h1:9XKwyrmOi2upsYWyjMQOPhO6ebl7AEHSViBVu9U/2jc=
code.squareroundforest.org/arpio/wand v0.0.0-20251210193110-60acf6e82022/go.mod h1:dwEF9zEIbOxbLxytOKSuRQPKmvbhPjDCJcoXM3j8cOU=
github.com/iancoleman/strcase v0.3.0 h1:nTXanmYxhfFAMjZL34Ov6gkzEsSJZ5DbhxWjvSASxEI=
github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU=
golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254=

View File

@ -0,0 +1,6 @@
package main
import "fmt"
import "code.squareroundforest.org/arpio/wand"
func main() {
wand.Exec(fmt.Println)
}

View File

@ -0,0 +1,18 @@
module c128RsAggPny5Yt3rXQnjA
go 1.25.4
require (
code.squareroundforest.org/arpio/bind v0.0.0-20251105181644-3443251be2d5 // indirect
code.squareroundforest.org/arpio/docreflect v0.0.0-20251031192707-01c5ff18fab1 // indirect
code.squareroundforest.org/arpio/html v0.0.0-20251103020946-e262eca50ac9 // indirect
code.squareroundforest.org/arpio/notation v0.0.0-20251101123932-5f5c05ee0239 // indirect
code.squareroundforest.org/arpio/textedit v0.0.0-20251207224821-c75c3965789f // indirect
code.squareroundforest.org/arpio/textfmt v0.0.0-20251207234108-fed32c8bbe18 // indirect
code.squareroundforest.org/arpio/wand v0.0.0-20251210193110-60acf6e82022 // indirect
github.com/iancoleman/strcase v0.3.0 // indirect
golang.org/x/sys v0.38.0 // indirect
golang.org/x/term v0.37.0 // indirect
)
replace code.squareroundforest.org/arpio/wand => /Users/arpio/work/code/wand

View File

@ -0,0 +1,20 @@
code.squareroundforest.org/arpio/bind v0.0.0-20251105181644-3443251be2d5 h1:SIgLIawD6Vv7rAvUobpVshLshdwFEJ0NOUrWpheS088=
code.squareroundforest.org/arpio/bind v0.0.0-20251105181644-3443251be2d5/go.mod h1:tTCmCwFABKNm3PO0Dclsp4zWhNQFTfg9+uSrgoarZFI=
code.squareroundforest.org/arpio/docreflect v0.0.0-20251031192707-01c5ff18fab1 h1:bJi41U5yGQykg6jVlD2AdWiznvx3Jg7ZpzEU85syOXw=
code.squareroundforest.org/arpio/docreflect v0.0.0-20251031192707-01c5ff18fab1/go.mod h1:/3xQI36oJG8qLBxT2fSS61P5/+i1T64fTX9GHRh8XhA=
code.squareroundforest.org/arpio/html v0.0.0-20251103020946-e262eca50ac9 h1:b7voJlwe0jKH568X+O7b/JTAUrHLTSKNSSL+hhV2Q/Q=
code.squareroundforest.org/arpio/html v0.0.0-20251103020946-e262eca50ac9/go.mod h1:hq+2CENEd4bVSZnOdq38FUFOJJnF3OTQRv78qMGkNlE=
code.squareroundforest.org/arpio/notation v0.0.0-20251101123932-5f5c05ee0239 h1:JvLVMuvF2laxXkIZbHC1/0xtKyKndAwIHbIIWkHqTzc=
code.squareroundforest.org/arpio/notation v0.0.0-20251101123932-5f5c05ee0239/go.mod h1:ait4Fvg9o0+bq5hlxi9dAcPL5a+/sr33qsZPNpToMLY=
code.squareroundforest.org/arpio/textedit v0.0.0-20251207224821-c75c3965789f h1:gomu8xTD953IkL3M528qVEuZ2z93C2I6Hr4vyIwE7kI=
code.squareroundforest.org/arpio/textedit v0.0.0-20251207224821-c75c3965789f/go.mod h1:nXdFdxdI69JrkIT97f+AEE4OgplmxbgNFZC5j7gsdqs=
code.squareroundforest.org/arpio/textfmt v0.0.0-20251207234108-fed32c8bbe18 h1:2aa62CYm9ld5SNoFxWzE2wUN0xjVWQ+xieoeFantdg4=
code.squareroundforest.org/arpio/textfmt v0.0.0-20251207234108-fed32c8bbe18/go.mod h1:+0G3gufMAP8SCEIrDT1D/DaVOSfjS8EwPTBs5vfxqQg=
code.squareroundforest.org/arpio/wand v0.0.0-20251210193110-60acf6e82022 h1:9XKwyrmOi2upsYWyjMQOPhO6ebl7AEHSViBVu9U/2jc=
code.squareroundforest.org/arpio/wand v0.0.0-20251210193110-60acf6e82022/go.mod h1:dwEF9zEIbOxbLxytOKSuRQPKmvbhPjDCJcoXM3j8cOU=
github.com/iancoleman/strcase v0.3.0 h1:nTXanmYxhfFAMjZL34Ov6gkzEsSJZ5DbhxWjvSASxEI=
github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU=
golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254=

View File

@ -0,0 +1,6 @@
package main
import . "strings"
import "code.squareroundforest.org/arpio/wand"
func main() {
wand.Exec(Split)
}

View File

@ -0,0 +1,18 @@
module djTBWFYLXSZapqVl11Wnpg
go 1.25.4
require (
code.squareroundforest.org/arpio/bind v0.0.0-20251105181644-3443251be2d5 // indirect
code.squareroundforest.org/arpio/docreflect v0.0.0-20251031192707-01c5ff18fab1 // indirect
code.squareroundforest.org/arpio/html v0.0.0-20251103020946-e262eca50ac9 // indirect
code.squareroundforest.org/arpio/notation v0.0.0-20251101123932-5f5c05ee0239 // indirect
code.squareroundforest.org/arpio/textedit v0.0.0-20251207224821-c75c3965789f // indirect
code.squareroundforest.org/arpio/textfmt v0.0.0-20251207234108-fed32c8bbe18 // indirect
code.squareroundforest.org/arpio/wand v0.0.0-20251210193110-60acf6e82022 // indirect
github.com/iancoleman/strcase v0.3.0 // indirect
golang.org/x/sys v0.38.0 // indirect
golang.org/x/term v0.37.0 // indirect
)
replace code.squareroundforest.org/arpio/wand => /Users/arpio/work/code/wand

View File

@ -0,0 +1,20 @@
code.squareroundforest.org/arpio/bind v0.0.0-20251105181644-3443251be2d5 h1:SIgLIawD6Vv7rAvUobpVshLshdwFEJ0NOUrWpheS088=
code.squareroundforest.org/arpio/bind v0.0.0-20251105181644-3443251be2d5/go.mod h1:tTCmCwFABKNm3PO0Dclsp4zWhNQFTfg9+uSrgoarZFI=
code.squareroundforest.org/arpio/docreflect v0.0.0-20251031192707-01c5ff18fab1 h1:bJi41U5yGQykg6jVlD2AdWiznvx3Jg7ZpzEU85syOXw=
code.squareroundforest.org/arpio/docreflect v0.0.0-20251031192707-01c5ff18fab1/go.mod h1:/3xQI36oJG8qLBxT2fSS61P5/+i1T64fTX9GHRh8XhA=
code.squareroundforest.org/arpio/html v0.0.0-20251103020946-e262eca50ac9 h1:b7voJlwe0jKH568X+O7b/JTAUrHLTSKNSSL+hhV2Q/Q=
code.squareroundforest.org/arpio/html v0.0.0-20251103020946-e262eca50ac9/go.mod h1:hq+2CENEd4bVSZnOdq38FUFOJJnF3OTQRv78qMGkNlE=
code.squareroundforest.org/arpio/notation v0.0.0-20251101123932-5f5c05ee0239 h1:JvLVMuvF2laxXkIZbHC1/0xtKyKndAwIHbIIWkHqTzc=
code.squareroundforest.org/arpio/notation v0.0.0-20251101123932-5f5c05ee0239/go.mod h1:ait4Fvg9o0+bq5hlxi9dAcPL5a+/sr33qsZPNpToMLY=
code.squareroundforest.org/arpio/textedit v0.0.0-20251207224821-c75c3965789f h1:gomu8xTD953IkL3M528qVEuZ2z93C2I6Hr4vyIwE7kI=
code.squareroundforest.org/arpio/textedit v0.0.0-20251207224821-c75c3965789f/go.mod h1:nXdFdxdI69JrkIT97f+AEE4OgplmxbgNFZC5j7gsdqs=
code.squareroundforest.org/arpio/textfmt v0.0.0-20251207234108-fed32c8bbe18 h1:2aa62CYm9ld5SNoFxWzE2wUN0xjVWQ+xieoeFantdg4=
code.squareroundforest.org/arpio/textfmt v0.0.0-20251207234108-fed32c8bbe18/go.mod h1:+0G3gufMAP8SCEIrDT1D/DaVOSfjS8EwPTBs5vfxqQg=
code.squareroundforest.org/arpio/wand v0.0.0-20251210193110-60acf6e82022 h1:9XKwyrmOi2upsYWyjMQOPhO6ebl7AEHSViBVu9U/2jc=
code.squareroundforest.org/arpio/wand v0.0.0-20251210193110-60acf6e82022/go.mod h1:dwEF9zEIbOxbLxytOKSuRQPKmvbhPjDCJcoXM3j8cOU=
github.com/iancoleman/strcase v0.3.0 h1:nTXanmYxhfFAMjZL34Ov6gkzEsSJZ5DbhxWjvSASxEI=
github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU=
golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254=

View File

@ -0,0 +1,6 @@
package main
import str "strings"
import "code.squareroundforest.org/arpio/wand"
func main() {
wand.Exec(str.Split)
}

View File

@ -0,0 +1,18 @@
module fbneVWE281vn9cQJ87O0vA
go 1.25.4
require (
code.squareroundforest.org/arpio/bind v0.0.0-20251105181644-3443251be2d5 // indirect
code.squareroundforest.org/arpio/docreflect v0.0.0-20251031192707-01c5ff18fab1 // indirect
code.squareroundforest.org/arpio/html v0.0.0-20251103020946-e262eca50ac9 // indirect
code.squareroundforest.org/arpio/notation v0.0.0-20251101123932-5f5c05ee0239 // indirect
code.squareroundforest.org/arpio/textedit v0.0.0-20251207224821-c75c3965789f // indirect
code.squareroundforest.org/arpio/textfmt v0.0.0-20251207234108-fed32c8bbe18 // indirect
code.squareroundforest.org/arpio/wand v0.0.0-20251210193110-60acf6e82022 // indirect
github.com/iancoleman/strcase v0.3.0 // indirect
golang.org/x/sys v0.38.0 // indirect
golang.org/x/term v0.37.0 // indirect
)
replace code.squareroundforest.org/arpio/wand => /Users/arpio/work/code/wand

View File

@ -0,0 +1,20 @@
code.squareroundforest.org/arpio/bind v0.0.0-20251105181644-3443251be2d5 h1:SIgLIawD6Vv7rAvUobpVshLshdwFEJ0NOUrWpheS088=
code.squareroundforest.org/arpio/bind v0.0.0-20251105181644-3443251be2d5/go.mod h1:tTCmCwFABKNm3PO0Dclsp4zWhNQFTfg9+uSrgoarZFI=
code.squareroundforest.org/arpio/docreflect v0.0.0-20251031192707-01c5ff18fab1 h1:bJi41U5yGQykg6jVlD2AdWiznvx3Jg7ZpzEU85syOXw=
code.squareroundforest.org/arpio/docreflect v0.0.0-20251031192707-01c5ff18fab1/go.mod h1:/3xQI36oJG8qLBxT2fSS61P5/+i1T64fTX9GHRh8XhA=
code.squareroundforest.org/arpio/html v0.0.0-20251103020946-e262eca50ac9 h1:b7voJlwe0jKH568X+O7b/JTAUrHLTSKNSSL+hhV2Q/Q=
code.squareroundforest.org/arpio/html v0.0.0-20251103020946-e262eca50ac9/go.mod h1:hq+2CENEd4bVSZnOdq38FUFOJJnF3OTQRv78qMGkNlE=
code.squareroundforest.org/arpio/notation v0.0.0-20251101123932-5f5c05ee0239 h1:JvLVMuvF2laxXkIZbHC1/0xtKyKndAwIHbIIWkHqTzc=
code.squareroundforest.org/arpio/notation v0.0.0-20251101123932-5f5c05ee0239/go.mod h1:ait4Fvg9o0+bq5hlxi9dAcPL5a+/sr33qsZPNpToMLY=
code.squareroundforest.org/arpio/textedit v0.0.0-20251207224821-c75c3965789f h1:gomu8xTD953IkL3M528qVEuZ2z93C2I6Hr4vyIwE7kI=
code.squareroundforest.org/arpio/textedit v0.0.0-20251207224821-c75c3965789f/go.mod h1:nXdFdxdI69JrkIT97f+AEE4OgplmxbgNFZC5j7gsdqs=
code.squareroundforest.org/arpio/textfmt v0.0.0-20251207234108-fed32c8bbe18 h1:2aa62CYm9ld5SNoFxWzE2wUN0xjVWQ+xieoeFantdg4=
code.squareroundforest.org/arpio/textfmt v0.0.0-20251207234108-fed32c8bbe18/go.mod h1:+0G3gufMAP8SCEIrDT1D/DaVOSfjS8EwPTBs5vfxqQg=
code.squareroundforest.org/arpio/wand v0.0.0-20251210193110-60acf6e82022 h1:9XKwyrmOi2upsYWyjMQOPhO6ebl7AEHSViBVu9U/2jc=
code.squareroundforest.org/arpio/wand v0.0.0-20251210193110-60acf6e82022/go.mod h1:dwEF9zEIbOxbLxytOKSuRQPKmvbhPjDCJcoXM3j8cOU=
github.com/iancoleman/strcase v0.3.0 h1:nTXanmYxhfFAMjZL34Ov6gkzEsSJZ5DbhxWjvSASxEI=
github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU=
golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254=

View File

@ -0,0 +1,18 @@
module k_vqGB1P94wR5O4M7XiD4A
go 1.25.4
require (
code.squareroundforest.org/arpio/bind v0.0.0-20251105181644-3443251be2d5 // indirect
code.squareroundforest.org/arpio/docreflect v0.0.0-20251031192707-01c5ff18fab1 // indirect
code.squareroundforest.org/arpio/html v0.0.0-20251103020946-e262eca50ac9 // indirect
code.squareroundforest.org/arpio/notation v0.0.0-20251101123932-5f5c05ee0239 // indirect
code.squareroundforest.org/arpio/textedit v0.0.0-20251207224821-c75c3965789f // indirect
code.squareroundforest.org/arpio/textfmt v0.0.0-20251207234108-fed32c8bbe18 // indirect
code.squareroundforest.org/arpio/wand v0.0.0-20251210193110-60acf6e82022 // indirect
github.com/iancoleman/strcase v0.3.0 // indirect
golang.org/x/sys v0.38.0 // indirect
golang.org/x/term v0.37.0 // indirect
)
replace code.squareroundforest.org/arpio/wand => /Users/arpio/work/code/wand

View File

@ -0,0 +1,20 @@
code.squareroundforest.org/arpio/bind v0.0.0-20251105181644-3443251be2d5 h1:SIgLIawD6Vv7rAvUobpVshLshdwFEJ0NOUrWpheS088=
code.squareroundforest.org/arpio/bind v0.0.0-20251105181644-3443251be2d5/go.mod h1:tTCmCwFABKNm3PO0Dclsp4zWhNQFTfg9+uSrgoarZFI=
code.squareroundforest.org/arpio/docreflect v0.0.0-20251031192707-01c5ff18fab1 h1:bJi41U5yGQykg6jVlD2AdWiznvx3Jg7ZpzEU85syOXw=
code.squareroundforest.org/arpio/docreflect v0.0.0-20251031192707-01c5ff18fab1/go.mod h1:/3xQI36oJG8qLBxT2fSS61P5/+i1T64fTX9GHRh8XhA=
code.squareroundforest.org/arpio/html v0.0.0-20251103020946-e262eca50ac9 h1:b7voJlwe0jKH568X+O7b/JTAUrHLTSKNSSL+hhV2Q/Q=
code.squareroundforest.org/arpio/html v0.0.0-20251103020946-e262eca50ac9/go.mod h1:hq+2CENEd4bVSZnOdq38FUFOJJnF3OTQRv78qMGkNlE=
code.squareroundforest.org/arpio/notation v0.0.0-20251101123932-5f5c05ee0239 h1:JvLVMuvF2laxXkIZbHC1/0xtKyKndAwIHbIIWkHqTzc=
code.squareroundforest.org/arpio/notation v0.0.0-20251101123932-5f5c05ee0239/go.mod h1:ait4Fvg9o0+bq5hlxi9dAcPL5a+/sr33qsZPNpToMLY=
code.squareroundforest.org/arpio/textedit v0.0.0-20251207224821-c75c3965789f h1:gomu8xTD953IkL3M528qVEuZ2z93C2I6Hr4vyIwE7kI=
code.squareroundforest.org/arpio/textedit v0.0.0-20251207224821-c75c3965789f/go.mod h1:nXdFdxdI69JrkIT97f+AEE4OgplmxbgNFZC5j7gsdqs=
code.squareroundforest.org/arpio/textfmt v0.0.0-20251207234108-fed32c8bbe18 h1:2aa62CYm9ld5SNoFxWzE2wUN0xjVWQ+xieoeFantdg4=
code.squareroundforest.org/arpio/textfmt v0.0.0-20251207234108-fed32c8bbe18/go.mod h1:+0G3gufMAP8SCEIrDT1D/DaVOSfjS8EwPTBs5vfxqQg=
code.squareroundforest.org/arpio/wand v0.0.0-20251210193110-60acf6e82022 h1:9XKwyrmOi2upsYWyjMQOPhO6ebl7AEHSViBVu9U/2jc=
code.squareroundforest.org/arpio/wand v0.0.0-20251210193110-60acf6e82022/go.mod h1:dwEF9zEIbOxbLxytOKSuRQPKmvbhPjDCJcoXM3j8cOU=
github.com/iancoleman/strcase v0.3.0 h1:nTXanmYxhfFAMjZL34Ov6gkzEsSJZ5DbhxWjvSASxEI=
github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU=
golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254=

View File

@ -0,0 +1,5 @@
package main
import "code.squareroundforest.org/arpio/wand"
func main() {
wand.Exec(println)
}

View File

@ -0,0 +1,18 @@
module pPXRXBwDsfnuJDBB1hbG7w
go 1.25.4
require (
code.squareroundforest.org/arpio/bind v0.0.0-20251105181644-3443251be2d5 // indirect
code.squareroundforest.org/arpio/docreflect v0.0.0-20251031192707-01c5ff18fab1 // indirect
code.squareroundforest.org/arpio/html v0.0.0-20251103020946-e262eca50ac9 // indirect
code.squareroundforest.org/arpio/notation v0.0.0-20251101123932-5f5c05ee0239 // indirect
code.squareroundforest.org/arpio/textedit v0.0.0-20251207224821-c75c3965789f // indirect
code.squareroundforest.org/arpio/textfmt v0.0.0-20251207234108-fed32c8bbe18 // indirect
code.squareroundforest.org/arpio/wand v0.0.0-20251210193110-60acf6e82022 // indirect
github.com/iancoleman/strcase v0.3.0 // indirect
golang.org/x/sys v0.38.0 // indirect
golang.org/x/term v0.37.0 // indirect
)
replace code.squareroundforest.org/arpio/wand => /Users/arpio/work/code/wand

View File

@ -0,0 +1,20 @@
code.squareroundforest.org/arpio/bind v0.0.0-20251105181644-3443251be2d5 h1:SIgLIawD6Vv7rAvUobpVshLshdwFEJ0NOUrWpheS088=
code.squareroundforest.org/arpio/bind v0.0.0-20251105181644-3443251be2d5/go.mod h1:tTCmCwFABKNm3PO0Dclsp4zWhNQFTfg9+uSrgoarZFI=
code.squareroundforest.org/arpio/docreflect v0.0.0-20251031192707-01c5ff18fab1 h1:bJi41U5yGQykg6jVlD2AdWiznvx3Jg7ZpzEU85syOXw=
code.squareroundforest.org/arpio/docreflect v0.0.0-20251031192707-01c5ff18fab1/go.mod h1:/3xQI36oJG8qLBxT2fSS61P5/+i1T64fTX9GHRh8XhA=
code.squareroundforest.org/arpio/html v0.0.0-20251103020946-e262eca50ac9 h1:b7voJlwe0jKH568X+O7b/JTAUrHLTSKNSSL+hhV2Q/Q=
code.squareroundforest.org/arpio/html v0.0.0-20251103020946-e262eca50ac9/go.mod h1:hq+2CENEd4bVSZnOdq38FUFOJJnF3OTQRv78qMGkNlE=
code.squareroundforest.org/arpio/notation v0.0.0-20251101123932-5f5c05ee0239 h1:JvLVMuvF2laxXkIZbHC1/0xtKyKndAwIHbIIWkHqTzc=
code.squareroundforest.org/arpio/notation v0.0.0-20251101123932-5f5c05ee0239/go.mod h1:ait4Fvg9o0+bq5hlxi9dAcPL5a+/sr33qsZPNpToMLY=
code.squareroundforest.org/arpio/textedit v0.0.0-20251207224821-c75c3965789f h1:gomu8xTD953IkL3M528qVEuZ2z93C2I6Hr4vyIwE7kI=
code.squareroundforest.org/arpio/textedit v0.0.0-20251207224821-c75c3965789f/go.mod h1:nXdFdxdI69JrkIT97f+AEE4OgplmxbgNFZC5j7gsdqs=
code.squareroundforest.org/arpio/textfmt v0.0.0-20251207234108-fed32c8bbe18 h1:2aa62CYm9ld5SNoFxWzE2wUN0xjVWQ+xieoeFantdg4=
code.squareroundforest.org/arpio/textfmt v0.0.0-20251207234108-fed32c8bbe18/go.mod h1:+0G3gufMAP8SCEIrDT1D/DaVOSfjS8EwPTBs5vfxqQg=
code.squareroundforest.org/arpio/wand v0.0.0-20251210193110-60acf6e82022 h1:9XKwyrmOi2upsYWyjMQOPhO6ebl7AEHSViBVu9U/2jc=
code.squareroundforest.org/arpio/wand v0.0.0-20251210193110-60acf6e82022/go.mod h1:dwEF9zEIbOxbLxytOKSuRQPKmvbhPjDCJcoXM3j8cOU=
github.com/iancoleman/strcase v0.3.0 h1:nTXanmYxhfFAMjZL34Ov6gkzEsSJZ5DbhxWjvSASxEI=
github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU=
golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254=

View File

@ -0,0 +1,6 @@
package main
import "fmt"
import "code.squareroundforest.org/arpio/wand"
func main() {
wand.Exec(func(a)
}

View File

@ -0,0 +1,18 @@
module qo6bOwdhYFcgh80Jx-WTyg
go 1.25.4
require (
code.squareroundforest.org/arpio/bind v0.0.0-20251105181644-3443251be2d5 // indirect
code.squareroundforest.org/arpio/docreflect v0.0.0-20251031192707-01c5ff18fab1 // indirect
code.squareroundforest.org/arpio/html v0.0.0-20251103020946-e262eca50ac9 // indirect
code.squareroundforest.org/arpio/notation v0.0.0-20251101123932-5f5c05ee0239 // indirect
code.squareroundforest.org/arpio/textedit v0.0.0-20251207224821-c75c3965789f // indirect
code.squareroundforest.org/arpio/textfmt v0.0.0-20251207234108-fed32c8bbe18 // indirect
code.squareroundforest.org/arpio/wand v0.0.0-20251210193110-60acf6e82022 // indirect
github.com/iancoleman/strcase v0.3.0 // indirect
golang.org/x/sys v0.38.0 // indirect
golang.org/x/term v0.37.0 // indirect
)
replace code.squareroundforest.org/arpio/wand => /Users/arpio/work/code/wand

View File

@ -0,0 +1,20 @@
code.squareroundforest.org/arpio/bind v0.0.0-20251105181644-3443251be2d5 h1:SIgLIawD6Vv7rAvUobpVshLshdwFEJ0NOUrWpheS088=
code.squareroundforest.org/arpio/bind v0.0.0-20251105181644-3443251be2d5/go.mod h1:tTCmCwFABKNm3PO0Dclsp4zWhNQFTfg9+uSrgoarZFI=
code.squareroundforest.org/arpio/docreflect v0.0.0-20251031192707-01c5ff18fab1 h1:bJi41U5yGQykg6jVlD2AdWiznvx3Jg7ZpzEU85syOXw=
code.squareroundforest.org/arpio/docreflect v0.0.0-20251031192707-01c5ff18fab1/go.mod h1:/3xQI36oJG8qLBxT2fSS61P5/+i1T64fTX9GHRh8XhA=
code.squareroundforest.org/arpio/html v0.0.0-20251103020946-e262eca50ac9 h1:b7voJlwe0jKH568X+O7b/JTAUrHLTSKNSSL+hhV2Q/Q=
code.squareroundforest.org/arpio/html v0.0.0-20251103020946-e262eca50ac9/go.mod h1:hq+2CENEd4bVSZnOdq38FUFOJJnF3OTQRv78qMGkNlE=
code.squareroundforest.org/arpio/notation v0.0.0-20251101123932-5f5c05ee0239 h1:JvLVMuvF2laxXkIZbHC1/0xtKyKndAwIHbIIWkHqTzc=
code.squareroundforest.org/arpio/notation v0.0.0-20251101123932-5f5c05ee0239/go.mod h1:ait4Fvg9o0+bq5hlxi9dAcPL5a+/sr33qsZPNpToMLY=
code.squareroundforest.org/arpio/textedit v0.0.0-20251207224821-c75c3965789f h1:gomu8xTD953IkL3M528qVEuZ2z93C2I6Hr4vyIwE7kI=
code.squareroundforest.org/arpio/textedit v0.0.0-20251207224821-c75c3965789f/go.mod h1:nXdFdxdI69JrkIT97f+AEE4OgplmxbgNFZC5j7gsdqs=
code.squareroundforest.org/arpio/textfmt v0.0.0-20251207234108-fed32c8bbe18 h1:2aa62CYm9ld5SNoFxWzE2wUN0xjVWQ+xieoeFantdg4=
code.squareroundforest.org/arpio/textfmt v0.0.0-20251207234108-fed32c8bbe18/go.mod h1:+0G3gufMAP8SCEIrDT1D/DaVOSfjS8EwPTBs5vfxqQg=
code.squareroundforest.org/arpio/wand v0.0.0-20251210193110-60acf6e82022 h1:9XKwyrmOi2upsYWyjMQOPhO6ebl7AEHSViBVu9U/2jc=
code.squareroundforest.org/arpio/wand v0.0.0-20251210193110-60acf6e82022/go.mod h1:dwEF9zEIbOxbLxytOKSuRQPKmvbhPjDCJcoXM3j8cOU=
github.com/iancoleman/strcase v0.3.0 h1:nTXanmYxhfFAMjZL34Ov6gkzEsSJZ5DbhxWjvSASxEI=
github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU=
golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254=

View File

@ -0,0 +1,23 @@
package main
import "bytes"
import "io"
import "code.squareroundforest.org/arpio/wand"
func main() {
wand.Exec(func(in io.Reader) (out io.Reader) {
out = bytes.NewBuffer(nil)
buf := make([]byte, 1)
for {
n, err := in.Read(buf)
if n == 1 {
buf[0] = buf[0] * 2
out.(io.Writer).Write(buf)
}
if err != nil {
return out
}
}
return out
})
}

View File

@ -0,0 +1,18 @@
module wtqcAdcxZGXt7vrbvOkxAw
go 1.25.4
require (
code.squareroundforest.org/arpio/bind v0.0.0-20251105181644-3443251be2d5 // indirect
code.squareroundforest.org/arpio/docreflect v0.0.0-20251031192707-01c5ff18fab1 // indirect
code.squareroundforest.org/arpio/html v0.0.0-20251103020946-e262eca50ac9 // indirect
code.squareroundforest.org/arpio/notation v0.0.0-20251101123932-5f5c05ee0239 // indirect
code.squareroundforest.org/arpio/textedit v0.0.0-20251207224821-c75c3965789f // indirect
code.squareroundforest.org/arpio/textfmt v0.0.0-20251207234108-fed32c8bbe18 // indirect
code.squareroundforest.org/arpio/wand v0.0.0-20251210193110-60acf6e82022 // indirect
github.com/iancoleman/strcase v0.3.0 // indirect
golang.org/x/sys v0.38.0 // indirect
golang.org/x/term v0.37.0 // indirect
)
replace code.squareroundforest.org/arpio/wand => /Users/arpio/work/code/wand

View File

@ -0,0 +1,20 @@
code.squareroundforest.org/arpio/bind v0.0.0-20251105181644-3443251be2d5 h1:SIgLIawD6Vv7rAvUobpVshLshdwFEJ0NOUrWpheS088=
code.squareroundforest.org/arpio/bind v0.0.0-20251105181644-3443251be2d5/go.mod h1:tTCmCwFABKNm3PO0Dclsp4zWhNQFTfg9+uSrgoarZFI=
code.squareroundforest.org/arpio/docreflect v0.0.0-20251031192707-01c5ff18fab1 h1:bJi41U5yGQykg6jVlD2AdWiznvx3Jg7ZpzEU85syOXw=
code.squareroundforest.org/arpio/docreflect v0.0.0-20251031192707-01c5ff18fab1/go.mod h1:/3xQI36oJG8qLBxT2fSS61P5/+i1T64fTX9GHRh8XhA=
code.squareroundforest.org/arpio/html v0.0.0-20251103020946-e262eca50ac9 h1:b7voJlwe0jKH568X+O7b/JTAUrHLTSKNSSL+hhV2Q/Q=
code.squareroundforest.org/arpio/html v0.0.0-20251103020946-e262eca50ac9/go.mod h1:hq+2CENEd4bVSZnOdq38FUFOJJnF3OTQRv78qMGkNlE=
code.squareroundforest.org/arpio/notation v0.0.0-20251101123932-5f5c05ee0239 h1:JvLVMuvF2laxXkIZbHC1/0xtKyKndAwIHbIIWkHqTzc=
code.squareroundforest.org/arpio/notation v0.0.0-20251101123932-5f5c05ee0239/go.mod h1:ait4Fvg9o0+bq5hlxi9dAcPL5a+/sr33qsZPNpToMLY=
code.squareroundforest.org/arpio/textedit v0.0.0-20251207224821-c75c3965789f h1:gomu8xTD953IkL3M528qVEuZ2z93C2I6Hr4vyIwE7kI=
code.squareroundforest.org/arpio/textedit v0.0.0-20251207224821-c75c3965789f/go.mod h1:nXdFdxdI69JrkIT97f+AEE4OgplmxbgNFZC5j7gsdqs=
code.squareroundforest.org/arpio/textfmt v0.0.0-20251207234108-fed32c8bbe18 h1:2aa62CYm9ld5SNoFxWzE2wUN0xjVWQ+xieoeFantdg4=
code.squareroundforest.org/arpio/textfmt v0.0.0-20251207234108-fed32c8bbe18/go.mod h1:+0G3gufMAP8SCEIrDT1D/DaVOSfjS8EwPTBs5vfxqQg=
code.squareroundforest.org/arpio/wand v0.0.0-20251210193110-60acf6e82022 h1:9XKwyrmOi2upsYWyjMQOPhO6ebl7AEHSViBVu9U/2jc=
code.squareroundforest.org/arpio/wand v0.0.0-20251210193110-60acf6e82022/go.mod h1:dwEF9zEIbOxbLxytOKSuRQPKmvbhPjDCJcoXM3j8cOU=
github.com/iancoleman/strcase v0.3.0 h1:nTXanmYxhfFAMjZL34Ov6gkzEsSJZ5DbhxWjvSASxEI=
github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU=
golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254=

View File

@ -0,0 +1,19 @@
package main
import "io"
import "code.squareroundforest.org/arpio/wand"
func main() {
wand.Exec(func(in io.Reader) (out io.Reader) {
buf := make([]byte, 1)
for {
n, err := in.Read(buf)
if n == 1 {
buf[0] = buf[0] * 2
out.Write(buf)
}
if err != nil {
return
}
}
})
}

2
lib.go
View File

@ -53,7 +53,7 @@ func Args(cmd Cmd, min, max int) Cmd {
func ShortForm(cmd Cmd, f ...string) Cmd {
if len(f)%2 != 0 {
f = append(f, "")
f = f[:len(f)-1]
}
cmd.shortForms = append(cmd.shortForms, f...)

View File

@ -1 +1,16 @@
package wand
import "testing"
func TestShortForm(t *testing.T) {
t.Run(
"undefined short form",
testExec(
testCase{
impl: ShortForm(Command("foo", func(struct{ Foo int }) {}), "f", "foo", "b"),
command: "foo -b",
},
"option not supported",
),
)
}

View File

@ -115,15 +115,6 @@ func isTime(t reflect.Type) bool {
return t.ConvertibleTo(reflect.TypeFor[time.Time]())
}
func isDuration(t reflect.Type) bool {
if t == nil {
return false
}
t = unpackType(t)
return t == reflect.TypeFor[time.Duration]()
}
func isStruct(t reflect.Type) bool {
if t == nil {
return false

View File

@ -44,7 +44,3 @@ func execInternal(command string, args ...string) error {
return nil
}
func execTransparent(command string, args ...string) error {
return execc(os.Stdin, os.Stdout, os.Stderr, command, args, nil)
}

View File

@ -15,12 +15,14 @@ import (
"unicode"
)
const selfPkg = "code.squareroundforest.org/arpio/wand"
type ExecOptions struct {
NoCache bool
ClearCache bool
CacheDir string
Import []string
InlineImport []string
ReplaceModule []string
}
func commandReader(in io.Reader) func() ([]string, error) {
@ -84,12 +86,6 @@ func commandReader(in io.Reader) func() ([]string, error) {
}
if escapePartial {
if escapeOne {
escapeOne = false
currentArg = append(currentArg, r)
continue
}
if r == '\\' {
escapeOne = true
continue
@ -140,12 +136,11 @@ func commandReader(in io.Reader) func() ([]string, error) {
}
}
func hash(expression string, imports, inlineImports []string) (string, error) {
func hash(expression string, imports []string) (string, error) {
h := fnv.New128()
h.Write([]byte(expression))
allImports := append(imports, inlineImports...)
sort.Strings(allImports)
for _, i := range allImports {
sort.Strings(imports)
for _, i := range imports {
h.Write([]byte(i))
}
@ -162,7 +157,7 @@ func hash(expression string, imports, inlineImports []string) (string, error) {
return strings.TrimPrefix(buf.String(), "_"), nil
}
func printGoFile(fn string, expression string, imports []string, inlineImports []string) error {
func printGoFile(fn string, expression string, imports []string) error {
f, err := os.Create(fn)
if err != nil {
return err
@ -179,23 +174,29 @@ func printGoFile(fn string, expression string, imports []string, inlineImports [
fprintf("package main\n")
for _, i := range imports {
fprintf("import \"%s\"\n", strings.Split(i, "@")[0])
ii := strings.Split(i, "=")
p := ii[0]
if len(ii) > 1 {
p = strings.Join(ii[1:], "=")
}
for _, i := range inlineImports {
fprintf("import . \"%s\"\n", strings.Split(i, "@")[0])
if len(ii) == 1 {
fprintf("import \"%s\"\n", strings.Split(p, "@")[0])
} else {
fprintf("import %s \"%s\"\n", ii[0], strings.Split(p, "@")[0])
}
}
fprintf("import \"code.squareroundforest.org/arpio/wand\"\n")
fprintf("import \"%s\"\n", selfPkg)
fprintf("func main() {\n")
fprintf("wand.Exec(%s)\n", expression)
fprintf("}")
return err
}
func execWand(o ExecOptions, args []string) error {
func execWand(o ExecOptions, stdin io.Reader, stdout, stderr io.Writer, args []string) error {
expression, args := args[0], args[1:]
commandHash, err := hash(expression, o.Import, o.InlineImport)
commandHash, err := hash(expression, o.Import)
if err != nil {
return err
}
@ -233,6 +234,14 @@ func execWand(o ExecOptions, args []string) error {
return nil
}
goReplace := func(replace string) error {
if err := execInternal("go mod edit -replace", replace); err != nil {
return fmt.Errorf("failed to get go module: %w", err)
}
return nil
}
if err := os.Chdir(commandDir); err != nil {
return fmt.Errorf("failed to switch to temporary directory: %w", err)
}
@ -244,31 +253,44 @@ func execWand(o ExecOptions, args []string) error {
return fmt.Errorf("failed to initialize temporary module: %w", err)
}
var hasSelf bool
for _, pkg := range o.Import {
if err := goGet(pkg); err != nil {
pp := strings.Split(pkg, "=")
p := pp[0]
if len(pp) > 1 {
p = strings.Join(pp[1:], "=")
}
if err := goGet(p); err != nil {
return err
}
if p == selfPkg {
hasSelf = true
}
}
if !hasSelf {
if err := goGet(selfPkg); err != nil {
return err
}
}
for _, pkg := range o.InlineImport {
if err := goGet(pkg); err != nil {
for _, replace := range o.ReplaceModule {
if err := goReplace(replace); err != nil {
return err
}
}
if err := goGet("code.squareroundforest.org/arpio/wand"); err != nil {
return err
}
}
goFile := path.Join(commandDir, fmt.Sprintf("%s.go", commandHash))
if _, err := os.Stat(goFile); err != nil {
if err := printGoFile(goFile, expression, o.Import, o.InlineImport); err != nil {
if err := printGoFile(goFile, expression, o.Import); err != nil {
return fmt.Errorf("failed to create temporary go file: %w", err)
}
}
if err := execTransparent("go run", append([]string{commandDir}, args...)...); err != nil {
if err := execc(stdin, stdout, stderr, "go run", append([]string{commandDir}, args...), nil); err != nil {
return err
}
@ -281,7 +303,7 @@ func execWand(o ExecOptions, args []string) error {
return nil
}
func readExec(o ExecOptions, stdin io.Reader) error {
func readExec(o ExecOptions, stdin io.Reader, stdout, stderr io.Writer) error {
readCommand := commandReader(stdin)
for {
args, err := readCommand()
@ -297,16 +319,20 @@ func readExec(o ExecOptions, stdin io.Reader) error {
continue
}
if err := execWand(o, args); err != nil {
fmt.Fprintln(os.Stderr, err)
if err := execWand(o, stdin, stdout, stderr, args); err != nil {
fmt.Fprintln(stderr, err)
}
}
}
func Exec(o ExecOptions, stdin io.Reader, args ...string) error {
func execInput(o ExecOptions, stdin io.Reader, stdout, stderr io.Writer, args ...string) error {
if len(args) == 0 {
return readExec(o, stdin)
return readExec(o, stdin, stdout, stderr)
}
return execWand(o, args)
return execWand(o, stdin, stdout, stderr, args)
}
func Exec(o ExecOptions, stdin io.Reader, stdout io.Writer, args ...string) error {
return execInput(o, stdin, stdout, os.Stderr, args...)
}

202
tools/execwand_test.go Normal file
View File

@ -0,0 +1,202 @@
package tools
import (
"bytes"
"code.squareroundforest.org/arpio/notation"
"fmt"
"os"
"path/filepath"
"testing"
)
func TestExec(t *testing.T) {
wd, err := os.Getwd()
if err != nil {
t.Fatal(err)
}
cacheDir := filepath.Join(wd, "../internal/tests/tmpdir")
selfReplace := filepath.Join(wd, "..")
baseOpt := ExecOptions{
ClearCache: true,
CacheDir: cacheDir,
ReplaceModule: []string{fmt.Sprintf("%s=%s", selfPkg, selfReplace)},
}
t.Run("stdin", func(t *testing.T) {
var stdout, stderr bytes.Buffer
in := bytes.NewBufferString("strings.Split 1,2,3 ,")
o := baseOpt
o.Import = []string{"strings"}
if err := execInput(o, in, &stdout, &stderr); err != nil {
t.Fatal(err)
}
if stderr.String() != "" {
t.Fatal(stderr.String())
}
if stdout.String() != "1\n2\n3\n" {
t.Fatal(stdout.String())
}
})
t.Run("stdin multiple lines", func(t *testing.T) {
var stdout, stderr bytes.Buffer
in := bytes.NewBufferString("strings.Split 1,2,3 ,\nstrings.Split 4,5,6 ,")
o := baseOpt
o.Import = []string{"strings"}
if err := execInput(o, in, &stdout, &stderr); err != nil {
t.Fatal(err)
}
if stderr.String() != "" {
t.Fatal(stderr.String())
}
if stdout.String() != "1\n2\n3\n4\n5\n6\n" {
t.Fatal(stdout.String())
}
})
t.Run("args", func(t *testing.T) {
var stdin, stdout, stderr bytes.Buffer
o := baseOpt
o.Import = []string{"strings"}
if err := execInput(o, &stdin, &stdout, &stderr, "strings.Split", "1,2,3", ","); err != nil {
t.Fatal(err)
}
if stderr.String() != "" {
t.Fatal(stderr.String())
}
if stdout.String() != "1\n2\n3\n" {
t.Fatal(stdout.String())
}
})
t.Run("inline import", func(t *testing.T) {
var stdin, stdout, stderr bytes.Buffer
o := baseOpt
o.Import = []string{".=strings"}
if err := execInput(o, &stdin, &stdout, &stderr, "Split", "1,2,3", ","); err != nil {
t.Fatal(err)
}
if stderr.String() != "" {
t.Fatal(stderr.String())
}
if stdout.String() != "1\n2\n3\n" {
t.Fatal(stdout.String())
}
})
t.Run("named import", func(t *testing.T) {
var stdin, stdout, stderr bytes.Buffer
o := baseOpt
o.Import = []string{"str=strings"}
if err := execInput(o, &stdin, &stdout, &stderr, "str.Split", "1,2,3", ","); err != nil {
t.Fatal(err)
}
if stderr.String() != "" {
t.Fatal(stderr.String())
}
if stdout.String() != "1\n2\n3\n" {
t.Fatal(stdout.String())
}
})
t.Run("io args", func(t *testing.T) {
var stdout, stderr bytes.Buffer
in := bytes.NewBuffer([]byte{0, 1, 2})
o := baseOpt
o.Import = []string{"io", "bytes"}
expr := `func(in io.Reader) (out io.Reader) {
out = bytes.NewBuffer(nil)
buf := make([]byte, 1)
for {
n, err := in.Read(buf)
if n == 1 {
buf[0] = buf[0] * 2
out.(io.Writer).Write(buf)
}
if err != nil {
return out
}
}
return out
}`
if err := execInput(o, in, &stdout, &stderr, expr); err != nil {
t.Log(stderr.String())
t.Fatal(err)
}
if stderr.String() != "" {
t.Fatal(stderr.String())
}
if stdout.Len() != 3 || stdout.Bytes()[0] != 0 || stdout.Bytes()[1] != 2 || stdout.Bytes()[2] != 4 {
t.Fatal(notation.Sprint(stdout.Bytes()))
}
})
t.Run("escape", func(t *testing.T) {
t.Run("full", func(t *testing.T) {
var stdout, stderr bytes.Buffer
in := bytes.NewBufferString("'func(a string) { println(a) }' 'foo'")
if err := execInput(baseOpt, in, &stdout, &stderr); err != nil {
t.Log(stderr.String())
t.Fatal(err)
}
if stderr.String() != "foo\n" {
t.Fatal(stderr.String())
}
if stdout.String() != "" {
t.Fatal(stdout.String())
}
})
t.Run("one", func(t *testing.T) {
var stdout, stderr bytes.Buffer
in := bytes.NewBufferString("'func(a string) { println(a) }' foo\\ bar")
if err := execInput(baseOpt, in, &stdout, &stderr); err != nil {
t.Log(stderr.String())
t.Fatal(err)
}
if stderr.String() != "foo bar\n" {
t.Fatal(stderr.String())
}
if stdout.String() != "" {
t.Fatal(stdout.String())
}
})
t.Run("one and partial", func(t *testing.T) {
var stdout, stderr bytes.Buffer
in := bytes.NewBufferString("'func(a string) { println(a) }' \"\\'foo\\'\"")
if err := execInput(baseOpt, in, &stdout, &stderr); err != nil {
t.Log(stderr.String())
t.Fatal(err)
}
if stderr.String() != "'foo'\n" {
t.Fatal(stderr.String())
}
if stdout.String() != "" {
t.Fatal(stdout.String())
}
})
})
}

314
tools/lib_test.go Normal file
View File

@ -0,0 +1,314 @@
package tools_test
import (
"bytes"
"code.squareroundforest.org/arpio/wand/tools"
"testing"
)
func TestDocreflect(t *testing.T) {
var b bytes.Buffer
if err := tools.Docreflect(&b, "testlib", "code.squareroundforest.org/arpio/wand/internal/tests/testlib"); err != nil {
t.Fatal(err)
}
const expect = `/*
Generated with https://code.squareroundforest.org/arpio/docreflect
*/
package testlib
import "code.squareroundforest.org/arpio/docreflect"
func init() {
docreflect.Register("code.squareroundforest.org/arpio/wand/internal/tests/testlib", "")
docreflect.Register("code.squareroundforest.org/arpio/wand/internal/tests/testlib.Bar", "\nfunc(out, a, b, c)")
docreflect.Register("code.squareroundforest.org/arpio/wand/internal/tests/testlib.Baz", "\nfunc(o)")
docreflect.Register("code.squareroundforest.org/arpio/wand/internal/tests/testlib.CustomHelp", "\nfunc(o)")
docreflect.Register("code.squareroundforest.org/arpio/wand/internal/tests/testlib.Foo", "Foo sums three numbers.\nIt prints the sum to stdout.\n\nThe input numbers can be any integer.\n\nfunc(a, b, c)")
docreflect.Register("code.squareroundforest.org/arpio/wand/internal/tests/testlib.OptionWithHelp", "")
docreflect.Register("code.squareroundforest.org/arpio/wand/internal/tests/testlib.OptionWithHelp.Help", "Custom help.\n")
docreflect.Register("code.squareroundforest.org/arpio/wand/internal/tests/testlib.Options", "")
docreflect.Register("code.squareroundforest.org/arpio/wand/internal/tests/testlib.Options.Bar", "Bars, any number.\n")
docreflect.Register("code.squareroundforest.org/arpio/wand/internal/tests/testlib.Options.Duration", "Duration is another option.\n")
docreflect.Register("code.squareroundforest.org/arpio/wand/internal/tests/testlib.Options.Foo", "Foo is an option.\n")
docreflect.Register("code.squareroundforest.org/arpio/wand/internal/tests/testlib.Options.Some", "Some is an option of any type.\n")
docreflect.Register("code.squareroundforest.org/arpio/wand/internal/tests/testlib.Options.Time", "Time is the third option here.\n")
}`
if b.String() != expect {
t.Fatal(b.String())
}
}
func TestMan(t *testing.T) {
var b bytes.Buffer
if err := tools.Man(
&b,
tools.ManOptions{DateString: "2025-12-09", Version: "v0.9"},
"../internal/tests/testlib/cmd",
); err != nil {
t.Fatal(err)
}
const expect = `.TH "test" 1 "December 2025" "v0.9" "User Commands"
.br
.sp 1v
.in 0
.ti 0
\fBName:\fR
.br
.sp 1v
.in 4
.ti 4
test
.br
.sp 1v
.in 0
.ti 0
\fBSynopsis:\fR
.br
.sp 1v
.nf
test [options]... [--] <arg1 int> <arg2 int> <arg3 int>
test <subcommand>
.fi
.br
.sp 1v
.in 0
.ti 0
\fBOptions:\fR
.br
.sp 1v
.in 12
.ti 4
--help:\~Show help.
.br
.sp 1v
.in 0
.ti 0
\fBSubcommands:\fR
.br
.sp 1v
.in 0
.ti 0
Show help for each subcommand by calling <command> help or <command> --help.
.br
.sp 1v
.in 2
.ti 2
\fBtest foo (default)\fR
.br
.sp 1v
.in 4
.ti 4
\fBSynopsis:\fR
.br
.sp 1v
.nf
test foo [options]... [--] <arg1 int> <arg2 int> <arg3 int>
test foo <subcommand>
.fi
.br
.sp 1v
.in 4
.ti 4
\fBOptions:\fR
.br
.sp 1v
.in 12
.ti 4
--help:\~Show help.
.br
.sp 1v
.in 2
.ti 2
\fBtest bar\fR
.br
.sp 1v
.in 4
.ti 4
\fBSynopsis:\fR
.br
.sp 1v
.nf
test bar [options]... [--] <arg1 int> <arg2 int> <arg3 int>
test bar <subcommand>
.fi
.br
.sp 1v
.in 4
.ti 4
\fBOptions:\fR
.br
.sp 1v
.in 12
.ti 4
--help:\~Show help.
.br
.sp 1v
.in 2
.ti 2
\fBtest baz\fR
.br
.sp 1v
.in 4
.ti 4
\fBSynopsis:\fR
.br
.sp 1v
.nf
test baz [options]...
test baz <subcommand>
.fi
.br
.sp 1v
.in 4
.ti 4
\fBOptions:\fR
.br
.sp 1v
.in 25
.ti 4
--bar string [*]:\~\~\~\~
.br
.in 25
.ti 4
--duration duration:\~
.br
.in 25
.ti 4
--foo int:\~\~\~\~\~\~\~\~\~\~\~
.br
.in 25
.ti 4
--some any:\~\~\~\~\~\~\~\~\~\~
.br
.in 25
.ti 4
--time time:\~\~\~\~\~\~\~\~\~
.br
.in 25
.ti 4
--help:\~\~\~\~\~\~\~\~\~\~\~\~\~\~Show help.
.br
.sp 1v
.in 0
.ti 0
\fBEnvironment variables:\fR
.br
.sp 1v
.in 4
.ti 4
Every command line option's value can also be provided as an environment variable. Environment variable names need to use snake casing like myapp_foo_bar_baz or MYAPP_FOO_BAR_BAZ, or other casing that doesn't include the '-' dash character, and they need to be prefixed with the name of the application, as in the base name of the command.
.br
.sp 1v
.in 4
.ti 4
When both the environment variable and the command line option is defined, the command line option overrides the environment variable. Multiple values for the same environment variable can be defined by concatenating the values with the ':' separator character. When overriding multiple values with command line options, all the environment values of the same field are dropped.
.br
.sp 1v
.in 4
.ti 4
\fBExample environment variable:\fR
.br
.sp 1v
.nf
TEST_FOO=42
.fi
`
if b.String() != expect {
t.Fatal(b.String())
}
}
func TestMarkdown(t *testing.T) {
var b bytes.Buffer
if err := tools.Markdown(&b, tools.MarkdownOptions{}, "../internal/tests/testlib/cmd"); err != nil {
t.Fatal(err)
}
const expect = `# test
## Synopsis:
` + "```" + `
test [options]... [--] <arg1 int> <arg2 int> <arg3 int>
test <subcommand>
` + "```" + `
## Options:
- --help: Show help.
## Subcommands:
Show help for each subcommand by calling \<command\> help or \<command\> --help.
### test foo (default)
#### Synopsis:
` + "```" + `
test foo [options]... [--] <arg1 int> <arg2 int> <arg3 int>
test foo <subcommand>
` + "```" + `
#### Options:
- --help: Show help.
### test bar
#### Synopsis:
` + "```" + `
test bar [options]... [--] <arg1 int> <arg2 int> <arg3 int>
test bar <subcommand>
` + "```" + `
#### Options:
- --help: Show help.
### test baz
#### Synopsis:
` + "```" + `
test baz [options]...
test baz <subcommand>
` + "```" + `
#### Options:
- --bar string \[\*\]:
- --duration duration:
- --foo int:
- --some any:
- --time time:
- --help: Show help.
## Environment variables:
Every command line option's value can also be provided as an environment variable. Environment variable names
need to use snake casing like myapp\_foo\_bar\_baz or MYAPP\_FOO\_BAR\_BAZ, or other casing that doesn't include the
'-' dash character, and they need to be prefixed with the name of the application, as in the base name of the
command.
When both the environment variable and the command line option is defined, the command line option overrides the
environment variable. Multiple values for the same environment variable can be defined by concatenating the
values with the ':' separator character. When overriding multiple values with command line options, all the
environment values of the same field are dropped.
### Example environment variable:
` + "```" + `
TEST_FOO=42
` + "```" + `
`
if b.String() != expect {
t.Fatal(b.String())
}
}