xt/internal/console/console.go
2025-03-27 16:35:05 -06:00

62 lines
1.2 KiB
Go

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)
}