diff --git a/moonmath.go b/moonmath.go index ae09603..5de23cf 100644 --- a/moonmath.go +++ b/moonmath.go @@ -2,7 +2,10 @@ package main import ( "fmt" + "io" + "log/slog" "os" + "path/filepath" "strings" "code.humancabbage.net/sam/moonmath/coindesk" @@ -19,6 +22,10 @@ var CLI struct { } func main() { + logFile := setupLogging() + defer func() { + _ = logFile.Close() + }() ctx := kong.Parse(&CLI) if ctx.Error != nil { fail(ctx.Error) @@ -42,6 +49,35 @@ func main() { } } +func setupLogging() io.Closer { + homePath, err := os.UserHomeDir() + if err != nil { + panic(err) + } + + programConfigPath := filepath.Join(homePath, ".moonmath") + err = os.MkdirAll(programConfigPath, 0755) + if err != nil { + panic(err) + } + + errLogPath := filepath.Join(programConfigPath, "errors.log") + errLogFile, err := os.OpenFile( + errLogPath, + os.O_CREATE|os.O_APPEND|os.O_WRONLY, + 0600, + ) + if err != nil { + panic(err) + } + + slog.SetDefault(slog.New( + slog.NewTextHandler(errLogFile, &slog.HandlerOptions{ + Level: slog.LevelError, + }))) + return errLogFile +} + func fail(err error) { fmt.Printf("program error: %v\n", err) os.Exit(1) diff --git a/tui/asset/asset.go b/tui/asset/asset.go index ec98ee0..a9608d7 100644 --- a/tui/asset/asset.go +++ b/tui/asset/asset.go @@ -3,6 +3,7 @@ package asset import ( "context" "fmt" + "log/slog" "time" "code.humancabbage.net/sam/moonmath/coindesk" @@ -109,31 +110,16 @@ func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) { case refresh: m.refreshing = true return m, tea.Batch( - func() tea.Msg { - return m.indicator.Tick() - }, - func() tea.Msg { - // TODO: log errors - _ = m.math.Refresh(context.TODO()) - return Msg{m.math.Asset, m.math} - }, + m.resumeIndicator, + m.refresh, ) case moon.Math: m.math = msg refillProperties(&m) refillProjections(&m) return m, tea.Batch( - // schedule the next refresh - tea.Tick(time.Second*30, - func(t time.Time) tea.Msg { - return Msg{m.math.Asset, refresh{}} - }), - // wait a bit to stop the indicator, so that it's more obvious - // even when the refresh completes quickly. - tea.Tick(time.Millisecond*500, - func(t time.Time) tea.Msg { - return Msg{m.math.Asset, stopIndicator{}} - }), + m.stopIndicator(), + m.scheduleRefresh(), ) case stopIndicator: m.refreshing = false @@ -153,6 +139,37 @@ func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) { type refresh struct{} type stopIndicator struct{} +func (m Model) refresh() tea.Msg { + err := m.math.Refresh(context.TODO()) + if err != nil { + slog.Error("refresh", + "asset", m.math.Asset, + "err", err, + ) + } + return Msg{m.math.Asset, m.math} +} + +func (m Model) resumeIndicator() tea.Msg { + return m.indicator.Tick() +} + +func (m Model) scheduleRefresh() tea.Cmd { + return tea.Tick(time.Second*30, + func(t time.Time) tea.Msg { + return Msg{m.math.Asset, refresh{}} + }) +} + +func (m Model) stopIndicator() tea.Cmd { + // wait a bit to stop the indicator, so that it's more obvious + // even when the refresh completes quickly. + return tea.Tick(time.Millisecond*500, + func(t time.Time) tea.Msg { + return Msg{m.math.Asset, stopIndicator{}} + }) +} + func refillProperties(m *Model) { rows := []table.Row{ {"Asset", string(m.math.Asset)},