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  }