Skip to main content

src/internal/converter.gleam

import gleam/list
import gleam/result
import gleam/string
import xmlm.{type Attribute, type InputError, type Tag}

type Tree {
  Element(tag: Tag, children: List(Tree))
  Data(String)
}

pub fn to_lustre(xml: String) -> Result(String, String) {
  xml
  |> xmlm.from_string
  |> xmlm.with_stripping(True)
  |> xmlm.with_encoding(xmlm.Utf8)
  |> string_to_tree()
  |> result.map(tree_to_lustre_string(_, True))
  |> result.map_error(xmlm.input_error_to_string)
}

fn string_to_tree(input: xmlm.Input) -> Result(Tree, InputError) {
  use #(_dtd, tree, _input) <- result.try(xmlm.document_tree(
    input,
    Element,
    Data,
  ))

  Ok(tree)
}

fn tree_to_lustre_string(tree: Tree, root: Bool) -> String {
  case tree {
    Element(tag:, ..) as element ->
      "svg."
      <> tag.name.local
      <> "("
      <> attributes_to_lustre_string(tag.attributes, root)
      <> maybe_add_children(element)
      <> ")"
    Data(data) -> data
  }
}

fn maybe_add_children(element) {
  let assert Element(tag:, children:) = element
  case tag.name.local {
    "a"
    | "defs"
    | "g"
    | "marker"
    | "mask"
    | "missing_glyph"
    | "pattern"
    | "svg"
    | "switch"
    | "symbol"
    | "view"
    | "desc"
    | "metadata"
    | "title"
    | "filter"
    | "feDiffuseLighting"
    | "feMerge"
    | "feSpecularLighting"
    | "feTile"
    | "linearGradient"
    | "radialGradient"
    | "clipPath"
    | "foreignObject"
    | "textPath"
    | "tspan" ->
      ", ["
      <> string.join(list.map(children, tree_to_lustre_string(_, False)), ", ")
      <> "]"
    "text" | "script" | "style" ->
      ", "
      <> string.join(list.map(children, tree_to_lustre_string(_, False)), "")
    _ -> ""
  }
}

fn attributes_to_lustre_string(
  attributes: List(Attribute),
  root: Bool,
) -> String {
  let lustre_string =
    "["
    <> string.join(
      list.map(attributes, fn(attribute) {
        "attribute(\""
        <> attribute.name.local
        <> "\", \""
        <> attribute.value
        <> "\")"
      }),
      ", ",
    )
    <> "]"

  case root {
    False -> lustre_string
    True -> "add_defaults(attributes, " <> lustre_string <> ")"
  }
}