-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add ability to set bookmarks DB location with config (#3)
- Loading branch information
1 parent
1ae5bf1
commit c70c19d
Showing
9 changed files
with
362 additions
and
123 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
package lib | ||
|
||
import ( | ||
"errors" | ||
"os" | ||
"path/filepath" | ||
"runtime" | ||
"strings" | ||
|
||
"github.com/knadh/koanf/parsers/toml" | ||
"github.com/knadh/koanf/providers/file" | ||
"github.com/knadh/koanf/v2" | ||
) | ||
|
||
const configFilename = "armaria.toml" | ||
const databaseFilename = "bookmarks.db" | ||
|
||
// mkDirAllFn creates a directory if it doesn't already exist. | ||
type mkDirAllFn func(path string, perm os.FileMode) error | ||
|
||
// userHomeFn returns the home directory of the current user. | ||
type userHomeFn func() (string, error) | ||
|
||
// joinFn joins path segments together. | ||
type joinFn func(elem ...string) string | ||
|
||
// GetDBPathConfig gets the path to the bookmarks database from the config file. | ||
func GetDBPathConfig() (string, error) { | ||
config, err := getConfig(runtime.GOOS, os.UserHomeDir, filepath.Join) | ||
if err != nil && !errors.Is(err, ErrConfigMissing) { | ||
return "", err | ||
} | ||
|
||
if errors.Is(err, ErrConfigMissing) { | ||
return "", nil | ||
} else { | ||
return config.String("db"), nil | ||
} | ||
} | ||
|
||
// getConfig parses the config file. | ||
// If the sentinerl error ErrConfigMissing then it doesn't exist. | ||
func getConfig(goos string, userHome userHomeFn, join joinFn) (*koanf.Koanf, error) { | ||
configPath, err := getConfigPath(goos, userHome, join) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
var config = koanf.New(".") | ||
if err := config.Load(file.Provider(configPath), toml.Parser()); err != nil { | ||
if strings.Contains(err.Error(), "no such file or directory") { | ||
return nil, ErrConfigMissing | ||
} else { | ||
return nil, err | ||
} | ||
} | ||
|
||
return config, nil | ||
} | ||
|
||
// getDatabasePath gets the path to the bookmarks database. | ||
// The path will be (in order of precedence): | ||
// 1) The inputted path | ||
// 2) The path in the config file | ||
// 3) The default path (getFolderPath() + "bookmarks.db") | ||
func getDatabasePath(inputPath NullString, configPath string, goos string, mkDirAll mkDirAllFn, userHome userHomeFn, join joinFn) (string, error) { | ||
if inputPath.Valid && inputPath.Dirty { | ||
return inputPath.String, nil | ||
} else if configPath != "" { | ||
return configPath, nil | ||
} else { | ||
folder, err := getFolderPath(goos, userHome, join) | ||
if err != nil { | ||
return "", err | ||
} | ||
|
||
if err = mkDirAll(folder, os.ModePerm); err != nil { | ||
return "", err | ||
} | ||
|
||
return join(folder, databaseFilename), nil | ||
} | ||
} | ||
|
||
// getConfigPath gets the path to the config file. | ||
// The config file is a TOML file located at getFolderPath() + "bookmarks.db". | ||
func getConfigPath(goos string, userHome userHomeFn, join joinFn) (string, error) { | ||
folder, err := getFolderPath(goos, userHome, join) | ||
if err != nil { | ||
return "", err | ||
} | ||
|
||
return join(folder, configFilename), nil | ||
} | ||
|
||
// getFolderPath gets the path to the folder the config (and by default) the database are stored. | ||
// The folder is different per platform and maps to the following: | ||
// - Linux: ~/.armaria | ||
// - Windows: ~/AppData/Local/Armaria | ||
// - Mac: ~/Library/Application Support/Armaria | ||
func getFolderPath(goos string, userHome userHomeFn, join joinFn) (string, error) { | ||
home, err := userHome() | ||
if err != nil { | ||
return "", err | ||
} | ||
|
||
var folder string | ||
if goos == "linux" { | ||
folder = join(home, ".armaria") | ||
} else if goos == "windows" { | ||
folder = join(home, "AppData", "Local", "Armaria") | ||
} else if goos == "darwin" { | ||
folder = join(home, "Library", "Application Support", "Armaria") | ||
} else { | ||
panic("Unsupported operating system") | ||
} | ||
|
||
return folder, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
package lib | ||
|
||
import ( | ||
"os" | ||
"path" | ||
"testing" | ||
) | ||
|
||
func TestGetConfigPath(t *testing.T) { | ||
type test struct { | ||
goos string | ||
configPath string | ||
folderCreated bool | ||
} | ||
|
||
tests := []test{ | ||
{ | ||
goos: "windows", | ||
configPath: "~/AppData/Local/Armaria/armaria.toml", | ||
folderCreated: false, | ||
}, | ||
{ | ||
goos: "linux", | ||
configPath: "~/.armaria/armaria.toml", | ||
folderCreated: false, | ||
}, | ||
{ | ||
goos: "darwin", | ||
configPath: "~/Library/Application Support/Armaria/armaria.toml", | ||
folderCreated: false, | ||
}, | ||
} | ||
|
||
userHome := func() (string, error) { | ||
return "~", nil | ||
} | ||
|
||
for _, tc := range tests { | ||
t.Run(tc.goos, func(t *testing.T) { | ||
folderCreated := false | ||
|
||
got, err := getConfigPath(tc.goos, userHome, path.Join) | ||
if err != nil { | ||
t.Fatalf("unexpected error: %+v", err) | ||
} | ||
|
||
if folderCreated != tc.folderCreated { | ||
t.Fatalf("folder created: got %+v; want %+v", folderCreated, tc.folderCreated) | ||
} | ||
|
||
if got != tc.configPath { | ||
t.Errorf("db: got %+v; want %+v", got, tc.configPath) | ||
} | ||
}) | ||
} | ||
} | ||
|
||
func TestGetDatabasePath(t *testing.T) { | ||
type test struct { | ||
inputPath NullString | ||
configPath string | ||
goos string | ||
folder string | ||
db string | ||
folderCreated bool | ||
} | ||
|
||
tests := []test{ | ||
{ | ||
inputPath: NullStringFromPtr(nil), | ||
configPath: "", | ||
goos: "windows", | ||
folder: "~/AppData/Local/Armaria", | ||
db: "~/AppData/Local/Armaria/bookmarks.db", | ||
folderCreated: true, | ||
}, | ||
{ | ||
inputPath: NullStringFromPtr(nil), | ||
configPath: "", | ||
goos: "linux", | ||
folder: "~/.armaria", | ||
db: "~/.armaria/bookmarks.db", | ||
folderCreated: true, | ||
}, | ||
{ | ||
inputPath: NullStringFromPtr(nil), | ||
configPath: "", | ||
goos: "darwin", | ||
folder: "~/Library/Application Support/Armaria", | ||
db: "~/Library/Application Support/Armaria/bookmarks.db", | ||
folderCreated: true, | ||
}, | ||
{ | ||
inputPath: NullStringFrom("bookmarks.db"), | ||
configPath: "", | ||
db: "bookmarks.db", | ||
folderCreated: false, | ||
}, | ||
} | ||
|
||
userHome := func() (string, error) { | ||
return "~", nil | ||
} | ||
|
||
for _, tc := range tests { | ||
t.Run(tc.goos, func(t *testing.T) { | ||
folderCreated := false | ||
mkDirAll := func(path string, perm os.FileMode) error { | ||
folderCreated = true | ||
if path != tc.folder { | ||
t.Errorf("folder: got %+v; want %+v", path, tc.folder) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
got, err := getDatabasePath(tc.inputPath, tc.configPath, tc.goos, mkDirAll, userHome, path.Join) | ||
if err != nil { | ||
t.Fatalf("unexpected error: %+v", err) | ||
} | ||
|
||
if folderCreated != tc.folderCreated { | ||
t.Fatalf("folder created: got %+v; want %+v", folderCreated, tc.folderCreated) | ||
} | ||
|
||
if got != tc.db { | ||
t.Errorf("db: got %+v; want %+v", got, tc.db) | ||
} | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.