diff options
| author | Lukasz Kasprzak <lukas@labunix.xyz> | 2026-07-28 21:39:49 +0200 |
|---|---|---|
| committer | Lukasz Kasprzak <lukas@labunix.xyz> | 2026-07-28 21:39:49 +0200 |
| commit | 175aa1343ff04383a5a1a8ba166505933d9be46a (patch) | |
| tree | 15c5e52a08a5484fe17747ef98e649a3ea8f076f | |
| parent | d5d175118138249fe7d9e85d1bc65fb0b7e3a398 (diff) | |
| download | lectio-175aa1343ff04383a5a1a8ba166505933d9be46a.tar.gz lectio-175aa1343ff04383a5a1a8ba166505933d9be46a.zip | |
config: drop go-toml and the legacy TOML migrations
The one-shot config.toml -> config.ini and books.toml -> books.ini
migrations (a transition aid from the pre-INI format) are removed, along
with the github.com/pelletier/go-toml/v2 dependency and the now-dead
`toml:` struct tags. The live config format has been INI for many
releases; anyone still on a .toml recreates it (the format is
self-documenting on first run). Leaves 3 direct deps, all in active use
(bubbletea, lipgloss, go-pdf/fpdf). Tests updated to write INI.
| -rw-r--r-- | go.mod | 1 | ||||
| -rw-r--r-- | go.sum | 2 | ||||
| -rw-r--r-- | internal/config/config.go | 132 | ||||
| -rw-r--r-- | internal/config/config_test.go | 46 |
4 files changed, 40 insertions, 141 deletions
@@ -6,7 +6,6 @@ require ( github.com/charmbracelet/bubbletea v1.3.10 github.com/charmbracelet/lipgloss v1.1.0 github.com/go-pdf/fpdf v0.9.0 - github.com/pelletier/go-toml/v2 v2.4.2 ) require ( @@ -30,8 +30,6 @@ github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELU github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo= github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc= github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk= -github.com/pelletier/go-toml/v2 v2.4.2 h1:M2fKKbmyvI+hGId/D0W64qDBMVhJnNR10O5gIbMc//Q= -github.com/pelletier/go-toml/v2 v2.4.2/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= diff --git a/internal/config/config.go b/internal/config/config.go index 0596194..a225a12 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -2,10 +2,8 @@ // // Resolution order: LECTIO_CONFIG env var -> ~/.config/lectio/config.ini // (auto-seeded from the embedded default on first run, honoring -// XDG_CONFIG_HOME via os.UserConfigDir()) -> built-in defaults. A pre-existing -// config.toml (lectio's former format) at the default location is converted to -// config.ini once, on first run, and left on disk. INI that fails to parse -// falls back to defaults with a warning on stderr. +// XDG_CONFIG_HOME via os.UserConfigDir()) -> built-in defaults. INI that fails +// to parse falls back to defaults with a warning on stderr. package config import ( @@ -16,8 +14,6 @@ import ( "strconv" "strings" - "github.com/pelletier/go-toml/v2" - "github.com/lukaszkasprzak/lectio/internal/bible" "github.com/lukaszkasprzak/lectio/internal/calendar" "github.com/lukaszkasprzak/lectio/internal/ini" @@ -159,36 +155,36 @@ func NormalizeSiglaStyle(s string) string { } } -// Config holds lectio's user-configurable settings. The toml tags are used only -// by the one-shot config.toml -> config.ini migration; the live format is INI. +// Config holds lectio's user-configurable settings. The live format is INI, +// parsed field-by-field in readINI and written by renderConfigINI. type Config struct { - SchemaVersion int `toml:"schema_version"` - Lectionary string `toml:"lectionary"` - Versions []string `toml:"versions"` - DefaultVersion string `toml:"default_version"` - Width int `toml:"width"` - All bool `toml:"all"` - Offline bool `toml:"offline"` - UILanguage string `toml:"ui_language"` - ReadingLang string `toml:"reading_lang"` - ReadingVersion string `toml:"reading_version"` - SiglaStyle string `toml:"sigla_style"` - WebTheme string `toml:"web_theme"` - WebPort int `toml:"web_port"` - WebDisplay string `toml:"web_display"` - WebMono bool `toml:"web_mono"` - WebVersions []string `toml:"web_versions"` - Pager string `toml:"pager"` - Parts map[string]map[string]bool `toml:"parts"` + SchemaVersion int + Lectionary string + Versions []string + DefaultVersion string + Width int + All bool + Offline bool + UILanguage string + ReadingLang string + ReadingVersion string + SiglaStyle string + WebTheme string + WebPort int + WebDisplay string + WebMono bool + WebVersions []string + Pager string + Parts map[string]map[string]bool // Computed-calendar placement options (INI [calendar] section; INI-only). - CalEpiphany string `toml:"-"` - CalAscension string `toml:"-"` - CalCorpusChristi string `toml:"-"` + CalEpiphany string + CalAscension string + CalCorpusChristi string // Use is the ordered stack of user calendar-layer ids (INI "use"); each id // names a <CalendarsDir>/<id>.ini file layered over the universal calendar. - Use []string `toml:"-"` + Use []string } // PartShown reports whether a part renders: true unless explicitly set false. @@ -403,8 +399,7 @@ func BooksPath() (string, error) { // UserBooksINI returns the bytes of the optional user books.ini (see BooksPath), // or nil when absent/unreadable -- callers then fall back to bible's embedded -// default table. A former books.toml at the same location is converted to -// books.ini once, on first read (the old file is kept). +// default table. func UserBooksINI() []byte { p, err := BooksPath() if err != nil { @@ -413,59 +408,7 @@ func UserBooksINI() []byte { if b, err := os.ReadFile(p); err == nil { return b } - tomlPath := filepath.Join(filepath.Dir(p), "books.toml") - data, err := os.ReadFile(tomlPath) - if err != nil { - return nil - } - out := booksTOMLtoINI(data) - if out == nil { - return nil - } - _ = os.WriteFile(p, out, 0o644) - fmt.Fprintf(os.Stderr, "lectio: migrated %s -> books.ini (old file kept)\n", tomlPath) - return out -} - -// booksTOMLtoINI converts a former books.toml (dialect -> book -> [forms]) into -// books.ini. Returns nil if the TOML is unparseable. -func booksTOMLtoINI(data []byte) []byte { - var raw map[string]map[string][]string - if err := toml.Unmarshal(data, &raw); err != nil { - return nil - } - var b strings.Builder - b.WriteString("# lectio book names & abbreviations, per dialect (migrated from books.toml).\n") - emit := func(d string) { - books := raw[d] - if books == nil { - return - } - fmt.Fprintf(&b, "\n[%s]\n", d) - keys := make([]string, 0, len(books)) - for k := range books { - keys = append(keys, k) - } - sort.Strings(keys) - for _, k := range keys { - fmt.Fprintf(&b, "%s = %s\n", k, strings.Join(books[k], ", ")) - } - } - seen := map[string]bool{"en": true, "pl": true, "la": true} - emit("en") - emit("pl") - emit("la") - others := make([]string, 0) - for d := range raw { - if !seen[d] { - others = append(others, d) - } - } - sort.Strings(others) - for _, d := range others { - emit(d) - } - return []byte(b.String()) + return nil } // seedIfMissing writes the embedded default config to path if nothing is @@ -482,24 +425,6 @@ func seedIfMissing(path string) error { return os.WriteFile(path, renderConfigINI(Default()), 0o644) } -// migrateFromTOML converts a former config.toml sitting next to iniPath into -// config.ini, once. The old file is left in place (so the change is -// reversible). A missing or unreadable config.toml is not an error. -func migrateFromTOML(iniPath string) { - tomlPath := filepath.Join(filepath.Dir(iniPath), "config.toml") - data, err := os.ReadFile(tomlPath) - if err != nil { - return - } - cfg := Default() - if err := toml.Unmarshal(data, &cfg); err != nil { - return - } - normalize(&cfg) - _ = Save(cfg) // writes config.ini - fmt.Fprintf(os.Stderr, "lectio: migrated %s -> config.ini (old file kept)\n", tomlPath) -} - // Load reads lectio's configuration, following the package's resolution order // (see the package doc comment). It always returns a usable Config: on a // missing config dir or an INI parse error it falls back to Default() (warning @@ -516,7 +441,6 @@ func Load() (Config, error) { if isDefault { if _, statErr := os.Stat(path); os.IsNotExist(statErr) { - migrateFromTOML(path) // convert a former config.toml if present if err := seedIfMissing(path); err != nil { return def, err } diff --git a/internal/config/config_test.go b/internal/config/config_test.go index dd75808..4d172c0 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -68,8 +68,8 @@ func TestLoadOverride(t *testing.T) { dir := t.TempDir() t.Setenv("XDG_CONFIG_HOME", dir) os.MkdirAll(filepath.Join(dir, "lectio"), 0o755) - os.WriteFile(filepath.Join(dir, "lectio", "config.toml"), - []byte("offline = true\ndefault_version = \"wuj\"\n"), 0o644) + os.WriteFile(filepath.Join(dir, "lectio", "config.ini"), + []byte("offline = true\ndefault_version = wuj\n"), 0o644) cfg, _ := Load() if !cfg.Offline || cfg.DefaultVersion != "wuj" { t.Errorf("override not applied: %+v", cfg) @@ -121,8 +121,8 @@ func TestWebDisplayLoadsSetting(t *testing.T) { dir := t.TempDir() t.Setenv("XDG_CONFIG_HOME", dir) os.MkdirAll(filepath.Join(dir, "lectio"), 0o755) - os.WriteFile(filepath.Join(dir, "lectio", "config.toml"), - []byte("web_display = \"vertical\"\n"), 0o644) + os.WriteFile(filepath.Join(dir, "lectio", "config.ini"), + []byte("web_display = vertical\n"), 0o644) cfg, err := Load() if err != nil { t.Fatal(err) @@ -136,8 +136,8 @@ func TestWebDisplayNormalizesUnknown(t *testing.T) { dir := t.TempDir() t.Setenv("XDG_CONFIG_HOME", dir) os.MkdirAll(filepath.Join(dir, "lectio"), 0o755) - os.WriteFile(filepath.Join(dir, "lectio", "config.toml"), - []byte("web_display = \"bogus\"\n"), 0o644) + os.WriteFile(filepath.Join(dir, "lectio", "config.ini"), + []byte("web_display = bogus\n"), 0o644) cfg, err := Load() if err != nil { t.Fatal(err) @@ -158,8 +158,8 @@ func TestUILanguageLoadsPL(t *testing.T) { dir := t.TempDir() t.Setenv("XDG_CONFIG_HOME", dir) os.MkdirAll(filepath.Join(dir, "lectio"), 0o755) - os.WriteFile(filepath.Join(dir, "lectio", "config.toml"), - []byte("ui_language = \"pl\"\n"), 0o644) + os.WriteFile(filepath.Join(dir, "lectio", "config.ini"), + []byte("ui_language = pl\n"), 0o644) cfg, err := Load() if err != nil { t.Fatal(err) @@ -176,8 +176,8 @@ func TestUILanguagePreservesAnyCode(t *testing.T) { dir := t.TempDir() t.Setenv("XDG_CONFIG_HOME", dir) os.MkdirAll(filepath.Join(dir, "lectio"), 0o755) - os.WriteFile(filepath.Join(dir, "lectio", "config.toml"), - []byte("ui_language = \"FR\"\n"), 0o644) + os.WriteFile(filepath.Join(dir, "lectio", "config.ini"), + []byte("ui_language = FR\n"), 0o644) cfg, err := Load() if err != nil { t.Fatal(err) @@ -210,8 +210,8 @@ func TestPagerLoadsSetting(t *testing.T) { dir := t.TempDir() t.Setenv("XDG_CONFIG_HOME", dir) os.MkdirAll(filepath.Join(dir, "lectio"), 0o755) - os.WriteFile(filepath.Join(dir, "lectio", "config.toml"), - []byte("pager = \"less -R\"\n"), 0o644) + os.WriteFile(filepath.Join(dir, "lectio", "config.ini"), + []byte("pager = less -R\n"), 0o644) cfg, err := Load() if err != nil { t.Fatal(err) @@ -303,28 +303,6 @@ func TestUseLayers(t *testing.T) { } } -func TestMigrateTOMLToINI(t *testing.T) { - dir := t.TempDir() - t.Setenv("XDG_CONFIG_HOME", dir) - os.MkdirAll(filepath.Join(dir, "lectio"), 0o755) - // only the old TOML exists -> Load converts once and writes config.ini. - os.WriteFile(filepath.Join(dir, "lectio", "config.toml"), - []byte("ui_language = \"pl\"\ndefault_version = \"wuj\"\n"), 0o644) - cfg, err := Load() - if err != nil { - t.Fatal(err) - } - if cfg.UILanguage != "pl" || cfg.DefaultVersion != "wuj" { - t.Errorf("migrated values wrong: %+v", cfg) - } - if _, err := os.Stat(filepath.Join(dir, "lectio", "config.ini")); err != nil { - t.Error("config.ini not written on migration") - } - if _, err := os.Stat(filepath.Join(dir, "lectio", "config.toml")); err != nil { - t.Error("old config.toml should be kept (reversible)") - } -} - func TestPartShown(t *testing.T) { var empty Config if !empty.PartShown("new", "psalm") { |
