Files
commandkit/outcome.go

174 lines
6.0 KiB
Go
Raw Normal View History

package commandkit
import (
"errors"
"io"
"strings"
2026-10-11 01:15:13 +00:00
)
// Severity classifies a nonterminal diagnostic.
2026-10-11 01:15:13 +00:00
type Severity string
2026-10-11 01:15:13 +00:00
const (
// SeverityInfo marks informational content.
SeverityInfo Severity = "info"
// SeverityWarning marks a concern that does not fail the operation.
2026-10-11 01:15:13 +00:00
SeverityWarning Severity = "warning"
// SeverityDebug marks diagnostic detail for debug output.
SeverityDebug Severity = "debug"
// SeverityProgress marks progress information.
SeverityProgress Severity = "progress"
)
// OutcomeKind classifies the result of a command invocation.
type OutcomeKind string
const (
// OutcomeSuccess indicates that the operation completed successfully.
OutcomeSuccess OutcomeKind = "success"
// OutcomeUsage indicates invalid command syntax or declared input.
OutcomeUsage OutcomeKind = "usage"
// OutcomeOperational indicates an operational failure.
OutcomeOperational OutcomeKind = "operational"
// OutcomeDomain indicates a result classified by application domain logic.
OutcomeDomain OutcomeKind = "domain"
)
// DeliveryState describes the result of presenting content to a destination.
type DeliveryState uint8
const (
// DeliveryNone indicates that no content was submitted to the destination.
DeliveryNone DeliveryState = iota
// DeliverySuppressed indicates that all submitted content was intentionally hidden.
DeliverySuppressed
// DeliveryComplete indicates that all requested delivery completed.
DeliveryComplete
// DeliveryIncomplete indicates that rendering or destination writing failed.
DeliveryIncomplete
)
// ErrReporterClosed is returned when a reporting handle is used after invocation finalization.
var ErrReporterClosed = errors.New("reporter is closed")
// Detail renders optional content to the supplied writer.
type Detail func(io.Writer) error
// Explanation contains terminal content for a failed outcome.
type Explanation struct {
// Message is the literal opening text.
Message string
// Details render additional content in order.
Details []Detail
}
// PresentationResult records delivery observations for the two output channels.
type PresentationResult struct {
// Output records delivery to the result destination.
Output DeliveryResult
// Diagnostics records delivery to the diagnostic destination.
Diagnostics DeliveryResult
}
// DeliveryResult records rendering and writing errors for one destination.
type DeliveryResult struct {
// State summarizes content delivery to the destination.
State DeliveryState
// RenderError is the first rendering error for the destination.
RenderError error
// WriteError is the first destination write error.
WriteError error
}
// Diagnostic is passive nonterminal message data returned by a provider.
//
// Write renders the diagnostic to a supplied writer when the owning integration
// submits it through the invocation reporter. A nil Write function has no detail.
type Diagnostic struct {
2026-10-11 01:15:13 +00:00
// Severity classifies the diagnostic without depending on a provider library.
Severity Severity
// Write renders the diagnostic to the supplied writer.
Write Detail
}
// Acquired pairs a provider value with independent diagnostics and failure context.
2026-10-11 01:15:13 +00:00
type Acquired[T any] struct {
// Value is the value returned by the acquisition callback.
Value T
// Diagnostics are passive nonterminal messages for the owning command to submit.
2026-10-11 01:15:13 +00:00
Diagnostics []Diagnostic
// Explanation provides terminal context if the owning command returns failure.
Explanation Explanation
2026-10-11 01:15:13 +00:00
}
// Outcome is the classified result of an operation.
//
// Cause records an optional operation error without selecting the outcome or
// generating operator-facing text. Presentation records delivery observations
// separately. Invocation coordination treats a zero Outcome as success with status 0.
type Outcome struct {
// Kind classifies the result.
Kind OutcomeKind
// Status is the suggested process status for this result.
Status int
// Domain names the application-defined classification when Kind is OutcomeDomain.
Domain string
// Cause is the optional operation error, preserved for caller inspection.
Cause error
// Explanation contains terminal content for a failed outcome.
Explanation Explanation
// Presentation records delivery observations populated by CommandKit.
Presentation PresentationResult
}
// Success returns a successful outcome with status 0.
func Success() Outcome {
return Outcome{Kind: OutcomeSuccess}
}
// Failure returns an operational outcome with status 1 and a terminal explanation.
func Failure(message string, details ...Detail) Outcome {
return Outcome{Kind: OutcomeOperational, Status: 1, Explanation: Explanation{Message: message, Details: cloneDetails(details)}}
}
// Usage returns a usage outcome with status 2 and a terminal explanation.
func Usage(message string, details ...Detail) Outcome {
return Outcome{Kind: OutcomeUsage, Status: 2, Explanation: Explanation{Message: message, Details: cloneDetails(details)}}
}
// Domain returns an application-domain outcome with the supplied name and status.
func Domain(name string, status int) Outcome {
return Outcome{Kind: OutcomeDomain, Status: status, Domain: name}
}
// WithExplanation returns a copy of the outcome with the supplied terminal explanation.
func (o Outcome) WithExplanation(message string, details ...Detail) Outcome {
o.Explanation = Explanation{Message: message, Details: cloneDetails(details)}
return o
}
// WithCause returns a copy of the outcome with the supplied optional cause.
func (o Outcome) WithCause(cause error) Outcome {
o.Cause = cause
return o
}
func cloneDetails(details []Detail) []Detail {
if details == nil {
return nil
}
return append([]Detail{}, details...)
}
// MessageDiagnostic returns passive diagnostic data that writes message followed
// by a newline when submitted through a reporter.
2026-10-11 01:15:13 +00:00
func MessageDiagnostic(severity Severity, message string) Diagnostic {
return Diagnostic{
Severity: severity,
Write: func(w io.Writer) error {
_, err := io.WriteString(w, strings.TrimSuffix(message, "\n")+"\n")
return err
},
}
}