# SPDX-FileCopyrightText: ☭ 2022 Emery Hemingway # SPDX-License-Identifier: AGPL-3.0-or-later import std/[sets, tables] from std/sequtils import toSeq from std/strutils import parseHexInt, parseInt, unescape import npeg import preserves, preserves/xmlhooks import ../../syndicate, ./schemas/session_requests export Args, `$` type Value = Assertion ArgsParser = object dict: seq[(Value, Value)] nextKey: Assertion proc push[T](parser: var ArgsParser; v: sink T) = add(parser.dict, (move parser.nextKey, toPreserve(v, Ref))) const argsPeg = peg("Args", parser: ArgsParser): # "Because friends don't let friends write parsers by hand" - Ico Args <- Pair * *(*(Space | {','}) * Pair) Pair <- Key * '=' * Value Key <- +{'0'..'9', 'A'..'z', '_'}: parser.nextKey = toSymbol($0, Ref) for (key, _) in parser.dict: validate(key != parser.nextKey) # check for duplicate keys Value <- String | Size | Hex | Natural | True | False String <- '"' * >(*(('\\' * {'\\', '"'}) | ({'\x20'..'\x7e'} - {'\\', '"'}))) * '"': push(parser, unescape($0)) Size <- >(+Digit) * >({'K','M','G','P'}): var i = parseInt($1) case $2 of "K": i = i shl 10 of "M": i = i shl 20 of "G": i = i shl 30 of "P": i = i shl 40 push(parser, i) Hex <- "0x" * >(+Xdigit): push(parser, parseHexInt($1)) Natural <- ('1' * +Digit) | ({'2'..'9'} * *Digit): push(parser, parseInt($0)) True <- "true" | "on" | "yes" | "1": push(parser, true) False <- "false" | "off" | "no" | "0": push(parser, false) proc argumentsToPreserves(text: Value): (bool, Value) {.gcsafe.} = if text.isString: var p: ArgsParser let match = match(argsPeg, text.string, p) if match.ok: result[0] = true result[1] = initDictionary[Ref]() result[1].dict = move p.dict else: raiseAssert text.string[match.matchMax..text.string.high] type CreateRequest* = session_requests.CreateRequest[Ref] CloseRequest* = session_requests.CloseRequest[Ref] CreateAttrs* = session_requests.CreateAttrs[Ref] # TODO: schema code-generator is making these generic when they are not when defined(genode): import genode, genode/roms from std/asyncdispatch import scheduleCallbacks proc newConfigFacet*(env: GenodeEnv; ds: Ref; turn: var Turn): Facet {.discardable.} = ## Create a new `Facet` that processes the "config" ROM ## and publishes each request to `ds`. facet(turn) do (turn: var Turn): let facet = turn.facet var configHandle: Handle let configRom = env.newRomHandler("config") do (rom: RomClient): update(rom) run(facet) do (turn: var Turn): var pr = rom.xml.toPreserve(Ref) if pr.isRecord("config"): replace(turn, ds, configHandle, pr) scheduleCallbacks() proc newSessionRequestsFacet*(env: GenodeEnv; ds: Ref; turn: var Turn): Facet {.discardable.} = ## Create a new `Facet` that processes the "session_requests" ROM ## and publishes each request to `ds`. facet(turn) do (turn: var Turn): let facet = turn.facet var requestHandles = initTable[Preserve[Ref], Handle]() let sessionsRom = env.newRomHandler("session_requests") do (rom: RomClient): update(rom) run(facet) do (turn: var Turn): var currentRequests: HashSet[Value] for req in rom.xml.toPreserve(Ref).fields: var req = req if req.isRecord "create": # best effort conversion of the arguments ad-hoc text format var dict: Assertion ok: bool try: (ok, dict) = argumentsToPreserves(req.record[1].record[0]) except: discard if ok: req.record[1] = move dict else: echo "failed to parse ", req.fields[1] incl(currentRequests, move req) var publishedRequests = requestHandles.keys.toSeq.toHashSet # each requests in the XML is published seperately for req in publishedRequests: if req notin currentRequests: var h: Handle discard pop(requestHandles, req, h) retract(turn, h) for req in currentRequests: if req notin publishedRequests: requestHandles[req] = publish(turn, ds, req) scheduleCallbacks()