#html-xml #svg #xml #markup #html #tags

tagu

以编程方式编写SVG / HTML / XML

1 个不稳定版本

0.1.6 2023年7月11日
0.1.5 2023年7月9日

#22 in 数据格式

Download history 906/week @ 2024-03-13 413/week @ 2024-03-20 555/week @ 2024-03-27 595/week @ 2024-04-03 299/week @ 2024-04-10 662/week @ 2024-04-17 379/week @ 2024-04-24 354/week @ 2024-05-01 957/week @ 2024-05-08 810/week @ 2024-05-15 881/week @ 2024-05-22 481/week @ 2024-05-29 713/week @ 2024-06-05 543/week @ 2024-06-12 616/week @ 2024-06-19 678/week @ 2024-06-26

2,660 每月下载量
用于 3 crates

MIT 许可证

56KB
1.5K SLoC

通过链式结构体或闭包以编程方式构建xml / html / svg。不使用模板引擎,编写看起来像rust的数据/标记。用户可以通过inline()函数控制格式。

您可以在githubcrates.io上找到tagu。文档在docs.rs

适配器示例

use tagu::build;
use tagu::prelude::*;

fn main() -> std::fmt::Result {
    let a = build::elem("a");
    let b = build::elem("b");
    let c = build::elem("c");
    let it = build::from_iter((0..5).map(|i| build::elem(format_move!("x{}", i)).inline()));
    let all = a.append(b.append(c.append(it)));

    tagu::render(all, tagu::stdout_fmt())
}

输出文本

<a>
    <b>
        <c>
            <x0></x0>
            <x1></x1>
            <x2></x2>
            <x3></x3>
            <x4></x4>
        </c>
    </b>
</a>

堆栈示例

use tagu::build;
use tagu::prelude::*;

fn main() -> std::fmt::Result {
    let all = build::from_stack(|stack| {
        let a = build::elem("a");
        let b = build::elem("b");
        let c = build::elem("c").with_tab("");

        let mut stack = stack.push(a)?.push(b)?.push(c)?;

        for i in 0..5 {
            let e = build::elem(format_move!("x{}", i)).inline();
            stack.put(e)?;
        }
        stack.pop()?.pop()?.pop()
    });

    tagu::render(all.with_tab(" "), tagu::stdout_fmt())
}

输出文本

<a>
 <b>
→→<c>
→→→<x0></x0>
→→→<x1></x1>
→→→<x2></x2>
→→→<x3></x3>
→→→<x4></x4>
→→</c>
 </b>
</a>

适配器2示例

use tagu::build;
use tagu::prelude::*;

fn main() -> std::fmt::Result {
    let all = build::elem("a").append_with(|| {
        elems!(
            build::single("test"),
            build::elem("b").append_with(|| {
                let it =
                    build::from_iter((0..5).map(|i| build::elem(format_move!("x{}", i)).inline()));

                build::elem("c").append_with(|| it)
            }),
            build::elem("bbbb").append_with(|| {
                elems!(
                    tagu::util::comment("this is comment"),
                    build::single("k").with(("apple", 5))
                )
            })
        )
    });

    tagu::render(all, tagu::stdout_fmt())
}

输出

<a>
    <test/>
    <b>
        <c>
            <x0></x0>
            <x1></x1>
            <x2></x2>
            <x3></x3>
            <x4></x4>
        </c>
    </b>
    <bbbb>
        <!--this is comment-->
        <k apple="5"/>
    </bbbb>
</a>

SVG示例

use tagu::build;
use tagu::prelude::*;

fn main() -> std::fmt::Result {
    let width = 100.0;
    let height = 100.0;

    let rect = build::single("rect").with(attrs!(
        ("x1", 0),
        ("y1", 0),
        ("rx", 20),
        ("ry", 20),
        ("width", width),
        ("height", height),
        ("style", "fill:blue")
    ));

    let style = build::elem("style")
        .inline()
        .append(build::raw(".test{fill:none;stroke:white;stroke-width:3}"));

    let svg = build::elem("svg").with(attrs!(
        ("xmlns", "http://www.w3.org/2000/svg"),
        ("viewBox", format_move!("0 0 {} {}", width, height))
    ));

    let rows = build::from_stack(|mut f| {
        for r in (0..50).step_by(5) {
            if r % 10 == 0 {
                let c = build::single("circle").with(attrs!(("cx", 50.0), ("cy", 50.0), ("r", r)));
                f.put(c)?;
            } else {
                let r = build::single("rect").with(attrs!(
                    ("x", 50 - r),
                    ("y", 50 - r),
                    ("width", r * 2),
                    ("height", r * 2)
                ));
                f.put(r)?;
            }
        }
        Ok(f)
    });

    let table = build::elem("g").with(("class", "test")).append(rows);

    let all = svg.append(style).append(rect).append(table);

    tagu::render(all, tagu::stdout_fmt())
}

输出

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
    <style>.test{fill:none;stroke:white;stroke-width:3}</style>
    <rect x1="0" y1="0" rx="20" ry="20" width="100" height="100" style="fill:blue"/>
    <g class="test">
            <circle cx="50" cy="50" r="0"/>
            <rect x="45" y="45" width="10" height="10"/>
            <circle cx="50" cy="50" r="10"/>
            <rect x="35" y="35" width="30" height="30"/>
            <circle cx="50" cy="50" r="20"/>
            <rect x="25" y="25" width="50" height="50"/>
            <circle cx="50" cy="50" r="30"/>
            <rect x="15" y="15" width="70" height="70"/>
            <circle cx="50" cy="50" r="40"/>
            <rect x="5" y="5" width="90" height="90"/>
    </g>
</svg>

查看其他示例输出https://github.com/tiby312/tagu/tree/main/assets

使用哪种方法?

您可以通过构建长适配器链来追加元素,或者您可以在动态渲染元素时将其渲染到写入器。在链式操作中,您不需要担心处理错误,因为在链式操作过程中实际上没有任何内容被写入。然而,您通常需要“颠倒”构建东西。您必须首先构建最嵌套的元素,然后才能将其附加到更大的元素中。在动态渲染中,您确实需要处理错误,但元素的处理顺序与它们被渲染的顺序相匹配。您可以混合使用,因为您可以从闭包创建元素,然后将这些元素链接在一起。

内联函数

默认情况下,标签插入换行符和制表符。如果您在一个元素上调用inline(),那么它内部的所有元素都将内联。

是否有转义XML保护?

属性通过转义保护器进行传递。标签名称通过转义保护器进行传递。用户可以使用 raw_escpapable()from_stack_escapable() 函数来绕过这一限制。这将返回唯一不实现 elem::Locked 的元素类型。 render() 需要链式元素实现 Locked。如果用户链入原始元素,则整个链将不会实现 Locked。相反,用户将不得不使用 render_escapable()。元素链式系统通过让每个元素实现一个 render_head() 和一个 render_tail() 函数来工作。

标签器包发生了什么?

我让标签器包保持原样,并创建了一个全新的包,因为虽然它具有标签器的所有功能,但它更加复杂。有些人可能更喜欢标签器的简单性。然而,我建议人们选择 tagu 而不是 tagger,因为它在我看来更加灵活。能够在我的经验中将元素链像结构体一样传递的能力非常有用。

无运行时依赖