diff --git a/Makefile b/Makefile index a09e526..675b412 100644 --- a/Makefile +++ b/Makefile @@ -5,15 +5,17 @@ VERSION?=${shell git describe --tags} all: lint test build -run: - @echo "building ${VERSION}" - go run ./cmd/jlv assets/example.log -.PHONY: build +run: build + ./bin/jlv assets/example.log +.PHONY: run -run.stdin: - @echo "building ${VERSION}" - go run ./cmd/jlv < assets/example.log -.PHONY: build +run.version: build + ./bin/jlv --version +.PHONY: run.version + +run.stdin: build + ./bin/jlv < assets/example.log +.PHONY: run.stdin build: @echo "building ${VERSION}" diff --git a/cmd/jlv/main.go b/cmd/jlv/main.go index b3f57cd..9fee1a9 100644 --- a/cmd/jlv/main.go +++ b/cmd/jlv/main.go @@ -1,6 +1,7 @@ package main import ( + "bytes" "flag" "fmt" "os" @@ -15,12 +16,23 @@ import ( "github.com/hedhyw/json-log-viewer/internal/pkg/source/readerinput" ) +// version will be set on build. +var version = "development" + const configFileName = ".jlv.jsonc" func main() { configPath := flag.String("config", "", "Path to the config") + printVersion := flag.Bool("version", false, "Print version") flag.Parse() + if *printVersion { + // nolint: forbidigo // Version command. + print("github.com/hedhyw/json-log-viewer@" + version + "\n") + + return + } + cfg, err := readConfig(*configPath) if err != nil { fatalf("Error reading config: %s\n", err) @@ -30,21 +42,37 @@ func main() { switch flag.NArg() { case 0: - sourceInput = readerinput.New(os.Stdin, cfg.StdinReadTimeout) + sourceInput, err = getStdinSource(cfg) + if err != nil { + fatalf("Stdin: %s\n", err) + } case 1: sourceInput = fileinput.New(flag.Arg(0)) default: fatalf("Invalid arguments, usage: %s file.log\n", os.Args[0]) } - appModel := app.NewModel(sourceInput, cfg) - program := tea.NewProgram(appModel, tea.WithAltScreen()) + appModel := app.NewModel(sourceInput, cfg, version) + program := tea.NewProgram(appModel, tea.WithInputTTY(), tea.WithAltScreen()) if _, err := program.Run(); err != nil { fatalf("Error running program: %s\n", err) } } +func getStdinSource(cfg *config.Config) (source.Input, error) { + stat, err := os.Stdin.Stat() + if err != nil { + return nil, fmt.Errorf("stat: %w", err) + } + + if stat.Mode()&os.ModeNamedPipe == 0 { + return readerinput.New(bytes.NewReader(nil), cfg.StdinReadTimeout), nil + } + + return readerinput.New(os.Stdin, cfg.StdinReadTimeout), nil +} + func fatalf(message string, args ...any) { fmt.Fprintf(os.Stderr, message, args...) os.Exit(1) diff --git a/internal/app/app.go b/internal/app/app.go index 2cffba3..89b45a0 100644 --- a/internal/app/app.go +++ b/internal/app/app.go @@ -17,9 +17,15 @@ type Application struct { FooterStyle lipgloss.Style LastWindowSize tea.WindowSizeMsg + + Version string } -func newApplication(sourceInput source.Input, config *config.Config) Application { +func newApplication( + sourceInput source.Input, + config *config.Config, + version string, +) Application { const ( initialWidth = 70 initialHeight = 20 @@ -36,11 +42,17 @@ func newApplication(sourceInput source.Input, config *config.Config) Application Width: initialWidth, Height: initialHeight, }, + + Version: version, } } // NewModel initializes a new application model. It accept the path // to the file with logs. -func NewModel(sourceInput source.Input, config *config.Config) tea.Model { - return newStateInitial(newApplication(sourceInput, config)) +func NewModel( + sourceInput source.Input, + config *config.Config, + version string, +) tea.Model { + return newStateInitial(newApplication(sourceInput, config, version)) } diff --git a/internal/app/app_test.go b/internal/app/app_test.go index 2122c71..195baa1 100644 --- a/internal/app/app_test.go +++ b/internal/app/app_test.go @@ -14,12 +14,14 @@ import ( "github.com/hedhyw/json-log-viewer/internal/pkg/tests" ) +const testVersion = "v0.0.1" + func newTestModel(tb testing.TB, content []byte) tea.Model { tb.Helper() testFile := tests.RequireCreateFile(tb, content) - model := app.NewModel(fileinput.New(testFile), config.GetDefaultConfig()) + model := app.NewModel(fileinput.New(testFile), config.GetDefaultConfig(), testVersion) model = handleUpdate(model, model.Init()()) return model diff --git a/internal/app/stateinitial_test.go b/internal/app/stateinitial_test.go index 388605b..e197e4d 100644 --- a/internal/app/stateinitial_test.go +++ b/internal/app/stateinitial_test.go @@ -22,6 +22,7 @@ func TestStateInitial(t *testing.T) { model := app.NewModel( readerinput.New(bytes.NewReader([]byte{}), time.Millisecond), config.GetDefaultConfig(), + testVersion, ) _, ok := model.(app.StateInitialModel) diff --git a/internal/app/stateloaded.go b/internal/app/stateloaded.go index 314f7e9..c30f46c 100644 --- a/internal/app/stateloaded.go +++ b/internal/app/stateloaded.go @@ -67,7 +67,7 @@ func (s StateLoadedModel) viewTable() string { } func (s StateLoadedModel) viewHelp() string { - return "\n" + s.help.View(s.keys) + return "\n" + s.Version + " " + s.help.View(s.keys) } // Update handles events. It implements tea.Model. diff --git a/internal/app/stateloaded_test.go b/internal/app/stateloaded_test.go index 70f0ff8..1dd3ef9 100644 --- a/internal/app/stateloaded_test.go +++ b/internal/app/stateloaded_test.go @@ -57,6 +57,12 @@ func TestStateLoaded(t *testing.T) { _, ok = model.(app.StateErrorModel) assert.Truef(t, ok, "%s", model) }) + + t.Run("version_printed", func(t *testing.T) { + t.Parallel() + + assert.Contains(t, model.View(), testVersion) + }) } func TestStateLoadedQuit(t *testing.T) {