130 lines
4.3 KiB
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()
|