177 lines
5.6 KiB
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')
|
|
}
|