inline-xml 0.3.2

Embed XML data directly in your Rust code
Documentation
// Copyright (C) 2023 Benjamin Stürz
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
use crate as inline_xml;
use crate::*;

#[test]
fn simple() {
    let tree = xml_tag! {
        <html>
            <body>
                <h1 align="center">Hello World</h1>
                <p>This is an example.</p>
            </body>
        </html>
    };

    let tree2 = Tag {
        name: "html".into(),
        attrs: vec![],
        inner: Some(Xml(vec![
            Content::Tag(Tag {
                name: "body".into(),
                attrs: vec![],
                inner: Some(Xml(vec![
                    Content::Tag(Tag {
                        name: "h1".into(),
                        attrs: vec![
                            Attr {
                                name: "align".into(),
                                value: "center".into(),
                            }
                        ],
                        inner: Some(Xml(vec![
                            Content::Word("Hello".into()),
                            Content::Word("World".into()),
                        ])),
                    }),
                    Content::Tag(Tag {
                        name: "p".into(),
                        attrs: vec![],
                        inner: Some(Xml(vec![
                            Content::Word("This".into()),
                            Content::Word("is".into()),
                            Content::Word("an".into()),
                            Content::Word("example".into()),
                            Content::Word(".".into()),
                        ]))
                    })
                ]))
            })
        ]))
    };

    assert_eq!(tree, tree2)
}

#[test]
fn dynamic_attr() {
    let f = || "center";
    let tree = xml_tag! {
        <p align={f()} />
    };

    let tree2 = Tag {
        name: "p".into(),
        attrs: vec! [
            Attr {
                name: "align".into(),
                value: "center".into(),
            }
        ],
        inner: None,
    };

    assert_eq!(tree, tree2)
}

#[test]
fn dynamic() {
    let f = || "Hello <code>World";
    let tree1 = xml_tag! {
        <p>
        {
            f()
        }
        </p>
    };
    let tree2 = Tag {
        name: "p".into(),
        attrs: vec![],
        inner: Some(Xml(vec![
            Content::Word("Hello &lt;code&gt;World".into()),
        ]))
    };

    assert_eq!(tree1, tree2);
}

#[test]
fn reference() {
    let x = 42;
    let xr = &x;
    let tree1 = xml_tag! {
        <p>{xr}</p>
    };

    let tree2 = Tag {
        name: "p".into(),
        attrs: vec![],
        inner: Some(Xml(vec![
            Content::Word("42".into())
        ])),
    };

    assert_eq!(tree1, tree2);
}

#[test]
fn option() {
    let x = Some(42);
    let y: Option<u32> = None;

    let tree1 = xml! {
        <p>{x}</p>
        <p>{y}</p>
    };
    let tree2 = Xml(vec! [
        Content::Tag(Tag {
            name: "p".into(),
            attrs: vec![],
            inner: Some(Xml(vec![
                Content::Word("42".into())
            ])),
        }),
        Content::Tag(Tag {
            name: "p".into(),
            attrs: vec![],
            inner: Some(Xml(vec![])),
        }),
    ]);

    assert_eq!(tree1, tree2);
}

#[test]
fn collect_xml() {
    let v = [1, 2, 3];
    let tree1 = xml_tag! {
        <root>
        {v.into_iter().map(|x| xml! { <p>{x}</p> }).collect_xml()}
        </root>
    };
    let tree2 = Tag {
        name: "root".into(),
        attrs: vec![],
        inner: Some(Xml(vec![
            Content::Tag(Tag {
                name: "p".into(),
                attrs: vec![],
                inner: Some(Xml(vec![Content::Word("1".into())]))
            }),
            Content::Tag(Tag {
                name: "p".into(),
                attrs: vec![],
                inner: Some(Xml(vec![Content::Word("2".into())]))
            }),
            Content::Tag(Tag {
                name: "p".into(),
                attrs: vec![],
                inner: Some(Xml(vec![Content::Word("3".into())]))
            }),
        ]))
    };

    assert_eq!(tree1, tree2);
}


#[test]
fn number_attr_value() {
    let tag = xml_tag! {
        <x value={42} />
    };
    assert_eq!(tag.attrs[0].value, "42");
}

#[test]
fn nonstring_attr_value() {
    struct S;
    impl ToString for S {
        fn to_string(&self) -> String { "S".to_string() }
    }

    let tag = xml_tag! {
        <x value={S} />
    };
    assert_eq!(tag.attrs[0].value, "S");
}

#[test]
fn attr_xml_injection() {
    let malstr = r#"" /><tag2 x=""#;
    let tag = xml_tag! {
        <tag value={malstr} />
    };

    let expected = r#"<tag value="\" /><tag2 x=\"" />"#;
    assert_eq!(tag.to_string(), expected);
}

#[test]
fn attr_backspace_escape() {
    let malstr = "\\";
    let tag = xml_tag! {
        <tag value={malstr} />
    };

    let expected = r#"<tag value="\\" />"#;
    assert_eq!(tag.to_string(), expected);
}

#[test]
fn float() {
    let tag = xml_tag! {
        <value>1.1</value>
    };

    let tag2 = Tag {
        name: "value".into(),
        attrs: vec![],
        inner: Some(Xml(vec![Content::Word("1.1".into())])),
    };

    assert_eq!(tag, tag2);
}

#[test]
fn namespace() {
    let tag = xml_tag! {
        <ns:tag />
    };

    let tag2 = Tag {
        name: "ns:tag".into(),
        attrs: vec![],
        inner: None,
    };

    assert_eq!(tag, tag2);
}

#[test]
fn namespace_attr() {
    let tag = xml_tag! {
        <tag ns:attr="42" />
    };

    let tag2 = Tag {
        name: "tag".into(),
        attrs: vec![
            Attr {
                name: "ns:attr".into(),
                value: "42".into(),
            },
        ],
        inner: None,
    };

    assert_eq!(tag, tag2);
}

#[test]
fn document() {
    let xml = xml! {
        <html>
            <h1>"Example Document"</h1>
            <p>"This is some example text."</p>
        </html>
    };
    let doc = xml
        .into_document()
        .expect("Failed to construct document.")
        .with_xml_version("1.0")
        .with_xml_encoding("UTF-8")
        .with_doctype("html");

    let expected = r#"<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html>
<html>
    <h1>Example Document</h1>
    <p>This is some example text.</p>
</html>"#;
    assert_eq!(doc.to_string(), expected);
}

#[test]
fn empty_document() {
    let xml = xml! { <root /> };

    let doc = xml.into_document().expect("Failed to construct document.");
    let expected = "<root />";
    assert_eq!(doc.to_string(), expected);
}

#[test]
fn minus_in_tag() {
    let tag = xml_tag! {
        <name-with-minus />
    };

    let tag2 = Tag {
        name: "name-with-minus".into(),
        attrs: vec![],
        inner: None,
    };

    assert_eq!(tag, tag2);
}

#[test]
fn html_meta() {
    let tag = xml_tag! {
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    };

    let tag2 = Tag {
        name: "meta".into(),
        attrs: vec![
            Attr {
                name: "http-equiv".into(),
                value: "Content-Type".into(),
            },
            Attr {
                name: "content".into(),
                value: "text/html; charset=utf-8".into(),
            },
        ],
        inner: None,
    };

    assert_eq!(tag, tag2);
}