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 日

#2549解析器实现

Download history 489/week @ 2024-06-02 7/week @ 2024-06-09 1/week @ 2024-06-16 18/week @ 2024-07-07

每月 179 次下载

MIT 许可证

38KB
640 代码行

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 解析器 100% 等价的 XML 解析器。它与 Noita 本身一样不遵守 XML 规范。它还可以以字符串的形式产生语义等效的输出。

另外(仅为了好玩),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 - 使用来自 indexmap crate 的 IndexMap 而不是 HashMap 作为属性。如果你想在输出中保留属性的顺序,这很有用。默认启用,你可以禁用它来摆脱那个依赖项,如果你不在乎属性顺序。

依赖项

~1–1.6MB
~31K SLoC