package console import ( "context" "errors" "fmt" "io" "os" "os/signal" "time" ) type C[A any] struct { io.Reader io.Writer err io.Writer args A abort func() cancelfns []func(context.Context) error } func New[A any](args A) (context.Context, *C[A]) { ctx := context.Background() ctx, abort := context.WithCancel(ctx) ctx, stop := signal.NotifyContext(ctx, os.Interrupt) go func() { <-ctx.Done(); stop() }() // restore interrupt function console := &C[A]{Reader: os.Stdin, Writer: os.Stdout, err: os.Stderr, args: args, abort: abort} return ctx, console } func (c *C[A]) Args() A { return c.args } func (c *C[A]) Shutdown() error { fmt.Fprintln(c.err, "shutting down ", len(c.cancelfns), " cancel functions...") defer fmt.Fprintln(c.err, "done") ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() c.abort() var err error for _, fn := range c.cancelfns { err = errors.Join(err, fn(ctx)) } return err } func (c *C[A]) AddCancel(fn func(context.Context) error) { c.cancelfns = append(c.cancelfns, fn) } func (c *C[A]) IfFatal(err error) { if err == nil { return } fmt.Fprintln(c.err, err) err = c.Shutdown() if err != nil { fmt.Fprintln(c.err, err) } os.Exit(1) }