87 lines
2.6 KiB
Nim
87 lines
2.6 KiB
Nim
# SPDX-FileCopyrightText: 2021 ☭ Emery Hemingway
|
|
# SPDX-License-Identifier: Unlicense
|
|
|
|
import std/[parseutils, strtabs, tables, xmltree]
|
|
import ../preserves
|
|
|
|
proc toPreserveFromString*(s: string; E: typedesc): Preserve[E] =
|
|
# This is a bad and slow thing to do, but that is XML.
|
|
case s
|
|
of "false", "no", "off":
|
|
result = toPreserve(false, E)
|
|
of "true", "yes", "on":
|
|
result = toPreserve(true, E)
|
|
else:
|
|
var
|
|
n: BiggestInt
|
|
f: BiggestFloat
|
|
if parseBiggestInt(s, n) == s.len:
|
|
result = toPreserve(n, E)
|
|
elif parseHex(s, n) == s.len:
|
|
result = toPreserve(n, E)
|
|
elif parseFloat(s, f) == s.len:
|
|
result = toPreserve(f, E)
|
|
else:
|
|
result = toPreserve(s, E)
|
|
|
|
proc toPreserveHook*(xn: XmlNode; E: typedesc): Preserve[E] =
|
|
if xn.kind == xnElement:
|
|
result = Preserve[E](kind: pkRecord)
|
|
if not xn.attrs.isNil:
|
|
var attrs = initDictionary[E]()
|
|
for xk, xv in xn.attrs.pairs:
|
|
attrs[toSymbol(xk, E)] = toPreserveFromString(xv, E)
|
|
result.record.add(attrs)
|
|
var isText = xn.len > 0
|
|
# escaped text is broken up and must be concatenated
|
|
for child in xn.items:
|
|
if child.kind == xnElement:
|
|
isText = false
|
|
break
|
|
if isText:
|
|
result.record.add(toPreserve(xn.innerText, E))
|
|
else:
|
|
for child in xn.items:
|
|
case child.kind
|
|
of xnElement:
|
|
result.record.add(toPreserveHook(child, E))
|
|
of xnText, xnVerbatimText, xnCData, xnEntity:
|
|
result.record.add(toPreserve(text(child), E))
|
|
of xnComment:
|
|
discard
|
|
result.record.add(toSymbol(xn.tag, E))
|
|
# record labels are stored after the fields
|
|
|
|
proc toUnquotedString[E](pr: Preserve[E]): string {.inline.} =
|
|
case pr.kind
|
|
of pkString:
|
|
pr.string
|
|
of pkBoolean:
|
|
if pr.bool: "true" else: "false"
|
|
else: $pr
|
|
|
|
proc fromPreserveHook*[E](xn: var XmlNode; pr: Preserve[E]): bool =
|
|
if pr.kind == pkRecord and pr.label.kind == pkSymbol:
|
|
xn = newElement($pr.label)
|
|
var i: int
|
|
for e in pr.fields:
|
|
if i == 0 and e.kind == pkDictionary:
|
|
var pairs = newSeqOfCap[tuple[key, val: string]](e.dict.len)
|
|
for key, val in e.dict.items:
|
|
pairs.add((key.toUnquotedString, val.toUnquotedString,))
|
|
xn.attrs = pairs.toXmlAttributes
|
|
elif e.kind == pkString:
|
|
xn.add newText(e.string)
|
|
else:
|
|
var child: XmlNode
|
|
result = fromPreserveHook(child, e)
|
|
if not result: return
|
|
xn.add child
|
|
inc i
|
|
result = true
|
|
|
|
when isMainModule:
|
|
var xn = newElement("foobar")
|
|
var pr = xn.toPreserveHook(void)
|
|
assert fromPreserveHook(xn, pr)
|