diff --git a/config/config.go b/config/config.go new file mode 100644 index 0000000..24d8f15 --- /dev/null +++ b/config/config.go @@ -0,0 +1,90 @@ +package config + +import ( + "errors" + "fmt" + "os" + "path/filepath" + + "github.com/pelletier/go-toml/v2" +) + +type Config struct { + JournalDir string `toml:"journal_dir"` +} + +func Default() (Config, error) { + home, err := os.UserHomeDir() + if err != nil { + return Config{}, fmt.Errorf("cannot determine home dir: %w", err) + } + return Config{ + JournalDir: filepath.Join(home, "journal"), + }, nil +} + +func Path() (string, error) { + cd, err := os.UserConfigDir() + if err != nil { + return "", fmt.Errorf("could not determine user config dir: %w", err) + } + configPath := filepath.Join(cd, "journal.toml") + return configPath, nil +} + +func LoadOrCreate() (Config, bool, error) { + + configPath, err := Path() + if err != nil { + return Config{}, false, fmt.Errorf("could not determine user config dir: %w", err) + } + + f, err := os.Open(configPath) + + if err != nil && !errors.Is(err, os.ErrNotExist) { + fmt.Printf("\n\nerr: %v %T\n\n", err, err) + return Config{}, false, fmt.Errorf("could not open user config '%s': %w", configPath, err) + } else if err != nil { // not exist + f, err = os.Create(configPath) + if err != nil { + return Config{}, false, fmt.Errorf("could not create user config '%s': %w", configPath, err) + } + def, err := Default() + if err != nil { + return Config{}, false, fmt.Errorf("could not generate default config: %w", err) + } + + err = toml.NewEncoder(f).Encode(def) + if err != nil { + return Config{}, false, fmt.Errorf("could not encode new config: %w", err) + } + + err = f.Close() + if err != nil { + return Config{}, false, fmt.Errorf("could not close file: %w", err) + } + + return def, true, createJournalDirIfNecessary(def) + } else { + config := Config{} + err = toml.NewDecoder(f).Decode(&config) + if err != nil { + return Config{}, false, fmt.Errorf("could not decode config in '%s': %w", configPath, err) + } + err = f.Close() + if err != nil { + return Config{}, false, fmt.Errorf("could not close file: %w", err) + } + return config, false, createJournalDirIfNecessary(config) + } +} + +func createJournalDirIfNecessary(c Config) error { + err := os.Mkdir(c.JournalDir, 0777) + if err != nil && errors.Is(err, os.ErrExist) { + return nil + } else if err != nil { + return err + } + return nil +} diff --git a/go.mod b/go.mod index c853880..8290d7a 100644 --- a/go.mod +++ b/go.mod @@ -21,6 +21,7 @@ require ( github.com/lucasb-eyer/go-colorful v1.4.0 // indirect github.com/mattn/go-runewidth v0.0.24 // indirect github.com/muesli/cancelreader v0.2.2 // indirect + github.com/pelletier/go-toml/v2 v2.4.3 github.com/rivo/uniseg v0.4.7 // indirect github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect golang.org/x/sync v0.21.0 // indirect diff --git a/go.sum b/go.sum index 99e76e3..1718fa5 100644 --- a/go.sum +++ b/go.sum @@ -34,6 +34,8 @@ github.com/mattn/go-runewidth v0.0.24 h1:cpokDiIn0MGnhdHwuWnJBITySJ20QyNGnY2kR/a github.com/mattn/go-runewidth v0.0.24/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs= github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA= github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo= +github.com/pelletier/go-toml/v2 v2.4.3 h1:GTRvJQutkOSftxIFD5xw9aepkYNuPWmVJpffdDPYVpY= +github.com/pelletier/go-toml/v2 v2.4.3/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no= diff --git a/main.go b/main.go index 0646c82..be53335 100644 --- a/main.go +++ b/main.go @@ -15,10 +15,23 @@ import ( "charm.land/bubbles/v2/textarea" tea "charm.land/bubbletea/v2" "charm.land/lipgloss/v2" + "code.ppl.town/justin/journal/config" ) func main() { - p := tea.NewProgram(initialModel()) + + cfg, created, err := config.LoadOrCreate() + if err != nil { + fmt.Printf("could not get config: %s", err.Error()) + os.Exit(1) + } + if created { + cp, _ := config.Path() + fmt.Printf("configuration created at '%s' - please check and re-run\n", cp) + os.Exit(0) + } + + p := tea.NewProgram(initialModel(cfg)) if _, err := p.Run(); err != nil { log.Fatal(err) @@ -43,13 +56,8 @@ type model struct { transientMsgs []transientMsg } -func getWriter() (WriteSeekCloser, error) { - hd, err := os.UserHomeDir() - if err != nil { - return nil, err - } - - fp := filepath.Join(hd, "journal") +func getWriter(cfg config.Config) (WriteSeekCloser, error) { + fp := cfg.JournalDir fpS, err := os.Stat(fp) if err != nil { return nil, err @@ -67,7 +75,7 @@ func getWriter() (WriteSeekCloser, error) { return file, nil } -func initialModel() model { +func initialModel(cfg config.Config) model { ti := textarea.New() ti.Placeholder = "What's happening Peter ..." ti.SetVirtualCursor(false) @@ -77,7 +85,7 @@ func initialModel() model { ti.SetHeight(20) ti.SetWidth(80) - fh, err := getWriter() + fh, err := getWriter(cfg) if err != nil { panic(err) }