// External programs can load Lib.kiss with (loadFrom "nat-archive-tool" "src/nat/Lib.kiss") (load "Lib.kiss") (let [[archiveDir] (Sys.args) controller (new ArchiveController (new Archive archiveDir) (new CLI))] (loop (Sys.print ">> ") (let [command (.trim (.toString (.readLine (Sys.stdin))))] (controller.tryRunCommand command)))) (prop &mut :ArchiveController controller) (prop :kiss_tools.KeyShortcutHandler shortcutHandler null) (defNew []) (method :Void enterText [prompt :String->Void resolve maxLength] (Sys.print "$prompt ") (loop (let [entered (.toString (.readLine (Sys.stdin)))] (if !(<= entered.length maxLength) (Sys.print "Try again? ") {(resolve entered) (break)})))) (method :Void enterNumber [prompt :Float->Void resolve min max &opt inStepsOf] (Sys.print "$prompt ") (loop (let [entered (Std.parseFloat (.toString (.readLine (Sys.stdin))))] (if (or !(<= min entered max) (and inStepsOf !(= 0 (% (- entered min) inStepsOf)))) (Sys.print "Try again? ") {(resolve entered) (break)})))) (method :Void chooseEntry [prompt :Archive archive :Entry->Void resolve] (_chooseEntry prompt archive resolve ->(chooseEntry "empty name doesn't match any entries. Try again?" archive resolve))) (method :Void _chooseEntry [prompt :Archive archive :Entry->Void resolve :Void->Void onEmptyString] // TODO allow narrowing down with a tag string (enterText "entry name for $prompt" ->:Void name { (if !name (onEmptyString) (let [matchingEntries (controller.nameSystem.getEntries name)] (case (the Array matchingEntries) ([e] (resolve e)) ([] (chooseEntry "name $name doesn't match any entries. Try again?" archive resolve)) // TODO disambiguate entries with the same names by listing stringified versions of them and using enterNumber (multipleEntries (throw "ambiguous between multiple entries")) (otherwise))))} Math.POSITIVE_INFINITY)) (method :Void chooseEntries [prompt archive :Array->Void resolve min max] (_chooseEntries prompt archive resolve min max [])) (method :Void _chooseEntries [prompt archive :Array->Void resolve min max :Array collectedEntries] (let [onEmptyString ->:Void (if (<= min collectedEntries.length) (resolve collectedEntries) (throw "not enough entries chosen")) &mut :Void->Void chooseNextEntry null _chooseNextEntry ->:Void {(_chooseEntry prompt archive ->:Void e {(collectedEntries.push e) // If the maximum is reached, return it (if (= max collectedEntries.length) (resolve collectedEntries) // Otherwise, recurse (chooseNextEntry))} onEmptyString)}] (set chooseNextEntry _chooseNextEntry) (_chooseNextEntry))) (method handleChanges [:Archive archive :ChangeSet changeSet] (archive.processSystems this) (doFor e changeSet (print (archive.fullString e)))) (method :Void displayMessage [message] (print message)) (method :Void reportError [error] (print error)) (method :Void onSelectionChanged [:Array selectedEntries :Array _] (print "Selected:") (controller.PrintSelectedEntries selectedEntries))