diff --git a/src/nat/Archive.kiss b/src/nat/Archive.kiss index d779b9d..c242a2d 100644 --- a/src/nat/Archive.kiss +++ b/src/nat/Archive.kiss @@ -14,7 +14,8 @@ // Assign entries to the Systems that care about them (doFor =>id entry entries (system.checkEntryInOrOut this entry)) - (systems.push system)) + (systems.push system) + system) (defmethod :Entry createEntry [:Entry->Dynamic initializer] // initializer returns Dynamic so ->:Void isn't required (let [e (_newEntry)] @@ -34,7 +35,17 @@ (Path.join [archiveDir "entries" (e.id.withExtension "json")]) (Json.stringify e))) -// TODO adding or removing components or files should save the Entry and re-check it in or out of systems +(defmethod fullData [:Entry e] + (object + id e.id + components + (for =>type id e.components + =>type (haxe.Json.parse (File.getContent (haxe.io.Path.join [archiveDir "components" "$(dictGet e.components type).json"])))) + files + e.files)) + +(defmethod fullString [:Entry e] + (haxe.Json.stringify (fullData e) null "\t")) (defun :Entry _newEntry [] (object diff --git a/src/nat/ArchiveController.hx b/src/nat/ArchiveController.hx index d3ca7dc..322f108 100644 --- a/src/nat/ArchiveController.hx +++ b/src/nat/ArchiveController.hx @@ -4,6 +4,7 @@ import kiss.Prelude; import kiss.List; import kiss.Operand; import haxe.Constraints; +import uuid.Uuid; enum CommandArgType { SelectedEntry; diff --git a/src/nat/ArchiveController.kiss b/src/nat/ArchiveController.kiss index b0c8f38..b03f3ed 100644 --- a/src/nat/ArchiveController.kiss +++ b/src/nat/ArchiveController.kiss @@ -1,3 +1,5 @@ +(load "Lib.kiss") + (defmethod :Void _collectAndValidateArg [:CommandArg arg :Dynamic->Void continuation] (case arg.type (SelectedEntry @@ -67,7 +69,9 @@ (< max entries.length)) (ui.reportError "The requested command expects between $min and $max entries. You chose: $entries.length") - (continuation selectedEntries))))))) + (continuation selectedEntries))) + min + max)))) (defmethod :Void->Void _composeArgCollector [:Array collectedArgs :CommandArg arg :Void->Void lastCollector] (lambda :Void [] @@ -90,18 +94,17 @@ (groups (expList args) 2) methodArgs (for [name type] argPairs - // TODO write a macroCase macro that simplifies this terrible mess, + // TODO write an exprCase macro that simplifies this terrible mess, // and maybe adds back precise pattern matching instead of relying // on partial string matching - (let [expAsStr (Std.string type)] - (cond - ((< -1 (max (expAsStr.indexOf "SelectedEntry") (expAsStr.indexOf "OneEntry"))) `:nat.Entry ,name) - ((< -1 (max (expAsStr.indexOf "SelectedEntries") (expAsStr.indexof "Entries"))) `:Array ,name) - ((< -1 (expAsStr.indexOf "Text")) `:String ,name) - ((< -1 (expAsStr.indexOf "Number")) `:Float ,name)))) + (exprCase type + ((exprOr SelectedEntry OneEntry) `:nat.Entry ,name) + ((exprOr (SelectedEntries _ _) (Entries _ _)) `:Array ,name) + ((Text _ _) `:String ,name) + ((Number _ _ _) `:Float ,name))) commandArgs (for [name type] argPairs - ~`(object name ,(symbolName name) type ,type))] + `(object name ,(symbolName name) type ,type))] `{ (defmethod ,name [,@methodArgs] ,@body) (dictSet commands ,(symbolName name) (object args [,@commandArgs] handler (the Function ,name)))})) @@ -115,13 +118,15 @@ (defcommand selectEntry [e OneEntry] (set selectedEntries [e])) - /* (defcommand selectEntries [entries (Entries null null)] (set selectedEntries entries)) - */ (defcommand selectLastChangeSet [] (set selectedEntries lastChangeSet)) + + (defcommand printSelectedEntries [entries (SelectedEntries null null)] + (doFor e entries (ui.displayMessage (archive.fullString e)))) - /*(defcommand createEntry [name (Text null null)] - ~name)*/) \ No newline at end of file + (defcommand createEntry [name (Text null null)] + (archive.createEntry ->e + (addComponent archive e Name name)))) \ No newline at end of file diff --git a/src/nat/ArchiveUI.hx b/src/nat/ArchiveUI.hx index 960196a..ed9a334 100644 --- a/src/nat/ArchiveUI.hx +++ b/src/nat/ArchiveUI.hx @@ -6,12 +6,12 @@ interface ArchiveUI { /** * Prompt the user to enter text */ - function enterText(prompt:String, resolve:(String) -> Void, ?minLength:Int, ?maxLength:Float):Void; + function enterText(prompt:String, resolve:(String) -> Void, minLength:Int, maxLength:Float):Void; /** * Prompt the user to enter a number */ - function enterNumber(prompt:String, resolve:(Float) -> Void, ?min:Float, ?max:Float, ?inStepsOf:Float):Void; + function enterNumber(prompt:String, resolve:(Float) -> Void, min:Float, max:Float, ?inStepsOf:Float):Void; /** * Prompt the user to choose a single Entry @@ -21,13 +21,18 @@ interface ArchiveUI { /** * Prompt the user to choose multiple Entries */ - function chooseEntries(prompt:String, archive:Archive, resolve:(Array) -> Void, ?min:Int, ?max:Float):Void; + function chooseEntries(prompt:String, archive:Archive, resolve:(Array) -> Void, min:Int, max:Float):Void; /** * Update the interface to reflect changes made to Entries through commands */ function handleChanges(changeSet:ChangeSet):Void; + /** + * Tell the user something useful + */ + function displayMessage(message:String):Void; + /** * Tell the user that something is wrong */ diff --git a/src/nat/CLI.hx b/src/nat/CLI.hx index 7172699..1d7d437 100644 --- a/src/nat/CLI.hx +++ b/src/nat/CLI.hx @@ -5,6 +5,7 @@ import kiss.List; import kiss.Operand; import sys.FileSystem; import nat.ArchiveController.CommandArgType; +using StringTools; @:build(kiss.Kiss.build()) class CLI implements ArchiveUI {} diff --git a/src/nat/CLI.kiss b/src/nat/CLI.kiss index e4a8d94..94a55df 100644 --- a/src/nat/CLI.kiss +++ b/src/nat/CLI.kiss @@ -1,14 +1,20 @@ +(load "Lib.kiss") + (defun :Void main [] (let [[archiveDir] (Sys.args) controller (new ArchiveController (new Archive archiveDir) (new CLI))] - (controller.runCommand (dictGet controller.commands "selectEntry")))) + (loop + (Sys.print ">> ") + (let [command + (.trim (.toString (.readLine (Sys.stdin))))] + (controller.runCommand (dictGet controller.commands command)))))) (defnew []) -(defmethod :Void enterText [prompt resolve &opt minLength maxLength] +(defmethod :Void enterText [prompt resolve minLength maxLength] (Sys.print "$prompt ") (loop (let [entered (.toString (.readLine (Sys.stdin)))] @@ -17,7 +23,7 @@ {(resolve entered) (break)})))) -(defmethod :Void enterNumber [prompt resolve &opt min max inStepsOf] +(defmethod :Void enterNumber [prompt resolve min max &opt inStepsOf] (Sys.print "$prompt ") (loop (let [entered (Std.parseFloat (.toString (.readLine (Sys.stdin))))] @@ -29,12 +35,28 @@ {(resolve entered) (break)})))) -(defmethod :Void chooseEntry [prompt archive resolve] - (resolve null)) +(defmethod :Void chooseEntry [prompt :Archive archive resolve] + // TODO allow narrowing down with a tag string + (enterText "entry name for $prompt" + ->name { + (let [matchingEntries []] + (.process (archive.addSystem + (stringComponentSystem archive Name name + (lambda [archive e] + (matchingEntries.push e)))) archive) -(defmethod :Void chooseEntries [prompt archive resolve &opt min max] + (case (the Array matchingEntries) + ([e] (resolve e)) + // TODO disambiguate entries with the same names by listing stringified versions of them and using enterNumber + (multipleEntries (throw "ambiguous between multiple entries"))))} + 0 Math.POSITIVE_INFINITY)) + +(defmethod :Void chooseEntries [prompt archive resolve min max] (resolve [])) (defmethod handleChanges [changeSet]) +(defmethod :Void displayMessage [message] + (print message)) + (defmethod :Void reportError [error] ~error) \ No newline at end of file diff --git a/src/nat/System.hx b/src/nat/System.hx index 657d74f..4083cd6 100644 --- a/src/nat/System.hx +++ b/src/nat/System.hx @@ -3,7 +3,7 @@ package nat; import kiss.Prelude; typedef EntryChecker = (Archive, Entry) -> Bool; -typedef EntryProcessor = (Archive, Entry) -> Void; +typedef EntryProcessor = (Archive, Entry) -> Dynamic; // Whatever value is returned will be dropped, but this is easier than requiring ->:Void @:build(kiss.Kiss.build()) class System {} diff --git a/src/nat/System.kiss b/src/nat/System.kiss index 2714df9..1debcd2 100644 --- a/src/nat/System.kiss +++ b/src/nat/System.kiss @@ -1,7 +1,9 @@ (defprop :Map entries (new Map)) (defmethod :Void process [:Archive archive] - (doFor e (entries.iterator) (processEntry archive e))) + (doFor e (entries.iterator) + (processEntry archive e) + (archive.refreshEntry e))) (defnew [&prop :EntryChecker canProcessEntry &prop :EntryProcessor processEntry] diff --git a/src/nat/systems/TagSystem.kiss b/src/nat/systems/TagSystem.kiss index 5d56920..80c164e 100644 --- a/src/nat/systems/TagSystem.kiss +++ b/src/nat/systems/TagSystem.kiss @@ -1,8 +1,8 @@ (load "../Lib.kiss") -// TODO make a &super annotation that passes an argument to the super constructor -(defnew [&prop :String tagFilterString - &prop :EntryProcessor processor] +(defnew [&prop :String tagFilterString // This doesn't need to be a &prop because it will be captured by the lambda, but maybe it will be useful to query from the system later + // TODO make a &super annotation that passes an argument to the super constructor + :EntryProcessor processor] [] (super (lambda [:Archive archive :Entry e] (tagsMatch archive e tagFilterString))