I am trying to build a string using text/template, where the template string could have arbitrary properties that are resolved via a map.
What I am trying to accomplish is identifying where one/any of the template properties is not resolved and return an error.
At the moment, I am using regexp but reaching out to the community of see if there was a better solution.
package main
import (
"bytes"
"fmt"
"regexp"
"text/template"
)
func main() {
data := "teststring/{{.someData}}/{{.notExist}}/{{.another}}"
// the issue here is that data can be arbitrary so i cannot do
// a lot of unknown if statements
t := template.Must(template.New("").Parse(data))
var b bytes.Buffer
fillers := map[string]interface{}{
"someData": "123",
"another": true,
// in this case, notExist is not defined, so the template will
// note resolve it
}
if err := t.Execute(&b, fillers); err != nil {
panic(err)
}
fmt.Println(b.String())
// teststring/123/<no value>/true
// here i am trying to catch if a the template required a value that was not provided
hasResolved := regexp.MustCompile(`<no value>`)
fmt.Println(hasResolved.MatchString(b.String()))
// add notExist to the fillers map
fillers["notExist"] = "testdata"
b.Reset()
if err := t.Execute(&b, fillers); err != nil {
panic(err)
}
fmt.Println(b.String())
fmt.Println(hasResolved.MatchString(b.String()))
// Output:
// teststring/123/<no value>/true
// true
// teststring/123/testdata/true
// false
}
You can let it fail by settings the options on the template:
func (t *Template) Option(opt ...string) *Template
"missingkey=default" or "missingkey=invalid"
The default behavior: Do nothing and continue execution.
If printed, the result of the index operation is the string
"<no value>".
"missingkey=zero"
The operation returns the zero value for the map type's element.
"missingkey=error"
Execution stops immediately with an error.
If you set it to missingkey=error, you get what what want.
t = t.Options("missingkey=error")
Is there a way to define an OptionFlag with urfave/cli?
I'm looking for something that will look like this.
mycli --format json
mycli --format xml
I know I can use the StringFlag, but it would be great if I could have the --help show what are the valid options/values for this flag, so it is transparent for the end-user of mycli.
This way the Flag could also be validated against the options to inform the user he has provided an invalid value for this flag for example, Which ofcourse can also be done with the StringFlag, but would rather have something more sofisticated that does all of this.
I also filed an issue on the Github repository. Maybe it is a missing feature, which I would be happy to contribute with some guidance.
https://github.com/urfave/cli/issues/1154
I think you want the StringSliceFlag It allows you to define valid/default values for a flag.
package main
import (
"fmt"
"log"
"os"
"github.com/urfave/cli/v2"
)
func main() {
validMeetings := []string{"standup", "postmortem", "jourfix"}
meetings := cli.NewStringSlice(validMeetings...)
app := &cli.App{
Flags: []cli.Flag{
&cli.StringSliceFlag{
Value: meetings,
Name: "meeting",
Usage: "use one of the default values"},
},
Action: func(c *cli.Context) error {
m := c.StringSlice("meeting")
ok := false
for _, selected := range m {
for _, valid := range validMeetings {
if selected == valid {
ok = true
}
}
}
if !ok {
return fmt.Errorf("you must use one of %v", validMeetings)
}
fmt.Printf("%s\n", c.String("meeting"))
return nil
},
}
err := app.Run(os.Args)
if err != nil {
log.Fatal(err)
}
}
https://github.com/urfave/cli/blob/4f74020d9f07911f0fdb8facbc1f557a12cd2a93/app_test.go#L760
Is it possible to hide the golang messages? I show you an example:
package main
import (
"flag"
"fmt"
"os"
)
var signal = flag.String("z", "", "")
func main() {
flag.Usage = func() {
fmt.Printf("Usage: kata -z <command>\n\n")
fmt.Printf(" test\tTesting\n")
fmt.Printf(" version\tVersion\n")
fmt.Println("")
}
flag.Parse()
if len(os.Args) != 3 {
flag.Usage()
os.Exit(1)
}
switch *signal {
case "test":
fmt.Println("testing...")
case "version":
fmt.Println("0.0.1")
default:
fmt.Println("incorrect...")
}
}
This app show to user the next information:
https://play.golang.org/p/oYwADdmlAJ
But if I write in the command-line kata -flag, the system returns: flag needs an argument: or flag provided but not defined: and the information that I show you before.
I would like to know if it's possible to hide the golang messages?
P.S.: If you don't understand my question, I can rephrase.
Using the global functions in flag actually passes through to a global flag.FlagSet called flag.CommandLine. Internally, this prints errors to an output, which is stderr by default. You can suppress the messages by setting this explicitly to, for example, ioutil.Discard:
flag.CommandLine.SetOutput(ioutil.Discard)
flag.Parse()
This will discard all messages output internally by flag.Parse(). You could also log it to anywhere else you choose by passing in an appropriate io.Writer.
I have found a solution:
Before:
flag.Parse()
if len(os.Args) != 3 {
flag.Usage()
os.Exit(1)
}
Now:
if len(os.Args) != 3 || os.Args[1] != "-z" {
flag.Usage()
os.Exit(1)
} else {
flag.Parse()
}
I am developing an web app with Go. So far so good, but now I am integrating Wercker as a CI tool and started caring about testing. But my app relies heavily on Cobra/Viper configuration/flags/environment_variables scheme, and I do not know how to properly init Viper values before running my test suite. Any help would be much appreciated.
When I use Cobra/Viper or any other combination of CLI helpers, my way of doing this is to have the CLI tool run a function whose sole purpose will be to get arguments and pass them to another method who will do the actual work.
Here is a short (and dumb) example using Cobra :
package main
import (
"fmt"
"os"
"github.com/spf13/cobra"
)
func main() {
var Cmd = &cobra.Command{
Use: "boom",
Short: "Explode all the things!",
Run: Boom,
}
if err := Cmd.Execute(); err != nil {
fmt.Println(err)
os.Exit(-1)
}
}
func Boom(cmd *cobra.Command, args []string) {
boom(args...)
}
func boom(args ...string) {
for _, arg := range args {
println("boom " + arg)
}
}
Here, the Boom function is hard to test, but the boom one is easy.
You can see another (non-dumb) example of this here (and the correspond test here).
i have found an easy way to test commands with multiple level sub commands, it is not professional but it worked well.
assume we have a command like this
RootCmd = &cobra.Command{
Use: "cliName",
Short: "Desc",
}
SubCmd = &cobra.Command{
Use: "subName",
Short: "Desc",
}
subOfSubCmd = &cobra.Command{
Use: "subOfSub",
Short: "Desc",
Run: Exec
}
//commands relationship
RootCmd.AddCommand(SubCmd)
SubCmd.AddCommand(subOfSubCmd)
When testing the subOfSubCmd we can do this way:
func TestCmd(t *testing.T) {
convey.Convey("test cmd", t, func() {
args := []string{"subName", "subOfSub"}
RootCmd.SetArgs(args)
RootCmd.Execute()
})
}
With the flag package, is there a good way to distinguish if a string flag was passed?
For example, when the flag is not passed, I want to set it to a dynamic default value. However, I want to set it to empty if the flag was provided but with a value of "".
Current I am doing the following:
flagHost = flag.String(flagHostFlagKey, "", "...")
...
setHostname := false
for _, arg := range os.Args {
if arg == "-"+flagHostFlagKey {
setHostname = true
}
}
if !setHostname {
...
Which seems to work fine, but is kind of ugly. Is there a better way while staying with the standard flag package?
Use the flag.Visit()
Description:
Visit visits the command-line flags in lexicographical order, calling fn for each. It visits only those flags that have been set.
use:
func isFlagPassed(name string) bool {
found := false
flag.Visit(func(f *flag.Flag) {
if f.Name == name {
found = true
}
})
return found
}
The built-in flag types don't support distinguishing default values and explicit assignment to the default value. However, the flag package is quite flexible, and allows you to roll your own type that does, using the flag.Value interface.
Here's a full example that contains a string flag which records if it's been assigned to.
package main
import (
"flag"
"fmt"
)
type stringFlag struct {
set bool
value string
}
func (sf *stringFlag) Set(x string) error {
sf.value = x
sf.set = true
return nil
}
func (sf *stringFlag) String() string {
return sf.value
}
var filename stringFlag
func init() {
flag.Var(&filename, "filename", "the filename")
}
func main() {
flag.Parse()
if !filename.set {
fmt.Println("--filename not set")
} else {
fmt.Printf("--filename set to %q\n", filename.value)
}
}
Here's some example runs:
$ go run a.go -filename=abc
--filename set to "abc"
$ go run a.go -filename=
--filename set to ""
$ go run a.go
--filename not set
The issue with using a custom flag type (the stringFlag example in this thread) is you'll slightly upset the PrintDefaults output (i.e. --help). For example, with a string username flag and a stringFlag servername flag, --help looks like this:
-server value
server:port (default localhost:1234)
-username string
username (default "kimmi")
Note these are both string arguments as far as the user is concerned, but presented differently as a stringFlag is not a string.
flag's Flagset has an internal map that includes the flags that were declared ('formal') and those actually set ('actual'). The former is available via Lookup(), though alas the latter is not exposed, or you could just write:
var servername = flag.String("server", "localhost:8129", "server:port")
flag.Parse()
if f := flag.CommandLine.LookupActual("server"); f != nil {
fmt.Printf("server set to %#v\n", f)
} else {
fmt.Printf("server not set\n")
}
Seems like the best you can do, if you want consistent PrintDefaults() output, is to use Visit to extract your own view of 'actual' (VisitAll does the same thing with 'formal'):
var servername = flag.String("server", "localhost:8129", "server:port")
flag.Parse()
flagset := make(map[string]bool)
flag.Visit(func(f *flag.Flag) { flagset[f.Name]=true } )
if flagset["server"] {
fmt.Printf("server set via flags\n")
} else {
fmt.Printf("server not explicitly set, using default\n")
}
To use a dynamic default value for a flag, create the flag with the default set to the dynamic value:
func main() {
flagHost = flag.String(flagHostFlagKey, computedHostFlag(), "...")
flag.Parse()
// *flagHost equals the return value from computedHostFlag() if
// the flag is not specified on the command line.
...
}
With this approach, it's not necessary to detect if the flag is specified on the command line. Also, help shows the correct default.
If the computed value depends on other flags or is expensive to calculate, then use the approach suggested in one of the other answers.
Face with same problem, but have even complex case with bool flag, in this case computedHostFlag() not working, since you can provide to flag creation only true or false. "type stringFlag struct" solution also not the best, since ruin idea of default values.
Solve it in this way: create two sets of flags, with different default values, after parse - just check - if flag in first flagset have the same value that flag from second flagset - that it means that flag value was provided by user from command line. If they different - than this mean that flag was set by default.
package main
import (
"fmt"
"flag"
)
func main() {
args := []string{"-foo="}
flagSet1 := flag.NewFlagSet("flagSet1", flag.ContinueOnError)
foo1 := flagSet1.String("foo", "-", ``)
boolFoo1 := flagSet1.Bool("boolfoo", false, ``)
flagSet1.Parse(args)
flagSet2 := flag.NewFlagSet("flagSet2", flag.ContinueOnError)
foo2 := flagSet2.String("foo", "+", ``)
boolFoo2 := flagSet2.Bool("boolfoo", true, ``)
flagSet2.Parse(args)
if *foo1 != *foo2 {
fmt.Println("foo flag set by default")
} else {
fmt.Println("foo flag provided by user")
}
if *boolFoo1 != *boolFoo2 {
fmt.Println("boolfoo flag set by default")
} else {
fmt.Println("boolfoo flag provided by user")
}
}
playground: https://play.golang.org/p/BVceE_pN5PO , for real CLI execution, you can do something like that: https://play.golang.org/p/WNvDaaPj585
Same as https://stackoverflow.com/a/35809400/3567989 but with a pointer to a string instead of a custom struct. The *string is nil if unset, non-nil if set.
package main
import (
"flag"
"fmt"
)
type stringPtrFlag struct {
ptr **string
}
func (f stringPtrFlag) String() string {
if *f.ptr == nil {
return ""
}
return **f.ptr
}
func (f stringPtrFlag) Set(s string) error {
*f.ptr = &s
return nil
}
var filename *string
func init() {
flag.Var(stringPtrFlag{&filename}, "filename", "the filename")
}
func main() {
flag.Parse()
if filename == nil {
fmt.Println("--filename not set")
} else {
fmt.Printf("--filename set to %q\n", *filename)
}
}
I think a more reliable way is to check whether any flag in the command-line parameters (os.Args[1:]) is prefixed by "prefix" + str, so the function:
func isInSlice(str string, list []string, prefix string) bool {
for _, v := range list {
if strings.HasPrefix(v, prefix + str) {
return true
}
}
return false
}
I found that we have the Lookup() method:
func isFlagPassed(name string) bool {
rs := flag.Lookup(name)
return rs != nil
}
Full docs
The FlagSet does not have a function LookupActual() in my environment (go version go1.13.4 windows/amd64), and the internal map actual mentioned in Ben L's answer can not be accessed directly.
I have an approach to check if a flag is set using reflect:
import "reflect"
fs := flag.NewFlagSet("the flags", flag.ExitOnError)
flag_name := "host"
host := fs.String(flag_name, "localhost", "specify the host address")
// other flags
fs.Parse(os.Args[1:])
if reflect.Indirect(reflect.ValueOf(fs)).FieldByName("actual").MapIndex(reflect.ValueOf(flag_name)).IsValid() {
fmt.Printf("the host flag is set with value %v", *host)
} else {
fmt.Printf("the host flag is not set")
}