github.com/haraldrudell/parl@v0.4.176/pflags/option-data.go (about) 1 /* 2 © 2020–present Harald Rudell <harald.rudell@gmail.com> (https://haraldrudell.github.io/haraldrudell/) 3 ISC License 4 */ 5 6 package pflags 7 8 import ( 9 "flag" 10 "fmt" 11 "time" 12 13 "github.com/haraldrudell/parl/perrors" 14 ) 15 16 const ( 17 typeMismatch = "option %s type %T: bad yaml data pointer of type: %T" 18 ) 19 20 var NoOptions []OptionData 21 22 // OptionData contain options data for the flag package 23 // - OptionData is used for command-line options to be declarative 24 // - instead of flag.BoolVar and similar invocations scattered about the codebase 25 // all options are in a single OptionData slice that is iterated so that AddOption is invoked for each element 26 // - to invoke [flag.BoolVar]. pointer, name, value and usage is required 27 // - [flag.BoolVar] is consumer-provided storage, [flag.Bool] is flag-provided storage 28 type OptionData struct { 29 P interface{} // P is a reference to where the effective value of this option is stored 30 Name string // Name is the option name without hyphen, “debug” for -debug 31 Value interface{} // Value is the default value for this option 32 Usage string // printable string describing what this option does 33 Y interface{} // reference to effective value in YamlData 34 } 35 36 // AddOption executes flag.BoolVar and such on the options map 37 // - this defines the option to the flag package 38 // - all options must be defined prior to invoking [flag.Parse] 39 // - options are defined using value pointers, ie [flag.BoolVar] not flag.Bool[], 40 // so [flag.Parse] updates effective option values directly 41 func (o *OptionData) AddOption() { 42 switch effectiveValuep := o.P.(type) { 43 case *bool: 44 if value, ok := o.Value.(bool); !ok { 45 o.defaultValuePanic() 46 } else { 47 flag.BoolVar(effectiveValuep, o.Name, value, o.Usage) 48 } 49 if y := o.Y; y != nil { 50 if _, ok := y.(*bool); !ok { 51 o.yamlPointerPanic() 52 } 53 } 54 case *time.Duration: 55 if value, ok := o.Value.(time.Duration); !ok { 56 o.defaultValuePanic() 57 } else { 58 flag.DurationVar(effectiveValuep, o.Name, value, o.Usage) 59 } 60 if y := o.Y; y != nil { 61 if _, ok := y.(*time.Duration); !ok { 62 o.yamlPointerPanic() 63 } 64 } 65 case *float64: 66 if value, ok := o.Value.(float64); !ok { 67 o.defaultValuePanic() 68 } else { 69 flag.Float64Var(effectiveValuep, o.Name, value, o.Usage) 70 } 71 if y := o.Y; y != nil { 72 if _, ok := y.(*float64); !ok { 73 o.yamlPointerPanic() 74 } 75 } 76 case *int64: 77 if value, ok := o.Value.(int64); !ok { 78 o.defaultValuePanic() 79 } else { 80 flag.Int64Var(effectiveValuep, o.Name, value, o.Usage) 81 } 82 if y := o.Y; y != nil { 83 if _, ok := y.(*int64); !ok { 84 o.yamlPointerPanic() 85 } 86 } 87 case *int: 88 if value, ok := o.Value.(int); !ok { 89 o.defaultValuePanic() 90 } else { 91 flag.IntVar(effectiveValuep, o.Name, value, o.Usage) 92 } 93 if y := o.Y; y != nil { 94 if _, ok := y.(*int); !ok { 95 o.yamlPointerPanic() 96 } 97 } 98 case *string: 99 if value, ok := o.Value.(string); !ok { 100 o.defaultValuePanic() 101 } else { 102 flag.StringVar(effectiveValuep, o.Name, value, o.Usage) 103 } 104 if y := o.Y; y != nil { 105 if _, ok := y.(*string); !ok { 106 o.yamlPointerPanic() 107 } 108 } 109 case *uint64: 110 if value, ok := o.Value.(uint64); !ok { 111 o.defaultValuePanic() 112 } else { 113 flag.Uint64Var(effectiveValuep, o.Name, value, o.Usage) 114 } 115 if y := o.Y; y != nil { 116 if _, ok := y.(*uint64); !ok { 117 o.yamlPointerPanic() 118 } 119 } 120 case *uint: 121 if value, ok := o.Value.(uint); !ok { 122 o.defaultValuePanic() 123 } else { 124 flag.UintVar(effectiveValuep, o.Name, value, o.Usage) 125 } 126 if y := o.Y; y != nil { 127 if _, ok := y.(*uint); !ok { 128 o.yamlPointerPanic() 129 } 130 } 131 case *[]string: 132 if value, ok := o.Value.([]string); !ok { 133 o.defaultValuePanic() 134 } else { 135 flag.Var(NewStringSliceValue(effectiveValuep, value), o.Name, o.Usage) 136 } 137 if y := o.Y; y != nil { 138 if _, ok := y.(*[]string); !ok { 139 o.yamlPointerPanic() 140 } 141 } 142 default: 143 panic(perrors.ErrorfPF("option '%s' Unknown options type: %T", o.Name, effectiveValuep)) 144 } 145 } 146 147 // ApplyYaml copies a value read from yaml into the effective value location 148 func (o *OptionData) ApplyYaml() (err error) { 149 150 // check if this OptionData is updated from yaml 151 var yamlDataPointer = o.Y 152 if yamlDataPointer == nil { 153 return // does not have YamlValue 154 } 155 156 // assign the effective value with the YamlData field’s value 157 switch valuePointer := o.P.(type) { 158 case *bool: 159 typedPointer, ok := yamlDataPointer.(*bool) 160 if !ok { 161 return perrors.Errorf(typeMismatch, o.Name, o.P, yamlDataPointer) 162 } 163 *valuePointer = *typedPointer 164 case *time.Duration: 165 typedPointer, ok := yamlDataPointer.(*time.Duration) 166 if !ok { 167 return perrors.Errorf(typeMismatch, o.Name, o.P, yamlDataPointer) 168 } 169 *valuePointer = *typedPointer 170 case *float64: 171 typedPointer, ok := yamlDataPointer.(*float64) 172 if !ok { 173 return perrors.Errorf(typeMismatch, o.Name, o.P, yamlDataPointer) 174 } 175 *valuePointer = *typedPointer 176 case *int64: 177 typedPointer, ok := yamlDataPointer.(*int64) 178 if !ok { 179 return perrors.Errorf(typeMismatch, o.Name, o.P, yamlDataPointer) 180 } 181 *valuePointer = *typedPointer 182 case *int: 183 typedPointer, ok := yamlDataPointer.(*int) 184 if !ok { 185 return perrors.Errorf(typeMismatch, o.Name, o.P, yamlDataPointer) 186 } 187 *valuePointer = *typedPointer 188 case *string: 189 typedPointer, ok := yamlDataPointer.(*string) 190 if !ok { 191 return perrors.Errorf(typeMismatch, o.Name, o.P, yamlDataPointer) 192 } 193 *valuePointer = *typedPointer 194 case *uint64: 195 typedPointer, ok := yamlDataPointer.(*uint64) 196 if !ok { 197 return perrors.Errorf(typeMismatch, o.Name, o.P, yamlDataPointer) 198 } 199 *valuePointer = *typedPointer 200 case *uint: 201 typedPointer, ok := yamlDataPointer.(*uint) 202 if !ok { 203 return perrors.Errorf(typeMismatch, o.Name, o.P, yamlDataPointer) 204 } 205 *valuePointer = *typedPointer 206 case *[]string: 207 typedPointer, ok := yamlDataPointer.(*[]string) 208 if !ok { 209 return perrors.Errorf(typeMismatch, o.Name, o.P, yamlDataPointer) 210 } 211 *valuePointer = *typedPointer 212 default: 213 return perrors.Errorf("option %s: unknown value type: %T", o.Name, o.P) 214 } 215 216 return 217 } 218 219 func (o *OptionData) defaultValuePanic() { 220 var expTyp = fmt.Sprintf("%T", o.P) 221 panic(perrors.ErrorfPF("option '%s': default value not %s: %T", o.Name, expTyp[1:], o.Value)) 222 } 223 224 func (o *OptionData) yamlPointerPanic() { 225 panic(perrors.ErrorfPF("option '%s' yaml pointer not %T: %T", o.Name, o.P, o.Y)) 226 } 227 228 func (o *OptionData) ValueDump() (valueS string) { 229 switch effectiveValuep := o.P.(type) { 230 case *bool: 231 return fmt.Sprintf("%v", *effectiveValuep) 232 case *time.Duration: 233 return fmt.Sprintf("%v", *effectiveValuep) 234 case *float64: 235 return fmt.Sprintf("%v", *effectiveValuep) 236 case *int64: 237 return fmt.Sprintf("%v", *effectiveValuep) 238 case *int: 239 return fmt.Sprintf("%v", *effectiveValuep) 240 case *string: 241 return fmt.Sprintf("%v", *effectiveValuep) 242 case *uint64: 243 return fmt.Sprintf("%v", *effectiveValuep) 244 case *uint: 245 return fmt.Sprintf("%v", *effectiveValuep) 246 case *[]string: 247 return fmt.Sprintf("%v", *effectiveValuep) 248 default: 249 panic(perrors.ErrorfPF("option '%s' Unknown options type: %T", o.Name, effectiveValuep)) 250 } 251 }