From 7c0266e94378f6121719939c6d53915eb72eed3e Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Sat, 6 Sep 2025 15:04:37 +0300 Subject: 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 --- cmd/internal/hexai-action/main.go | 42 +++++++++++++++++++++++++++++++- cmd/internal/hexai-action/main_test.go | 44 ++++++++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+), 1 deletion(-) create mode 100644 cmd/internal/hexai-action/main_test.go (limited to 'cmd') 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) + } +} + -- cgit v1.2.3