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 <> ")"
}
}