
`import xml` owns escaping for XML and Atom feed output. The helpers
build elements, attributes, and documents from values, so application
code does not concatenate raw markup.

The `StructuredMarkupStringRule` (`VCS001`) flags string-assembled
markup; this module is what new code should use.

## Elements and attributes

```text
import xml

let attrs = [xml.xml_attr("href", "https://example.com/atom"),
             xml.xml_attr("rel", "self")]
let link = xml.xml_empty_element("link", attrs)
let title = xml.xml_text_element("title", xml.xml_attrs(), "Issue updates")
```

| Helper | Purpose |
|---|---|
| `xml_attr(name, value)` | Build a `XmlAttribute` (value escaped at emit time). |
| `xml_attrs()` | Empty attribute list (cheap constructor). |
| `xml_element(name, attrs, body)` | `<name ...>body</name>`; body is already-built XML. |
| `xml_text_element(name, attrs, value)` | `<name ...>escaped text</name>`. |
| `xml_empty_element(name, attrs)` | `<name ... />` self-closing form. |
| `xml_document(root)` | Prepends the standard XML declaration. |

## Escaping

The escaping helpers are public so handlers that build a custom feed can
still rely on the module's escape rules:

| Helper | Escapes |
|---|---|
| `xml_escape_text(value)` | `&`, `<`, `>` for element text. |
| `xml_escape_attr(value)` | `&`, `<`, `>`, `"` for attribute values. |
| `xml_name_valid(name)` | `False` if the name has whitespace or `<>/"` characters. |

`xml_element` and `xml_empty_element` reject invalid names by returning
an empty string. Treat that as a bug in the caller, not a runtime
fallback: a bad name means the application built a tag from untrusted
data, and the right fix is to validate that input first.

## Feed construction

```text
import xml

let entries = []
for row in rows {
    let title = xml.xml_text_element("title", xml.xml_attrs(), row.title)
    let id = xml.xml_text_element("id", xml.xml_attrs(), row.id)
    let updated = xml.xml_text_element("updated", xml.xml_attrs(), row.updated_at)
    let entry = xml.xml_element("entry", xml.xml_attrs(), title + id + updated)
    entries.append(entry)
}
let feed_body = title_element + link_element + entries.join("")
let feed = xml.xml_element("feed",
    [xml.xml_attr("xmlns", "http://www.w3.org/2005/Atom")],
    feed_body)
return xml.xml_document(feed)
```

## Avoiding string-built markup

Do not concatenate raw `<` and `>` to build elements; the boundary's
markup check rule will flag it, and one missed escape leaks the value as
markup. If the helpers in this module do not cover a tag your feed
needs, add a new helper here and use it - do not work around the rule
locally.
