(prop :Array dailyEntries []) (prop :Array bonusEntries []) (prop :Array todoEntries []) (defNew [&prop :String textFile] (let [s (Stream.fromFile textFile) &mut lastHeader ""] // TODO could be whileLet (loop (case (s.takeLine) ((Some "DAILY") (set lastHeader "DAILY")) ((Some "BONUS") (set lastHeader "BONUS")) ((Some "TODO") (set lastHeader "TODO")) ((when (apply = (concat ["-"] (line.split ""))) (Some line)) (continue)) ((Some "") (continue)) ((Some line) (.push (case lastHeader ("DAILY" dailyEntries) ("BONUS" bonusEntries) ("TODO" todoEntries) (otherwise (throw "bad header"))) (object type (case lastHeader ("BONUS" Bonus) ("TODO" Todo) ("DAILY" (Daily (case (line.split ":") ([noColon] (collect (range 7))) ([::&mut preColon ...afterColon] (set line (afterColon.join ":")) (sort (filter [ // disambiguate Th from T and Su from S: (when (contains preColon "Th") {(set preColon (StringTools.replace preColon "Th" "")) 4}) (when (contains preColon "Su") {(set preColon (StringTools.replace preColon "Su" "")) 0}) (when (contains preColon "M") 1) (when (contains preColon "T") 2) (when (contains preColon "W") 3) (when (contains preColon "F") 5) (when (contains preColon "S") 6) ]))) (otherwise (throw "bad line"))))) (otherwise (throw "bad header"))) labels (for l (line.split "/") (object label (StringTools.trim (StringTools.replace l "|" "")) points (count (l.split "") ->c (= c "|"))))))) (otherwise (break)))))) (function :String stringify [:Entry e] "$(ifLet [(when !(= days.length 7) (Daily days)) e.type] (+ (.join (for day days (case day (0 "Su") (1 "M") (2 "T") (3 "W") (4 "Th") (5 "F") (6 "S") (otherwise (throw "bad day")))) "") ": ") "")$(.join (for label e.labels "${label.label} $(* "|" label.points)") "/")") (method :Void save [] (localVar &mut content "DAILY\n-----\n") (+= content (.join (map dailyEntries stringify) "\n") "\n") (+= content "\nBONUS\n-----\n") (+= content (.join (map bonusEntries stringify) "\n") "\n") (+= content "\nTODO\n----\n") (+= content (.join (map todoEntries stringify) "\n") "\n") (File.saveContent textFile content)) // With rotating entries, the active one is the first one with the lowest score: (function :EntryLabel activeLabel [:Entry e] (let [lowScore (apply min (for label e.labels label.points))] (doFor label e.labels (when (= lowScore label.points) (return label))) (throw "no active?!"))) (function isActive [:Entry e] (case e.type ((Daily days) (contains days (.getDay (Date.now)))) (otherwise true)))