package runnable import "os/exec" import "fmt" import "github.com/codecrafters-io/shell-starter-go/app/core" import "github.com/codecrafters-io/shell-starter-go/app/tokenize" import "github.com/codecrafters-io/shell-starter-go/app/builtin" type Runnable interface { Run(ctx *core.Context) core.ExitStatus } type ExternalCommand struct { Args []string ExecPath string Name string } func (self ExternalCommand) Run(ctx *core.Context) core.ExitStatus { cmd := exec.Command(self.ExecPath, self.Args...) cmd.Args[0] = self.Name cmd.Stdout = ctx.Stdout cmd.Stderr = ctx.Stderr cmd.Stdin = ctx.Stdin err := cmd.Run() if err != nil { if exiterr, ok := err.(*exec.ExitError); ok { return core.ExitStatus(exiterr.ExitCode()) // fmt.Printf("Handle():exiterr=%#v\n", exiterr.ExitCode()) } else { fmt.Printf("Handle():err=%#v\n", err) return core.ExitStatus(65535) } } return 0 } type ParseError struct { code ParseErrorCode value string } type ParseErrorCode int const ( ParseErrorCodeNothing ParseErrorCode = iota ParseErrorCodeNotFound ) func (e ParseError) Error() string { switch e.code { case ParseErrorCodeNotFound: return fmt.Sprintf("%s: not found", e.value) case ParseErrorCodeNothing: return "" default: return fmt.Sprintf("unknown ParseErrorCode code: %d .value=%q", e.code, e.value) } } func Parse(ctx *core.Context, command_line string) (Runnable, error) { tokens, err := tokenize.Tokenize(command_line) if err != nil { return nil, err } //fmt.Printf("Parse():tokens=%#v\n", tokens) if len(tokens) == 0 { // ie. empty or whitespace-only input return nil, ParseError{ code: ParseErrorCodeNothing, } } bltn, err := builtin.Parse(ctx, tokens[0], tokens[1:]) if err == nil { // it was a builtin return bltn, nil } if e, ok := err.(builtin.ParseError); ok { switch e.Code { case builtin.ParseErrorCodeUsage: return nil, err case builtin.ParseErrorCodeUnknownBuiltin: exec_path, err := exec.LookPath(tokens[0]) if err != nil { // command not found return nil, ParseError{ code: ParseErrorCodeNotFound, value: tokens[0], } } return ExternalCommand{ ExecPath: exec_path, Name: tokens[0], Args: tokens[1:], }, nil } } panic("unexpected builtin.Parse(); only builtin.ParseError is expected") }