package state import ( "sort" "time" ) type PruneCandidate struct { Path string UpdatedAt time.Time Owner *OwnerScope } type PrunePlanOptions struct { Now time.Time OlderThan *time.Duration KeepLatest *int } type PrunePlan struct { Pruned []PruneCandidate Preserved []PruneCandidate } func CatalogPruneCandidates(s CatalogState, scope OwnerScope) []PruneCandidate { candidates := make([]PruneCandidate, 0, len(s.Outputs)) for _, output := range s.Outputs { if output.PipelineID != scope.PipelineID || output.DestinationID != scope.DestinationID { continue } owner := scope candidates = append(candidates, PruneCandidate{ Path: output.Path, UpdatedAt: output.UpdatedAt, Owner: &owner, }) } return candidates } func PlanPrune(candidates []PruneCandidate, options PrunePlanOptions) PrunePlan { ordered := append([]PruneCandidate(nil), candidates...) sortPruneCandidatesNewestFirst(ordered) if options.OlderThan == nil && options.KeepLatest == nil { return PrunePlan{ Pruned: []PruneCandidate{}, Preserved: ordered, } } preservedByPath := make(map[string]struct{}) if options.KeepLatest != nil { keep := *options.KeepLatest if keep < 0 { keep = 0 } if keep > len(ordered) { keep = len(ordered) } for _, candidate := range ordered[:keep] { preservedByPath[candidate.Path] = struct{}{} } } plan := PrunePlan{ Pruned: []PruneCandidate{}, Preserved: []PruneCandidate{}, } cutoff := time.Time{} if options.OlderThan != nil { now := options.Now.UTC() if now.IsZero() { now = time.Now().UTC() } cutoff = now.Add(-*options.OlderThan) } for _, candidate := range ordered { if _, preserved := preservedByPath[candidate.Path]; preserved { plan.Preserved = append(plan.Preserved, candidate) continue } if options.OlderThan == nil || candidate.UpdatedAt.Before(cutoff) { plan.Pruned = append(plan.Pruned, candidate) continue } plan.Preserved = append(plan.Preserved, candidate) } sortPruneCandidatesOldestFirst(plan.Pruned) sortPruneCandidatesNewestFirst(plan.Preserved) return plan } func sortPruneCandidatesNewestFirst(candidates []PruneCandidate) { sort.Slice(candidates, func(i, j int) bool { if !candidates[i].UpdatedAt.Equal(candidates[j].UpdatedAt) { return candidates[i].UpdatedAt.After(candidates[j].UpdatedAt) } return candidates[i].Path < candidates[j].Path }) } func sortPruneCandidatesOldestFirst(candidates []PruneCandidate) { sort.Slice(candidates, func(i, j int) bool { if !candidates[i].UpdatedAt.Equal(candidates[j].UpdatedAt) { return candidates[i].UpdatedAt.Before(candidates[j].UpdatedAt) } return candidates[i].Path < candidates[j].Path }) }