204 lines
7.8 KiB
Go
204 lines
7.8 KiB
Go
package commandkit
|
|
|
|
import (
|
|
"errors"
|
|
"io"
|
|
"strings"
|
|
)
|
|
|
|
// Severity classifies a nonterminal provider diagnostic. Terminal failure
|
|
// context belongs in an Outcome explanation instead.
|
|
type Severity string
|
|
|
|
const (
|
|
// SeverityInfo marks informational content.
|
|
SeverityInfo Severity = "info"
|
|
// SeverityWarning marks a concern that does not fail the operation.
|
|
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 writer supplied by the reporter.
|
|
// Use that writer only during the callback. A callback must not submit another
|
|
// message, queue deferred work, or start another rendering operation through
|
|
// the same invocation's reporter.
|
|
type Detail func(io.Writer) error
|
|
|
|
// Explanation contains terminal content for a failed outcome. CommandKit
|
|
// presents it during invocation finalization, including in quiet mode.
|
|
type Explanation struct {
|
|
// Message is the literal opening text.
|
|
Message string
|
|
// Details render additional content in order.
|
|
Details []Detail
|
|
}
|
|
|
|
// PresentationResult records output and diagnostic delivery observations
|
|
// populated by CommandKit during invocation finalization. It is not a message
|
|
// submission path or a transcript.
|
|
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.
|
|
// The state distinguishes no submitted content, intentionally suppressed
|
|
// content, completed delivery, and incomplete delivery. RenderError and
|
|
// WriteError retain the first error of their kind for that 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. It
|
|
// does not emit output or select an invocation outcome; the owning integration
|
|
// submits it through the invocation reporter.
|
|
//
|
|
// 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 {
|
|
// 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. The owning command chooses how to submit each diagnostic and whether
|
|
// to attach the explanation to a failed outcome.
|
|
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.
|
|
Diagnostics []Diagnostic
|
|
// Explanation provides terminal context if the owning command returns failure.
|
|
Explanation Explanation
|
|
}
|
|
|
|
// Outcome is the classified result of an operation. Constructing or returning
|
|
// an outcome does not itself write output; Execute presents its explanation
|
|
// during finalization.
|
|
//
|
|
// 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. Presentation failures change a success or usage outcome to
|
|
// operational status 1; a domain outcome keeps its classification and nonzero
|
|
// status, while a zero domain status becomes 1.
|
|
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. The message and details are presented once when the outcome is
|
|
// returned from an invocation; an optional operation error can be attached with
|
|
// WithCause and is not rendered automatically.
|
|
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. The
|
|
// message and details are presented when the outcome is returned from an
|
|
// invocation; an optional cause is preserved separately with WithCause.
|
|
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. The application owns the classification and status decision.
|
|
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, replacing any existing explanation. CommandKit presents it once
|
|
// during finalization, including in quiet mode.
|
|
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 operation
|
|
// cause. The cause is preserved for caller inspection and does not select an
|
|
// outcome or generate operator-facing text.
|
|
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.
|
|
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
|
|
},
|
|
}
|
|
}
|