Xml2xpathobject

use "raw/"
use "debug"

use @pony_triggergc[None](ptr: Pointer[None])
use @pony_ctx[Pointer[None]]()

type Xml2XPathResult is (None | Array[Xml2Node] | Bool | String val | F64)

primitive Xml2XPathObject
  """
  Factory for converting a raw libxml2 `xmlXPathObject*` into a high-level
  `Xml2XPathResult` (nodes, boolean, number, or string) usable from Pony.
  """
  fun apply(
    xml2doc: Xml2Doc tag,
    ptrx: NullablePointer[XmlXPathObject])
    : Xml2XPathResult
  =>
    """
    Wrap a nullable `xmlXPathObject*` and return an `Xml2XPathResult`
    matching the XPath result type.

    Behaviour:

    - If `ptrx` is `None` or cannot be dereferenced, returns `None`.
    - If the XPath type is `nodeset` and `nodesetval` is non-null, converts
      it into an `Array[Xml2Node]`, one wrapper per node in the set.
    - If the type is `boolean`, returns a Pony `Bool` (`true` when
      `boolval == 1`, otherwise `false`).
    - If the type is `number`, returns a Pony `F64` from `floatval`.
    - If the type is `string`, returns an immutable Pony `String` cloned
      from `stringval`.
    - For undefined, point, range, location-set, user, or XSLT-tree types,
      returns `None` (currently unsupported).

    This function does not take ownership of the underlying `xmlXPathObject*`
    lifetime; it only interprets its contents and wraps them into Pony data
    structures.
    """
    try
      let ptr: XmlXPathObject = ptrx.apply()?
      match ptr.xmltype
      | XPathTypeUndefined() => return None
      | XPathTypeNodeset() =>
        if ptr.nodesetval.is_none() then
          return None
        else
          let nodearray: Array[Xml2Node] = Array[Xml2Node]
          let nodeset': NullablePointer[XmlNodeSet] = ptr.nodesetval
          let nodeset: XmlNodeSet = nodeset'.apply()?
          let nodearray': Array[NullablePointer[XmlNode]] =
            Array[NullablePointer[XmlNode]].from_cpointer(
              nodeset.nodeTab, nodeset.nodeNr.usize())

          for f in nodearray'.values() do
            nodearray.push(Xml2Node.fromPTR(xml2doc, f)?)
          end
          return nodearray
        end
      | XPathTypeBoolean() => return (ptr.boolval == 1)
      | XPathTypeNumber() => return ptr.floatval
      | XPathTypeString() =>
        return String.from_cstring(ptr.stringval).clone()
      | XPathTypePoint() => return None // As yet unsupported
      | XPathTypeRange() => return None // As yet unsupported
      | XPathTypeLocationSet() => return None // As yet unsupported
      | XPathTypeUsers() => return None // As yet unsupported
      | XPathTypeXsltTree() => return None // As yet unsupported
      else
        return None
      end
    else
      return None
    end