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}23242526272829pub fn three_way(current []snapshot.FileEntry, base []snapshot.FileEntry, target []snapshot.FileEntry, filters []string, exclude []string) Plan {30 base_map := snapshot.index_files(base)31 target_map := snapshot.index_files(target)32 current_map := snapshot.index_files(current)3334 mut paths := []string{}35 for path in base_map.keys() {36 if in_scope(path, filters) && !excluded(path, exclude) {37 paths << path38 }39 }40 for path in target_map.keys() {41 if in_scope(path, filters) && !excluded(path, exclude) && path !in base_map {42 paths << path43 }44 }45 paths.sort()4647 mut plan := Plan{48 files: current.clone()49 applied: []string{}50 }51 for path in paths {52 from := base_map[path] or {53 snapshot.FileEntry{54 path: path55 }56 }57 to := target_map[path] or {58 snapshot.FileEntry{59 path: path60 }61 }62 if same_entry(from, to) {63 continue64 }65 here := current_map[path] or {66 snapshot.FileEntry{67 path: path68 }69 }70 if same_entry(here, from) {71 plan.files = set_entry(plan.files, path, to, path in target_map)72 plan.applied << path73 continue74 }75 if same_entry(here, to) {76 77 continue78 }79 plan.conflict = path80 return plan81 }82 return plan83}8485pub fn in_scope(path string, filters []string) bool {86 if filters.len == 0 {87 return true88 }89 for raw in filters {90 filter := raw.trim_right('/')91 if path == filter || path.starts_with('${filter}/') {92 return true93 }94 }95 return false96}9798fn excluded(path string, exclude []string) bool {99 for name in exclude {100 if path == name {101 return true102 }103 }104 return false105}106107108109fn same_entry(a snapshot.FileEntry, b snapshot.FileEntry) bool {110 if a.hash == '' && b.hash == '' {111 return true112 }113 return a.hash == b.hash && a.executable == b.executable114}115116117118fn set_entry(files []snapshot.FileEntry, path string, entry snapshot.FileEntry, keep bool) []snapshot.FileEntry {119 mut out := []snapshot.FileEntry{}120 mut placed := false121 for file in files {122 if file.path == path {123 if keep {124 out << entry125 }126 placed = true127 continue128 }129 if !placed && file.path > path {130 if keep {131 out << entry132 }133 placed = true134 }135 out << file136 }137 if !placed && keep {138 out << entry139 }140 return out141}