import std/[times, os, strutils, strbasics, sequtils, strscans, sets, strtabs, tables], parseopt import soon/[conditions, config, typechecker] const VERSION = "0.1" const configDir = expandTilde("~/.config/soon/") const defaultConfigFile = configDir & "soon.toml" const defaultCalendarFile = configDir & "calendar.soon" const MONTHS = DefaultLocale.MMM.toHashSet + DefaultLocale.MMMM.toHashSet const WEEKDAYS = DefaultLocale.ddd.toHashSet + DefaultLocale.dddd.toHashSet proc printHelp(badCmd: string) = if badCmd != "": echo badCmd & " is not a valid option\n" echo """ Usage: soon [options] Options: -a print agenda (default behavior if no options) -c print reference calendar -t print todos -e open default calendar file in $EDITOR -i open TUI in interactive mode to schedule/archive events -w, -m, -y print agenda for upcoming week, month, or year -j print the modified julian day -d print today's date -p, --past=DAYS days in past to print agenda (default: 1) -f, --future=DAYS days in future to print agenda (default: 14) -h, --help show this help -v, --version print version Check the docs at https://codeberg.org/historia/soon """ quit() proc printVersion() = echo "soon " & VERSION quit() proc createDefaultCalendarFile(file: string) = if not fileExists(file): writeFile(file, "# This is your first calendar file. Put events and todos in here.") # Splits a string on whitespace, but maintains whitespace in date groups proc splitWhitespaceExceptParens(str:string): seq[string] = var output: seq[string] = newSeq[string]() var inParens = false for token in str.splitWhitespace: if inParens: output.add(output.pop & ' ' & token) else: output.add(token) for c in token: if c == '(': inParens = true if c == ')': inParens = false return output # Returns Table[conditionType, seq of conditions] proc parseDateToTable(date:string): Table[string,seq[string]] = var table = initTable[string,seq[string]]() let conditions = splitWhitespaceExceptParens(date) for c in conditions: let condType = checkType(c) if not table.hasKey(condType): table[condType] = @[] table[condType].add(c) return table proc getEvents(cal: seq[string], date: DateTime): seq[string] = var events: seq[string] = newSeq[string]() for line in cal: var (success, dateConditions, event) = scanTuple(line, "$*,$*") if success: dateConditions.strip event.strip let conditions = parseDateToTable(dateConditions) var match = true # key = Condition Type (e.g. month), cs = Seq of conditions for k,cs in conditions: var keyMatched = false for c in cs: if checkCond(c, date) == true: keyMatched = true break if not keyMatched: match = false break if match: events.add(event) else: echo "WARNING: Cannot parse line: ", line return events proc printEvents(cal: seq[string], date: DateTime, format: string) = let events = getEvents(cal, date) if events.len > 0: for e in events: echo date.format(format), " ", e proc printCalendar(calendar: seq[string], config: StringTableRef) = let past = 0 - parseInt(config["past"]) let future = parseInt(config["future"]) let now = now() for i in past..future: let curDay = now + i.days printEvents(calendar, curDay, "ddd yyyy MMM dd") proc openCalendar(file: string): seq[string] = let f = open(file) for line in f.lines: if line == "": continue if line.strip[0] == '#': continue result.add(line) f.close() proc processCalendars(config: StringTableRef) = var calendar = newSeq[string]() for file in os.walkDirRec(config["calendarPath"]): if file.toLower().endsWith(".soon"): calendar = calendar.concat(openCalendar(file)) printCalendar(calendar, config) proc parseArgs(config: StringTableRef): seq[string] = for kind, key, val in getopt(): case kind of cmdEnd: break of cmdLongOption, cmdShortOption: case key of "past", "p": config["past"] = val of "future", "f": config["future"] = val of "help", "h": printHelp("") of "version", "v": printVersion() else: result.add(key) of cmdArgument: printHelp(key) proc validateConfig(config: StringTableRef) = createDefaultCalendarFile(config["path"] / config["defaultFile"]) proc main() = var conf = config.loadConfig() let commands = parseArgs(conf) validateConfig(conf) processCalendars(conf) when isMainModule: main()