yunion.io/x/cloudmux@v0.3.10-0-alpha.1/cmd/cmx/shell/cmd.go (about) 1 // Copyright 2019 Yunion 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package shell 16 17 import ( 18 "fmt" 19 20 "yunion.io/x/cloudmux/pkg/cloudprovider" 21 ) 22 23 type Command struct { 24 prefix string 25 } 26 27 func NewCommand(prefix string) *Command { 28 return &Command{ 29 prefix: prefix, 30 } 31 } 32 33 // NewCO returns a Command with Option 34 func NewCO[OPT any](cmd *Command) *CommandOption[OPT] { 35 return &CommandOption[OPT]{ 36 Command: cmd, 37 opt: new(OPT), 38 } 39 } 40 41 type CommandOption[OPT any] struct { 42 *Command 43 opt *OPT 44 isList bool 45 useGetterList bool 46 requireRegion bool 47 requireZone bool 48 requireHost bool 49 } 50 51 func (co *CommandOption[OPT]) RawRun(suffix string, desc string, cb func(ICloudProvider, *OPT) error) { 52 R(co.opt, fmt.Sprintf("%s-%s", co.prefix, suffix), desc, cb) 53 } 54 55 func (co *CommandOption[OPT]) Run(suffix string, desc string, cb func(ICloudProvider, *OPT) (any, error)) { 56 co.RawRun(suffix, desc, func(cli ICloudProvider, opt *OPT) error { 57 data, err := cb(cli, opt) 58 if err != nil { 59 return err 60 } 61 return co.processData(data) 62 }) 63 } 64 65 func (co *CommandOption[OPT]) processData(data any) error { 66 if data == nil { 67 return nil 68 } 69 if co.isList { 70 lo, ok := interface{}(co.opt).(IListOption) 71 if ok { 72 cols := []string{} 73 if !lo.IsDetails() { 74 cols = lo.GetColumns() 75 } 76 PrintList(data, 0, lo.GetOffset(), lo.GetLimit(), cols) 77 return nil 78 } else { 79 if co.useGetterList { 80 PrintGetterList(data, nil) 81 return nil 82 } 83 PrintList(data, 0, 0, 0, nil) 84 return nil 85 } 86 } 87 PrintObject(data) 88 return nil 89 } 90 91 func (co *CommandOption[OPT]) UseList() *CommandOption[OPT] { 92 co.isList = true 93 return co 94 } 95 96 func (co *CommandOption[OPT]) UseGetterList() *CommandOption[OPT] { 97 co.UseList().useGetterList = true 98 return co 99 } 100 101 func (co *CommandOption[OPT]) RunByProvider(suffix string, desc string, cb func(cloudprovider.ICloudProvider, *OPT) (any, error)) { 102 co.Run(suffix, desc, func(cli ICloudProvider, opt *OPT) (any, error) { 103 return cb(cli.GetProvider(), opt) 104 }) 105 } 106 107 func (co *CommandOption[OPT]) RequireRegion() *CommandOption[OPT] { 108 co.requireRegion = true 109 return co 110 } 111 112 func (co *CommandOption[OPT]) RunByRegion( 113 suffix string, desc string, 114 cb func(cli cloudprovider.ICloudRegion, args *OPT) (any, error), 115 ) { 116 co.RawRun(suffix, desc, func(cli ICloudProvider, o *OPT) error { 117 return iterResources( 118 "region", 119 func() ([]cloudprovider.ICloudRegion, error) { 120 return cli.GetProvider().GetIRegions(), nil 121 }, 122 cli.GetDefaultRegionId(), 123 co.requireRegion, 124 func(region cloudprovider.ICloudRegion) error { 125 data, err := cb(region, o) 126 if err != nil { 127 return err 128 } 129 return co.processData(data) 130 }, 131 ) 132 }) 133 } 134 135 func (co *CommandOption[OPT]) RequireZone() *CommandOption[OPT] { 136 co.requireZone = true 137 return co 138 } 139 140 func (co *CommandOption[OPT]) RunByZone( 141 suffix string, desc string, 142 cb func(cloudprovider.ICloudZone, *OPT) (any, error), 143 ) { 144 co.RunByRegion(suffix, desc, func(ir cloudprovider.ICloudRegion, o *OPT) (any, error) { 145 return nil, iterResources( 146 "zone", 147 ir.GetIZones, 148 interface{}(o).(IZoneBaseOptions).GetZoneId(), 149 co.requireZone, 150 func(zone cloudprovider.ICloudZone) error { 151 data, err := cb(zone, o) 152 if err != nil { 153 return err 154 } 155 return co.processData(data) 156 }, 157 ) 158 }) 159 } 160 161 func (co *CommandOption[OPT]) RequireHost() *CommandOption[OPT] { 162 co.requireHost = true 163 return co 164 } 165 166 func (co *CommandOption[OPT]) RunByHost( 167 suffix string, desc string, 168 cb func(cloudprovider.ICloudHost, *OPT) (any, error), 169 ) { 170 co.RunByZone(suffix, desc, func(iz cloudprovider.ICloudZone, o *OPT) (any, error) { 171 return nil, iterResources( 172 "host", 173 iz.GetIHosts, 174 interface{}(o).(IHostBaseOptions).GetHostId(), 175 co.requireHost, 176 func(host cloudprovider.ICloudHost) error { 177 data, err := cb(host, o) 178 if err != nil { 179 return err 180 } 181 return co.processData(data) 182 }, 183 ) 184 }) 185 } 186 187 type IRunner[O any, C any] interface { 188 RequireRegion() IRunner[O, C] 189 RequireZone() IRunner[O, C] 190 RequireHost() IRunner[O, C] 191 192 List(suffix, desc string, cb func(cli C, args *O) (any, error)) 193 GetterList(suffix, desc string, cb func(cli C, args *O) (any, error)) 194 Run(suffix, desc string, cb func(cli C, args *O) (any, error)) 195 } 196 197 type sBaseRunner[O any, C any] struct { 198 cmd *Command 199 runF func(*CommandOption[O]) func(string, string, func(C, *O) (any, error)) 200 coFs []func(*CommandOption[O]) *CommandOption[O] 201 } 202 203 func newBaseRunner[O any, C any]( 204 cmd *Command, 205 runF func(*CommandOption[O]) func(string, string, func(C, *O) (any, error)), 206 ) IRunner[O, C] { 207 return &sBaseRunner[O, C]{ 208 cmd: cmd, 209 runF: runF, 210 coFs: make([]func(*CommandOption[O]) *CommandOption[O], 0), 211 } 212 } 213 214 func (r *sBaseRunner[O, C]) newCO() *CommandOption[O] { 215 co := NewCO[O](r.cmd) 216 for _, f := range r.coFs { 217 co = f(co) 218 } 219 return co 220 } 221 222 func (r *sBaseRunner[O, C]) addCOF(f func(*CommandOption[O]) *CommandOption[O]) *sBaseRunner[O, C] { 223 r.coFs = append(r.coFs, f) 224 return r 225 } 226 227 func (r *sBaseRunner[O, C]) useList() *sBaseRunner[O, C] { 228 r.addCOF(func(co *CommandOption[O]) *CommandOption[O] { 229 co.UseList() 230 return co 231 }) 232 return r 233 } 234 235 func (r *sBaseRunner[O, C]) useGetterList() *sBaseRunner[O, C] { 236 r.addCOF(func(co *CommandOption[O]) *CommandOption[O] { 237 co.UseGetterList() 238 return co 239 }) 240 return r 241 } 242 243 func (r *sBaseRunner[O, C]) RequireRegion() IRunner[O, C] { 244 r.addCOF(func(co *CommandOption[O]) *CommandOption[O] { 245 co.RequireRegion() 246 return co 247 }) 248 return r 249 } 250 251 func (r *sBaseRunner[O, C]) RequireZone() IRunner[O, C] { 252 r.addCOF(func(co *CommandOption[O]) *CommandOption[O] { 253 co.RequireZone() 254 return co 255 }) 256 return r 257 } 258 259 func (r *sBaseRunner[O, C]) RequireHost() IRunner[O, C] { 260 r.addCOF(func(co *CommandOption[O]) *CommandOption[O] { 261 co.RequireHost() 262 return co 263 }) 264 return r 265 } 266 267 func (r *sBaseRunner[O, C]) list(suffix, desc string, cb func(cli C, args *O) (any, error)) { 268 r.Run(suffix, desc, cb) 269 } 270 271 func (r *sBaseRunner[O, C]) List(suffix, desc string, cb func(cli C, args *O) (any, error)) { 272 r.useList() 273 r.list(suffix, desc, cb) 274 } 275 276 func (r *sBaseRunner[O, C]) GetterList(suffix, desc string, cb func(cli C, args *O) (any, error)) { 277 r.useGetterList() 278 r.list(suffix, desc, cb) 279 } 280 281 func (r *sBaseRunner[O, C]) Run(suffix, desc string, cb func(cli C, args *O) (any, error)) { 282 r.runF(r.newCO())(suffix, desc, cb) 283 } 284 285 type sProviderRunner[O any] struct { 286 IRunner[O, cloudprovider.ICloudProvider] 287 } 288 289 func ProviderR[O any](cmd *Command) IRunner[O, cloudprovider.ICloudProvider] { 290 return &sProviderRunner[O]{ 291 IRunner: newBaseRunner( 292 cmd, 293 func(co *CommandOption[O]) func(string, string, func(cloudprovider.ICloudProvider, *O) (any, error)) { 294 return co.RunByProvider 295 }), 296 } 297 } 298 299 type sRegionRunner[O any] struct { 300 IRunner[O, cloudprovider.ICloudRegion] 301 } 302 303 func RegionR[O any](cmd *Command) IRunner[O, cloudprovider.ICloudRegion] { 304 return &sRegionRunner[O]{ 305 IRunner: newBaseRunner( 306 cmd, 307 func(co *CommandOption[O]) func(string, string, func(cloudprovider.ICloudRegion, *O) (any, error)) { 308 return co.RunByRegion 309 }), 310 } 311 } 312 313 type sZoneRunner[O any] struct { 314 IRunner[O, cloudprovider.ICloudZone] 315 } 316 317 func ZoneR[O any](cmd *Command) IRunner[O, cloudprovider.ICloudZone] { 318 return &sZoneRunner[O]{ 319 IRunner: newBaseRunner( 320 cmd, 321 func(co *CommandOption[O]) func(string, string, func(cloudprovider.ICloudZone, *O) (any, error)) { 322 return co.RunByZone 323 }), 324 } 325 } 326 327 type sHostRunner[O IHostBaseOptions] struct { 328 IRunner[O, cloudprovider.ICloudHost] 329 } 330 331 func HostR[O IHostBaseOptions](cmd *Command) IRunner[O, cloudprovider.ICloudHost] { 332 return &sHostRunner[O]{ 333 IRunner: newBaseRunner( 334 cmd, 335 func(co *CommandOption[O]) func(string, string, func(cloudprovider.ICloudHost, *O) (any, error)) { 336 return co.RunByHost 337 }, 338 ), 339 } 340 } 341 342 type SEmptyOptionRunner[C any] struct { 343 runner IRunner[EmptyOption, C] 344 } 345 346 func EmptyOptionRunner[C any]( 347 prefix string, 348 rf func(*Command) IRunner[EmptyOption, C], 349 ) *SEmptyOptionRunner[C] { 350 cmd := NewCommand(prefix) 351 r := rf(cmd) 352 return &SEmptyOptionRunner[C]{ 353 runner: r, 354 } 355 } 356 357 func (er *SEmptyOptionRunner[C]) Run(suffix, desc string, cb func(cli C) (any, error)) { 358 er.runner.Run(suffix, desc, func(cli C, args *EmptyOption) (any, error) { 359 return cb(cli) 360 }) 361 } 362 363 func (er *SEmptyOptionRunner[C]) List(suffix, desc string, cb func(cli C) (any, error)) { 364 er.runner.List(suffix, desc, func(cli C, args *EmptyOption) (any, error) { 365 return cb(cli) 366 }) 367 } 368 369 func (er *SEmptyOptionRunner[C]) GetterList(suffix, desc string, cb func(cli C) (any, error)) { 370 er.runner.GetterList(suffix, desc, func(cli C, args *EmptyOption) (any, error) { 371 return cb(cli) 372 }) 373 } 374 375 func EmptyOptionProviderR(prefix string) *SEmptyOptionRunner[cloudprovider.ICloudProvider] { 376 return EmptyOptionRunner(prefix, ProviderR[EmptyOption]) 377 } 378 379 func EmptyOptionRegionR(prefix string) *SEmptyOptionRunner[cloudprovider.ICloudRegion] { 380 return EmptyOptionRunner(prefix, RegionR[EmptyOption]) 381 } 382 383 func EmptyOptionZoneR(prefix string) *SEmptyOptionRunner[cloudprovider.ICloudZone] { 384 return EmptyOptionRunner(prefix, ZoneR[EmptyOption]) 385 }