12345678module snapshot910import os11import src.storage1213pub struct RestoreReport {14pub:15 written int16 removed int17}18192021222324pub fn restore_files(store &storage.Store, root string, ig Ignore, target []FileEntry, remove []string) !RestoreReport {25 mut wanted := map[string]bool{}26 for entry in target {27 wanted[entry.path] = true28 }29 mut removed := 030 for path in remove {31 full := os.join_path(root, path)32 if !os.is_file(full) || ig.matches(path, false) || path in wanted {33 continue34 }35 os.rm(full)!36 removed++37 prune_empty_dirs(root, os.dir(full))38 }39 40 41 42 for entry in target {43 full := os.join_path(root, entry.path)44 if os.is_dir(full) {45 os.rmdir(full) or {46 return error('${entry.path} is a folder of ignored files where a saved file belongs — move it, then run the command again')47 }48 }49 }50 for entry in target {51 data := store.get_object(entry.hash)!52 write_file(os.join_path(root, entry.path), data, entry.executable)!53 }54 return RestoreReport{55 written: target.len56 removed: removed57 }58}5960fn write_file(path string, data []u8, executable bool) ! {61 os.mkdir_all(os.dir(path), os.MkdirParams{})!62 tmp := '${path}.koh-tmp-${os.getpid()}'63 os.write_file_array(tmp, data)!64 mode := if executable { 0o755 } else { 0o644 }65 os.chmod(tmp, mode)!66 os.rename(tmp, path)!67}686970fn prune_empty_dirs(root string, dir string) {71 mut current := dir72 for current.len > root.len && current.starts_with(root) {73 if (os.ls(current) or { return }).len > 0 {74 return75 }76 os.rmdir(current) or { return }77 current = os.dir(current)78 }79}