From 5201295109d0ec6452b1044f01dae1b6708e79dc Mon Sep 17 00:00:00 2001 From: a-mt Date: Sat, 19 Jan 2019 13:11:21 +0100 Subject: [PATCH] Add writeas config command --- cmd/writeas/cli.go | 26 ++++++++++- cmd/writeas/commands.go | 77 ++++++++++++++++++++++++++++++ cmd/writeas/userconfig.go | 98 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 200 insertions(+), 1 deletion(-) diff --git a/cmd/writeas/cli.go b/cmd/writeas/cli.go index 6092688..f99a0b3 100644 --- a/cmd/writeas/cli.go +++ b/cmd/writeas/cli.go @@ -289,13 +289,37 @@ func main() { }, }, }, + { + Name: "config", + Usage: "Get and set options", + UsageText: "config name [value]\n writeas config [command options]", + Action: cmdOptions, + Flags: []cli.Flag{ + cli.BoolFlag{ + Name: "edit, e", + Usage: "Opens an editor to modify the config file", + }, + cli.BoolFlag{ + Name: "list, l", + Usage: "List all variables set in config file, along with their values", + }, + cli.BoolFlag{ + Name: "list-all, a", + Usage: "List all config variables, along with their values", + }, + cli.BoolFlag{ + Name: "verbose, v", + Usage: "Make the operation more talkative", + }, + }, + }, } cli.CommandHelpTemplate = `NAME: {{.Name}} - {{.Usage}} USAGE: - writeas {{.Name}}{{if .Flags}} [command options]{{end}} [arguments...]{{if .Description}} + writeas {{if .UsageText}}{{.UsageText}}{{else}}{{.Name}}{{if .Flags}} [command options]{{end}} [arguments...]{{end}}{{if .Description}} DESCRIPTION: {{.Description}}{{end}}{{if .Flags}} diff --git a/cmd/writeas/commands.go b/cmd/writeas/commands.go index 20e2b7c..941e8d9 100644 --- a/cmd/writeas/commands.go +++ b/cmd/writeas/commands.go @@ -8,6 +8,7 @@ import ( "io/ioutil" "os" "path/filepath" + "strconv" ) func cmdPost(c *cli.Context) error { @@ -240,3 +241,79 @@ func cmdAuth(c *cli.Context) error { func cmdLogOut(c *cli.Context) error { return DoLogOut(c) } + +func cmdOptions(c *cli.Context) error { + + // Edit config file + if c.Bool("e") { + composeConfig() + + // List configs + } else if c.Bool("l") || c.Bool("a") { + uc, err := loadConfig() + if err != nil { + ErrorlnQuit(fmt.Sprintf("Error loading config: %v", err), 1) + } + printConfig(uc, "", c.Bool("a")) + + // Check arguments + } else { + nargs := len(c.Args()) + + // No arguments nor options: display command usage + if nargs == 0 { + cli.ShowSubcommandHelp(c) + return nil + } + name := c.Args().Get(0) + value := c.Args().Get(1) + + // Load config file + uc, err := loadConfig() + if err != nil { + ErrorlnQuit(fmt.Sprintf("Error loading config: %v", err), 1) + } + + // Get reflection of field + rval, err := getConfigField(uc, name) + if err != nil { + ErrorlnQuit(fmt.Sprintf("%v", err), 1) + } + + // Print value + if nargs == 1 { + fmt.Printf("%s=%v\n", name, *rval) + + // Set value + } else { + + // Cast and set value + switch typ := rval.Kind().String(); typ { + case "bool": + b, err := strconv.ParseBool(value) + if err != nil { + ErrorlnQuit(fmt.Sprintf("error: \"%s\" is not a valid boolean", value), 1) + } + rval.SetBool(b) + + case "int": + i, err := strconv.ParseInt(value, 0, 0) + if err != nil { + ErrorlnQuit(fmt.Sprintf("error: \"%s\" is not a valid integer", value), 1) + } + rval.SetInt(i) + + case "string": + rval.SetString(value) + } + + // Save config to file + err = saveConfig(uc) + if err != nil { + ErrorlnQuit(fmt.Sprintf("Unable to save config: %s", err), 1) + } + fmt.Println("Saved config.") + } + } + return nil +} diff --git a/cmd/writeas/userconfig.go b/cmd/writeas/userconfig.go index e904dff..b3d35f2 100644 --- a/cmd/writeas/userconfig.go +++ b/cmd/writeas/userconfig.go @@ -7,6 +7,10 @@ import ( "gopkg.in/ini.v1" "io/ioutil" "path/filepath" + "reflect" + "fmt" + "os" + "strings" ) const ( @@ -88,3 +92,97 @@ func saveUser(u *writeas.AuthUser) error { } return nil } + +// Prints all values of the given struct +// For subfields: the field name is separated with dots (ex: Posts.Directory=) +func printConfig(x interface{}, prefix string, showEmptyValues bool) { + values := reflect.ValueOf(x) + + if values.Kind() == reflect.Ptr { + values = values.Elem() + } + typ := values.Type() + + for i := 0; i < typ.NumField(); i++ { + val := values.Field(i) + name := typ.Field(i).Name + + if prefix != "" { + name = prefix + "." + name + } + if(val.Kind() == reflect.Struct) { + printConfig(val.Interface(), name, showEmptyValues) + } else { + if showEmptyValues || val.Interface() != reflect.Zero(val.Type()).Interface() { + fmt.Printf("%s=%v\n", name, val) + } + } + } +} + +// Get the value of a given field +// For subfields: the name should be separated with dots (ex: Posts.Directory) +func getConfigField(x interface{}, name string) (*reflect.Value, error) { + path := strings.Split(name, ".") + values := reflect.ValueOf(x) + + if values.Kind() == reflect.Ptr { + values = values.Elem() + } + for _, part := range path { + values = values.FieldByName(part) + + if !values.IsValid() { + err := fmt.Errorf("error: key does not contain a section: %v", name) + return nil, err + } + } + if values.Kind() == reflect.Struct { + err := fmt.Errorf("error: key does not contain a section: %v", name) + return nil, err + } + return &values, nil +} + +// Opens an editor to modify the config file +func composeConfig() error { + filename := filepath.Join(userDataDir(), userConfigFile) + + // Open the editor + cmd := editPostCmd(filename) + if cmd == nil { + fmt.Println(noEditorErr) + return nil + } + cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr + if err := cmd.Start(); err != nil { + if debug { + panic(err) + } else { + Errorln("Error starting editor: %s", err) + return nil + } + } + + // Wait until the editor is closed + if err := cmd.Wait(); err != nil { + if debug { + panic(err) + } else { + Errorln("Editor finished with error: %s", err) + return nil + } + } + + // Check if the config file is valid + _, err := loadConfig() + if err != nil { + if debug { + panic(err) + } else { + Errorln("Error loading config: %s", err) + return nil + } + } + return nil +}