501 lines
25 KiB
Go
501 lines
25 KiB
Go
package ckprompt
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"strconv"
|
|
|
|
commandkit "gitea.maximumdirect.net/eric/CommandKit"
|
|
"gitea.maximumdirect.net/eric/CommandKit/integrations/ckconfig"
|
|
configkit "gitea.maximumdirect.net/eric/ConfigKit"
|
|
promptkit "gitea.maximumdirect.net/eric/promptkit"
|
|
)
|
|
|
|
// PromptRequest selects one named prompt for inspection.
|
|
type PromptRequest struct {
|
|
// Selection carries the configuration and overlay values supplied to the command.
|
|
Selection ckconfig.Selection
|
|
// ID is the selected named prompt.
|
|
ID string
|
|
}
|
|
|
|
// ProfileRequest selects one named profile for inspection.
|
|
type ProfileRequest struct {
|
|
// Selection carries the configuration and overlay values supplied to the command.
|
|
Selection ckconfig.Selection
|
|
// ID is the selected named profile.
|
|
ID string
|
|
}
|
|
|
|
// CatalogRequest selects the configuration and native namespace filter for a
|
|
// prompt, profile, or backend catalog operation. A zero Filter preserves
|
|
// PromptKit's native default selection.
|
|
type CatalogRequest struct {
|
|
// Selection carries the configuration and overlay supplied to the command.
|
|
Selection ckconfig.Selection
|
|
// Filter is forwarded unchanged to each filterable native catalog. Its zero
|
|
// value preserves PromptKit's default namespace selection.
|
|
Filter promptkit.NamespaceFilter
|
|
}
|
|
|
|
// NamespaceRequest selects the configuration and native resource kinds for a
|
|
// namespace catalog operation. An empty Kinds slice preserves PromptKit's
|
|
// native default selection.
|
|
type NamespaceRequest struct {
|
|
// Selection carries the configuration and overlay supplied to the command.
|
|
Selection ckconfig.Selection
|
|
// Kinds is forwarded unchanged to the native namespace catalog. An empty
|
|
// slice preserves PromptKit's default kind selection.
|
|
Kinds []promptkit.ResourceKind
|
|
}
|
|
|
|
// RequirementsRequest selects a prompt context for native requirements
|
|
// inspection.
|
|
type RequirementsRequest struct {
|
|
// Selection carries the configuration and overlay values supplied to the command.
|
|
Selection ckconfig.Selection
|
|
// Inspection forwards PromptKit's native prompt, version, profile, variable,
|
|
// and session discovery context. Optional version, profile, and session
|
|
// strings retain PromptKit's empty-value semantics.
|
|
Inspection promptkit.RequirementInspectionRequest
|
|
}
|
|
|
|
// Suite describes application-owned input, shared state, and optional catalog,
|
|
// named inspection, requirements discovery, concrete request validation, and
|
|
// smoke testing capabilities. Its private issues retain adapter construction
|
|
// failures.
|
|
// Factory results and callback effects remain caller-owned: Command does not
|
|
// deep-copy arbitrary objects or repair aliases returned by factories.
|
|
// A reached operation prepares one input, applies its captured selectors, and
|
|
// checks the input before creating state and calling the selected provider.
|
|
// Factories are not invoked during construction, installation, help, or version,
|
|
// and need not run after failure.
|
|
type Suite[I, S any] struct {
|
|
// NewInput is called once when inspection, requirements, validation, smoke,
|
|
// or catalog input preparation is reached, before applying selectors and
|
|
// calling CheckInput.
|
|
// If nil, input is the zero value of I, including nil for pointer types.
|
|
NewInput func() I
|
|
// NewState is called once when an inspection, validation, smoke, or catalog
|
|
// callback is reached, after input checks pass. If nil, state is the zero
|
|
// value of S, including nil for pointer types.
|
|
NewState func() S
|
|
// Selectors declares the original typed flags accepted by inspection,
|
|
// requirements, validation, smoke, and catalog commands.
|
|
Selectors []commandkit.Flag[I]
|
|
// CheckInput validates selected inspection, requirements, validation, smoke,
|
|
// or catalog input before state creation.
|
|
CheckInput func(ckconfig.Selection, I) error
|
|
// InspectPrompt inspects the selected prompt after input and state
|
|
// preparation. Its acquisition carries independent loader diagnostics and
|
|
// terminal context alongside any native inspection result.
|
|
InspectPrompt func(context.Context, PromptRequest, I, S) (commandkit.Acquired[*promptkit.PromptInspection], error)
|
|
// InspectProfile inspects the selected profile after input and state
|
|
// preparation. Its acquisition carries independent loader diagnostics and
|
|
// terminal context alongside any native inspection result.
|
|
InspectProfile func(context.Context, ProfileRequest, I, S) (commandkit.Acquired[*promptkit.ProfileInspection], error)
|
|
// InspectRequirements inspects native PromptKit requirements after input and
|
|
// state preparation. Its acquisition carries independent loader diagnostics
|
|
// and terminal context alongside any native inspection result. It is
|
|
// optional; a nil callback omits `promptkit requirements show` without
|
|
// disabling validation or other capabilities.
|
|
InspectRequirements func(context.Context, RequirementsRequest, I, S) (commandkit.Acquired[*promptkit.RequirementInspection], error)
|
|
// ValidateRequest validates a concrete PromptKit request after input and
|
|
// state preparation. It is optional; a nil callback omits
|
|
// `promptkit requirements validate` without disabling discovery or other
|
|
// capabilities. The acquisition carries independent diagnostics, terminal
|
|
// context, and an empty value on success; PromptKit errors are returned
|
|
// unchanged where supported.
|
|
ValidateRequest func(context.Context, ValidationRequest, I, S) (commandkit.Acquired[struct{}], error)
|
|
// SmokeTest runs one concrete native smoke-test operation after input and
|
|
// state preparation. It is optional; nil omits only the smoke-test command.
|
|
// The acquisition retains a native report, independent diagnostics, and
|
|
// terminal context even when the callback also returns an error. For
|
|
// `test all`, CommandKit calls the captured callback sequentially for each
|
|
// selected kind after preparing input and state once.
|
|
SmokeTest func(context.Context, SmokeRequest, I, S) (commandkit.Acquired[*promptkit.SmokeTestReport], error)
|
|
// ListPrompts lists prompt identities after input and state preparation.
|
|
// It is optional independently of the other catalogs; nil omits only this
|
|
// catalog command.
|
|
ListPrompts func(context.Context, CatalogRequest, I, S) (commandkit.Acquired[[]promptkit.PromptIdentity], error)
|
|
// ListProfiles lists profile IDs after input and state preparation. It is
|
|
// optional independently of the other catalogs; nil omits only this catalog
|
|
// command.
|
|
ListProfiles func(context.Context, CatalogRequest, I, S) (commandkit.Acquired[[]string], error)
|
|
// ListBackends lists backend IDs after input and state preparation. It is
|
|
// optional independently of the other catalogs; nil omits only this catalog
|
|
// command.
|
|
ListBackends func(context.Context, CatalogRequest, I, S) (commandkit.Acquired[[]string], error)
|
|
// ListNamespaces lists native namespace names after input and state
|
|
// preparation. It is optional independently of the other catalogs; nil omits
|
|
// only this catalog command.
|
|
ListNamespaces func(context.Context, NamespaceRequest, I, S) (commandkit.Acquired[[]string], error)
|
|
// ListCollections lists collection information after input and state
|
|
// preparation. It is optional independently of the other catalogs; nil
|
|
// omits only this catalog command.
|
|
ListCollections func(context.Context, ckconfig.Selection, I, S) (commandkit.Acquired[[]promptkit.CollectionInfo], error)
|
|
|
|
issues []error
|
|
}
|
|
|
|
// WithRequestValidation attaches PromptKit-backed concrete request validation
|
|
// to suite while preserving its other capabilities and retained issues. Those
|
|
// issues remain attached if capabilities are narrowed before Command captures
|
|
// the suite. The
|
|
// builder, when present, runs before the engine loader and receives the seed,
|
|
// input, state, and invocation context. Its returned request is passed to
|
|
// PromptKit unchanged; the loader receives the seed's original configuration
|
|
// selection. A nil builder forwards the seed request unchanged. A builder
|
|
// failure prevents engine loading and preserves its supported error identity.
|
|
// Native validation errors supply terminal explanations and are returned with
|
|
// supported identity, and
|
|
// configuration diagnostics and terminal context from engine loading remain
|
|
// separate in the acquisition. A nil loader is retained as a suite wiring
|
|
// issue and does not install a validation callback. Callback references and
|
|
// objects remain caller-owned.
|
|
func WithRequestValidation[I, S any](
|
|
suite Suite[I, S],
|
|
load func(context.Context, ckconfig.Selection, I, S) (*promptkit.Engine, error),
|
|
build func(context.Context, ValidationRequest, I, S) (promptkit.RunRequest, error),
|
|
) Suite[I, S] {
|
|
if load == nil {
|
|
issues := append([]error(nil), suite.issues...)
|
|
suite.issues = append(issues, errors.New("PromptKit engine loader is nil"))
|
|
return suite
|
|
}
|
|
|
|
suite.ValidateRequest = func(ctx context.Context, request ValidationRequest, input I, state S) (commandkit.Acquired[struct{}], error) {
|
|
if err := promptKitContextError(ctx); err != nil {
|
|
return commandkit.Acquired[struct{}]{}, fmt.Errorf("PromptKit request validation before building: %w", err)
|
|
}
|
|
|
|
concreteRequest := request.Request
|
|
if build != nil {
|
|
built, err := build(ctx, request, input, state)
|
|
if err != nil {
|
|
return commandkit.Acquired[struct{}]{}, err
|
|
}
|
|
concreteRequest = built
|
|
}
|
|
|
|
if err := promptKitContextError(ctx); err != nil {
|
|
return commandkit.Acquired[struct{}]{}, fmt.Errorf("PromptKit request validation after building: %w", err)
|
|
}
|
|
|
|
engine, _, messages, err := loadPromptKitEngine(ctx, request.Selection, input, state, "", load)
|
|
if err != nil {
|
|
return commandkit.Acquired[struct{}]{Diagnostics: messages.diagnostics, Explanation: messages.explanation}, err
|
|
}
|
|
if err := promptKitContextError(ctx); err != nil {
|
|
return commandkit.Acquired[struct{}]{Diagnostics: messages.diagnostics, Explanation: messages.explanation}, fmt.Errorf("PromptKit request validation before native validation: %w", err)
|
|
}
|
|
|
|
if err := engine.ValidateRequest(ctx, concreteRequest); err != nil {
|
|
messages = messages.withOperationError(err)
|
|
return commandkit.Acquired[struct{}]{Diagnostics: messages.diagnostics, Explanation: messages.explanation}, err
|
|
}
|
|
if err := promptKitContextError(ctx); err != nil {
|
|
return commandkit.Acquired[struct{}]{Diagnostics: messages.diagnostics, Explanation: messages.explanation}, fmt.Errorf("PromptKit request validation after native validation: %w", err)
|
|
}
|
|
return commandkit.Acquired[struct{}]{Diagnostics: messages.diagnostics, Explanation: messages.explanation}, nil
|
|
}
|
|
return suite
|
|
}
|
|
|
|
type engineLoader[I, S any] func(context.Context, ckconfig.Selection, I, S) (*promptkit.Engine, error)
|
|
|
|
type promptKitAcquisition struct {
|
|
diagnostics []commandkit.Diagnostic
|
|
explanation commandkit.Explanation
|
|
}
|
|
|
|
// withOperationError deliberately presents a native PromptKit operation error.
|
|
// Loader and request-builder errors remain application-owned. Native structured
|
|
// context takes precedence when an acquisition already supplies it.
|
|
func (messages promptKitAcquisition) withOperationError(err error) promptKitAcquisition {
|
|
if err == nil || messages.explanation.Message != "" {
|
|
return messages
|
|
}
|
|
for _, detail := range messages.explanation.Details {
|
|
if detail != nil {
|
|
return messages
|
|
}
|
|
}
|
|
messages.explanation.Message = err.Error()
|
|
return messages
|
|
}
|
|
|
|
// ConfigScope adapts PromptKit's captured configuration inspection to the
|
|
// standard configuration commands, including native comparison when the scope
|
|
// declares Diff. The loader is not called during declaration construction or
|
|
// installation; it is called only when a config operation selects the
|
|
// promptkit scope, with that operation's input and shared state. PromptKit
|
|
// independent diagnostics, terminal context, and supported error identity are
|
|
// returned through ckconfig's acquisition and comparison contracts.
|
|
func ConfigScope[I, S any](load func(context.Context, ckconfig.Selection, I, S) (*promptkit.Engine, error)) ckconfig.Scope[I, S] {
|
|
scope := ckconfig.Scope[I, S]{
|
|
Name: "promptkit",
|
|
Summary: "PromptKit engine configuration.",
|
|
Operations: []ckconfig.Operation{ckconfig.Validate, ckconfig.Show, ckconfig.Sources, ckconfig.Explain, ckconfig.Diff},
|
|
}
|
|
if load == nil {
|
|
return ckconfig.WithScopeError(scope, errors.New("PromptKit configuration loader is nil"))
|
|
}
|
|
|
|
scope.Acquire = func(ctx context.Context, request ckconfig.Request, input I, state S) (commandkit.Acquired[ckconfig.View], error) {
|
|
_, inspection, messages, err := loadPromptKitEngine(ctx, request.Selection, input, state, "", load)
|
|
if err != nil {
|
|
return commandkit.Acquired[ckconfig.View]{Diagnostics: messages.diagnostics, Explanation: messages.explanation}, err
|
|
}
|
|
return commandkit.Acquired[ckconfig.View]{
|
|
Value: ckconfig.View{
|
|
Effective: inspection,
|
|
Sources: inspection,
|
|
Explanation: inspection,
|
|
},
|
|
Diagnostics: messages.diagnostics,
|
|
Explanation: messages.explanation,
|
|
}, nil
|
|
}
|
|
scope.Compare = func(ctx context.Context, request ckconfig.ComparisonRequest[I], state S) (commandkit.Acquired[[]configkit.Difference], error) {
|
|
if err := promptKitContextError(ctx); err != nil {
|
|
return commandkit.Acquired[[]configkit.Difference]{}, fmt.Errorf("PromptKit scope %q comparison before left load: %w", scope.Name, err)
|
|
}
|
|
|
|
_, leftInspection, messages, err := loadPromptKitEngine(ctx, request.Left.Selection, request.Left.Input, state, "left", load)
|
|
if err != nil {
|
|
return commandkit.Acquired[[]configkit.Difference]{Diagnostics: messages.diagnostics, Explanation: messages.explanation}, fmt.Errorf("PromptKit scope %q left comparison load: %w", scope.Name, err)
|
|
}
|
|
|
|
if err := promptKitContextError(ctx); err != nil {
|
|
return commandkit.Acquired[[]configkit.Difference]{Diagnostics: messages.diagnostics, Explanation: messages.explanation}, fmt.Errorf("PromptKit scope %q comparison after left load: %w", scope.Name, err)
|
|
}
|
|
|
|
_, rightInspection, rightMessages, err := loadPromptKitEngine(ctx, request.Right.Selection, request.Right.Input, state, "right", load)
|
|
messages = appendPromptKitAcquisition(messages, rightMessages)
|
|
if err != nil {
|
|
return commandkit.Acquired[[]configkit.Difference]{Diagnostics: messages.diagnostics, Explanation: messages.explanation}, fmt.Errorf("PromptKit scope %q right comparison load: %w", scope.Name, err)
|
|
}
|
|
|
|
if err := promptKitContextError(ctx); err != nil {
|
|
return commandkit.Acquired[[]configkit.Difference]{Diagnostics: messages.diagnostics, Explanation: messages.explanation}, fmt.Errorf("PromptKit scope %q comparison before native diff: %w", scope.Name, err)
|
|
}
|
|
|
|
differences, err := leftInspection.Diff(rightInspection)
|
|
messages = messages.withOperationError(err)
|
|
if err != nil {
|
|
return commandkit.Acquired[[]configkit.Difference]{Diagnostics: messages.diagnostics, Explanation: messages.explanation}, fmt.Errorf("PromptKit scope %q native configuration diff: %w", scope.Name, err)
|
|
}
|
|
return commandkit.Acquired[[]configkit.Difference]{Value: differences, Diagnostics: messages.diagnostics, Explanation: messages.explanation}, nil
|
|
}
|
|
return scope
|
|
}
|
|
|
|
func promptKitContextError(ctx context.Context) error {
|
|
if ctx == nil {
|
|
return nil
|
|
}
|
|
return ctx.Err()
|
|
}
|
|
|
|
// FromEngine supplies eight PromptKit-backed callbacks: prompt, profile,
|
|
// backend, namespace, and collection catalogs, plus named prompt and profile
|
|
// inspection and requirements discovery.
|
|
// The requirements callback forwards RequirementsRequest.Inspection to
|
|
// Engine.InspectRequirements. Concrete request validation is an independent
|
|
// capability and can be composed explicitly with WithRequestValidation. Smoke
|
|
// testing is also an explicit capability installed with WithSmokeTesting.
|
|
// FromEngine does not call the loader during construction. Each selected
|
|
// operation passes its context, supplied selection, initialized input, and
|
|
// shared invocation state to the loader before calling PromptKit's public
|
|
// listing or inspection method. Applications can narrow, wrap, or replace these
|
|
// callbacks before Command captures them. A nil loader is retained as an
|
|
// adapter issue. Each callback returns independent loader diagnostics and
|
|
// terminal context alongside the native operation's value and error. Native
|
|
// operation errors supply terminal explanations when structured context is absent.
|
|
func FromEngine[I, S any](load func(context.Context, ckconfig.Selection, I, S) (*promptkit.Engine, error)) Suite[I, S] {
|
|
if load == nil {
|
|
return Suite[I, S]{issues: []error{errors.New("PromptKit engine loader is nil")}}
|
|
}
|
|
|
|
return Suite[I, S]{
|
|
ListPrompts: func(ctx context.Context, request CatalogRequest, input I, state S) (commandkit.Acquired[[]promptkit.PromptIdentity], error) {
|
|
engine, _, messages, err := loadPromptKitEngine(ctx, request.Selection, input, state, "", load)
|
|
if err != nil {
|
|
return commandkit.Acquired[[]promptkit.PromptIdentity]{Diagnostics: messages.diagnostics, Explanation: messages.explanation}, err
|
|
}
|
|
prompts, err := engine.ListPrompts(ctx, request.Filter)
|
|
messages = messages.withOperationError(err)
|
|
return commandkit.Acquired[[]promptkit.PromptIdentity]{Value: prompts, Diagnostics: messages.diagnostics, Explanation: messages.explanation}, err
|
|
},
|
|
ListProfiles: func(ctx context.Context, request CatalogRequest, input I, state S) (commandkit.Acquired[[]string], error) {
|
|
engine, _, messages, err := loadPromptKitEngine(ctx, request.Selection, input, state, "", load)
|
|
if err != nil {
|
|
return commandkit.Acquired[[]string]{Diagnostics: messages.diagnostics, Explanation: messages.explanation}, err
|
|
}
|
|
profiles, err := engine.ListProfiles(ctx, request.Filter)
|
|
messages = messages.withOperationError(err)
|
|
return commandkit.Acquired[[]string]{Value: profiles, Diagnostics: messages.diagnostics, Explanation: messages.explanation}, err
|
|
},
|
|
ListBackends: func(ctx context.Context, request CatalogRequest, input I, state S) (commandkit.Acquired[[]string], error) {
|
|
engine, _, messages, err := loadPromptKitEngine(ctx, request.Selection, input, state, "", load)
|
|
if err != nil {
|
|
return commandkit.Acquired[[]string]{Diagnostics: messages.diagnostics, Explanation: messages.explanation}, err
|
|
}
|
|
backends, err := engine.ListBackends(ctx, request.Filter)
|
|
messages = messages.withOperationError(err)
|
|
return commandkit.Acquired[[]string]{Value: backends, Diagnostics: messages.diagnostics, Explanation: messages.explanation}, err
|
|
},
|
|
ListNamespaces: func(ctx context.Context, request NamespaceRequest, input I, state S) (commandkit.Acquired[[]string], error) {
|
|
engine, _, messages, err := loadPromptKitEngine(ctx, request.Selection, input, state, "", load)
|
|
if err != nil {
|
|
return commandkit.Acquired[[]string]{Diagnostics: messages.diagnostics, Explanation: messages.explanation}, err
|
|
}
|
|
namespaces, err := engine.ListNamespaces(ctx, request.Kinds...)
|
|
messages = messages.withOperationError(err)
|
|
return commandkit.Acquired[[]string]{Value: namespaces, Diagnostics: messages.diagnostics, Explanation: messages.explanation}, err
|
|
},
|
|
ListCollections: func(ctx context.Context, selection ckconfig.Selection, input I, state S) (commandkit.Acquired[[]promptkit.CollectionInfo], error) {
|
|
engine, _, messages, err := loadPromptKitEngine(ctx, selection, input, state, "", load)
|
|
if err != nil {
|
|
return commandkit.Acquired[[]promptkit.CollectionInfo]{Diagnostics: messages.diagnostics, Explanation: messages.explanation}, err
|
|
}
|
|
collections, err := engine.ListCollections(ctx)
|
|
messages = messages.withOperationError(err)
|
|
return commandkit.Acquired[[]promptkit.CollectionInfo]{Value: collections, Diagnostics: messages.diagnostics, Explanation: messages.explanation}, err
|
|
},
|
|
InspectPrompt: func(ctx context.Context, request PromptRequest, input I, state S) (commandkit.Acquired[*promptkit.PromptInspection], error) {
|
|
engine, _, messages, err := loadPromptKitEngine(ctx, request.Selection, input, state, "", load)
|
|
if err != nil {
|
|
return commandkit.Acquired[*promptkit.PromptInspection]{Diagnostics: messages.diagnostics, Explanation: messages.explanation}, err
|
|
}
|
|
inspection, err := engine.InspectPrompt(ctx, request.ID)
|
|
messages = messages.withOperationError(err)
|
|
return commandkit.Acquired[*promptkit.PromptInspection]{Value: inspection, Diagnostics: messages.diagnostics, Explanation: messages.explanation}, err
|
|
},
|
|
InspectProfile: func(ctx context.Context, request ProfileRequest, input I, state S) (commandkit.Acquired[*promptkit.ProfileInspection], error) {
|
|
engine, _, messages, err := loadPromptKitEngine(ctx, request.Selection, input, state, "", load)
|
|
if err != nil {
|
|
return commandkit.Acquired[*promptkit.ProfileInspection]{Diagnostics: messages.diagnostics, Explanation: messages.explanation}, err
|
|
}
|
|
inspection, err := engine.InspectProfile(ctx, request.ID)
|
|
messages = messages.withOperationError(err)
|
|
return commandkit.Acquired[*promptkit.ProfileInspection]{Value: inspection, Diagnostics: messages.diagnostics, Explanation: messages.explanation}, err
|
|
},
|
|
InspectRequirements: func(ctx context.Context, request RequirementsRequest, input I, state S) (commandkit.Acquired[*promptkit.RequirementInspection], error) {
|
|
engine, _, messages, err := loadPromptKitEngine(ctx, request.Selection, input, state, "", load)
|
|
if err != nil {
|
|
return commandkit.Acquired[*promptkit.RequirementInspection]{Diagnostics: messages.diagnostics, Explanation: messages.explanation}, err
|
|
}
|
|
inspection, err := engine.InspectRequirements(ctx, request.Inspection)
|
|
messages = messages.withOperationError(err)
|
|
return commandkit.Acquired[*promptkit.RequirementInspection]{Value: inspection, Diagnostics: messages.diagnostics, Explanation: messages.explanation}, err
|
|
},
|
|
}
|
|
}
|
|
|
|
func loadPromptKitEngine[I, S any](
|
|
ctx context.Context,
|
|
selection ckconfig.Selection,
|
|
input I,
|
|
state S,
|
|
side string,
|
|
load engineLoader[I, S],
|
|
) (*promptkit.Engine, promptkit.ConfigurationInspection, promptKitAcquisition, error) {
|
|
engine, err := load(ctx, selection, input, state)
|
|
if err != nil {
|
|
var loadErr *configkit.LoadError
|
|
if errors.As(err, &loadErr) {
|
|
messages := promptKitDiagnostics("promptkit", side, loadErr.Sources(), loadErr.Diagnostics())
|
|
return nil, promptkit.ConfigurationInspection{}, messages, err
|
|
}
|
|
return nil, promptkit.ConfigurationInspection{}, promptKitAcquisition{}, err
|
|
}
|
|
if engine == nil {
|
|
return nil, promptkit.ConfigurationInspection{}, promptKitAcquisition{}, errors.New("PromptKit configuration loader returned a nil engine")
|
|
}
|
|
inspection, err := engine.InspectConfiguration()
|
|
messages := promptKitDiagnostics("promptkit", side, inspection.Sources(), inspection.Diagnostics())
|
|
messages = messages.withOperationError(err)
|
|
if err != nil {
|
|
return nil, inspection, messages, err
|
|
}
|
|
if ctx != nil {
|
|
if err := ctx.Err(); err != nil {
|
|
return nil, promptkit.ConfigurationInspection{}, messages, err
|
|
}
|
|
}
|
|
return engine, inspection, messages, nil
|
|
}
|
|
|
|
func appendPromptKitAcquisition(left, right promptKitAcquisition) promptKitAcquisition {
|
|
left.diagnostics = append(left.diagnostics, right.diagnostics...)
|
|
left.explanation.Details = append(left.explanation.Details, right.explanation.Details...)
|
|
if left.explanation.Message == "" {
|
|
left.explanation.Message = right.explanation.Message
|
|
}
|
|
return left
|
|
}
|
|
|
|
func promptKitDiagnostics(scope, side string, sources []configkit.SourceInfo, records []configkit.Diagnostic) promptKitAcquisition {
|
|
if len(records) == 0 {
|
|
return promptKitAcquisition{}
|
|
}
|
|
// Snapshot provider-owned slices before deferring their report to invocation.
|
|
sources = append([]configkit.SourceInfo(nil), sources...)
|
|
records = append([]configkit.Diagnostic(nil), records...)
|
|
for index := range records {
|
|
if records[index].Primary != nil {
|
|
primary := *records[index].Primary
|
|
records[index].Primary = &primary
|
|
}
|
|
records[index].Related = append([]configkit.Location(nil), records[index].Related...)
|
|
}
|
|
var warnings, failures []configkit.Diagnostic
|
|
for _, record := range records {
|
|
if record.Severity == configkit.SeverityError {
|
|
failures = append(failures, record)
|
|
} else {
|
|
warnings = append(warnings, record)
|
|
}
|
|
}
|
|
messages := promptKitAcquisition{}
|
|
if len(warnings) > 0 {
|
|
messages.diagnostics = []commandkit.Diagnostic{{
|
|
Severity: commandkit.SeverityWarning,
|
|
Write: func(writer io.Writer) error {
|
|
return writePromptKitDiagnostics(writer, scope, side, sources, warnings)
|
|
},
|
|
}}
|
|
}
|
|
if len(failures) > 0 {
|
|
messages.explanation = commandkit.Explanation{Details: []commandkit.Detail{func(writer io.Writer) error {
|
|
return writePromptKitDiagnostics(writer, scope, side, sources, failures)
|
|
}}}
|
|
}
|
|
return messages
|
|
}
|
|
|
|
func writePromptKitDiagnostics(writer io.Writer, scope, side string, sources []configkit.SourceInfo, records []configkit.Diagnostic) error {
|
|
heading := "Configuration scope " + strconv.Quote(scope)
|
|
if side != "" {
|
|
heading += " (" + side + ")"
|
|
}
|
|
if _, err := fmt.Fprintf(writer, "%s:\n", heading); err != nil {
|
|
return err
|
|
}
|
|
if len(sources) > 0 {
|
|
if _, err := io.WriteString(writer, "Source IDs:\n"); err != nil {
|
|
return err
|
|
}
|
|
for _, source := range sources {
|
|
if _, err := fmt.Fprintf(writer, " %s: label=%s, path=%s\n", strconv.Quote(source.ID), strconv.Quote(source.Label), strconv.Quote(source.Path)); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
return configkit.WriteDiagnostics(writer, records)
|
|
}
|