2026-05-31 02:13:58 +00:00
package state
import (
"fmt"
"gitea.maximumdirect.net/eric/distributor/internal/bundle"
)
type Outcome string
const (
OutcomeDestinationAbsent Outcome = "destination_absent"
OutcomeDestinationUnmanaged Outcome = "destination_unmanaged"
OutcomeInvalidState Outcome = "invalid_destination_state"
OutcomeIdentityMismatch Outcome = "destination_identity_mismatch"
OutcomeSameSource Outcome = "same_source_manifest"
OutcomeDestinationOlder Outcome = "destination_older"
OutcomeDestinationNewer Outcome = "destination_newer"
OutcomeSameCreatedConflict Outcome = "same_created_digest_conflict"
OutcomeDifferentSourceConflict Outcome = "different_source_conflict"
)
type DestinationStatus struct {
State * DistributorState
2026-06-08 18:53:22 +00:00
SharedRoot * SharedRootState
2026-05-31 02:13:58 +00:00
StateErr error
HasContents bool
}
type Comparison struct {
Outcome Outcome
Reason string
}
2026-06-08 18:53:22 +00:00
func CompareSharedRootOwner ( source bundle . Manifest , scope OwnerScope , status DestinationStatus ) Comparison {
if status . StateErr != nil {
return Comparison { Outcome : OutcomeInvalidState , Reason : status . StateErr . Error ( ) }
}
if status . SharedRoot != nil {
if err := ValidateSharedRoot ( * status . SharedRoot ) ; err != nil {
return Comparison { Outcome : OutcomeInvalidState , Reason : err . Error ( ) }
}
owner , ok := status . SharedRoot . Owner ( scope )
if ! ok {
return Comparison { Outcome : OutcomeDestinationAbsent , Reason : fmt . Sprintf ( "destination owner %s/%s is absent" , scope . PipelineID , scope . DestinationID ) }
}
return compareManifests ( source , owner . Source . Manifest )
}
if status . State != nil {
destinationState := * status . State
if err := Validate ( destinationState ) ; err != nil {
return Comparison { Outcome : OutcomeInvalidState , Reason : err . Error ( ) }
}
if destinationState . PipelineID != scope . PipelineID {
return Comparison { Outcome : OutcomeIdentityMismatch , Reason : fmt . Sprintf ( "pipeline id %q does not match %q" , destinationState . PipelineID , scope . PipelineID ) }
}
if destinationState . DestinationID != scope . DestinationID {
return Comparison { Outcome : OutcomeIdentityMismatch , Reason : fmt . Sprintf ( "destination id %q does not match %q" , destinationState . DestinationID , scope . DestinationID ) }
}
return compareManifests ( source , destinationState . Source . Manifest )
}
if status . HasContents {
return Comparison { Outcome : OutcomeDestinationUnmanaged , Reason : "destination has content but no distributor state" }
}
return Comparison { Outcome : OutcomeDestinationAbsent , Reason : "destination state is absent" }
}
func compareManifests ( source , destination bundle . Manifest ) Comparison {
if manifestsEqual ( source , destination ) {
return Comparison { Outcome : OutcomeSameSource , Reason : "destination source manifest matches source" }
}
if destination . ID != source . ID {
return Comparison { Outcome : OutcomeDifferentSourceConflict , Reason : "destination source id differs from source" }
}
if destination . Created . Before ( source . Created ) {
return Comparison { Outcome : OutcomeDestinationOlder , Reason : "destination source is older than source" }
}
if destination . Created . After ( source . Created ) {
return Comparison { Outcome : OutcomeDestinationNewer , Reason : "destination source is newer than source" }
}
if destination . Digest != source . Digest {
return Comparison { Outcome : OutcomeSameCreatedConflict , Reason : "destination source has same id and created time but different digest" }
}
return Comparison { Outcome : OutcomeInvalidState , Reason : "destination source differs from source without a supported comparison outcome" }
}
2026-05-31 02:13:58 +00:00
func Compare ( source bundle . Manifest , pipelineID , destinationID string , status DestinationStatus ) Comparison {
if status . StateErr != nil {
return Comparison { Outcome : OutcomeInvalidState , Reason : status . StateErr . Error ( ) }
}
if status . State == nil {
if status . HasContents {
return Comparison { Outcome : OutcomeDestinationUnmanaged , Reason : "destination has content but no distributor state" }
}
return Comparison { Outcome : OutcomeDestinationAbsent , Reason : "destination state is absent" }
}
destinationState := * status . State
if err := Validate ( destinationState ) ; err != nil {
return Comparison { Outcome : OutcomeInvalidState , Reason : err . Error ( ) }
}
if destinationState . PipelineID != pipelineID {
return Comparison { Outcome : OutcomeIdentityMismatch , Reason : fmt . Sprintf ( "pipeline id %q does not match %q" , destinationState . PipelineID , pipelineID ) }
}
if destinationState . DestinationID != destinationID {
return Comparison { Outcome : OutcomeIdentityMismatch , Reason : fmt . Sprintf ( "destination id %q does not match %q" , destinationState . DestinationID , destinationID ) }
}
2026-06-08 18:53:22 +00:00
return compareManifests ( source , destinationState . Source . Manifest )
2026-05-31 02:13:58 +00:00
}
func manifestsEqual ( a , b bundle . Manifest ) bool {
if a . SchemaVersion != b . SchemaVersion ||
a . ID != b . ID ||
a . Digest != b . Digest ||
! a . Created . Equal ( b . Created ) ||
len ( a . Files ) != len ( b . Files ) {
return false
}
for index := range a . Files {
if a . Files [ index ] != b . Files [ index ] {
return false
}
}
return true
}