123456789module apply1011import src.snapshot1213pub struct Plan {14pub mut:15 files []snapshot.FileEntry 16 applied []string 17 conflict string 18}1920pub fn conflicts(plan Plan) bool {21 return plan.conflict != ''22}23242526pub fn three_way(current []snapshot.FileEntry, base []snapshot.FileEntry, target []snapshot.FileEntry, filters []string) Plan {27 base_map := snapshot.index_files(base)28 target_map := snapshot.index_files(target)29 current_map := snapshot.index_files(current)3031 mut paths := []string{}32 for path in base_map.keys() {33 if in_scope(path, filters) {34 paths << path35 }36 }37 for path in target_map.keys() {38 if in_scope(path, filters) && path !in base_map {39 paths << path40 }41 }42 paths.sort()4344 mut plan := Plan{45 files: current.clone()46 applied: []string{}47 }48 for path in paths {49 from := base_map[path] or {50 snapshot.FileEntry{51 path: path52 }53 }54 to := target_map[path] or {55 snapshot.FileEntry{56 path: path57 }58 }59 if same_entry(from, to) {60 continue61 }62 here := current_map[path] or {63 snapshot.FileEntry{64 path: path65 }66 }67 if same_entry(here, from) {68 plan.files = set_entry(plan.files, path, to, path in target_map)69 plan.applied << path70 continue71 }72 if same_entry(here, to) {73 74 continue75 }76 plan.conflict = path77 return plan78 }79 return plan80}8182pub fn in_scope(path string, filters []string) bool {83 if filters.len == 0 {84 return true85 }86 for raw in filters {87 filter := raw.trim_right('/')88 if path == filter || path.starts_with('${filter}/') {89 return true90 }91 }92 return false93}94959697fn same_entry(a snapshot.FileEntry, b snapshot.FileEntry) bool {98 if a.hash == '' && b.hash == '' {99 return true100 }101 return a.hash == b.hash && a.executable == b.executable102}103104105106fn set_entry(files []snapshot.FileEntry, path string, entry snapshot.FileEntry, keep bool) []snapshot.FileEntry {107 mut out := []snapshot.FileEntry{}108 mut placed := false109 for file in files {110 if file.path == path {111 if keep {112 out << entry113 }114 placed = true115 continue116 }117 if !placed && file.path > path {118 if keep {119 out << entry120 }121 placed = true122 }123 out << file124 }125 if !placed && keep {126 out << entry127 }128 return out129}