diff --git a/cmd/notarius/main.go b/cmd/notarius/main.go new file mode 100644 index 0000000..3755b7c --- /dev/null +++ b/cmd/notarius/main.go @@ -0,0 +1,11 @@ +package main + +import ( + "os" + + "gitea.maximumdirect.net/eric/notarius/internal/cli" +) + +func main() { + os.Exit(cli.Run(os.Args[1:], os.Stdout, os.Stderr)) +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..1d49f68 --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module gitea.maximumdirect.net/eric/notarius + +go 1.24.0 diff --git a/internal/cli/run.go b/internal/cli/run.go new file mode 100644 index 0000000..a931c07 --- /dev/null +++ b/internal/cli/run.go @@ -0,0 +1,30 @@ +package cli + +import ( + "fmt" + "io" +) + +const usage = "Usage:\n notarius help\n" + +// Run executes the command-line interface and returns a process exit code. +func Run(args []string, stdout, stderr io.Writer) int { + if len(args) == 0 { + writeUsage(stdout) + return 0 + } + + switch args[0] { + case "help", "--help", "-h": + writeUsage(stdout) + return 0 + default: + fmt.Fprintf(stderr, "notarius: unknown command %q\n", args[0]) + writeUsage(stderr) + return 2 + } +} + +func writeUsage(w io.Writer) { + fmt.Fprint(w, usage) +} diff --git a/internal/cli/run_test.go b/internal/cli/run_test.go new file mode 100644 index 0000000..3d200fe --- /dev/null +++ b/internal/cli/run_test.go @@ -0,0 +1,75 @@ +package cli + +import ( + "bytes" + "strings" + "testing" +) + +func TestRunNoArgsWritesUsageToStdout(t *testing.T) { + var stdout bytes.Buffer + var stderr bytes.Buffer + + code := Run(nil, &stdout, &stderr) + + if code != 0 { + t.Fatalf("Run() code = %d, want 0", code) + } + if stdout.String() != usage { + t.Fatalf("stdout = %q, want %q", stdout.String(), usage) + } + if stderr.Len() != 0 { + t.Fatalf("stderr = %q, want empty", stderr.String()) + } +} + +func TestRunHelpArgsWriteUsageToStdout(t *testing.T) { + tests := []struct { + name string + args []string + }{ + {name: "help", args: []string{"help"}}, + {name: "long help flag", args: []string{"--help"}}, + {name: "short help flag", args: []string{"-h"}}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var stdout bytes.Buffer + var stderr bytes.Buffer + + code := Run(tt.args, &stdout, &stderr) + + if code != 0 { + t.Fatalf("Run() code = %d, want 0", code) + } + if stdout.String() != usage { + t.Fatalf("stdout = %q, want %q", stdout.String(), usage) + } + if stderr.Len() != 0 { + t.Fatalf("stderr = %q, want empty", stderr.String()) + } + }) + } +} + +func TestRunUnknownCommandWritesErrorAndUsageToStderr(t *testing.T) { + var stdout bytes.Buffer + var stderr bytes.Buffer + + code := Run([]string{"extract"}, &stdout, &stderr) + + if code != 2 { + t.Fatalf("Run() code = %d, want 2", code) + } + if stdout.Len() != 0 { + t.Fatalf("stdout = %q, want empty", stdout.String()) + } + gotStderr := stderr.String() + if !strings.Contains(gotStderr, "notarius: unknown command \"extract\"") { + t.Fatalf("stderr = %q, want unknown command error", gotStderr) + } + if !strings.Contains(gotStderr, usage) { + t.Fatalf("stderr = %q, want usage", gotStderr) + } +}