forward iteration
This commit is contained in:
85
lsm2/cli/main.go
Normal file
85
lsm2/cli/main.go
Normal file
@@ -0,0 +1,85 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"iter"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/docopt/docopt-go"
|
||||
"go.sour.is/pkg/lsm2"
|
||||
)
|
||||
|
||||
var usage = `
|
||||
Usage: lsm2 create <archive> <files>...`
|
||||
|
||||
type args struct {
|
||||
Create bool
|
||||
Archive string `docopt:"<archive>"`
|
||||
Files []string `docopt:"<files>"`
|
||||
}
|
||||
|
||||
func main() {
|
||||
opts, err := docopt.ParseDoc(usage)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Println(opts, must(opts.Bool("create")))
|
||||
console := console{os.Stdin, os.Stdout, os.Stderr}
|
||||
args := args{}
|
||||
err = opts.Bind(&args)
|
||||
fmt.Println(err)
|
||||
run(console, args)
|
||||
}
|
||||
|
||||
type console struct {
|
||||
Stdin io.Reader
|
||||
Stdout io.Writer
|
||||
Stderr io.Writer
|
||||
}
|
||||
|
||||
func (c console) Write(b []byte) (int, error) {
|
||||
return c.Stdout.Write(b)
|
||||
}
|
||||
|
||||
func run(console console, args args) error {
|
||||
switch {
|
||||
case args.Create:
|
||||
fmt.Fprintf(console, "creating %s from %v\n", filepath.Base(args.Archive), args.Files)
|
||||
out, err := os.OpenFile(args.Archive, os.O_CREATE|os.O_WRONLY, 0644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer out.Close()
|
||||
|
||||
filesWritten := 0
|
||||
defer func() { fmt.Fprintln(console, "wrote", filesWritten, "files") }()
|
||||
|
||||
return lsm2.WriteIter(out, iter.Seq[io.Reader](func(yield func(io.Reader) bool) {
|
||||
for _, name := range args.Files {
|
||||
f, err := os.Open(name)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
filesWritten++
|
||||
if !yield(f) {
|
||||
f.Close()
|
||||
return
|
||||
}
|
||||
f.Close()
|
||||
}
|
||||
}))
|
||||
default:
|
||||
return errors.New("unknown command")
|
||||
}
|
||||
}
|
||||
|
||||
func must[T any](v T, err error) T {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return v
|
||||
}
|
||||
104
lsm2/cli/main_test.go
Normal file
104
lsm2/cli/main_test.go
Normal file
@@ -0,0 +1,104 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCreate(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
wantErr bool
|
||||
wantOutput string
|
||||
}{
|
||||
{
|
||||
name: "no input files",
|
||||
args: args{
|
||||
Create: true,
|
||||
Archive: "test.txt",
|
||||
Files: []string{},
|
||||
},
|
||||
wantErr: false,
|
||||
wantOutput: "creating test.txt from []\nwrote 0 files\n",
|
||||
},
|
||||
{
|
||||
name: "one input file",
|
||||
args: args{
|
||||
Create: true,
|
||||
Archive: "test.txt",
|
||||
Files: []string{"test_input.txt"},
|
||||
},
|
||||
wantErr: false,
|
||||
wantOutput: "creating test.txt from [test_input.txt]\nwrote 1 files\n",
|
||||
},
|
||||
{
|
||||
name: "multiple input files",
|
||||
args: args{
|
||||
Create: true,
|
||||
Archive: "test.txt",
|
||||
Files: []string{"test_input1.txt", "test_input2.txt"},
|
||||
},
|
||||
wantErr: false,
|
||||
wantOutput: "creating test.txt from [test_input1.txt test_input2.txt]\nwrote 2 files\n",
|
||||
},
|
||||
{
|
||||
name: "non-existent input files",
|
||||
args: args{
|
||||
Create: true,
|
||||
Archive: "test.txt",
|
||||
Files: []string{"non_existent_file.txt"},
|
||||
}, wantErr: false,
|
||||
wantOutput: "creating test.txt from [non_existent_file.txt]\nwrote 0 files\n",
|
||||
},
|
||||
{
|
||||
name: "invalid command",
|
||||
args: args{
|
||||
Create: false,
|
||||
Archive: "test.txt",
|
||||
Files: []string{},
|
||||
},
|
||||
wantErr: true,
|
||||
wantOutput: "",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
// Create a temporary directory for the input files
|
||||
tmpDir, err := os.MkdirTemp("", "lsm2-cli-test")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(tmpDir)
|
||||
os.Chdir(tmpDir)
|
||||
|
||||
// Create the input files
|
||||
for _, file := range tc.args.Files {
|
||||
if file == "non_existent_file.txt" {
|
||||
continue
|
||||
}
|
||||
if err := os.WriteFile(file, []byte(file), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Create a buffer to capture the output
|
||||
var output bytes.Buffer
|
||||
|
||||
// Call the create function
|
||||
err = run(console{Stdout: &output}, tc.args)
|
||||
|
||||
// Check the output
|
||||
if output.String() != tc.wantOutput {
|
||||
t.Errorf("run() output = %q, want %q", output.String(), tc.wantOutput)
|
||||
}
|
||||
|
||||
// Check for errors
|
||||
if tc.wantErr && err == nil {
|
||||
t.Errorf("run() did not return an error")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user