get.porter.sh/porter@v1.3.0/pkg/exec/builder/flags.go (about) 1 package builder 2 3 import ( 4 "fmt" 5 "sort" 6 ) 7 8 // Flag represents a flag passed to a mixin command. 9 type Flag struct { 10 Name string 11 Values []string 12 } 13 14 type Dashes struct { 15 Long string 16 Short string 17 } 18 19 // NewFlag creates an instance of a Flag. 20 func NewFlag(name string, values ...string) Flag { 21 f := Flag{ 22 Name: name, 23 } 24 if len(values) > 0 { 25 f.Values = make([]string, len(values)) 26 copy(f.Values, values) 27 } 28 return f 29 } 30 31 // ToSlice converts to a string array suitable of command arguments suitable for passing to exec.Command 32 func (flag Flag) ToSlice(dashes Dashes) []string { 33 var flagName string 34 dash := dashes.Long 35 if len(flag.Name) == 1 { 36 dash = dashes.Short 37 } 38 flagName = fmt.Sprintf("%s%s", dash, flag.Name) 39 40 result := make([]string, 0, len(flag.Values)+1) 41 if len(flag.Values) == 0 { 42 result = append(result, flagName) 43 } else { 44 for _, value := range flag.Values { 45 result = append(result, flagName) 46 result = append(result, value) 47 } 48 } 49 return result 50 } 51 52 type Flags []Flag 53 54 // ToSlice converts to a string array suitable of command arguments suitable for passing to exec.Command 55 func (flags *Flags) ToSlice(dashes Dashes) []string { 56 result := make([]string, 0, 2*len(*flags)) 57 58 sort.Sort(flags) 59 for _, flag := range *flags { 60 result = append(result, flag.ToSlice(dashes)...) 61 } 62 63 return result 64 } 65 66 // UnmarshalYAML takes input like this 67 // flags: 68 // flag1: value 69 // flag2: value 70 // flag3: 71 // - value1 72 // - value2 73 // 74 // and turns it into this: 75 // 76 // []Flags{ {"flag1", []string{"value"}}, {"flag2", []string{"value"}}, {"flag3", []string{"value1", "value2"} } 77 func (flags *Flags) UnmarshalYAML(unmarshal func(interface{}) error) error { 78 flagMap := map[string]interface{}{} 79 err := unmarshal(&flagMap) 80 if err != nil { 81 return fmt.Errorf("could not unmarshal yaml into Step.Flags: %w", err) 82 } 83 84 *flags = make(Flags, 0, len(flagMap)) 85 for k, v := range flagMap { 86 f := Flag{} 87 f.Name = k 88 89 switch t := v.(type) { 90 case []interface{}: 91 f.Values = make([]string, len(t)) 92 for i := range t { 93 iv, ok := t[i].(string) 94 if !ok { 95 return fmt.Errorf("invalid yaml type for flag %s: %T", f.Name, t[i]) 96 } 97 f.Values[i] = iv 98 } 99 case nil: 100 // do nothing 101 default: 102 f.Values = make([]string, 1) 103 f.Values[0] = fmt.Sprintf("%v", v) 104 } 105 106 *flags = append(*flags, f) 107 } 108 109 return nil 110 } 111 112 // MarshalYAML writes out flags back into the proper format for mixin flags. 113 // Input: 114 // []Flags{ {"flag1", []string{"value"}}, {"flag2", []string{"value"}}, {"flag3", []string{"value1", "value2"} } 115 // 116 // Is turned into 117 // 118 // flags: 119 // flag1: value 120 // flag2: value 121 // flag3: 122 // - value1 123 // - value2 124 func (flags Flags) MarshalYAML() (interface{}, error) { 125 result := make(map[string]interface{}, len(flags)) 126 127 for _, flag := range flags { 128 if len(flag.Values) == 1 { 129 result[flag.Name] = flag.Values[0] 130 } else { 131 result[flag.Name] = flag.Values 132 } 133 } 134 135 return result, nil 136 } 137 138 func (flags Flags) Len() int { 139 return len(flags) 140 } 141 142 func (flags Flags) Swap(i, j int) { 143 flags[i], flags[j] = flags[j], flags[i] 144 } 145 146 func (flags Flags) Less(i, j int) bool { 147 return flags[i].Name < flags[j].Name 148 }