2017-02-07 21:33:23 +00:00
package flaeg
import (
"errors"
"fmt"
"io"
"io/ioutil"
"os"
"path"
"reflect"
"sort"
"strings"
"text/tabwriter"
"text/template"
"time"
2017-03-27 21:15:48 +00:00
flag "github.com/ogier/pflag"
2017-02-07 21:33:23 +00:00
)
// ErrParserNotFound is thrown when a field is flaged but not parser match its type
var ErrParserNotFound = errors . New ( "Parser not found or custom parser missing" )
// GetTypesRecursive links in flagmap a flag with its reflect.StructField
// You can whether provide objValue on a structure or a pointer to structure as first argument
// Flags are genereted from field name or from StructTag
func getTypesRecursive ( objValue reflect . Value , flagmap map [ string ] reflect . StructField , key string ) error {
name := key
switch objValue . Kind ( ) {
case reflect . Struct :
for i := 0 ; i < objValue . NumField ( ) ; i ++ {
if objValue . Type ( ) . Field ( i ) . Anonymous {
if err := getTypesRecursive ( objValue . Field ( i ) , flagmap , name ) ; err != nil {
return err
}
} else if len ( objValue . Type ( ) . Field ( i ) . Tag . Get ( "description" ) ) > 0 {
fieldName := objValue . Type ( ) . Field ( i ) . Name
if ! isExported ( fieldName ) {
return fmt . Errorf ( "Field %s is an unexported field" , fieldName )
}
name += objValue . Type ( ) . Name ( )
if tag := objValue . Type ( ) . Field ( i ) . Tag . Get ( "long" ) ; len ( tag ) > 0 {
fieldName = tag
}
if len ( key ) == 0 {
//Lower Camel Case
//name = strings.ToLower(string(fieldName[0])) + fieldName[1:]
name = strings . ToLower ( fieldName )
} else {
name = key + "." + strings . ToLower ( fieldName )
}
if _ , ok := flagmap [ name ] ; ok {
return errors . New ( "Tag already exists: " + name )
}
flagmap [ name ] = objValue . Type ( ) . Field ( i )
if err := getTypesRecursive ( objValue . Field ( i ) , flagmap , name ) ; err != nil {
return err
}
}
}
case reflect . Ptr :
if len ( key ) > 0 {
field := flagmap [ name ]
field . Type = reflect . TypeOf ( false )
flagmap [ name ] = field
}
typ := objValue . Type ( ) . Elem ( )
inst := reflect . New ( typ ) . Elem ( )
if err := getTypesRecursive ( inst , flagmap , name ) ; err != nil {
return err
}
default :
return nil
}
return nil
}
//GetPointerFlags returns flags on pointers
func GetBoolFlags ( config interface { } ) ( [ ] string , error ) {
flagmap := make ( map [ string ] reflect . StructField )
if err := getTypesRecursive ( reflect . ValueOf ( config ) , flagmap , "" ) ; err != nil {
return [ ] string { } , err
}
flags := make ( [ ] string , 0 , len ( flagmap ) )
for f , structField := range flagmap {
if structField . Type . Kind ( ) == reflect . Bool {
flags = append ( flags , f )
}
}
return flags , nil
}
//GetFlags returns flags
func GetFlags ( config interface { } ) ( [ ] string , error ) {
flagmap := make ( map [ string ] reflect . StructField )
if err := getTypesRecursive ( reflect . ValueOf ( config ) , flagmap , "" ) ; err != nil {
return [ ] string { } , err
}
flags := make ( [ ] string , 0 , len ( flagmap ) )
for f := range flagmap {
flags = append ( flags , f )
}
return flags , nil
}
//loadParsers loads default parsers and custom parsers given as parameter. Return a map [reflect.Type]parsers
// bool, int, int64, uint, uint64, float64,
func loadParsers ( customParsers map [ reflect . Type ] Parser ) ( map [ reflect . Type ] Parser , error ) {
parsers := map [ reflect . Type ] Parser { }
var boolParser boolValue
parsers [ reflect . TypeOf ( true ) ] = & boolParser
var intParser intValue
parsers [ reflect . TypeOf ( 1 ) ] = & intParser
var int64Parser int64Value
parsers [ reflect . TypeOf ( int64 ( 1 ) ) ] = & int64Parser
var uintParser uintValue
parsers [ reflect . TypeOf ( uint ( 1 ) ) ] = & uintParser
var uint64Parser uint64Value
parsers [ reflect . TypeOf ( uint64 ( 1 ) ) ] = & uint64Parser
var stringParser stringValue
parsers [ reflect . TypeOf ( "" ) ] = & stringParser
var float64Parser float64Value
parsers [ reflect . TypeOf ( float64 ( 1.5 ) ) ] = & float64Parser
2017-03-27 21:15:48 +00:00
var durationParser Duration
parsers [ reflect . TypeOf ( Duration ( time . Second ) ) ] = & durationParser
2017-02-07 21:33:23 +00:00
var timeParser timeValue
parsers [ reflect . TypeOf ( time . Now ( ) ) ] = & timeParser
for rType , parser := range customParsers {
parsers [ rType ] = parser
}
return parsers , nil
}
//ParseArgs : parses args return valmap map[flag]Getter, using parsers map[type]Getter
//args must be formated as like as flag documentation. See https://golang.org/pkg/flag
func parseArgs ( args [ ] string , flagmap map [ string ] reflect . StructField , parsers map [ reflect . Type ] Parser ) ( map [ string ] Parser , error ) {
//Return var
valmap := make ( map [ string ] Parser )
//Visitor in flag.Parse
flagList := [ ] * flag . Flag { }
visitor := func ( fl * flag . Flag ) {
flagList = append ( flagList , fl )
}
newParsers := map [ string ] Parser { }
flagSet := flag . NewFlagSet ( "flaeg.Load" , flag . ContinueOnError )
//Disable output
flagSet . SetOutput ( ioutil . Discard )
var err error
for flag , structField := range flagmap {
//for _, flag := range flags {
//structField := flagmap[flag]
if parser , ok := parsers [ structField . Type ] ; ok {
newparserValue := reflect . New ( reflect . TypeOf ( parser ) . Elem ( ) )
newparserValue . Elem ( ) . Set ( reflect . ValueOf ( parser ) . Elem ( ) )
newparser := newparserValue . Interface ( ) . ( Parser )
if short := structField . Tag . Get ( "short" ) ; len ( short ) == 1 {
// fmt.Printf("short : %s long : %s\n", short, flag)
flagSet . VarP ( newparser , flag , short , structField . Tag . Get ( "description" ) )
} else {
flagSet . Var ( newparser , flag , structField . Tag . Get ( "description" ) )
}
newParsers [ flag ] = newparser
} else {
err = ErrParserNotFound
}
}
// prevents case sensitivity issue
args = argsToLower ( args )
if err := flagSet . Parse ( args ) ; err != nil {
return nil , err
}
//Fill flagList with parsed flags
flagSet . Visit ( visitor )
//Return parsers on parsed flag
for _ , flag := range flagList {
valmap [ flag . Name ] = newParsers [ flag . Name ]
}
return valmap , err
}
func getDefaultValue ( defaultValue reflect . Value , defaultPointersValue reflect . Value , defaultValmap map [ string ] reflect . Value , key string ) error {
if defaultValue . Type ( ) != defaultPointersValue . Type ( ) {
return fmt . Errorf ( "Parameters defaultValue and defaultPointersValue must be the same struct. defaultValue type : %s is not defaultPointersValue type : %s" , defaultValue . Type ( ) . String ( ) , defaultPointersValue . Type ( ) . String ( ) )
}
name := key
switch defaultValue . Kind ( ) {
case reflect . Struct :
for i := 0 ; i < defaultValue . NumField ( ) ; i ++ {
if defaultValue . Type ( ) . Field ( i ) . Anonymous {
if err := getDefaultValue ( defaultValue . Field ( i ) , defaultPointersValue . Field ( i ) , defaultValmap , name ) ; err != nil {
return err
}
} else if len ( defaultValue . Type ( ) . Field ( i ) . Tag . Get ( "description" ) ) > 0 {
name += defaultValue . Type ( ) . Name ( )
fieldName := defaultValue . Type ( ) . Field ( i ) . Name
if tag := defaultValue . Type ( ) . Field ( i ) . Tag . Get ( "long" ) ; len ( tag ) > 0 {
fieldName = tag
}
if len ( key ) == 0 {
name = strings . ToLower ( fieldName )
} else {
name = key + "." + strings . ToLower ( fieldName )
}
if defaultValue . Field ( i ) . Kind ( ) != reflect . Ptr {
// if _, ok := defaultValmap[name]; ok {
// return errors.New("Tag already exists: " + name)
// }
defaultValmap [ name ] = defaultValue . Field ( i )
// fmt.Printf("%s: got default value %+v\n", name, defaultValue.Field(i))
}
if err := getDefaultValue ( defaultValue . Field ( i ) , defaultPointersValue . Field ( i ) , defaultValmap , name ) ; err != nil {
return err
}
}
}
case reflect . Ptr :
if ! defaultPointersValue . IsNil ( ) {
if len ( key ) != 0 {
//turn ptr fields to nil
defaultPointersNilValue , err := setPointersNil ( defaultPointersValue )
if err != nil {
return err
}
defaultValmap [ name ] = defaultPointersNilValue
// fmt.Printf("%s: got default value %+v\n", name, defaultPointersNilValue)
}
if ! defaultValue . IsNil ( ) {
if err := getDefaultValue ( defaultValue . Elem ( ) , defaultPointersValue . Elem ( ) , defaultValmap , name ) ; err != nil {
return err
}
} else {
if err := getDefaultValue ( defaultPointersValue . Elem ( ) , defaultPointersValue . Elem ( ) , defaultValmap , name ) ; err != nil {
return err
}
}
} else {
instValue := reflect . New ( defaultPointersValue . Type ( ) . Elem ( ) )
if len ( key ) != 0 {
defaultValmap [ name ] = instValue
// fmt.Printf("%s: got default value %+v\n", name, instValue)
}
if ! defaultValue . IsNil ( ) {
if err := getDefaultValue ( defaultValue . Elem ( ) , instValue . Elem ( ) , defaultValmap , name ) ; err != nil {
return err
}
} else {
if err := getDefaultValue ( instValue . Elem ( ) , instValue . Elem ( ) , defaultValmap , name ) ; err != nil {
return err
}
}
}
default :
return nil
}
return nil
}
//objValue a reflect.Value of a not-nil pointer on a struct
func setPointersNil ( objValue reflect . Value ) ( reflect . Value , error ) {
if objValue . Kind ( ) != reflect . Ptr {
return objValue , fmt . Errorf ( "Parameters objValue must be a not-nil pointer on a struct, not a %s" , objValue . Kind ( ) . String ( ) )
} else if objValue . IsNil ( ) {
return objValue , fmt . Errorf ( "Parameters objValue must be a not-nil pointer" )
} else if objValue . Elem ( ) . Kind ( ) != reflect . Struct {
// fmt.Printf("Parameters objValue must be a not-nil pointer on a struct, not a pointer on a %s\n", objValue.Elem().Kind().String())
return objValue , nil
}
//Clone
starObjValue := objValue . Elem ( )
nilPointersObjVal := reflect . New ( starObjValue . Type ( ) )
starNilPointersObjVal := nilPointersObjVal . Elem ( )
starNilPointersObjVal . Set ( starObjValue )
for i := 0 ; i < nilPointersObjVal . Elem ( ) . NumField ( ) ; i ++ {
if field := nilPointersObjVal . Elem ( ) . Field ( i ) ; field . Kind ( ) == reflect . Ptr && field . CanSet ( ) {
field . Set ( reflect . Zero ( field . Type ( ) ) )
}
}
return nilPointersObjVal , nil
}
//FillStructRecursive initialize a value of any taged Struct given by reference
func fillStructRecursive ( objValue reflect . Value , defaultPointerValmap map [ string ] reflect . Value , valmap map [ string ] Parser , key string ) error {
name := key
switch objValue . Kind ( ) {
case reflect . Struct :
for i := 0 ; i < objValue . Type ( ) . NumField ( ) ; i ++ {
if objValue . Type ( ) . Field ( i ) . Anonymous {
if err := fillStructRecursive ( objValue . Field ( i ) , defaultPointerValmap , valmap , name ) ; err != nil {
return err
}
} else if len ( objValue . Type ( ) . Field ( i ) . Tag . Get ( "description" ) ) > 0 {
name += objValue . Type ( ) . Name ( )
fieldName := objValue . Type ( ) . Field ( i ) . Name
if tag := objValue . Type ( ) . Field ( i ) . Tag . Get ( "long" ) ; len ( tag ) > 0 {
fieldName = tag
}
if len ( key ) == 0 {
name = strings . ToLower ( fieldName )
} else {
name = key + "." + strings . ToLower ( fieldName )
}
// fmt.Println(name)
if objValue . Field ( i ) . Kind ( ) != reflect . Ptr {
if val , ok := valmap [ name ] ; ok {
// fmt.Printf("%s : set def val\n", name)
if err := setFields ( objValue . Field ( i ) , val ) ; err != nil {
return err
}
}
}
if err := fillStructRecursive ( objValue . Field ( i ) , defaultPointerValmap , valmap , name ) ; err != nil {
return err
}
}
}
case reflect . Ptr :
if len ( key ) == 0 && ! objValue . IsNil ( ) {
if err := fillStructRecursive ( objValue . Elem ( ) , defaultPointerValmap , valmap , name ) ; err != nil {
return err
}
return nil
}
contains := false
for flag := range valmap {
// TODO replace by regexp
if strings . Contains ( flag , name + "." ) {
contains = true
break
}
}
needDefault := false
if _ , ok := valmap [ name ] ; ok {
needDefault = valmap [ name ] . Get ( ) . ( bool )
}
if contains && objValue . IsNil ( ) {
needDefault = true
}
if needDefault {
if defVal , ok := defaultPointerValmap [ name ] ; ok {
//set default pointer value
// fmt.Printf("%s : set default value %+v\n", name, defVal)
objValue . Set ( defVal )
} else {
return fmt . Errorf ( "flag %s default value not provided" , name )
}
}
if ! objValue . IsNil ( ) && contains {
if objValue . Type ( ) . Elem ( ) . Kind ( ) == reflect . Struct {
if err := fillStructRecursive ( objValue . Elem ( ) , defaultPointerValmap , valmap , name ) ; err != nil {
return err
}
}
}
default :
return nil
}
return nil
}
// SetFields sets value to fieldValue using tag as key in valmap
func setFields ( fieldValue reflect . Value , val Parser ) error {
if fieldValue . CanSet ( ) {
fieldValue . Set ( reflect . ValueOf ( val ) . Elem ( ) . Convert ( fieldValue . Type ( ) ) )
} else {
return errors . New ( fieldValue . Type ( ) . String ( ) + " is not settable." )
}
return nil
}
//PrintHelp generates and prints command line help
func PrintHelp ( flagmap map [ string ] reflect . StructField , defaultValmap map [ string ] reflect . Value , parsers map [ reflect . Type ] Parser ) error {
return PrintHelpWithCommand ( flagmap , defaultValmap , parsers , nil , nil )
}
//PrintError takes a not nil error and prints command line help
func PrintError ( err error , flagmap map [ string ] reflect . StructField , defaultValmap map [ string ] reflect . Value , parsers map [ reflect . Type ] Parser ) error {
if err != flag . ErrHelp {
fmt . Printf ( "Error : %s\n" , err )
}
if ! strings . Contains ( err . Error ( ) , ":No parser for type" ) {
PrintHelp ( flagmap , defaultValmap , parsers )
}
return err
}
//LoadWithParsers initializes config : struct fields given by reference, with args : arguments.
//Some custom parsers may be given.
func LoadWithParsers ( config interface { } , defaultValue interface { } , args [ ] string , customParsers map [ reflect . Type ] Parser ) error {
cmd := & Command {
Config : config ,
DefaultPointersConfig : defaultValue ,
}
_ , cmd . Name = path . Split ( os . Args [ 0 ] )
return LoadWithCommand ( cmd , args , customParsers , nil )
}
//Load initializes config : struct fields given by reference, with args : arguments.
//Some custom parsers may be given.
func Load ( config interface { } , defaultValue interface { } , args [ ] string ) error {
return LoadWithParsers ( config , defaultValue , args , nil )
}
// Command structure contains program/command information (command name and description)
// Config must be a pointer on the configuration struct to parse (it contains default values of field)
// DefaultPointersConfig contains default pointers values: those values are set on pointers fields if their flags are called
// It must be the same type(struct) as Config
// Run is the func which launch the program using initialized configuration structure
type Command struct {
Name string
Description string
Config interface { }
DefaultPointersConfig interface { } //TODO:case DefaultPointersConfig is nil
Run func ( ) error
Metadata map [ string ] string
}
//LoadWithCommand initializes config : struct fields given by reference, with args : arguments.
//Some custom parsers and some subCommand may be given.
func LoadWithCommand ( cmd * Command , cmdArgs [ ] string , customParsers map [ reflect . Type ] Parser , subCommand [ ] * Command ) error {
parsers , err := loadParsers ( customParsers )
if err != nil {
return err
}
tagsmap := make ( map [ string ] reflect . StructField )
if err := getTypesRecursive ( reflect . ValueOf ( cmd . Config ) , tagsmap , "" ) ; err != nil {
return err
}
defaultValmap := make ( map [ string ] reflect . Value )
if err := getDefaultValue ( reflect . ValueOf ( cmd . Config ) , reflect . ValueOf ( cmd . DefaultPointersConfig ) , defaultValmap , "" ) ; err != nil {
return err
}
valmap , errParseArgs := parseArgs ( cmdArgs , tagsmap , parsers )
if errParseArgs != nil && errParseArgs != ErrParserNotFound {
return PrintErrorWithCommand ( errParseArgs , tagsmap , defaultValmap , parsers , cmd , subCommand )
}
if err := fillStructRecursive ( reflect . ValueOf ( cmd . Config ) , defaultValmap , valmap , "" ) ; err != nil {
return err
}
if errParseArgs == ErrParserNotFound {
return errParseArgs
}
return nil
}
//PrintHelpWithCommand generates and prints command line help for a Command
func PrintHelpWithCommand ( flagmap map [ string ] reflect . StructField , defaultValmap map [ string ] reflect . Value , parsers map [ reflect . Type ] Parser , cmd * Command , subCmd [ ] * Command ) error {
// Define a templates
// Using POSXE STD : http://pubs.opengroup.org/onlinepubs/9699919799/
const helper = ` { { if . ProgDescription } } { { . ProgDescription } }
2017-03-27 21:15:48 +00:00
2017-02-07 21:33:23 +00:00
{ { end } } Usage : { { . ProgName } } [ -- flag = flag_argument ] [ - f [ flag_argument ] ] ... set flag_argument to flag ( s )
or : { { . ProgName } } [ -- flag [ = true | false | ] ] [ - f [ true | false | ] ] ... set true / false to boolean flag ( s )
{ { if . SubCommands } }
Available Commands : { { range $ subCmdName , $ subCmdDesc := . SubCommands } }
{ { printf "\t%-50s %s" $ subCmdName $ subCmdDesc } } { { end } }
Use "{{.ProgName}} [command] --help" for more information about a command .
{ { end } }
Flags :
`
// Use a struct to give data to template
type TempStruct struct {
ProgName string
ProgDescription string
SubCommands map [ string ] string
}
tempStruct := TempStruct { }
if cmd != nil {
tempStruct . ProgName = cmd . Name
tempStruct . ProgDescription = cmd . Description
tempStruct . SubCommands = map [ string ] string { }
if len ( subCmd ) > 1 && cmd == subCmd [ 0 ] {
for _ , c := range subCmd [ 1 : ] {
tempStruct . SubCommands [ c . Name ] = c . Description
}
}
} else {
_ , tempStruct . ProgName = path . Split ( os . Args [ 0 ] )
}
//Run Template
tmplHelper , err := template . New ( "helper" ) . Parse ( helper )
if err != nil {
return err
}
err = tmplHelper . Execute ( os . Stdout , tempStruct )
if err != nil {
return err
}
return printFlagsDescriptionsDefaultValues ( flagmap , defaultValmap , parsers , os . Stdout )
}
func printFlagsDescriptionsDefaultValues ( flagmap map [ string ] reflect . StructField , defaultValmap map [ string ] reflect . Value , parsers map [ reflect . Type ] Parser , output io . Writer ) error {
// Sort alphabetically & Delete unparsable flags in a slice
flags := [ ] string { }
for flag , field := range flagmap {
if _ , ok := parsers [ field . Type ] ; ok {
flags = append ( flags , flag )
}
}
sort . Strings ( flags )
// Process data
descriptions := [ ] string { }
defaultValues := [ ] string { }
flagsWithDashs := [ ] string { }
shortFlagsWithDash := [ ] string { }
for _ , flag := range flags {
field := flagmap [ flag ]
if short := field . Tag . Get ( "short" ) ; len ( short ) == 1 {
shortFlagsWithDash = append ( shortFlagsWithDash , "-" + short + "," )
} else {
shortFlagsWithDash = append ( shortFlagsWithDash , "" )
}
flagsWithDashs = append ( flagsWithDashs , "--" + flag )
//flag on pointer ?
if defVal , ok := defaultValmap [ flag ] ; ok {
if defVal . Kind ( ) != reflect . Ptr {
// Set defaultValue on parsers
parsers [ field . Type ] . SetValue ( defaultValmap [ flag ] . Interface ( ) )
}
if defVal := parsers [ field . Type ] . String ( ) ; len ( defVal ) > 0 {
defaultValues = append ( defaultValues , fmt . Sprintf ( "(default \"%s\")" , defVal ) )
} else {
defaultValues = append ( defaultValues , "" )
}
}
splittedDescriptions := split ( field . Tag . Get ( "description" ) , 80 )
for i , description := range splittedDescriptions {
descriptions = append ( descriptions , description )
if i != 0 {
defaultValues = append ( defaultValues , "" )
flagsWithDashs = append ( flagsWithDashs , "" )
shortFlagsWithDash = append ( shortFlagsWithDash , "" )
}
}
}
//add help flag
shortFlagsWithDash = append ( shortFlagsWithDash , "-h," )
flagsWithDashs = append ( flagsWithDashs , "--help" )
descriptions = append ( descriptions , "Print Help (this message) and exit" )
defaultValues = append ( defaultValues , "" )
return displayTab ( output , shortFlagsWithDash , flagsWithDashs , descriptions , defaultValues )
}
func split ( str string , width int ) [ ] string {
if len ( str ) > width {
index := strings . LastIndex ( str [ : width ] , " " )
if index == - 1 {
index = width
}
return append ( [ ] string { strings . TrimSpace ( str [ : index ] ) } , split ( strings . TrimSpace ( str [ index : ] ) , width ) ... )
}
return [ ] string { str }
}
func displayTab ( output io . Writer , columns ... [ ] string ) error {
nbRow := len ( columns [ 0 ] )
nbCol := len ( columns )
w := new ( tabwriter . Writer )
w . Init ( output , 0 , 4 , 1 , ' ' , 0 )
for i := 0 ; i < nbRow ; i ++ {
row := ""
for j , col := range columns {
row += col [ i ]
if j != nbCol - 1 {
row += "\t"
}
}
fmt . Fprintln ( w , row )
}
w . Flush ( )
return nil
}
//PrintErrorWithCommand takes a not nil error and prints command line help
func PrintErrorWithCommand ( err error , flagmap map [ string ] reflect . StructField , defaultValmap map [ string ] reflect . Value , parsers map [ reflect . Type ] Parser , cmd * Command , subCmd [ ] * Command ) error {
if err != flag . ErrHelp {
fmt . Printf ( "Error here : %s\n" , err )
}
PrintHelpWithCommand ( flagmap , defaultValmap , parsers , cmd , subCmd )
return err
}
//Flaeg struct contains commands (at least the root one)
//and row arguments (command and/or flags)
//a map of custom parsers could be use
type Flaeg struct {
calledCommand * Command
commands [ ] * Command ///rootCommand is th fist one in this slice
args [ ] string
commmandArgs [ ] string
customParsers map [ reflect . Type ] Parser
}
//New creats and initialize a pointer on Flaeg
func New ( rootCommand * Command , args [ ] string ) * Flaeg {
var f Flaeg
f . commands = [ ] * Command { rootCommand }
f . args = args
f . customParsers = map [ reflect . Type ] Parser { }
return & f
}
//AddCommand adds sub-command to the root command
func ( f * Flaeg ) AddCommand ( command * Command ) {
f . commands = append ( f . commands , command )
}
//AddParser adds custom parser for a type to the map of custom parsers
func ( f * Flaeg ) AddParser ( typ reflect . Type , parser Parser ) {
f . customParsers [ typ ] = parser
}
// Run calls the command with flags given as agruments
func ( f * Flaeg ) Run ( ) error {
if f . calledCommand == nil {
if _ , _ , err := f . findCommandWithCommandArgs ( ) ; err != nil {
return err
}
}
if _ , err := f . Parse ( f . calledCommand ) ; err != nil {
return err
}
return f . calledCommand . Run ( )
}
// Parse calls Flaeg Load Function end returns the parsed command structure (by reference)
// It returns nil and a not nil error if it fails
func ( f * Flaeg ) Parse ( cmd * Command ) ( * Command , error ) {
if f . calledCommand == nil {
f . commmandArgs = f . args
}
if err := LoadWithCommand ( cmd , f . commmandArgs , f . customParsers , f . commands ) ; err != nil {
return cmd , err
}
return cmd , nil
}
//splitArgs takes args (type []string) and return command ("" if rootCommand) and command's args
func splitArgs ( args [ ] string ) ( string , [ ] string ) {
if len ( args ) >= 1 && len ( args [ 0 ] ) >= 1 && string ( args [ 0 ] [ 0 ] ) != "-" {
if len ( args ) == 1 {
return strings . ToLower ( args [ 0 ] ) , [ ] string { }
}
return strings . ToLower ( args [ 0 ] ) , args [ 1 : ]
}
return "" , args
}
// findCommandWithCommandArgs returns the called command (by reference) and command's args
// the error returned is not nil if it fails
func ( f * Flaeg ) findCommandWithCommandArgs ( ) ( * Command , [ ] string , error ) {
commandName := ""
commandName , f . commmandArgs = splitArgs ( f . args )
if len ( commandName ) > 0 {
for _ , command := range f . commands {
if commandName == command . Name {
f . calledCommand = command
return f . calledCommand , f . commmandArgs , nil
}
}
return nil , [ ] string { } , fmt . Errorf ( "Command %s not found" , commandName )
}
f . calledCommand = f . commands [ 0 ]
return f . calledCommand , f . commmandArgs , nil
}
// GetCommand splits args and returns the called command (by reference)
// It returns nil and a not nil error if it fails
func ( f * Flaeg ) GetCommand ( ) ( * Command , error ) {
if f . calledCommand == nil {
_ , _ , err := f . findCommandWithCommandArgs ( )
return f . calledCommand , err
}
return f . calledCommand , nil
}
//isExported return true is the field (from fieldName) is exported,
//else false
func isExported ( fieldName string ) bool {
if len ( fieldName ) < 1 {
return false
}
if string ( fieldName [ 0 ] ) == strings . ToUpper ( string ( fieldName [ 0 ] ) ) {
return true
}
return false
}
func argToLower ( inArg string ) string {
if len ( inArg ) < 2 {
return strings . ToLower ( inArg )
}
var outArg string
dashIndex := strings . Index ( inArg , "--" )
if dashIndex == - 1 {
if dashIndex = strings . Index ( inArg , "-" ) ; dashIndex == - 1 {
return inArg
}
//-fValue
outArg = strings . ToLower ( inArg [ dashIndex : dashIndex + 2 ] ) + inArg [ dashIndex + 2 : ]
return outArg
}
//--flag
if equalIndex := strings . Index ( inArg , "=" ) ; equalIndex != - 1 {
//--flag=value
outArg = strings . ToLower ( inArg [ dashIndex : equalIndex ] ) + inArg [ equalIndex : ]
} else {
//--boolflag
outArg = strings . ToLower ( inArg [ dashIndex : ] )
}
return outArg
}
func argsToLower ( inArgs [ ] string ) [ ] string {
outArgs := make ( [ ] string , len ( inArgs ) , len ( inArgs ) )
for i , inArg := range inArgs {
outArgs [ i ] = argToLower ( inArg )
}
return outArgs
}