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