2026-10-16 15:19:32 +00:00
package engine
2026-10-24 00:15:23 +00:00
import "fmt"
2026-10-23 13:55:25 +00:00
type sequenceSelectorSegmentKind uint8
const (
sequenceSelectorLiteral sequenceSelectorSegmentKind = iota + 1
sequenceSelectorAnyMappingKey
)
type sequenceSelectorSegment struct {
kind sequenceSelectorSegmentKind
key string
}
type sequenceIdentitySelector [ ] sequenceSelectorSegment
type sequenceIdentityRegistration struct {
selector sequenceIdentitySelector
identityFields [ ] string
2026-10-23 20:59:23 +00:00
invalidPath * Path
}
// SequenceIdentitySelectorSegment carries one root-owned selector segment into
// an engine definition. This transport type remains behind the internal package
// boundary.
type SequenceIdentitySelectorSegment struct {
Key string
AnyMappingKey bool
}
// SequenceIdentityRegistrationInput carries detached root-owned registration
// data into an engine definition without exposing its compiled policy. A
// non-nil ExactPath selects exact-path registration; otherwise Selector is used.
type SequenceIdentityRegistrationInput struct {
ExactPath * Path
Selector [ ] SequenceIdentitySelectorSegment
IdentityFields [ ] string
2026-10-23 13:55:25 +00:00
}
type sequenceSelectorTrie struct {
literals map [ string ] * sequenceSelectorTrie
wildcard * sequenceSelectorTrie
fields [ ] string
terminal bool
}
2026-10-16 15:19:32 +00:00
type sequencePolicy struct {
2026-10-23 20:59:23 +00:00
selectorTrie * sequenceSelectorTrie
2026-10-16 15:19:32 +00:00
}
2026-10-23 20:59:23 +00:00
// DefinitionWithSequenceIdentityRegistrations attaches facade-owned
// registrations to a detached engine definition without exposing compiled
// engine policy.
func DefinitionWithSequenceIdentityRegistrations [ T any ] ( definition Definition [ T ] , registrations [ ] SequenceIdentityRegistrationInput ) Definition [ T ] {
if registrations == nil {
definition . sequenceIdentityRegistrations = nil
2026-10-16 19:23:43 +00:00
return definition
}
2026-10-23 20:59:23 +00:00
definition . sequenceIdentityRegistrations = make ( [ ] sequenceIdentityRegistration , len ( registrations ) )
for index , registration := range registrations {
compiled := sequenceIdentityRegistration { identityFields : append ( [ ] string ( nil ) , registration . IdentityFields ... ) }
if registration . ExactPath != nil {
path , err := normalizeDestination ( registration . ExactPath . clone ( ) )
if err != nil {
invalid := registration . ExactPath . clone ( )
compiled . invalidPath = & invalid
} else {
compiled . selector = make ( sequenceIdentitySelector , len ( path . segments ) )
for segmentIndex , segment := range path . segments {
compiled . selector [ segmentIndex ] = sequenceSelectorSegment { kind : sequenceSelectorLiteral , key : segment . key }
}
}
} else {
compiled . selector = make ( sequenceIdentitySelector , len ( registration . Selector ) )
for segmentIndex , segment := range registration . Selector {
kind := sequenceSelectorLiteral
if segment . AnyMappingKey {
kind = sequenceSelectorAnyMappingKey
}
compiled . selector [ segmentIndex ] = sequenceSelectorSegment { kind : kind , key : segment . Key }
}
2026-10-16 19:23:43 +00:00
}
2026-10-23 20:59:23 +00:00
definition . sequenceIdentityRegistrations [ index ] = compiled
2026-10-16 19:23:43 +00:00
}
return definition
}
2026-10-23 13:55:25 +00:00
func compileSequenceIdentityPolicy ( registrations [ ] sequenceIdentityRegistration ) ( sequencePolicy , [ ] Diagnostic ) {
policy := sequencePolicy { }
2026-10-24 00:15:23 +00:00
type positionedRegistration struct {
registration sequenceIdentityRegistration
position int
}
compiled := make ( [ ] positionedRegistration , 0 , len ( registrations ) )
2026-10-23 13:55:25 +00:00
var diagnostics [ ] Diagnostic
2026-10-24 00:15:23 +00:00
for position , registration := range registrations {
2026-10-23 13:55:25 +00:00
selector := cloneSequenceIdentitySelector ( registration . selector )
2026-10-23 20:59:23 +00:00
if registration . invalidPath != nil {
diagnostics = append ( diagnostics , Diagnostic {
Code : CodeOptionsInvalid , Severity : SeverityError , Path : registration . invalidPath . clone ( ) ,
Message : "sequence registration path must contain only non-reserved mapping keys" ,
} )
continue
}
2026-10-23 13:55:25 +00:00
fields , err := normalizeSequenceIdentityFields ( registration . identityFields )
if err != nil {
diagnostics = append ( diagnostics , Diagnostic {
Code : CodeOptionsInvalid , Severity : SeverityError , Path : selectorDiagnosticPath ( selector ) ,
Message : "sequence registration requires at least one identity field" ,
} )
continue
}
if err := validateSequenceIdentitySelector ( selector ) ; err != nil {
diagnostics = append ( diagnostics , Diagnostic {
Code : CodeOptionsInvalid , Severity : SeverityError , Path : selectorDiagnosticPath ( selector ) ,
Message : "sequence registration selector must contain only valid mapping segments" ,
} )
continue
}
2026-10-24 00:15:23 +00:00
compiled = append ( compiled , positionedRegistration {
registration : sequenceIdentityRegistration { selector : selector , identityFields : fields } ,
position : position + 1 ,
} )
2026-10-23 13:55:25 +00:00
}
for leftIndex := 0 ; leftIndex < len ( compiled ) ; leftIndex ++ {
for rightIndex := leftIndex + 1 ; rightIndex < len ( compiled ) ; rightIndex ++ {
2026-10-24 00:15:23 +00:00
left , right := compiled [ leftIndex ] . registration , compiled [ rightIndex ] . registration
2026-10-23 13:55:25 +00:00
if ! sequenceIdentitySelectorsOverlap ( left . selector , right . selector ) || equalSequenceIdentityFields ( left . identityFields , right . identityFields ) {
continue
}
diagnostics = append ( diagnostics , Diagnostic {
Code : CodeOptionsInvalid , Severity : SeverityError , Path : selectorDiagnosticPath ( left . selector ) ,
2026-10-24 00:15:23 +00:00
Message : fmt . Sprintf ( "sequence registrations %d and %d overlap with conflicting identity fields" , compiled [ leftIndex ] . position , compiled [ rightIndex ] . position ) ,
2026-10-23 13:55:25 +00:00
} )
}
}
if len ( diagnostics ) != 0 {
return policy , canonicalDiagnostics ( diagnostics )
}
unique := make ( [ ] sequenceIdentityRegistration , 0 , len ( compiled ) )
2026-10-24 00:15:23 +00:00
for _ , positioned := range compiled {
registration := positioned . registration
2026-10-23 13:55:25 +00:00
duplicate := false
for _ , existing := range unique {
if sequenceIdentitySelectorsEqual ( existing . selector , registration . selector ) && equalSequenceIdentityFields ( existing . identityFields , registration . identityFields ) {
duplicate = true
break
2026-10-16 15:19:32 +00:00
}
2026-10-23 13:55:25 +00:00
}
if duplicate {
2026-10-16 15:19:32 +00:00
continue
}
2026-10-23 13:55:25 +00:00
unique = append ( unique , registration )
2026-10-16 15:19:32 +00:00
}
2026-10-23 13:55:25 +00:00
for _ , registration := range unique {
policy . addRegistration ( registration )
}
return policy , nil
}
func ( p * sequencePolicy ) addRegistration ( registration sequenceIdentityRegistration ) {
if p . selectorTrie == nil {
p . selectorTrie = & sequenceSelectorTrie { }
}
node := p . selectorTrie
for _ , segment := range registration . selector {
if segment . kind == sequenceSelectorAnyMappingKey {
if node . wildcard == nil {
node . wildcard = & sequenceSelectorTrie { }
}
node = node . wildcard
continue
}
if node . literals == nil {
node . literals = make ( map [ string ] * sequenceSelectorTrie )
}
if node . literals [ segment . key ] == nil {
node . literals [ segment . key ] = & sequenceSelectorTrie { }
}
node = node . literals [ segment . key ]
}
node . fields = append ( [ ] string ( nil ) , registration . identityFields ... )
node . terminal = true
}
func ( p sequencePolicy ) identityFieldsAt ( path Path ) ( [ ] string , bool ) {
if ! path . valid ( ) {
return nil , false
}
if p . selectorTrie == nil {
2026-10-23 20:59:23 +00:00
return nil , false
2026-10-23 13:55:25 +00:00
}
current := [ ] * sequenceSelectorTrie { p . selectorTrie }
for _ , segment := range path . segments {
if segment . kind != pathSegmentKey && segment . kind != pathSegmentParsed {
return nil , false
}
next := make ( [ ] * sequenceSelectorTrie , 0 , len ( current ) * 2 )
for _ , node := range current {
if literal := node . literals [ segment . key ] ; literal != nil {
next = append ( next , literal )
}
if node . wildcard != nil {
next = append ( next , node . wildcard )
}
}
if len ( next ) == 0 {
return nil , false
}
current = next
}
for _ , node := range current {
if node . terminal {
return append ( [ ] string ( nil ) , node . fields ... ) , true
}
}
return nil , false
}
func ( p sequencePolicy ) registered ( path Path ) bool {
_ , ok := p . identityFieldsAt ( path )
return ok
}
func validateSequenceIdentitySelector ( selector sequenceIdentitySelector ) error {
for _ , segment := range selector {
switch segment . kind {
case sequenceSelectorLiteral :
if _ , err := normalizeDestination ( Keys ( segment . key ) ) ; err != nil {
return err
}
case sequenceSelectorAnyMappingKey :
default :
return errInvalidDestination
}
}
return nil
}
func sequenceIdentitySelectorsEqual ( left , right sequenceIdentitySelector ) bool {
if len ( left ) != len ( right ) {
return false
}
for index := range left {
if left [ index ] != right [ index ] {
return false
}
}
return true
}
func sequenceIdentitySelectorsOverlap ( left , right sequenceIdentitySelector ) bool {
if len ( left ) != len ( right ) {
return false
}
for index := range left {
if left [ index ] . kind == sequenceSelectorLiteral && right [ index ] . kind == sequenceSelectorLiteral && left [ index ] . key != right [ index ] . key {
return false
}
}
return true
}
func equalSequenceIdentityFields ( left , right [ ] string ) bool {
if len ( left ) != len ( right ) {
return false
}
for index := range left {
if left [ index ] != right [ index ] {
return false
}
}
return true
}
func cloneSequenceIdentitySelector ( selector sequenceIdentitySelector ) sequenceIdentitySelector {
return append ( sequenceIdentitySelector ( nil ) , selector ... )
}
func selectorDiagnosticPath ( selector sequenceIdentitySelector ) Path {
path := Path { }
for _ , segment := range selector {
if segment . kind != sequenceSelectorLiteral {
return Path { }
}
path = path . Key ( segment . key )
}
return path
2026-10-16 15:19:32 +00:00
}