package keybase import ( "context" "fmt" "io" "os/exec" ) // apiCmd holds the pipes that connect to an executed command type apiCmd struct { Stderr io.ReadCloser Stdin io.WriteCloser Stdout io.ReadCloser startFunc func() error } type cmd interface { Start() error Stdin() io.WriteCloser Stdout() io.ReadCloser Stderr() io.ReadCloser } type defaultCmd struct { apiCmd *apiCmd } func (c *defaultCmd) Start() error { return c.apiCmd.startFunc() } func (c *defaultCmd) Stdin() io.WriteCloser { return c.apiCmd.Stdin } func (c *defaultCmd) Stdout() io.ReadCloser { return c.apiCmd.Stdout } func (c *defaultCmd) Stderr() io.ReadCloser { return c.apiCmd.Stderr } var _ cmd = &defaultCmd{} func newApiCmd(ctx context.Context, execCmd string, args ...string) (cmd, error) { var err error // create a new apiCmd cmd := &apiCmd{} // set up the command api := exec.CommandContext(ctx, execCmd, args...) // an empty start func for failures cmd.startFunc = func() error { return nil } // hook up the stderr pipe if cmd.Stderr, err = api.StderrPipe(); err != nil { return nil, fmt.Errorf("failed to get pipe: %v", err) } // hook up the stdout pipe if cmd.Stdout, err = api.StdoutPipe(); err != nil { return nil, fmt.Errorf("failed to get pipe: %v", err) } // hook up the stdin pipe if cmd.Stdin, err = api.StdinPipe(); err != nil { return nil, fmt.Errorf("failed to get pipe: %v", err) } // create an anonymous function to start the commands cmd.startFunc = func() error { if err := api.Start(); err != nil { return fmt.Errorf("failed to start command: %v", err) } return nil } return &defaultCmd{cmd}, nil }