All checks were successful
		
		
	
	Test / Create distribution (push) Successful in 34s
				
			Test / Sandbox (push) Successful in 2m13s
				
			Test / Hakurei (push) Successful in 3m15s
				
			Test / Hpkg (push) Successful in 4m7s
				
			Test / Sandbox (race detector) (push) Successful in 4m14s
				
			Test / Hakurei (race detector) (push) Successful in 5m7s
				
			Test / Flake checks (push) Successful in 1m37s
				
			Should have done this when relocating this from container. Now is a good time to rename it before v0.3.x. Signed-off-by: Ophestra <cat@gensokyo.uk>
		
			
				
	
	
		
			112 lines
		
	
	
		
			3.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			112 lines
		
	
	
		
			3.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
// Package message provides interfaces and a base implementation for extended reporting on top of [log.Logger]
 | 
						|
package message
 | 
						|
 | 
						|
import (
 | 
						|
	"errors"
 | 
						|
	"log"
 | 
						|
	"sync/atomic"
 | 
						|
)
 | 
						|
 | 
						|
// Error is an error with a user-facing message.
 | 
						|
type Error interface {
 | 
						|
	// Message returns a user-facing error message.
 | 
						|
	Message() string
 | 
						|
 | 
						|
	error
 | 
						|
}
 | 
						|
 | 
						|
// GetMessage returns whether an error implements [Error], and the message if it does.
 | 
						|
func GetMessage(err error) (string, bool) {
 | 
						|
	var e Error
 | 
						|
	if !errors.As(err, &e) || e == nil {
 | 
						|
		return "", false
 | 
						|
	}
 | 
						|
	return e.Message(), true
 | 
						|
}
 | 
						|
 | 
						|
// Msg is used for package-wide verbose logging.
 | 
						|
type Msg interface {
 | 
						|
	// GetLogger returns the address of the underlying [log.Logger].
 | 
						|
	GetLogger() *log.Logger
 | 
						|
 | 
						|
	// IsVerbose atomically loads and returns whether [Msg] has verbose logging enabled.
 | 
						|
	IsVerbose() bool
 | 
						|
	// SwapVerbose atomically stores a new verbose state and returns the previous value held by [Msg].
 | 
						|
	SwapVerbose(verbose bool) bool
 | 
						|
	// Verbose passes its argument to the Println method of the underlying [log.Logger] if IsVerbose returns true.
 | 
						|
	Verbose(v ...any)
 | 
						|
	// Verbosef passes its argument to the Printf method of the underlying [log.Logger] if IsVerbose returns true.
 | 
						|
	Verbosef(format string, v ...any)
 | 
						|
 | 
						|
	// Suspend causes the embedded [Suspendable] to withhold writes to its downstream [io.Writer].
 | 
						|
	// Suspend returns false and is a noop if called between calls to Suspend and Resume.
 | 
						|
	Suspend() bool
 | 
						|
 | 
						|
	// Resume dumps the entire buffer held by the embedded [Suspendable] and stops withholding future writes.
 | 
						|
	// Resume returns false and is a noop if a call to Suspend does not precede it.
 | 
						|
	Resume() bool
 | 
						|
 | 
						|
	// BeforeExit runs implementation-specific cleanup code, and optionally prints warnings.
 | 
						|
	// BeforeExit is called before [os.Exit].
 | 
						|
	BeforeExit()
 | 
						|
}
 | 
						|
 | 
						|
// defaultMsg is the default implementation of the [Msg] interface.
 | 
						|
// The zero value is not safe for use. Callers should use the [New] function instead.
 | 
						|
type defaultMsg struct {
 | 
						|
	verbose atomic.Bool
 | 
						|
 | 
						|
	logger *log.Logger
 | 
						|
	Suspendable
 | 
						|
}
 | 
						|
 | 
						|
// New initialises a downstream [log.Logger] for a new [Msg].
 | 
						|
// The [log.Logger] should no longer be configured after [New] returns.
 | 
						|
// If downstream is nil, a new logger is initialised in its place.
 | 
						|
func New(downstream *log.Logger) Msg {
 | 
						|
	if downstream == nil {
 | 
						|
		downstream = log.New(log.Writer(), "container: ", 0)
 | 
						|
	}
 | 
						|
 | 
						|
	m := defaultMsg{logger: downstream}
 | 
						|
	m.Suspendable.Downstream = downstream.Writer()
 | 
						|
	downstream.SetOutput(&m.Suspendable)
 | 
						|
	return &m
 | 
						|
}
 | 
						|
 | 
						|
func (msg *defaultMsg) GetLogger() *log.Logger { return msg.logger }
 | 
						|
 | 
						|
func (msg *defaultMsg) IsVerbose() bool               { return msg.verbose.Load() }
 | 
						|
func (msg *defaultMsg) SwapVerbose(verbose bool) bool { return msg.verbose.Swap(verbose) }
 | 
						|
func (msg *defaultMsg) Verbose(v ...any) {
 | 
						|
	if msg.verbose.Load() {
 | 
						|
		msg.logger.Println(v...)
 | 
						|
	}
 | 
						|
}
 | 
						|
func (msg *defaultMsg) Verbosef(format string, v ...any) {
 | 
						|
	if msg.verbose.Load() {
 | 
						|
		msg.logger.Printf(format, v...)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// Resume calls [Suspendable.Resume] and prints a message if buffer was filled
 | 
						|
// between calls to [Suspendable.Suspend] and Resume.
 | 
						|
func (msg *defaultMsg) Resume() bool {
 | 
						|
	resumed, dropped, _, err := msg.Suspendable.Resume()
 | 
						|
	if err != nil {
 | 
						|
		// probably going to result in an error as well, so this message is as good as unreachable
 | 
						|
		msg.logger.Printf("cannot dump buffer on resume: %v", err)
 | 
						|
	}
 | 
						|
	if resumed && dropped > 0 {
 | 
						|
		msg.logger.Printf("dropped %d bytes while output is suspended", dropped)
 | 
						|
	}
 | 
						|
	return resumed
 | 
						|
}
 | 
						|
 | 
						|
// BeforeExit prints a message if called between calls to [Suspendable.Suspend] and Resume.
 | 
						|
func (msg *defaultMsg) BeforeExit() {
 | 
						|
	if msg.Resume() {
 | 
						|
		msg.logger.Printf("beforeExit reached on suspended output")
 | 
						|
	}
 | 
						|
}
 |