package ckconfig import ( "bytes" "fmt" "io" "strconv" commandkit "go.maximumdirect.net/commandkit" configkit "go.maximumdirect.net/configkit" "go.yaml.in/yaml/v4" ) type sourceReport struct { Scope string `yaml:"scope"` Sources []sourceDTO `yaml:"sources"` } type sourceDTO struct { ID string `yaml:"id"` Label string `yaml:"label"` Path string `yaml:"path"` Destination string `yaml:"destination"` Placement string `yaml:"placement"` DestinationIsFallback bool `yaml:"destination_is_fallback"` Layer string `yaml:"layer"` Kind string `yaml:"kind"` UnderlayIndex int `yaml:"underlay_index"` ImportedFrom *locationDTO `yaml:"imported_from"` VersionEvidence *sourceVersionEvidenceDTO `yaml:"version_evidence"` EmptyImportSelections []emptyImportSelectionDTO `yaml:"empty_import_selections"` } type emptyImportSelectionDTO struct { Path string `yaml:"path"` Location locationDTO `yaml:"location"` Selector string `yaml:"selector"` } type locationDTO struct { SourceID string `yaml:"source_id"` Line int `yaml:"line"` Column int `yaml:"column"` } type sourceVersionEvidenceDTO struct { Path string `yaml:"path"` Version versionDTO `yaml:"version"` Location *locationDTO `yaml:"location"` Redacted bool `yaml:"redacted"` } type versionDTO struct { Present bool `yaml:"present"` Null bool `yaml:"null"` Text string `yaml:"text"` } func runConfigSources[I, S any](invocation commandkit.Invocation, plan *configOperationPlan[I, S], state S) commandkit.Outcome { selected := plan.scopes[0] name := selected.scope.scope.Name reporter := invocation.Reporter() if err := invocation.Context.Err(); err != nil { return configFailure("Configuration source listing was canceled.", commandkit.Explanation{}, err) } if selected.scope.scope.Acquire == nil { cause := fmt.Errorf("configuration scope %q has no acquisition callback", name) return configFailure(fmt.Sprintf("Unable to list sources for configuration scope %q.", name), commandkit.Explanation{}, cause) } acquired, acquireErr := selected.scope.scope.Acquire(invocation.Context, Request{ Operation: Sources, Selection: plan.selection, }, selected.input, state) queueConfigDiagnostics(reporter, acquired.Diagnostics) if acquireErr != nil { return configFailure(fmt.Sprintf("Unable to list sources for configuration scope %q.", name), acquired.Explanation, acquireErr) } if err := invocation.Context.Err(); err != nil { return configFailure("Configuration source listing was canceled.", acquired.Explanation, err) } if acquired.Value.Sources == nil { cause := fmt.Errorf("configuration scope %q did not provide a source reader", name) return configFailure("Configuration scope did not provide source information.", acquired.Explanation, cause) } sources := acquired.Value.Sources.Sources() document := sourceReport{Scope: name, Sources: make([]sourceDTO, 0, len(sources))} for _, source := range sources { placement := "mapping" if source.PlacementMode == configkit.PlacementModeSequenceItem { placement = "sequence_item" } item := sourceDTO{ ID: source.ID, Label: source.Label, Path: source.Path, Destination: source.Destination.String(), Placement: placement, DestinationIsFallback: source.DestinationIsFallback, Layer: string(source.Layer), Kind: string(source.Kind), UnderlayIndex: source.UnderlayIndex, EmptyImportSelections: make([]emptyImportSelectionDTO, 0, len(source.EmptyImportSelections)), } if source.ImportedFrom != nil { item.ImportedFrom = &locationDTO{SourceID: source.ImportedFrom.SourceID, Line: source.ImportedFrom.Line, Column: source.ImportedFrom.Column} } if source.VersionEvidence != nil { check := source.VersionEvidence item.VersionEvidence = &sourceVersionEvidenceDTO{ Path: check.Path.String(), Version: versionDTO{ Present: check.Version.Present, Null: check.Version.Null, Text: check.Version.Text, }, Redacted: check.Redacted, } if check.Location != nil { item.VersionEvidence.Location = &locationDTO{SourceID: check.Location.SourceID, Line: check.Location.Line, Column: check.Location.Column} } } for _, selection := range source.EmptyImportSelections { item.EmptyImportSelections = append(item.EmptyImportSelections, emptyImportSelectionDTO{Path: selection.Path.String(), Location: locationDTO{SourceID: selection.Location.SourceID, Line: selection.Location.Line, Column: selection.Location.Column}, Selector: string(selection.Selector)}) } document.Sources = append(document.Sources, item) } if err := invocation.Context.Err(); err != nil { return configFailure("Configuration source listing was canceled.", acquired.Explanation, err) } return renderPreparedConfigReport(invocation, acquired.Explanation, func() ([]byte, error) { var report bytes.Buffer if plan.format == "yaml" { if err := encodeYAML(&report, document); err != nil { return nil, err } } else { writeSourceReportText(&report, document) } return report.Bytes(), nil }) } func writeSourceReportText(writer *bytes.Buffer, document sourceReport) { _, _ = fmt.Fprintf(writer, "Scope: %s\n", strconv.Quote(document.Scope)) if len(document.Sources) == 0 { _, _ = writer.WriteString("Sources: []\n") return } _, _ = writer.WriteString("Sources:\n") for _, source := range document.Sources { _, _ = fmt.Fprintf(writer, " - id: %s\n", strconv.Quote(source.ID)) _, _ = fmt.Fprintf(writer, " label: %s\n", strconv.Quote(source.Label)) _, _ = fmt.Fprintf(writer, " path: %s\n", strconv.Quote(source.Path)) _, _ = fmt.Fprintf(writer, " destination: %s\n", strconv.Quote(source.Destination)) _, _ = fmt.Fprintf(writer, " placement: %s\n", strconv.Quote(source.Placement)) _, _ = fmt.Fprintf(writer, " destination_is_fallback: %t\n", source.DestinationIsFallback) _, _ = fmt.Fprintf(writer, " layer: %s\n", strconv.Quote(source.Layer)) _, _ = fmt.Fprintf(writer, " kind: %s\n", strconv.Quote(source.Kind)) _, _ = fmt.Fprintf(writer, " underlay_index: %d\n", source.UnderlayIndex) if source.ImportedFrom == nil { _, _ = writer.WriteString(" imported_from: null\n") } else { _, _ = fmt.Fprintf(writer, " imported_from: {source_id: %s, line: %d, column: %d}\n", strconv.Quote(source.ImportedFrom.SourceID), source.ImportedFrom.Line, source.ImportedFrom.Column) } if source.VersionEvidence == nil { _, _ = writer.WriteString(" version_evidence: null\n") } else { check := source.VersionEvidence _, _ = fmt.Fprintf(writer, " version_evidence:\n path: %s\n", strconv.Quote(check.Path)) _, _ = fmt.Fprintf(writer, " version:\n present: %t\n null: %t\n text: %s\n", check.Version.Present, check.Version.Null, strconv.Quote(check.Version.Text)) if check.Location == nil { _, _ = writer.WriteString(" location: null\n") } else { _, _ = fmt.Fprintf(writer, " location: {source_id: %s, line: %d, column: %d}\n", strconv.Quote(check.Location.SourceID), check.Location.Line, check.Location.Column) } _, _ = fmt.Fprintf(writer, " redacted: %t\n", check.Redacted) } if len(source.EmptyImportSelections) == 0 { _, _ = writer.WriteString(" empty_import_selections: []\n") } else { _, _ = writer.WriteString(" empty_import_selections:\n") for _, selection := range source.EmptyImportSelections { _, _ = fmt.Fprintf(writer, " - path: %s\n location: {source_id: %s, line: %d, column: %d}\n selector: %s\n", strconv.Quote(selection.Path), strconv.Quote(selection.Location.SourceID), selection.Location.Line, selection.Location.Column, strconv.Quote(selection.Selector)) } } } } func encodeYAML(writer io.Writer, document any) error { encoder := yaml.NewEncoder(writer) encoder.SetIndent(2) return encoder.Encode(document) } func locationReports(locations []configkit.Location) []locationDTO { report := make([]locationDTO, 0, len(locations)) for _, location := range locations { report = append(report, locationDTO{SourceID: location.SourceID, Line: location.Line, Column: location.Column}) } return report }