macro_rules! xfmt {
    (move $($tt:tt)*) => { ... };
    ($($tt:tt)*) => { ... };
}
Expand description

Xml-like formatting syntax.

Returns a displayable object which can be formatted with {}.

Examples

Basic usage

let point = (20, 30);
let name = "World";

format_xml::xfmt! {
	<svg width="200" height="200">
		<line x1="0" y1="0" x2={point.0} y2={point.1} stroke="black" stroke-width="2" />
		<text x={point.1} y={point.0}>"Hello '"{name}"'!"</text>
	</svg>
}

The resulting string is <svg width="200" height="200"><line x1="0" y1="0" x2="20" y2="30" stroke="black" stroke-width="2" /><text x="30" y="20">Hello 'World!'</text></svg>.

The value arguments can be arbitrary expressions. They are inlined in the formatting braces and are outside the string literals.

The values inside formatting braces are escaped by default, the text literals are not. Use the escape hatch to bypass automatic escaping.

Formatting specifiers

let value = 42;

format_xml::xfmt! {
	<span data-value={value}>{value:#x?}</span>
}

The resulting string is <span data-value="42">0x2a</span>.

The rules for the specifiers are exactly the same as Rust’s standard formatting syntax.

Escaping

let value = "\"quote\"";
let text = "<script>&</script>";
format_xml::xfmt! {
	<p data-value={value}>{text}</p>
}

The resulting string is <p data-value="&quot;quote&quot;">&lt;script&gt;&amp;&lt;/script&gt;</p>.

The values inside formatting braces are escaped by default, the text literals are not.

  • Text elements escape <, &, >.
  • Attribute values escape <, &, >, ', ".
  • Comment nodes escape -- by removing it altogether.
  • CDATA sections escape ]]>.

Escaping is not implemented in some HTML contexts: inside <script>, <style> tags or their respective attribute equivalents (event handlers and inline styles), do not format user controlled values in these locations!

Supported syntax

format_xml::xfmt! {
	<!doctype html>
	<?xml version="1.0" encoding="UTF-8"?>
	<tag-name></tag-name>
	<self-closing-tag />
	<!-- "comment" -->
	<![CDATA["cdata"]]>
}

The resulting string is <!doctype html><?xml version="1.0" encoding="UTF-8"?><tag-name></tag-name><self-closing-tag /><!-- comment --><![CDATA[cdata]]>.

Examples of element naming and namespace syntax support:

format_xml::xfmt! {
	<tag>
	<tag-foo>
	<tag.foo>
	<ns:tag>
	<"_t-0.z">
}

The resulting string is <tag><tag-foo><tag.foo><ns:tag><_t-0.z>.

There are no restrictions on matching open/close tags or reject tags which cannot be self-closing.

Unfinished implementation:

  • Document type definitions (DTD) are not correctly implemented. The <!doctype> tag is barely functional.
  • Processing instructions are not correctly implemented. The <?xml?> tag is barely functional.

Control flow

let switch = true;
let opt = Some("World");

format_xml::xfmt! {
	if let Some(name) = (opt) {
		<h1>"Hello "{name}</h1>
	}
	else if (switch) {
		<h1>"Hello User"</h1>
	}
}

The resulting string is <h1>Hello World</h1>.

let result: Result<f32, i32> = Err(13);

format_xml::xfmt! {
	match result {
		Ok(f) => <i>{f}</i>,
		Err(i) => <b>{i}</b>,
	}
}

The resulting string is <b>13</b>.

format_xml::xfmt! {
	<ul>
	for i in (1..=5) {
		let times_five = i * 5;
		<li>{i}"*5="{times_five}</li>
	}
	</ul>
}

The resulting string is <ul><li>1*5=5</li><li>2*5=10</li><li>3*5=15</li><li>4*5=20</li><li>5*5=25</li></ul>.

Control flow is only supported outside tags, not in attributes.

Escape hatch

fn compose(f: &mut std::fmt::Formatter, a: i32) -> std::fmt::Result {
	format_xml::write!(f, <span>{a}</span>)
}

format_xml::xfmt! {
	<p>|f| compose(f, 42)?;</p>
}

The resulting string is <p><span>42</span></p>.

Closure syntax provides an escape hatch to inject code if needed. The argument’s type is &mut Formatter.

Important! Anything written to the formatter f is not escaped. This makes it useful to compose different components wich is not possible with {}.