diff --git a/projects/nat-archive-tool/src/nat/Archive.kiss b/projects/nat-archive-tool/src/nat/Archive.kiss index d4eaddad..d779b9d9 100644 --- a/projects/nat-archive-tool/src/nat/Archive.kiss +++ b/projects/nat-archive-tool/src/nat/Archive.kiss @@ -2,15 +2,13 @@ [:Array systems [] - :Map templates - (new Map) :Map entries (let [entryDir (Path.join [archiveDir "entries"]) componentDir (Path.join [archiveDir "components"])] (FileSystem.createDirectory entryDir) (FileSystem.createDirectory componentDir) (let [entryFiles (FileSystem.readDirectory entryDir)] - (for file entryFiles =>(file.withoutExtension) ~(the Entry (Json.parse (File.getContent (Path.join [archiveDir "entries" file])))))))]) + (for file entryFiles =>(file.withoutExtension) (the Entry (Json.parse (File.getContent (Path.join [archiveDir "entries" file])))))))]) (defmethod addSystem [:System system] // Assign entries to the Systems that care about them @@ -18,18 +16,20 @@ (system.checkEntryInOrOut this entry)) (systems.push system)) -(defmethod addTemplate [name template] - (dictSet templates name template)) - -(defmethod :Entry createEntry [template] +(defmethod :Entry createEntry [:Entry->Dynamic initializer] // initializer returns Dynamic so ->:Void isn't required (let [e (_newEntry)] - (.prepareEntry (dictGet templates template) e) - (saveEntry e) - (doFor system systems - (system.checkEntryInOrOut this e)) + (initializer e) + (refreshEntry e) e)) -(defmethod saveEntry [:Entry e] +// After modifying an entry, this must be called. If you are writing in a createEntry initializer or a system's processEntry function, this will happen automatically. +// Otherwise, you can guarantee it happens automatically by using the (withWritableEntry) macro in Lib.kiss +(defmethod refreshEntry [:Entry e] + (_saveEntry e) + (doFor system systems + (system.checkEntryInOrOut this e))) + +(defmethod _saveEntry [:Entry e] (File.saveContent (Path.join [archiveDir "entries" (e.id.withExtension "json")]) (Json.stringify e))) diff --git a/projects/nat-archive-tool/src/nat/Lib.kiss b/projects/nat-archive-tool/src/nat/Lib.kiss index 6bfed819..544049ba 100644 --- a/projects/nat-archive-tool/src/nat/Lib.kiss +++ b/projects/nat-archive-tool/src/nat/Lib.kiss @@ -7,23 +7,31 @@ (defmacro _componentPath [archive e componentType] `(haxe.io.Path.join [.archiveDir (the nat.Archive ,archive) "components" (+ (dictGet (the Map .components ,e) ,(symbolName componentType)) ".json")])) -(defmacro getComponent [archive e componentType] +// Changes to the object returned by (readComponent) will not be saved! Use (withWritableComponents) for making changes +(defmacro readComponent [archive e componentType] // TODO add to the documentation a hint that macros should use fully qualified type paths so macro caller classes don't need to import everything `(the nat.components ,componentType (tink.Json.parse (sys.io.File.getContent (_componentPath ,archive ,e ,componentType))))) // Components have to be saved individually after writing because the Entity only knows their ids, -// not the data they contain. This is more ergonomically done by using (withComponents...) -(defmacro saveComponent [archive e componentType c] +// not the data they contain. This is more ergonomically done by using (withWritableComponents...) +(defmacro writeComponent [archive e componentType c] `(sys.io.File.saveContent (_componentPath ,archive ,e ,componentType) (tink.Json.stringify (the nat.components ,componentType ,c)))) +// TODO check not overwriting a component +(defmacro addComponent [archive e componentType c] + `(let [componentId (Uuid.v4)] + (dictSet .components ,e ,(symbolName componentType) componentId) + (writeComponent ,archive ,e ,componentType ,c) + ,e)) + // Retrieve multiple components from an Entity with mutable access. // All components will be serialized after the block is done. -(defmacro withComponents [archive e bindings &rest body] +(defmacro withWritableComponents [archive e bindings &rest body] (let [bindingPairs (groups (expList bindings) 2 Throw) bindingList @@ -34,16 +42,31 @@ (symbol)] (doFor [name type] bindingPairs (bindingList.push `&mut ,name) - (bindingList.push `(getComponent ,archive ,e ,type)) - (saveList.push `(saveComponent ,archive ,e ,type ,name))) + (bindingList.push `(readComponent ,archive ,e ,type)) + (saveList.push `(writeComponent ,archive ,e ,type ,name))) `(let [,@bindingList ,retValSymbol {,@body}] ,@saveList ,retValSymbol))) +(defmacro withWritableEntry [archive e &rest body] + (let [retValSymbol + (symbol)] + `(let [,retValSymbol {,@body}] + (archive.refreshEntry ,e) + ,retValSymbol))) + +// Create a system that selects Entries according to a single string component (i.e. Name or Author) matching the given value +(defmacro stringComponentSystem [archive componentType value process] + `(new System + (lambda [archive :Entry e] + ?(and (hasComponent e ,componentType) + (= ,value (readComponent ,archive e ,componentType)))) + ,process)) + (defun tagList [archive e] (let [t - (getComponent archive e Tags)] + (readComponent archive e Tags)] (collect (t.keys)))) (defun tagsMatch [archive e tagsBoolExp] diff --git a/projects/nat-archive-tool/src/nat/Template.hx b/projects/nat-archive-tool/src/nat/Template.hx deleted file mode 100644 index d4a3065d..00000000 --- a/projects/nat-archive-tool/src/nat/Template.hx +++ /dev/null @@ -1,5 +0,0 @@ -package nat; - -interface Template { - function prepareEntry(e:Entry):Void; -} diff --git a/projects/nat-archive-tool/src/test/TestMain.kiss b/projects/nat-archive-tool/src/test/TestMain.kiss index fa8c0d1f..af783b2f 100644 --- a/projects/nat-archive-tool/src/test/TestMain.kiss +++ b/projects/nat-archive-tool/src/test/TestMain.kiss @@ -26,7 +26,7 @@ (assert (tagsMatch archive song2 "(and song religious)")) (assert !(tagsMatch archive song2 "(and song western)")) - (withComponents archive song1 + (withWritableComponents archive song1 [author Author name Name] (assert (= author "Rafael Krux"))