cli + tui to publish to leaflet (wip) & manage tasks, notes & watch/read lists 馃崈
charm
leaflet
readability
golang
1// package shared contains constants used across the codebase
2package shared
3
4import (
5 "errors"
6 "fmt"
7 "io"
8 "os"
9 "path/filepath"
10 "time"
11
12 "github.com/charmbracelet/log"
13)
14
15var (
16 ErrConfig error = fmt.Errorf("configuration error")
17)
18
19func ConfigError(m string, err error) error {
20 return errors.Join(ErrConfig, fmt.Errorf("%s: %w", m, err))
21}
22
23func IsConfigError(err error) bool {
24 return errors.Is(err, ErrConfig)
25}
26
27// CompoundWriter writes every payload to two sinks:
28// 1. a primary sink (typically [os.Stdout] or [os.Stderr])
29// 2. a secondary sink (typically [*os.File])
30//
31// It satisfies io.Writer.
32type CompoundWriter struct {
33 primary io.Writer
34 secondary io.Writer
35}
36
37// New creates a new [CompoundWriter]
38func New(primary io.Writer, secondary io.Writer) *CompoundWriter {
39 return &CompoundWriter{
40 primary: primary,
41 secondary: secondary,
42 }
43}
44
45func LogWithStdErr(w io.WriteCloser) *CompoundWriter {
46 return New(os.Stderr, w)
47}
48
49func LogWithStdOut(w io.WriteCloser) *CompoundWriter {
50 return New(os.Stdout, w)
51}
52
53// Write writes p to both instances of [io.Writer]
54func (cw *CompoundWriter) Write(p []byte) (int, error) {
55 var err error
56 var n1, n2 int
57
58 if n1, err = cw.primary.Write(p); err != nil {
59 return n1, err
60 }
61 if n2, err = cw.secondary.Write(p); err != nil {
62 return n2, err
63 }
64 return len(p), nil
65}
66
67func FallbackLogger() *log.Logger {
68 return log.NewWithOptions(os.Stderr, log.Options{
69 Prefix: "[DEBUG]",
70 ReportTimestamp: true,
71 ReportCaller: true,
72 TimeFormat: time.Kitchen,
73 Level: log.DebugLevel,
74 })
75}
76
77// NewDebugLoggerWithFile creates a new debug logger that writes to both stderr and a log file
78func NewDebugLoggerWithFile(configDir string) *log.Logger {
79 logger := FallbackLogger()
80 logsDir := filepath.Join(configDir, "logs")
81 if err := os.MkdirAll(logsDir, 0755); err != nil {
82 return logger
83 }
84
85 logFile := filepath.Join(logsDir, fmt.Sprintf("publication_%s.log", time.Now().Format("2006-01-02")))
86 file, err := os.OpenFile(logFile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
87 if err != nil {
88 return logger
89 }
90
91 w := LogWithStdErr(file)
92 logger.SetOutput(w)
93 return logger
94}