diff --git a/analyzer/questions.go b/analyzer/questions.go index 4504aeb..3cc6fcb 100644 --- a/analyzer/questions.go +++ b/analyzer/questions.go @@ -1,15 +1,15 @@ package analyzer -import "github.com/AlecAivazis/survey/v2" +import ( + "github.com/AlecAivazis/survey/v2" + "mermerd/config" +) func ConnectionQuestion() survey.Prompt { return &survey.Input{ Message: "Connection string", Suggest: func(toComplete string) []string { - return []string{ - "postgresql://user:password@localhost:5432/dvdrental", - "mysql://root:password@tcp(127.0.0.1:3306)/db", - } + return config.ConnectionStringSuggestions }, } } diff --git a/config/config.go b/config/config.go index 6232e13..a08f513 100644 --- a/config/config.go +++ b/config/config.go @@ -3,3 +3,4 @@ package config var ShowAllConstraints bool var Schema string var ConnectionString string +var ConnectionStringSuggestions []string diff --git a/config/config_file.go b/config/config_file.go new file mode 100644 index 0000000..af8b7c3 --- /dev/null +++ b/config/config_file.go @@ -0,0 +1,85 @@ +package config + +import ( + "errors" + "fmt" + "github.com/fatih/color" + "gopkg.in/yaml.v3" + "io/ioutil" + "os" + "path/filepath" +) + +const fileMode = os.FileMode(0600) + +type FileConfig struct { + ConnectionStrings []string `yaml:"connectionStrings"` +} + +func initialConfig() FileConfig { + return FileConfig{ + ConnectionStrings: []string{ + "postgresql://user:password@localhost:5432/yourDb", + "mysql://root:password@tcp(127.0.0.1:3306)/yourDb", + }, + } +} + +func LoadConfigFile() error { + dirname, err := os.UserHomeDir() + if err != nil { + fmt.Println("Could not get home directory", err.Error()) + return err + } + + configFilePath := filepath.FromSlash(fmt.Sprintf("%s/.mermerd", dirname)) + file, err := os.OpenFile(configFilePath, os.O_RDONLY, fileMode) + if errors.Is(err, os.ErrNotExist) { + file, err = createInitialConfig(configFilePath) + } + + if err != nil { + fmt.Println("Could not open file", err.Error()) + return err + } + + fileContent, err := ioutil.ReadAll(file) + if err != nil { + fmt.Println("Could not read config file data", err.Error()) + return err + } + + var config FileConfig + err = yaml.Unmarshal(fileContent, &config) + if err != nil { + fmt.Println("Error reading content of config file", err.Error()) + return err + } + + ConnectionStringSuggestions = config.ConnectionStrings + return nil +} + +func createInitialConfig(configFilePath string) (*os.File, error) { + color.Yellow("Config file does not exist. Creating a new one in %s", configFilePath) + file, err := os.OpenFile(configFilePath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, fileMode) + configValue, err := yaml.Marshal(initialConfig()) + if err != nil { + fmt.Println("could not create initial config", err.Error()) + return nil, err + } + + _, err = file.Write(configValue) + if err != nil { + fmt.Println("could not write to config file", err.Error()) + return nil, err + } + + err = file.Sync() + if err != nil { + fmt.Println("could not save initial config", err.Error()) + return nil, err + } + + return file, nil +} diff --git a/go.mod b/go.mod index 574e7bb..40800bb 100644 --- a/go.mod +++ b/go.mod @@ -8,6 +8,7 @@ require ( github.com/fatih/color v1.13.0 github.com/go-sql-driver/mysql v1.6.0 github.com/jackc/pgx/v4 v4.14.1 + gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b ) require ( diff --git a/go.sum b/go.sum index 444ae9a..1a2e0a0 100644 --- a/go.sum +++ b/go.sum @@ -83,11 +83,13 @@ github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:C github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.4/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.8 h1:AkaSdXYQOWeaO3neb8EM634ahkXXe3jYbVh/F9lq+GI= github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= @@ -201,10 +203,12 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= diff --git a/main.go b/main.go index 077f8c4..1bc3c23 100644 --- a/main.go +++ b/main.go @@ -10,6 +10,13 @@ import ( ) func main() { + err := config.LoadConfigFile() + if err != nil { + fmt.Println(err.Error()) + util.ShowError() + return + } + flag.BoolVar(&config.ShowAllConstraints, "ac", false, "(allConstraints - default: false) Contain all constraints of selected tables, even though the table of the resulting constraint was not selected") flag.StringVar(&config.Schema, "s", "", "(schema - default: asks) The schema that should be used") flag.StringVar(&config.ConnectionString, "c", "", "(connectionString - default: asks) The connection string that should be used") diff --git a/readme.md b/readme.md index 0f7a9c2..c3b31a6 100644 --- a/readme.md +++ b/readme.md @@ -2,6 +2,11 @@ Create [Mermaid-Js](https://mermaid-js.github.io/mermaid/#/entityRelationshipDiagram) ERD diagrams from existing tables. +## Installation + +Just head over to the [Releases](https://github.com/KarnerTh/mermerd/releases) page and download the right executable +for your operating system. + ## Features * Supports PostgreSQL and MySQL @@ -41,6 +46,17 @@ Some configurations can be set via command line parameters. The parameters can a resulting constraint is not in the list of selected tables. These tables do not have any column infos and are only present via their table name. +## Config file + +Mermerd uses a yaml configuration file in your home directory called `.mermerd` (is created on first use). + +```yaml +# These connection strings are available as suggestions in the cli (use tab to access) +connectionStrings: + - postgresql://user:password@localhost:5432/yourDb + - mysql://root:password@tcp(127.0.0.1:3306)/yourDb +``` + ## Connection strings Examples for connection strings: @@ -67,8 +83,8 @@ The table constraints are analysed and interpreted as listed: ## Roadmap +* [x] Configurable suggestions for connection string input * [ ] Unit tests -* [ ] Configurable suggestions for connection string input * [ ] Support `}o--o|` relation (currently displayed as `}o--||`) * [ ] Improve output file naming * [ ] Take unique constraints into account