Add frontmatter, markdown, path, xml, rss, and web packages
Sync local packages into the registry repo and update index.json and README.md to include all 9 packages.
This commit is contained in:
109
packages/xml/lib.lux
Normal file
109
packages/xml/lib.lux
Normal file
@@ -0,0 +1,109 @@
|
||||
// xml - XML builder for Lux
|
||||
//
|
||||
// Provides a simple API for constructing well-formed XML documents.
|
||||
// Designed for generating RSS feeds, sitemaps, and other XML output.
|
||||
|
||||
// An XML attribute (key-value pair)
|
||||
type Attr =
|
||||
| Attr(String, String)
|
||||
|
||||
// An XML node: element with tag, attributes, and children, or text content
|
||||
type Node =
|
||||
| Element(String, List<Attr>, List<Node>)
|
||||
| Text(String)
|
||||
| CData(String)
|
||||
| Raw(String)
|
||||
|
||||
// Escape special XML characters in text content
|
||||
pub fn escape(s: String): String =
|
||||
String.replace(
|
||||
String.replace(
|
||||
String.replace(
|
||||
String.replace(
|
||||
String.replace(s, "&", "&"),
|
||||
"<", "<"),
|
||||
">", ">"),
|
||||
"\"", """),
|
||||
"'", "'")
|
||||
|
||||
// Create a text node
|
||||
pub fn text(content: String): Node = Text(content)
|
||||
|
||||
// Create a CDATA section
|
||||
pub fn cdata(content: String): Node = CData(content)
|
||||
|
||||
// Create a raw (unescaped) node
|
||||
pub fn raw(content: String): Node = Raw(content)
|
||||
|
||||
// Create an element with tag, attributes, and children
|
||||
pub fn element(tag: String, attrs: List<Attr>, children: List<Node>): Node =
|
||||
Element(tag, attrs, children)
|
||||
|
||||
// Create an element with no attributes
|
||||
pub fn el(tag: String, children: List<Node>): Node =
|
||||
Element(tag, [], children)
|
||||
|
||||
// Create a self-closing element with attributes and no children
|
||||
pub fn selfClosing(tag: String, attrs: List<Attr>): Node =
|
||||
Element(tag, attrs, [])
|
||||
|
||||
// Create an attribute
|
||||
pub fn attr(key: String, value: String): Attr =
|
||||
Attr(key, value)
|
||||
|
||||
// Create an element with text content
|
||||
pub fn textEl(tag: String, content: String): Node =
|
||||
Element(tag, [], [Text(content)])
|
||||
|
||||
// Create an element with attributes and text content
|
||||
pub fn textElAttr(tag: String, attrs: List<Attr>, content: String): Node =
|
||||
Element(tag, attrs, [Text(content)])
|
||||
|
||||
// Render a single attribute to string
|
||||
fn renderAttr(a: Attr): String =
|
||||
match a {
|
||||
Attr(k, v) => " " + k + "=\"" + escape(v) + "\"",
|
||||
}
|
||||
|
||||
// Render a list of attributes to string
|
||||
fn renderAttrs(attrs: List<Attr>): String =
|
||||
match List.head(attrs) {
|
||||
None => "",
|
||||
Some(a) => renderAttr(a) + match List.tail(attrs) {
|
||||
Some(rest) => renderAttrs(rest),
|
||||
None => "",
|
||||
},
|
||||
}
|
||||
|
||||
// Render child nodes to string
|
||||
fn renderChildren(nodes: List<Node>): String =
|
||||
match List.head(nodes) {
|
||||
None => "",
|
||||
Some(node) => render(node) + match List.tail(nodes) {
|
||||
Some(rest) => renderChildren(rest),
|
||||
None => "",
|
||||
},
|
||||
}
|
||||
|
||||
// Render a node to an XML string
|
||||
pub fn render(node: Node): String =
|
||||
match node {
|
||||
Element(tag, attrs, children) => {
|
||||
let attrStr = renderAttrs(attrs)
|
||||
if List.length(children) == 0 then
|
||||
"<" + tag + attrStr + "/>"
|
||||
else
|
||||
"<" + tag + attrStr + ">" + renderChildren(children) + "</" + tag + ">"
|
||||
},
|
||||
Text(content) => escape(content),
|
||||
CData(content) => "<![CDATA[" + content + "]]>",
|
||||
Raw(content) => content,
|
||||
}
|
||||
|
||||
// Render a complete XML document with declaration
|
||||
pub fn document(root: Node): String =
|
||||
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + render(root)
|
||||
|
||||
// Render with a custom declaration (e.g., for RSS stylesheets)
|
||||
pub fn documentWithDecl(decl: String, root: Node): String =
|
||||
decl + "\n" + render(root)
|
||||
Reference in New Issue
Block a user