syndicate-genode/src/syndicate_genode/parent_facets.nim

130 lines
4.3 KiB
Nim

# 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()