package day import ( "fmt" "strings" ) // Change describes one slot transition for the confirmation table. type Change struct { Index int FromID *string // nil = was empty ToID *string // nil = cleared } // ResolveLog computes the write set for a log command. activityID nil means // plan-fill: every target slot takes its plan block's activity, and any slot // without one aborts the whole command (all-or-nothing). func ResolveLog(targets []int, activityID *string, blocks, planBlocks map[int]string, blockSize int) (map[int]*string, []Change, error) { assignments := make(map[int]*string, len(targets)) var overwrites []Change if activityID == nil { var missing []string for _, i := range targets { if _, ok := planBlocks[i]; !ok { missing = append(missing, SlotLabel(i, blockSize)) } } if len(missing) > 0 { return nil, nil, fmt.Errorf("no planned activity at %s — nothing painted", strings.Join(missing, ", ")) } } for _, i := range targets { to := activityID if to == nil { v := planBlocks[i] to = &v } assignments[i] = to if from, ok := blocks[i]; ok && from != *to { f := from overwrites = append(overwrites, Change{Index: i, FromID: &f, ToID: to}) } } return assignments, overwrites, nil } // ResolveClear computes the write set for a clear command. Only slots that // are actually logged are touched; clearing an empty slot is a no-op. func ResolveClear(targets []int, blocks map[int]string) (map[int]*string, []Change) { assignments := make(map[int]*string) var cleared []Change for _, i := range targets { if from, ok := blocks[i]; ok { f := from assignments[i] = nil cleared = append(cleared, Change{Index: i, FromID: &f, ToID: nil}) } } return assignments, cleared }