2026-10-11 01:06:21 +00:00
package ckconfig
2026-10-07 14:05:26 +00:00
import (
"fmt"
2026-11-07 04:47:23 +00:00
"io"
2026-11-07 03:57:10 +00:00
"text/template"
2026-10-07 14:05:26 +00:00
2026-11-04 03:52:46 +00:00
commandkit "go.maximumdirect.net/commandkit"
2026-11-04 14:58:20 +00:00
configkit "go.maximumdirect.net/configkit"
2026-10-11 01:39:23 +00:00
)
2026-10-07 14:05:26 +00:00
type sourceReport struct {
2026-11-07 05:06:47 +00:00
Scope string ` json:"scope" `
Sources [ ] sourceDTO ` json:"sources" `
2026-10-07 14:05:26 +00:00
}
type sourceDTO struct {
2026-11-07 05:06:47 +00:00
ID string ` json:"id" `
Label string ` json:"label" `
Path string ` json:"path" `
Destination string ` json:"destination" `
Placement string ` json:"placement" `
DestinationIsFallback bool ` json:"destination_is_fallback" `
Layer string ` json:"layer" `
Kind string ` json:"kind" `
UnderlayIndex int ` json:"underlay_index" `
ImportedFrom * locationDTO ` json:"imported_from" `
VersionEvidence * sourceVersionEvidenceDTO ` json:"version_evidence" `
EmptyImportSelections [ ] emptyImportSelectionDTO ` json:"empty_import_selections" `
2026-11-04 15:00:04 +00:00
}
type emptyImportSelectionDTO struct {
2026-11-07 05:06:47 +00:00
Path string ` json:"path" `
Location locationDTO ` json:"location" `
Selector string ` json:"selector" `
2026-10-07 14:05:26 +00:00
}
type locationDTO struct {
2026-11-07 05:06:47 +00:00
SourceID string ` json:"source_id" `
Line int ` json:"line" `
Column int ` json:"column" `
2026-10-07 14:05:26 +00:00
}
2026-11-04 15:00:04 +00:00
type sourceVersionEvidenceDTO struct {
2026-11-07 05:06:47 +00:00
Path string ` json:"path" `
Version versionDTO ` json:"version" `
Location * locationDTO ` json:"location" `
Redacted bool ` json:"redacted" `
2026-10-17 18:31:11 +00:00
}
type versionDTO struct {
2026-11-07 05:06:47 +00:00
Present bool ` json:"present" `
Null bool ` json:"null" `
Text string ` json:"text" `
2026-10-17 18:31:11 +00:00
}
2026-10-11 01:39:23 +00:00
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
2026-10-26 03:20:17 +00:00
reporter := invocation . Reporter ( )
2026-10-07 16:47:28 +00:00
if err := invocation . Context . Err ( ) ; err != nil {
2026-10-26 03:20:17 +00:00
return configFailure ( "Configuration source listing was canceled." , commandkit . Explanation { } , err )
2026-10-07 16:47:28 +00:00
}
2026-10-11 01:39:23 +00:00
if selected . scope . scope . Acquire == nil {
2026-10-26 03:20:17 +00:00
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 )
2026-10-07 14:05:26 +00:00
}
2026-10-11 01:39:23 +00:00
acquired , acquireErr := selected . scope . scope . Acquire ( invocation . Context , Request {
Operation : Sources ,
2026-11-07 04:39:34 +00:00
Selection : selected . selection ,
2026-10-11 01:39:23 +00:00
} , selected . input , state )
2026-11-07 04:32:24 +00:00
queueConfigAcquisition ( reporter , acquired )
2026-11-07 04:47:23 +00:00
if acquireErr == nil && invocation . Context . Err ( ) != nil {
err := invocation . Context . Err ( )
2026-10-26 03:20:17 +00:00
return configFailure ( "Configuration source listing was canceled." , acquired . Explanation , err )
2026-10-07 16:47:28 +00:00
}
2026-10-07 14:05:26 +00:00
if acquired . Value . Sources == nil {
2026-11-07 04:47:23 +00:00
if acquireErr != nil {
explanation := sourceSelectionExplanation ( acquired . Explanation , "Source inventory is unavailable." )
return configFailure ( fmt . Sprintf ( "Unable to list sources for configuration scope %q." , name ) , explanation , acquireErr )
}
2026-10-26 03:20:17 +00:00
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 )
2026-10-07 14:05:26 +00:00
}
2026-10-11 01:39:23 +00:00
2026-10-07 14:05:26 +00:00
sources := acquired . Value . Sources . Sources ( )
document := sourceReport { Scope : name , Sources : make ( [ ] sourceDTO , 0 , len ( sources ) ) }
for _ , source := range sources {
2026-11-07 04:47:23 +00:00
if plan . sourceID . Set && source . ID != plan . sourceID . Value {
continue
}
2026-10-17 18:31:11 +00:00
placement := "mapping"
if source . PlacementMode == configkit . PlacementModeSequenceItem {
placement = "sequence_item"
}
2026-10-07 14:05:26 +00:00
item := sourceDTO {
2026-10-17 18:31:11 +00:00
ID : source . ID ,
Label : source . Label ,
Path : source . Path ,
Destination : source . Destination . String ( ) ,
Placement : placement ,
2026-11-04 15:00:04 +00:00
DestinationIsFallback : source . DestinationIsFallback ,
2026-10-17 18:31:11 +00:00
Layer : string ( source . Layer ) ,
Kind : string ( source . Kind ) ,
UnderlayIndex : source . UnderlayIndex ,
2026-11-04 15:00:04 +00:00
EmptyImportSelections : make ( [ ] emptyImportSelectionDTO , 0 , len ( source . EmptyImportSelections ) ) ,
2026-10-07 14:05:26 +00:00
}
if source . ImportedFrom != nil {
item . ImportedFrom = & locationDTO { SourceID : source . ImportedFrom . SourceID , Line : source . ImportedFrom . Line , Column : source . ImportedFrom . Column }
}
2026-11-04 14:58:20 +00:00
if source . VersionEvidence != nil {
check := source . VersionEvidence
2026-11-04 15:00:04 +00:00
item . VersionEvidence = & sourceVersionEvidenceDTO {
2026-10-17 18:31:11 +00:00
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 {
2026-11-04 15:00:04 +00:00
item . VersionEvidence . Location = & locationDTO { SourceID : check . Location . SourceID , Line : check . Location . Line , Column : check . Location . Column }
2026-10-17 18:31:11 +00:00
}
}
2026-11-04 15:00:04 +00:00
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 ) } )
}
2026-10-07 14:05:26 +00:00
document . Sources = append ( document . Sources , item )
2026-11-07 04:47:23 +00:00
if plan . sourceID . Set {
break
}
2026-10-07 14:05:26 +00:00
}
2026-11-07 04:47:23 +00:00
if plan . sourceID . Set && len ( document . Sources ) == 0 {
message := fmt . Sprintf ( "Source ID %q was not found in configuration scope %q." , plan . sourceID . Value , name )
if acquireErr != nil {
return configFailure ( fmt . Sprintf ( "Unable to list sources for configuration scope %q." , name ) , sourceSelectionExplanation ( acquired . Explanation , message ) , acquireErr )
}
return configFailure ( message , acquired . Explanation , fmt . Errorf ( "source ID %q was not found" , plan . sourceID . Value ) )
}
if acquireErr == nil && invocation . Context . Err ( ) != nil {
err := invocation . Context . Err ( )
2026-10-26 03:20:17 +00:00
return configFailure ( "Configuration source listing was canceled." , acquired . Explanation , err )
2026-10-07 16:47:28 +00:00
}
2026-11-07 03:57:10 +00:00
_ = reporter . Report ( commandkit . Report { Value : document , TextTemplate : sourceTextTemplate } )
2026-11-07 04:47:23 +00:00
if acquireErr != nil {
return configFailure ( fmt . Sprintf ( "Unable to list sources for configuration scope %q." , name ) , acquired . Explanation , acquireErr )
}
2026-11-07 03:57:10 +00:00
return commandkit . Success ( )
2026-10-07 14:05:26 +00:00
}
2026-11-07 04:47:23 +00:00
func sourceSelectionExplanation ( explanation commandkit . Explanation , message string ) commandkit . Explanation {
explanation . Details = append ( append ( [ ] commandkit . Detail ( nil ) , explanation . Details ... ) , func ( writer io . Writer ) error {
_ , err := fmt . Fprintln ( writer , message )
return err
} )
return explanation
}
2026-11-07 03:57:10 +00:00
var sourceTextTemplate = template . Must ( commandkit . ParseTextTemplate ( "sources" , ` Scope : { { printf "%q" . Scope } }
{ { if . Sources } } Sources :
{ { range . Sources } } - id : { { printf "%q" . ID } }
label : { { printf "%q" . Label } }
path : { { printf "%q" . Path } }
destination : { { printf "%q" . Destination } }
placement : { { printf "%q" . Placement } }
destination_is_fallback : { { . DestinationIsFallback } }
layer : { { printf "%q" . Layer } }
kind : { { printf "%q" . Kind } }
underlay_index : { { . UnderlayIndex } }
{ { if . ImportedFrom } } imported_from : { source_id : { { printf "%q" . ImportedFrom . SourceID } } , line : { { . ImportedFrom . Line } } , column : { { . ImportedFrom . Column } } }
{ { else } } imported_from : null
{ { end } } { { if . VersionEvidence } } version_evidence :
path : { { printf "%q" . VersionEvidence . Path } }
version :
present : { { . VersionEvidence . Version . Present } }
null : { { . VersionEvidence . Version . Null } }
text : { { printf "%q" . VersionEvidence . Version . Text } }
{ { if . VersionEvidence . Location } } location : { source_id : { { printf "%q" . VersionEvidence . Location . SourceID } } , line : { { . VersionEvidence . Location . Line } } , column : { { . VersionEvidence . Location . Column } } }
{ { else } } location : null
{ { end } } redacted : { { . VersionEvidence . Redacted } }
{ { else } } version_evidence : null
{ { end } } { { if . EmptyImportSelections } } empty_import_selections :
{ { range . EmptyImportSelections } } - path : { { printf "%q" . Path } }
location : { source_id : { { printf "%q" . Location . SourceID } } , line : { { . Location . Line } } , column : { { . Location . Column } } }
selector : { { printf "%q" . Selector } }
{ { end } } { { else } } empty_import_selections : [ ]
{ { end } } { { end } } { { else } } Sources : [ ]
{ { end } } ` , nil ) )
2026-10-11 01:39:23 +00:00
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
2026-10-07 14:05:26 +00:00
}