aboutsummaryrefslogtreecommitdiffstats
path: root/datamaps/config.go
diff options
context:
space:
mode:
Diffstat (limited to 'datamaps/config.go')
-rw-r--r--datamaps/config.go267
1 files changed, 267 insertions, 0 deletions
diff --git a/datamaps/config.go b/datamaps/config.go
new file mode 100644
index 0000000..ec6f91e
--- /dev/null
+++ b/datamaps/config.go
@@ -0,0 +1,267 @@
+package datamaps
+
+import (
+ "log"
+ "os"
+ "path/filepath"
+)
+
+const (
+ configDirName = "datamaps"
+ dbName = "datamaps.db"
+)
+
+// Help text.
+const Usage = `
+usage: datamaps COMMAND [OPTIONS]
+
+-Managing datamap files-
+
+Command: datamap
+
+All datamap related actions. Before datamaps can do much, it must know about a datamap.
+A datamap starts life as a CSV file with the format:
+
+cell_key,template_sheet,cell_reference,type
+key1,sheet1,A10,TEXT
+key2,sheet1,A11,NUMBER
+...
+
+Options:
+ --import PATH Import a datamap the csv datamap at PATH
+ --datamapname NAME Name for imported datamap
+`
+
+// mocking funcs in go https://stackoverflow.com/questions/19167970/mock-functions-in-go
+// we only need the func signature to create the type. This is pretty weird, we're want to mock
+// os.UserHomeDir so that we can set it to something like /tmp in our tests. Here we are creating
+// two types: getUserConfigDir to represent the os.UserConfigDir function, dbPathChecker as a wrapper
+// which which we can assign methods to that holds the value of the func os.UserConfigDir and the
+// method, check(), which does the work, using the passed in func to determine the user $HOME/.config
+// directory.
+// Which is a lot of work for what it is, but it does make this testable and serves as an example
+// of how things could be done in Go.
+
+// getUserConfigDir allows replaces os.UserConfigDir
+// for testing purposes.
+type getUserConfigDir func() (string, error)
+
+// DBPathChecker contains the func used to create the user config dir.
+type DBPathChecker struct {
+ userConfig getUserConfigDir
+}
+
+// NewDBPathChecker creates a DBPathChecker using whatever
+// func you want as the argument, as long as it matches the
+// type os.UserConfigDir. This makes it convenient for testing
+// and was done as an experiment here to practice mocking in Go.
+func NewDBPathChecker(h getUserConfigDir) *DBPathChecker {
+ return &DBPathChecker{userConfig: h}
+}
+
+// Check returns true if the necessary config files (including
+// the database) are in place - false if not
+func (db *DBPathChecker) Check() bool {
+ userConfig, err := db.userConfig()
+ if err != nil {
+ log.Fatal(err)
+ }
+ dbPath := filepath.Join(userConfig, "datamaps.db")
+ if _, err := os.Stat(dbPath); os.IsNotExist(err) {
+ return false
+ }
+ return true
+}
+
+// SetUp creates the config directory and requisite files
+func SetUp() (string, error) {
+ dir, err := os.UserConfigDir()
+ if err != nil {
+ return "", err
+ }
+ // check if config folder exists
+ configPath := filepath.Join(dir, configDirName)
+ dbPath := filepath.Join(configPath, dbName)
+ if _, err := os.Stat(configPath); os.IsNotExist(err) {
+ log.Println("Config directory does not exist.")
+ log.Printf("Creating config directory %s.\n", configPath)
+ if err := os.Mkdir(filepath.Join(dir, "datamaps"), 0700); err != nil {
+ return "", err
+ }
+ } else {
+ log.Println("Config directory found.")
+ }
+ if _, err := os.Stat(dbPath); os.IsNotExist(err) {
+ log.Println("Database does not exist.")
+ _, err = os.Create(dbPath)
+ if err != nil {
+ return "", err
+ }
+ log.Printf("Creating database file at %s.\n", dbPath)
+ _, err := setupDB(dbPath)
+ if err != nil {
+ return "", err
+ }
+ } else {
+ log.Println("Database file found.")
+ }
+ return dir, nil
+}
+
+func userConfigDir() (string, error) {
+ dir, err := os.UserConfigDir()
+ if err != nil {
+ return "", err
+ }
+
+ configPath := filepath.Join(dir, configDirName)
+
+ return configPath, nil
+}
+
+func defaultDMPath() (string, error) {
+ dir, err := os.UserHomeDir()
+ if err != nil {
+ return "", err
+ }
+
+ return filepath.Join(dir, "Documents", "datamaps"), nil
+}
+
+func defaultXLSXPath() (string, error) {
+ dir, err := os.UserHomeDir()
+ if err != nil {
+ return "", err
+ }
+
+ return filepath.Join(dir, "Documents", "datamaps", "import"), nil
+}
+
+// Options for the whole CLI application.
+type Options struct {
+ // Command is the main CLI sub-command (e.g. "datamap" handles all datamap
+ // operations and the flags that follow pertain only to that operation.
+ Command string
+
+ // DBPath is the path to the database file.
+ DBPath string
+
+ // DMPath is the path to a datamap file.
+ DMPath string
+
+ // DMNane is the name of a datamap, whether setting or querying.
+ DMName string
+
+ // XLSXPath is the path to a directory containing ".xlsx" files for
+ // importing.
+ XLSXPath string
+
+ // ReturnName is the name of a Return, whether setting or querying.
+ ReturnName string
+
+ // DMOverwrite is currently not used.
+ DMOverwrite bool
+
+ // DMInitial is currently not used.
+ DMInitial bool
+
+ // MasterOutPutPath is where the master.xlsx file is to be saved
+ MasterOutPutPath string
+}
+
+func defaultOptions() *Options {
+ dbpath, err := userConfigDir()
+ if err != nil {
+ log.Fatalf("Unable to get user config directory %v", err)
+ }
+
+ dmPath, err := defaultDMPath()
+ if err != nil {
+ log.Fatalf("unable to get default datamaps directory path: %v", err)
+ }
+
+ xlsxPath, err := defaultXLSXPath()
+ if err != nil {
+ log.Fatalf("unable to get default XLSX directory path: %v", err)
+ }
+
+ homeDir, err := os.UserHomeDir()
+ if err != nil {
+ log.Fatal("unable to get user home directory")
+ }
+
+ return &Options{
+ Command: "help",
+ DBPath: filepath.Join(dbpath, dbName),
+ DMPath: dmPath,
+ DMName: "Unnamed Datamap",
+ XLSXPath: xlsxPath,
+ ReturnName: "Unnamed Return",
+ DMOverwrite: false,
+ DMInitial: false,
+ MasterOutPutPath: filepath.Join(homeDir, "Desktop"),
+ }
+}
+
+// nextString get the next string in a slice.
+func nextString(args []string, i *int, message string) string {
+ if len(args) > *i+1 {
+ *i++
+ } else {
+ log.Fatal(message)
+ }
+
+ return args[*i]
+}
+
+func processOptions(opts *Options, allArgs []string) {
+ if len(allArgs) == 0 {
+ allArgs = append(allArgs, "help")
+ }
+ switch allArgs[0] {
+ case "import":
+ opts.Command = "import"
+ case "help":
+ opts.Command = "help"
+ case "datamap":
+ opts.Command = "datamap"
+ case "setup":
+ opts.Command = "setup"
+ case "server":
+ opts.Command = "server"
+ case "createmaster":
+ opts.Command = "createmaster"
+ default:
+ log.Fatal("No relevant command provided.")
+ }
+
+ restArgs := allArgs[1:]
+
+ for i := 0; i < len(allArgs[1:]); i++ {
+ arg := restArgs[i]
+ switch arg {
+ case "--xlsxpath":
+ opts.XLSXPath = nextString(restArgs, &i, "xlsx directory path required")
+ case "--returnname":
+ opts.ReturnName = nextString(restArgs, &i, "return name required")
+ case "--import":
+ opts.DMPath = nextString(restArgs, &i, "import path required")
+ case "--datamapname":
+ opts.DMName = nextString(restArgs, &i, "datamap name required")
+ case "--overwrite":
+ opts.DMOverwrite = true
+ case "--initial":
+ opts.DMInitial = true
+ case "--masteroutputdir":
+ opts.MasterOutPutPath = nextString(restArgs, &i, "master output directory required")
+ }
+ }
+}
+
+//ParseOptions for CLI.
+func ParseOptions() *Options {
+ opts := defaultOptions()
+ processOptions(opts, os.Args[1:])
+
+ return opts
+}