diff --git a/Makefile b/Makefile index cbdc9a3..a4553fc 100644 --- a/Makefile +++ b/Makefile @@ -28,17 +28,17 @@ fmt: $(sources) iniparser.gen.go docreflect.gen.go go fmt ./... iniparser.gen.go: ini.treerack - go run script/ini-parser/parser.go wand < ini.treerack > iniparser.gen.go || rm -f iniparser.gen.go + go run internal/script/ini-parser/parser.go wand < ini.treerack > iniparser.gen.go || rm -f iniparser.gen.go docreflect.gen.go: $(sources) - go run script/docreflect/docs.go \ + go run internal/script/docreflect/docs.go \ wand \ code.squareroundforest.org/arpio/wand/tools \ > docreflect.gen.go \ || rm -f docreflect.gen.go docreflect_test.go: $(sources) - go run script/docreflect/docs.go \ + go run internal/script/docreflect/docs.go \ wand \ code.squareroundforest.org/arpio/wand/internal/tests/testlib \ > docreflect_test.go \ @@ -54,10 +54,10 @@ docreflect_test.go: $(sources) ./cmd/wand .build/wand.1: $(sources) iniparser.gen.go docreflect.gen.go .build - go run script/man/man.go $(date) $(version) ./cmd/wand > .build/wand.1 + go run internal/script/man/man.go $(date) $(version) ./cmd/wand > .build/wand.1 cmd/wand/readme.md: $(sources) docreflect.gen.go - go run script/markdown/md.go 0 ./cmd/wand > cmd/wand/readme.md + go run internal/script/markdown/md.go 0 ./cmd/wand > cmd/wand/readme.md $(prefix)/bin: mkdir -p $(prefix)/bin diff --git a/example/stdlib/go.mod b/example/stdlib/go.mod new file mode 100644 index 0000000..d0fd51a --- /dev/null +++ b/example/stdlib/go.mod @@ -0,0 +1,19 @@ +module example/stdlib + +go 1.25.4 + +require code.squareroundforest.org/arpio/wand v0.0.0-20260107174558-dd6be747c6ec + +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 + 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 => ../.. diff --git a/example/stdlib/go.sum b/example/stdlib/go.sum new file mode 100644 index 0000000..c51f2cb --- /dev/null +++ b/example/stdlib/go.sum @@ -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-20260107174558-dd6be747c6ec h1:jWW2QBcMMe1VNfTGG9gSJAE7x4ynvhEp0riJ7sLA7MM= +code.squareroundforest.org/arpio/wand v0.0.0-20260107174558-dd6be747c6ec/go.mod h1:rYqrSmdkBlKjGwEPzzWAIRQKQJCpkdzG7vDiL6Fux9Y= +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= diff --git a/example/stdlib/split.go b/example/stdlib/split.go new file mode 100644 index 0000000..83938ac --- /dev/null +++ b/example/stdlib/split.go @@ -0,0 +1,10 @@ +package main + +import ( + "strings" + "code.squareroundforest.org/arpio/wand" +) + +func main() { + wand.Exec(strings.Split) +} diff --git a/script/docreflect/docs.go b/internal/script/docreflect/docs.go similarity index 100% rename from script/docreflect/docs.go rename to internal/script/docreflect/docs.go diff --git a/script/ini-parser/parser.go b/internal/script/ini-parser/parser.go similarity index 100% rename from script/ini-parser/parser.go rename to internal/script/ini-parser/parser.go diff --git a/script/man/man.go b/internal/script/man/man.go similarity index 100% rename from script/man/man.go rename to internal/script/man/man.go diff --git a/script/markdown/md.go b/internal/script/markdown/md.go similarity index 100% rename from script/markdown/md.go rename to internal/script/markdown/md.go diff --git a/readme.md b/readme.md index 2924028..ac7fd3d 100644 --- a/readme.md +++ b/readme.md @@ -1,3 +1,143 @@ # Wand +Wand is a library to wrap Go functions as executable binaries. It supports: + +- hierarch of subcommands +- binding of command line options +- binding of environment variables +- binding of configuration entries +- positional arguments +- automatically generated help and documentation from go doc + +## Example: + +``` +package main + +import ( + "strings" + "code.squareroundforest.org/arpio/wand" +) + +func main() { + wand.Exec(strings.Split) +} +``` + +## Lib docs: + +- [./lib.go](./lib.go) +- [go docs html](https://godocs.io/code.squareroundforest.org/arpio/wand) + +## wand tool + +The wand tool helps with following tasks: + +- generating code from the go doc of the library that is converted by the wand library into an executable binary +- generating man(roff) or markdown documentation +- executing arbitrary Go functions in the command line addressed by their go path + +Generating code with the docs of the wrapped library is particularly useful, because this way the automatically +generated help can be based on the go docs. This is an important part of the attempt to achieve that ideal +situation, where a solution to a problem is presented both as a library and a command line tool, and there is +only a single place ("source of truth") of documentation. + +Executing arbitrary Go functions in the command line is only a lucky side effect of the way wand was built. +While it's not the primary function of the tool, it can be useful when just quickly trying out some Go functions +or expressions, without having to create a throwaway main.go file. + +### wand tool installation + +``` +sudo make install +``` + +or in the current user's home: + +``` +prefix=~/.local make install +``` + +(See the recommended way below on how to use the locked version in the build tooling of a project.) + +tool usage: docreflect for lib and man for cmd + +### wand tool usage + +Let's consider a project hierarchy such as in the root we have a library (mylib), and in the ./cmd directory we +have a main package, wrapping one or more functions of that library with wand as an executable binary: + +``` +./cmd/main.go +./lib.go +``` + +Generating the compiled docs from go doc of a library: + +``` +wand docreflect mylib . +``` + +Generating a man page for the command: + +``` +wand manpages --date-string $(2026-01-07) --version v1.0.0 ./cmd +``` + +### wand tool bonus usage + +``` +wand --import strings strings.Split 1:2:3 : +``` + +### wand tool docs + +- [./cmd/readme.md](./cmd/readme.md) + +When installed: + +``` +man wand +``` + +When not installed with man pages: + +``` +wand help +``` + +### wand tool usage with go.mod version + +When generating the docs for a shared project, we may want to ensure that always the same version of wand is +used for both docreflect and manpages. Best is to use the version that is defined in go.mod. This can be +achieved using trivial scripts, the example below showing docreflect: + +``` +package main + +import ( + "code.squareroundforest.org/arpio/wand/tools" + "log" + "os" +) + +func main() { + if len(os.Args) < 2 { + log.Fatalln("expected package name") + } + + if err := tools.Docreflect(os.Stdout, os.Args[1], os.Args[2:]...); err != nil { + log.Fatalln(err) + } +} +``` + +and calling it, e.g. from a Makefile, with go run like: + +``` +go run script/gendocs.go mylib . gopath.to/me/mylib > docs.gen.go +``` + +--- + *Made in Berlin, DE*