123module cli45import json26import os7import src.events8import src.fmt9import src.history10import src.snapshot11import src.storage1213pub fn cmd_status(raw []string) int {14 args := parse('status', raw, Options{15 flags: ['json']16 }) or { return usage_error(err.msg()) }17 store := open_store() or { return refuse(args.json(), err.msg()) }18 defer {19 store.close()20 }21 head := history.head(store) or {22 return refuse(args.json(), 'this repo has no saves yet — run "koh save \\"first save\\""')23 }24 unfinished := snapshot.restore_pending(store)25 changes := compare_with_head(store, head) or {26 27 reason := if unfinished {28 '${err.msg()} — ${snapshot.restore_unfinished}'29 } else {30 err.msg()31 }32 return refuse(args.json(), reason)33 }3435 if args.json() {36 line := history.line(store) or { return refuse(true, err.msg()) }37 flags := events.latest_flags(store) or { return refuse(true, err.msg()) }38 head_flag := flags[head.id] or { events.Flag{} }39 emit_json(json2.encode(StatusJson{40 head_id: head.id41 head_msg: head.message42 dirty: changes.dirty43 modified: changes.modified44 added: changes.added45 deleted: changes.removed46 ignore_changed: changes.ignore_changed47 restore_unfinished: unfinished48 saves: line.len49 size_bytes: os.file_size(os.join_path(store.root, storage.file_name))50 head_flagged: head_flag.flagged51 }))52 return 053 }54 if unfinished {55 warn(snapshot.restore_unfinished)56 }57 if !changes.dirty {58 ok('no changes')59 if changes.ignore_changed {60 note('${snapshot.ignore_file} changed since [${head.id}]')61 }62 return 063 }64 for path in changes.added {65 println(' ${fmt.green('+')} ${path}')66 }67 for path in changes.modified {68 println(' ${fmt.amber('~')} ${path}')69 }70 for path in changes.removed {71 println(' ${fmt.red('-')} ${path}')72 }73 note('${changes.added.len} added · ${changes.modified.len} modified · ${changes.removed.len} removed')74 if changes.ignore_changed {75 warn('${snapshot.ignore_file} changed since [${head.id}] — the next save records the new rules')76 }77 return 078}7980pub struct Changes {81pub:82 dirty bool83 added []string84 modified []string85 removed []string86 ignore_changed bool87}8889struct StatusJson {90 head_id string91 head_msg string92 dirty bool93 modified []string94 added []string95 deleted []string96 ignore_changed bool97 restore_unfinished bool98 saves int99 size_bytes u64100 head_flagged bool101}102103pub fn compare_with_head(store &storage.Store, head history.Save) !Changes {104 before := snapshot.index_files(history.files_of(store, head.id)!)105 after := snapshot.index_files(snapshot.walk(store, store.root, snapshot.load(store.root))!)106 mut added := []string{}107 mut modified := []string{}108 mut removed := []string{}109 for path in after.keys() {110 if path !in before {111 added << path112 } else if before[path].hash != after[path].hash113 || before[path].executable != after[path].executable {114 modified << path115 }116 }117 for path in before.keys() {118 if path !in after {119 removed << path120 }121 }122 added.sort()123 modified.sort()124 removed.sort()125 ignore_changed := snapshot.ignore_file in added || snapshot.ignore_file in modified126 return Changes{127 dirty: added.len + modified.len + removed.len > 0128 added: added129 modified: modified130 removed: removed131 ignore_changed: ignore_changed132 }133}