summaryrefslogtreecommitdiff
path: root/cmd
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2025-09-06 15:04:37 +0300
committerPaul Buetow <paul@buetow.org>2025-09-06 15:04:37 +0300
commit7c0266e94378f6121719939c6d53915eb72eed3e (patch)
tree64017871e9516fbe3cf2dfbd63729e790ed1e0ef /cmd
parente2418d22e0ff8d5d8bb883cf38b47dc94a6c308e (diff)
feat(hexai-action): add --infile/--outfile flags; docs and tests\n\n- Add flags to read from file and write to file\n- Refactor IO open into helper for testability\n- Add CLI integration-style test for IO\n- Update README and docs/usage.md with examples\n- Update docs/testing.md with instructions
Diffstat (limited to 'cmd')
-rw-r--r--cmd/internal/hexai-action/main.go42
-rw-r--r--cmd/internal/hexai-action/main_test.go44
2 files changed, 85 insertions, 1 deletions
diff --git a/cmd/internal/hexai-action/main.go b/cmd/internal/hexai-action/main.go
index 50e6774..8bcc3cd 100644
--- a/cmd/internal/hexai-action/main.go
+++ b/cmd/internal/hexai-action/main.go
@@ -2,15 +2,55 @@ package main
import (
"context"
+ "flag"
"fmt"
+ "io"
"os"
"codeberg.org/snonux/hexai/internal/hexaiaction"
)
func main() {
- if err := hexaiaction.Run(context.Background(), os.Stdin, os.Stdout, os.Stderr); err != nil {
+ infile := flag.String("infile", "", "Read input from this file instead of stdin")
+ outfile := flag.String("outfile", "", "Write output to this file instead of stdout")
+ flag.Parse()
+
+ in, out, closeIn, closeOut, err := openIO(*infile, *outfile)
+ if err != nil {
+ fmt.Fprintln(os.Stderr, err)
+ os.Exit(1)
+ }
+ defer closeIn()
+ defer closeOut()
+
+ if err := hexaiaction.Run(context.Background(), in, out, os.Stderr); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}
+
+// openIO returns readers/writers for infile/outfile flags with deferred closers.
+func openIO(infile, outfile string) (io.Reader, io.Writer, func(), func(), error) {
+ in := io.Reader(os.Stdin)
+ out := io.Writer(os.Stdout)
+ closeIn := func() {}
+ closeOut := func() {}
+
+ if path := infile; path != "" {
+ f, err := os.Open(path)
+ if err != nil {
+ return nil, nil, func() {}, func() {}, fmt.Errorf("hexai-action: cannot open infile: %w", err)
+ }
+ in = f
+ closeIn = func() { _ = f.Close() }
+ }
+ if path := outfile; path != "" {
+ f, err := os.Create(path)
+ if err != nil {
+ return nil, nil, func() {}, func() {}, fmt.Errorf("hexai-action: cannot open outfile: %w", err)
+ }
+ out = f
+ closeOut = func() { _ = f.Close() }
+ }
+ return in, out, closeIn, closeOut, nil
+}
diff --git a/cmd/internal/hexai-action/main_test.go b/cmd/internal/hexai-action/main_test.go
new file mode 100644
index 0000000..9603826
--- /dev/null
+++ b/cmd/internal/hexai-action/main_test.go
@@ -0,0 +1,44 @@
+package main
+
+import (
+ "io"
+ "os"
+ "path/filepath"
+ "testing"
+)
+
+// TestOpenIO_InOutFiles verifies that openIO opens the specified files
+// and that writing via the returned writer persists to disk.
+func TestOpenIO_InOutFiles(t *testing.T) {
+ dir := t.TempDir()
+ inPath := filepath.Join(dir, "in.txt")
+ outPath := filepath.Join(dir, "out.txt")
+
+ // Prepare input file
+ want := "hello world"
+ if err := os.WriteFile(inPath, []byte(want), 0o600); err != nil {
+ t.Fatalf("write infile: %v", err)
+ }
+
+ in, out, cin, cout, err := openIO(inPath, outPath)
+ if err != nil {
+ t.Fatalf("openIO: %v", err)
+ }
+ defer cin()
+ defer cout()
+
+ // Copy through to simulate main's behavior
+ if _, err := io.Copy(out.(io.Writer), in); err != nil {
+ t.Fatalf("copy: %v", err)
+ }
+
+ // Verify outfile content
+ got, err := os.ReadFile(outPath)
+ if err != nil {
+ t.Fatalf("read outfile: %v", err)
+ }
+ if string(got) != want {
+ t.Fatalf("mismatch: got %q want %q", string(got), want)
+ }
+}
+