24 个版本

0.0.25 2024年6月24日
0.0.24 2024年6月23日
0.0.22 2024年5月29日
0.0.21 2024年3月20日
0.0.14 2023年3月20日

284 每月下载量

31 每月下载次数
用于 toolproof

MIT/Apache

245KB
4K SLoC

非破坏性

github crates.io docs.rs build status

非破坏性编辑尝试尽可能多地保留现有结构,同时允许在原地修改文档。

该项目目前尚未完成!

有关如何使用 YAML 支持的详细信息,请参阅相应的模块


与文档一起工作

文档被反序列化为一个大型对象,其中所有访问和修改都必须发生。这使得 API 比典型的序列化库更难使用,但这是确保可以访问所有必要数据进行非破坏性编辑所必需的。

一个特殊的问题是,每个提供对文档可变访问的方法都需要有两个变体: as_<something>_mutinto_<something>_mut。如果我们查看 ValueMut::as_mapping_mutValueMut::into_mapping_mut 的差异,后者消耗 self 并返回一个与其相关联的生命周期的 ValueMut


use anyhow::Context;
use nondestructive::yaml;

let mut doc = yaml::from_slice(
    r"
    greeting: Hello World!
    "
)?;

// Access through the document:
assert_eq!(
    doc.as_ref().as_mapping().and_then(|m| m.get("greeting")?.as_str()),
    Some("Hello World!")
);

// Mutation through the document:
let mut mapping = doc.as_mut().into_mapping_mut().context("missing root mapping")?;
mapping.insert_str("greeting2", "Hello Rust!");

assert_eq!(
    doc.to_string(),
    r"
    greeting: Hello World!
    greeting2: Hello Rust!
    "
);

如果我们更改使用 into_mapping_mut 的行,我们将获得一个生命周期错误


error[E0716]: temporary value dropped while borrowed
  --> src\lib.rs:43:19
   |
20 | let mut mapping = doc.as_mut().as_mapping_mut().context("missing root mapping")?;
   |                   ^^^^^^^^^^^^                                                  - temporary value is freed at the end of this statement
   |                   |
   |                   creates a temporary value which is freed while still in use
21 | mapping.insert_str("greeting2", "Hello Rust!");
   | ---------------------------------------------- borrow later used here
   |
help: consider using a `let` binding to create a longer lived value
   |
20 + let binding = doc.as_mut();
21 ~ let mut mapping = binding.as_mapping_mut().context("missing root mapping")?;
   |

我们可以按照建议将其分配给局部变量,但是如果我们使用类似 Option::and_then 的组合器,则不可能做到这一点。


与文档一起工作的另一个重要方面是我们可以通过 标识符 全局 地处理值。这使得在应用它们之前存储所需的修改变得更容易。

use anyhow::Context;
use nondestructive::yaml;

let mut doc = yaml::from_slice(
    r"
    - 10
    - 24
    - 30
    "
)?;

let mut edits = Vec::new();

// Build a collection of edits:
for value in doc.as_ref().as_sequence().context("missing sequence")? {
    let Some(n) = value.as_u32() else {
        continue;
    };

    if n % 10 == 0 {
        edits.push((value.id(), n / 10));
    }
}

// Apply stored edits:
for (id, new_number) in edits {
    doc.value_mut(id).set_u32(new_number);
}

assert_eq!(
    doc.to_string(),
    r"
    - 1
    - 24
    - 3
    "
);

示例

这提供了对可用 API 的更广泛视图,以及访问器和修改器之间的差异。


use anyhow::Context;
use nondestructive::yaml;

let mut doc = yaml::from_slice(
    r"
    name: Descartes
    country: Grece
    "
)?;

let mapping = doc.as_ref().as_mapping().context("missing mapping")?;
let name = mapping.get("name").context("missing name")?;
assert_eq!(name.as_str(), Some("Descartes"));

let mut mapping = doc.as_mut().into_mapping_mut().context("missing mapping")?;
let mut name = mapping.get_mut("name").context("missing name")?;
name.set_string("Plato");

let mut country = mapping.get_mut("country").context("missing country")?;
country.set_string("Greece");

assert_eq!(
    doc.to_string(),
    r"
    name: Plato
    country: Greece
    "
);

依赖关系

~1.5MB
~25K ~25K SLoC