123module cli45import json6import src.fmt7import src.history8import src.snapshot9import src.storage1011pub fn cmd_status(raw []string) int {12 args := parse('status', raw, Options{13 flags: ['json']14 }) or { return usage_error(err.msg()) }15 store := open_store() or { return refuse(args.json(), err.msg()) }16 defer {17 store.close()18 }19 head := history.head(store) or {20 return refuse(args.json(), 'this repo has no saves yet — run "koh save \\"first save\\""')21 }22 unfinished := snapshot.restore_pending(store)23 changes := compare_with_head(store, head) or {24 25 reason := if unfinished {26 '${err.msg()} — ${snapshot.restore_unfinished}'27 } else {28 err.msg()29 }30 return refuse(args.json(), reason)31 }3233 if args.json() {34 emit_json(json.encode(StatusJson{35 head_id: head.id36 head_msg: head.message37 dirty: changes.dirty38 modified: changes.modified39 added: changes.added40 deleted: changes.removed41 ignore_changed: changes.ignore_changed42 restore_unfinished: unfinished43 }))44 return 045 }46 if unfinished {47 warn(snapshot.restore_unfinished)48 }49 if !changes.dirty {50 ok('no changes')51 if changes.ignore_changed {52 note('${snapshot.ignore_file} changed since [${head.id}]')53 }54 return 055 }56 for path in changes.added {57 println(' ${fmt.green('+')} ${path}')58 }59 for path in changes.modified {60 println(' ${fmt.amber('~')} ${path}')61 }62 for path in changes.removed {63 println(' ${fmt.red('-')} ${path}')64 }65 note('${changes.added.len} added · ${changes.modified.len} modified · ${changes.removed.len} removed')66 if changes.ignore_changed {67 warn('${snapshot.ignore_file} changed since [${head.id}] — the next save records the new rules')68 }69 return 070}7172pub struct Changes {73pub:74 dirty bool75 added []string76 modified []string77 removed []string78 ignore_changed bool79}8081struct StatusJson {82 head_id string83 head_msg string84 dirty bool85 modified []string86 added []string87 deleted []string88 ignore_changed bool89 restore_unfinished bool90}9192pub fn compare_with_head(store &storage.Store, head history.Save) !Changes {93 before := snapshot.index_files(history.files_of(store, head.id)!)94 after := snapshot.index_files(snapshot.walk(store, store.root, snapshot.load(store.root))!)95 mut added := []string{}96 mut modified := []string{}97 mut removed := []string{}98 for path in after.keys() {99 if path !in before {100 added << path101 } else if before[path].hash != after[path].hash102 || before[path].executable != after[path].executable {103 modified << path104 }105 }106 for path in before.keys() {107 if path !in after {108 removed << path109 }110 }111 added.sort()112 modified.sort()113 removed.sort()114 ignore_changed := snapshot.ignore_file in added || snapshot.ignore_file in modified115 return Changes{116 dirty: added.len + modified.len + removed.len > 0117 added: added118 modified: modified119 removed: removed120 ignore_changed: ignore_changed121 }122}