Files
commandkit/integrations/ckconfig/show.go

177 lines
5.6 KiB
Go

package ckconfig
import (
"bytes"
"fmt"
"io"
"strconv"
"unicode"
commandkit "gitea.maximumdirect.net/eric/CommandKit"
)
type effectiveDocument struct {
name string
view View
}
func runConfigShow[I, S any](invocation commandkit.Invocation, plan *configOperationPlan[I, S], state S) commandkit.Outcome {
documents := make([]effectiveDocument, 0, len(plan.scopes))
reporter := invocation.Reporter()
for _, selected := range plan.scopes {
if err := invocation.Context.Err(); err != nil {
return configFailure("Configuration show was canceled.", commandkit.Explanation{}, err)
}
if selected.scope.scope.Acquire == nil {
message := fmt.Sprintf("Configuration scope %q has no acquisition callback.", selected.scope.scope.Name)
return commandkit.Failure(message)
}
acquired, acquireErr := selected.scope.scope.Acquire(invocation.Context, Request{
Operation: Show,
Selection: plan.selection,
}, selected.input, state)
if acquireErr != nil {
return configAcquisitionFailure(reporter, "show", selected.scope.scope.Name, acquired, acquireErr)
}
queueConfigDiagnostics(reporter, acquired.Diagnostics)
if err := invocation.Context.Err(); err != nil {
return configFailure("Configuration show was canceled.", acquired.Explanation, err)
}
if acquired.Value.Effective == nil {
message := fmt.Sprintf("Configuration scope %q did not provide an effective configuration writer.", selected.scope.scope.Name)
return commandkit.Failure(message)
}
documents = append(documents, effectiveDocument{name: selected.scope.scope.Name, view: acquired.Value})
}
if err := invocation.Context.Err(); err != nil {
return configFailure("Configuration show was canceled.", commandkit.Explanation{}, err)
}
if len(documents) == 1 && plan.scopeName != "all" {
err := reporter.RenderOutput(func(writer io.Writer) error {
return documents[0].view.Effective.WriteEffective(writer)
})
if err != nil && invocation.Context.Err() != nil {
return configFailure("Configuration show was canceled.", commandkit.Explanation{}, invocation.Context.Err())
}
return commandkit.Success()
}
err := reporter.RenderOutput(func(writer io.Writer) error {
for index, document := range documents {
if err := invocation.Context.Err(); err != nil {
return err
}
var native bytes.Buffer
if err := document.view.Effective.WriteEffective(&native); err != nil {
return err
}
if err := invocation.Context.Err(); err != nil {
return err
}
framed, hasDirective := frameEffectiveYAML(document.name, native.Bytes())
if index > 0 && hasDirective {
if _, err := io.WriteString(writer, "...\n"); err != nil {
return err
}
}
if index < len(documents)-1 && len(framed) > 0 && framed[len(framed)-1] != '\n' {
framed = append(framed, '\n')
}
if _, err := writer.Write(framed); err != nil {
return err
}
}
return nil
})
if err != nil && invocation.Context.Err() != nil {
return configFailure("Configuration show was canceled.", commandkit.Explanation{}, invocation.Context.Err())
}
return commandkit.Success()
}
type yamlPreamble struct {
markerStart int
markerEnd int
markerFound bool
inlineMarker bool
preambleEnd int
hasDirective bool
}
func analyzeYAMLPreamble(native []byte) yamlPreamble {
var analysis yamlPreamble
for start := 0; start < len(native); {
end := len(native)
if newline := bytes.IndexByte(native[start:], '\n'); newline >= 0 {
end = start + newline + 1
}
lineEnd := end
if lineEnd > start && native[lineEnd-1] == '\n' {
lineEnd--
}
trimmed := bytes.TrimSpace(native[start:lineEnd])
if isYAMLDocumentStart(trimmed) {
content := bytes.TrimSpace(trimmed[3:])
analysis.markerStart = start
analysis.markerEnd = end
analysis.markerFound = true
analysis.inlineMarker = len(content) > 0 && content[0] != '#'
return analysis
}
if bytes.HasPrefix(trimmed, []byte("%YAML ")) || bytes.HasPrefix(trimmed, []byte("%TAG ")) {
analysis.hasDirective = true
}
if len(trimmed) == 0 || trimmed[0] == '#' || bytes.HasPrefix(trimmed, []byte("%YAML ")) || bytes.HasPrefix(trimmed, []byte("%TAG ")) {
analysis.preambleEnd = end
} else {
return analysis
}
start = end
}
return analysis
}
func frameEffectiveYAML(scope string, native []byte) ([]byte, bool) {
analysis := analyzeYAMLPreamble(native)
label := scope
for _, character := range scope {
if unicode.IsControl(character) {
label = strconv.Quote(scope)
break
}
}
comment := []byte("# scope: " + label + "\n")
if analysis.markerFound {
out := make([]byte, 0, len(native)+len(comment))
if analysis.inlineMarker {
out = append(out, native[:analysis.markerStart]...)
out = append(out, comment...)
out = append(out, native[analysis.markerStart:]...)
return out, analysis.hasDirective
}
out = append(out, native[:analysis.markerEnd]...)
if analysis.markerEnd > analysis.markerStart && native[analysis.markerEnd-1] != '\n' {
out = append(out, '\n')
}
out = append(out, comment...)
out = append(out, native[analysis.markerEnd:]...)
return out, analysis.hasDirective
}
result := make([]byte, 0, len(native)+len(comment)+5)
if analysis.hasDirective {
result = append(result, native[:analysis.preambleEnd]...)
}
result = append(result, []byte("---\n")...)
result = append(result, comment...)
if analysis.hasDirective {
result = append(result, native[analysis.preambleEnd:]...)
} else {
result = append(result, native...)
}
return result, analysis.hasDirective
}
func isYAMLDocumentStart(line []byte) bool {
return bytes.HasPrefix(line, []byte("---")) && (len(line) == 3 || line[3] == ' ' || line[3] == '\t')
}