package gonf import ( "bytes" "log/slog" "os/exec" ) var commands []*CommandPromise func init() { commands = make([]*CommandPromise, 0) } func Command(cmd string, args ...string) *CommandPromise { return CommandWithEnv([]string{}, cmd, args...) } func CommandWithEnv(env []string, cmd string, args ...string) *CommandPromise { return &CommandPromise{ args: args, chain: nil, cmd: cmd, env: env, err: nil, status: DECLARED, } } type CommandPromise struct { args []string chain []Promise cmd string env []string err error status Status Stdout bytes.Buffer Stderr bytes.Buffer } func (c *CommandPromise) IfRepaired(p ...Promise) Promise { c.chain = append(c.chain, p...) return c } func (c *CommandPromise) Promise() *CommandPromise { if c.status == DECLARED { c.status = PROMISED commands = append(commands, c) } return c } func (c *CommandPromise) Resolve() { cmd := exec.Command(c.cmd, c.args...) for _, e := range c.env { cmd.Env = append(cmd.Environ(), e) } cmd.Stdout = &c.Stdout cmd.Stderr = &c.Stderr if c.err = cmd.Run(); c.err != nil { c.status = BROKEN slog.Error("command", "args", c.args, "cmd", c.cmd, "env", c.env, "err", c.err, "stdout", c.Stdout.String(), "stderr", c.Stderr.String(), "status", c.Status) return } if c.Stdout.Len() == 0 && c.Stderr.Len() > 0 { c.status = BROKEN slog.Error("command", "args", c.args, "cmd", c.cmd, "env", c.env, "stdout", c.Stdout.String(), "stderr", c.Stderr.String(), "status", c.Status) return } c.status = REPAIRED slog.Info("command", "args", c.args, "cmd", c.cmd, "env", c.env, "stderr", c.Stderr.String(), "status", c.Status) // TODO add a notion of repaired? for _, p := range c.chain { p.Resolve() } } func (c CommandPromise) Status() Status { return c.status } func resolveCommands() (status Status) { status = KEPT for _, c := range commands { if c.status == PROMISED { c.Resolve() switch c.status { case BROKEN: return BROKEN case REPAIRED: status = REPAIRED } } } return }