4 个版本 (重大更新)

0.4.0 2024年6月5日
0.3.0 2024年6月3日
0.2.0 2024年6月2日
0.1.0 2024年6月2日

#2108 in 过程宏

Download history 486/week @ 2024-06-01 22/week @ 2024-06-08 1/week @ 2024-06-15 1/week @ 2024-06-22 33/week @ 2024-07-06 3/week @ 2024-07-13

每月下载量 184 次
用于 nxml-rs

MIT 许可证

18KB
257 行代码(不包括注释)

nxml-rs

Crates.io CI License discord link

nxml-rs 是 Rust 重写NXML

NXML 是一个伪 XML 解析器,可以读取和写入来自 Noita 的格式奇特且经常无效的 XML 文件。NXML(之前链接的 C#/.NET 版本)本身是来自 Poro 的 XML 解析器的移植,Poro 是 Noita 运行的自定义 Falling Everything 引擎所使用的。

简而言之,nxml-rs 是一个与 Noita 解析器完全等价的 XML 解析器。它对 XML 规范的遵循程度与 Noita 本身一样低。它还可以以字符串形式生成语义等效的输出。

此外(仅为了乐趣),nxml-rs 提供了一个 nxml! 宏,允许你通过直接在 Rust 代码中编写几乎类似于 XML 的内容(文本内容需要一些额外的字符,并且通常不支持,Noita 本身也并不真正使用它)来创建 nxml 元素。

因为它是 Rust,所以相对容易实现,解析几乎是零拷贝的 - 唯一的例外是非连续的裸文本,<a>hello<b/>world</a>,这种情况非常罕见,并且是 noita 解析器处理该问题的保留怪癖,通过 Cow 处理。

示例

use nxml_rs::*;

let mut entity = nxml_rs::parse(r#"
    <Entity>
        <LuaComponent
            script_source_file="mods/blah/etc/test.lua"
            execute_every_n_frame="-1">
        </LuaComponent>
        <TestComponent blah="blah" />
    </Entity>
"#).unwrap();

// A little DSL sugar thing, / is alias for .child("name").unwrap(),
// and % is for .attr("name").unwrap()
assert_eq!("-1", &entity / "LuaComponent" % "execute_every_n_frame");

let a_lot = 0;
let speed = 0;

// Make a new element with builder methods
let extra = Element::new("ElectricityComponent")
    .with_attr("energy", a_lot)
    .with_attr("probability_to_heat", "0")
    .with_attr("speed", speed);

// But it's more convenient to use the macro, the builder is rarely ever
// needed (and the following macro expands to above code)
let extra = nxml! {
    <ElectricityComponent energy={a_lot} probability_to_heat="0" {speed} />
};

// Clone everything into owned strings, making it a bit nicer to work with
let mut owned = entity.to_owned();

// A modification - entity.children is just a vec ¯\_(ツ)_/¯
owned.children.insert(1, extra);

// Still has the sugar
assert_eq!("0", &owned / "ElectricityComponent" % "probability_to_heat");

// And can be rendered back into a string (.display() making it pretty-printed)
assert_eq!(owned.display().to_string(), r#"
<Entity>
    <LuaComponent script_source_file="mods/blah/etc/test.lua" execute_every_n_frame="-1"/>
    <ElectricityComponent energy="0" probability_to_heat="0" speed="0"/>
    <TestComponent blah="blah"/>
</Entity>
"#.trim());

// DSL is defined for both of them, and / works with &mut
(&mut owned / "LuaComponent").remove_attr("execute_every_n_frame");
(&mut entity / "LuaComponent").remove_attr("execute_every_n_frame");

entity.children.remove(1);

// The EntityRef can be rendered too
assert_eq!(entity.to_string(), "<Entity><LuaComponent script_source_file=\"mods/blah/etc/test.lua\"/></Entity>");

Cargo 功能

  • indexmap - 使用来自 indexmapcrate 的 IndexMap 替代 HashMap 作为属性。如果你想在输出中保留属性的顺序,这很有用。默认启用,你可以禁用它来去除那个依赖项,如果你不关心属性顺序。

依赖项

~245–680KB
~16K SLoC