github.com/Benchkram/bob@v0.0.0-20220321080157-7c8f3876e225/pkg/execctl/tree.go (about) 1 package execctl 2 3 import ( 4 "context" 5 "github.com/Benchkram/bob/pkg/ctl" 6 "golang.org/x/sync/errgroup" 7 ) 8 9 // assert cmdTree implements the CommandTree interface 10 var _ ctl.CommandTree = (*cmdTree)(nil) 11 12 type cmdTree struct { 13 *Cmd 14 15 subcommands []*cmdTree 16 } 17 18 func NewCmdTree(root *Cmd, subcommands ...*Cmd) *cmdTree { 19 cmds := make([]*cmdTree, 0) 20 21 for _, cmd := range subcommands { 22 cmds = append(cmds, NewCmdTree(cmd)) 23 } 24 25 return &cmdTree{Cmd: root, subcommands: cmds} 26 } 27 28 func (c *cmdTree) Subcommands() []ctl.Command { 29 subs := make([]ctl.Command, 0) 30 31 for _, cmd := range c.subcommands { 32 subs = append(subs, cmd) 33 } 34 35 return subs 36 } 37 38 // Start starts the command if it's not already running. It will return ErrCmdAlreadyStarted if it is. 39 func (c *cmdTree) Start() error { 40 // use errgroup to speed up startup of the subcommands (run startup of them in parallel) 41 g, _ := errgroup.WithContext(context.Background()) 42 43 for _, sub := range c.subcommands { 44 // required or ref is replaced within loop 45 subsub := sub 46 47 g.Go(func() error { 48 return subsub.Start() 49 }) 50 } 51 52 err := g.Wait() 53 if err != nil { 54 return err 55 } 56 57 return c.Cmd.Start() 58 } 59 60 // Stop stops the running command with an os.Interrupt signal. It does not return an error if the command has 61 // already exited gracefully. 62 func (c *cmdTree) Stop() error { 63 err := c.Cmd.Stop() 64 if err != nil { 65 return err 66 } 67 68 g, _ := errgroup.WithContext(context.Background()) 69 70 for _, sub := range c.subcommands { 71 // required or ref is replaced within loop 72 subsub := sub 73 74 g.Go(func() error { 75 return subsub.Stop() 76 }) 77 } 78 79 return g.Wait() 80 } 81 82 // Restart first interrupts the command if it's already running, and then re-runs the command. 83 func (c *cmdTree) Restart() error { 84 err := c.Stop() 85 if err != nil { 86 return err 87 } 88 89 return c.Start() 90 }