// Copyright 2017 Google Inc. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package getopt import ( "fmt" "strconv" "strings" "time" ) type generic struct { p interface{} } // Flag is shorthand for CommandLine.Flag. func Flag(v interface{}, short rune, helpvalue ...string) Option { return CommandLine.long(v, "", short, helpvalue...) } // FlagLong is shorthand for CommandLine.LongFlag. func FlagLong(v interface{}, long string, short rune, helpvalue ...string) Option { return CommandLine.long(v, long, short, helpvalue...) } // Flag calls FlagLong with only a short flag name. func (s *Set) Flag(v interface{}, short rune, helpvalue ...string) Option { return s.long(v, "", short, helpvalue...) } // FlagLong returns an Option in Set s for setting v. If long is not "" then // the option has a long name, and if short is not 0, the option has a short // name. v must either be of type getopt.Value or a pointer to one of the // supported builtin types: // // bool, string, []string // int, int8, int16, int32, int64 // uint, uint8, uint16, uint32, uint64 // float32, float64 // time.Duration // // FlagLong will panic if v is not a getopt.Value or one of the supported // builtin types. // // The default value of the flag is the value of v at the time FlagLong is // called. func (s *Set) FlagLong(v interface{}, long string, short rune, helpvalue ...string) Option { return s.long(v, long, short, helpvalue...) } func (s *Set) long(v interface{}, long string, short rune, helpvalue ...string) (opt Option) { // Fix up our location when we return. if where := calledFrom(); where != "" { defer func() { if opt, ok := opt.(*option); ok { opt.where = where } }() } switch p := v.(type) { case Value: return s.addFlag(p, long, short, helpvalue...) case *bool: return s.addFlag(&generic{v}, long, short, helpvalue...).SetFlag() case *string, *[]string: return s.addFlag(&generic{v}, long, short, helpvalue...) case *int, *int8, *int16, *int32, *int64: return s.addFlag(&generic{v}, long, short, helpvalue...) case *uint, *uint8, *uint16, *uint32, *uint64: return s.addFlag(&generic{v}, long, short, helpvalue...) case *float32, *float64: return s.addFlag(&generic{v}, long, short, helpvalue...) case *time.Duration: return s.addFlag(&generic{v}, long, short, helpvalue...) default: panic(fmt.Sprintf("unsupported flag type: %T", v)) } } // genericValue returns the object underlying the generic Value v, or nil if v // is not a generic Value. func genericValue(v Value) interface{} { if g, ok := v.(*generic); ok { return g.p } return nil } func (g *generic) Set(value string, opt Option) error { strconvErr := func(err error) error { if e, ok := err.(*strconv.NumError); ok { switch e.Err { case strconv.ErrRange: err = fmt.Errorf("value out of range: %s", value) case strconv.ErrSyntax: err = fmt.Errorf("not a valid number: %s", value) } } return err } switch p := g.p.(type) { case *bool: switch strings.ToLower(value) { case "", "1", "true", "on", "t": *p = true case "0", "false", "off", "f": *p = false default: return fmt.Errorf("invalid value for bool %s: %q", opt.Name(), value) } return nil case *string: *p = value return nil case *[]string: a := strings.Split(value, ",") // If this is the first time we are seen then nil out the // default value. if opt.Count() <= 1 { *p = nil } *p = append(*p, a...) return nil case *int: i64, err := strconv.ParseInt(value, 0, strconv.IntSize) if err == nil { *p = int(i64) } return strconvErr(err) case *int8: i64, err := strconv.ParseInt(value, 0, 8) if err == nil { *p = int8(i64) } return strconvErr(err) case *int16: i64, err := strconv.ParseInt(value, 0, 16) if err == nil { *p = int16(i64) } return strconvErr(err) case *int32: i64, err := strconv.ParseInt(value, 0, 32) if err == nil { *p = int32(i64) } return strconvErr(err) case *int64: i64, err := strconv.ParseInt(value, 0, 64) if err == nil { *p = i64 } return strconvErr(err) case *uint: u64, err := strconv.ParseUint(value, 0, strconv.IntSize) if err == nil { *p = uint(u64) } return strconvErr(err) case *uint8: u64, err := strconv.ParseUint(value, 0, 8) if err == nil { *p = uint8(u64) } return strconvErr(err) case *uint16: u64, err := strconv.ParseUint(value, 0, 16) if err == nil { *p = uint16(u64) } return strconvErr(err) case *uint32: u64, err := strconv.ParseUint(value, 0, 32) if err == nil { *p = uint32(u64) } return strconvErr(err) case *uint64: u64, err := strconv.ParseUint(value, 0, 64) if err == nil { *p = u64 } return strconvErr(err) case *float32: f64, err := strconv.ParseFloat(value, 32) if err == nil { *p = float32(f64) } return strconvErr(err) case *float64: f64, err := strconv.ParseFloat(value, 64) if err == nil { *p = f64 } return strconvErr(err) case *time.Duration: v, err := time.ParseDuration(value) if err == nil { *p = v } return err } panic("internal error") } func (g *generic) String() string { switch p := g.p.(type) { case *bool: if *p { return "true" } return "false" case *string: return *p case *[]string: return strings.Join([]string(*p), ",") case *int: return strconv.FormatInt(int64(*p), 10) case *int8: return strconv.FormatInt(int64(*p), 10) case *int16: return strconv.FormatInt(int64(*p), 10) case *int32: return strconv.FormatInt(int64(*p), 10) case *int64: return strconv.FormatInt(*p, 10) case *uint: return strconv.FormatUint(uint64(*p), 10) case *uint8: return strconv.FormatUint(uint64(*p), 10) case *uint16: return strconv.FormatUint(uint64(*p), 10) case *uint32: return strconv.FormatUint(uint64(*p), 10) case *uint64: return strconv.FormatUint(*p, 10) case *float32: return strconv.FormatFloat(float64(*p), 'g', -1, 32) case *float64: return strconv.FormatFloat(*p, 'g', -1, 64) case *time.Duration: return p.String() } panic("internal error") }