Skip to content

Commit

Permalink
feat(xcli): 提供 XCli 基座
Browse files Browse the repository at this point in the history
  • Loading branch information
Ccheers committed Oct 16, 2024
1 parent 42cb099 commit 42d6cb7
Show file tree
Hide file tree
Showing 2 changed files with 113 additions and 21 deletions.
36 changes: 15 additions & 21 deletions xcli/define.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,21 @@ type ICommand interface {
Flags() *flag.FlagSet
}

func BuildCobraCommand(icmd ICommand) *cobra.Command {
c := &cobra.Command{
Use: icmd.Use(),
Short: icmd.Short(),
Long: icmd.Long(),
RunE: func(cmd *cobra.Command, args []string) error {
return icmd.Run(cmd.Context(), args)
},
}
ConvFlag2Pflag(icmd.Flags(), c.Flags())
return c
}

// ============================================== flag value ==============================================

type pflagValueAdapter struct {
value flag.Value
}
Expand All @@ -38,27 +53,6 @@ func (x *pflagValueAdapter) Type() string {
return "string"
}

func InitRootCommand(root ICommand, cmds ...ICommand) *cobra.Command {
rootCmd := BuildCobraCommand(root)
for _, sudCmd := range cmds {
rootCmd.AddCommand(BuildCobraCommand(sudCmd))
}
return rootCmd
}

func BuildCobraCommand(icmd ICommand) *cobra.Command {
c := &cobra.Command{
Use: icmd.Use(),
Short: icmd.Short(),
Long: icmd.Long(),
RunE: func(cmd *cobra.Command, args []string) error {
return icmd.Run(cmd.Context(), args)
},
}
ConvFlag2Pflag(icmd.Flags(), c.Flags())
return c
}

func ConvFlag2Pflag(src *flag.FlagSet, dst *pflag.FlagSet) {
src.VisitAll(func(f *flag.Flag) {
dst.Var(newPflagValueAdapter(f.Value), f.Name, f.Usage)
Expand Down
98 changes: 98 additions & 0 deletions xcli/xcli.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package xcli

import (
"context"
"os"
"os/signal"
"syscall"

"github.com/ccheers/xpkg/sync/errgroup"
"github.com/spf13/cobra"
)

type XCli struct {
rootCmd *cobra.Command
}

type XCliOptions struct {
short string
long string
cmdList ICommandList
}

func defaultXCliOptions() *XCliOptions {
return &XCliOptions{}
}

type XCliOption interface {
apply(opt *XCliOptions)
}

type XCliOptionFunc func(opt *XCliOptions)

func (f XCliOptionFunc) apply(opt *XCliOptions) {
f(opt)
}

func WithShort(short string) XCliOption {
return XCliOptionFunc(func(opt *XCliOptions) {
opt.short = short
})
}

func WithLong(long string) XCliOption {
return XCliOptionFunc(func(opt *XCliOptions) {
opt.long = long
})
}

func WithCommandList(cmdList ICommandList) XCliOption {
return XCliOptionFunc(func(opt *XCliOptions) {
opt.cmdList = cmdList
})
}

func NewXCli(name string, opts ...XCliOption) *XCli {
options := defaultXCliOptions()
for _, o := range opts {
o.apply(options)
}

rootCmd := &cobra.Command{
Use: name,
Short: options.short,
Long: options.long,
RunE: func(cmd *cobra.Command, args []string) error {
return cmd.Help()
},
}

for _, cmd := range options.cmdList {
rootCmd.AddCommand(BuildCobraCommand(cmd))
}

return &XCli{
rootCmd: rootCmd,
}
}

func (x *XCli) Run(ctx context.Context) error {
ctx, cancel := context.WithCancel(ctx)

eg := errgroup.WithCancel(ctx)
eg.Go(func(ctx context.Context) error {
return x.rootCmd.ExecuteContext(ctx)
})
eg.Go(func(ctx context.Context) error {
c := make(chan os.Signal, 2)
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
go func() {
<-c
cancel()
<-c
os.Exit(1) // second signal. Exit directly.
}()
return nil
})
return eg.Wait()
}

0 comments on commit 42d6cb7

Please sign in to comment.