我的结构如下所示:
#[derive(Serialize, Deserialize)]
pub struct TestItem {
pub a: String,
pub b: String,
}
#[derive(Serialize, Deserialize)]
pub struct Test {
pub items: Vec<TestItem>,
}
InlineTable
来序列化它,因为它更具可读性,但是 toml
板条箱默认为常规表格。例如我想要:
items = [
{ a = "testa1", b = "testb1" },
# etc
]
但是
toml::to_string(...)
给出
[[items]]
a = "testa1"
b = "testb1"
#etc
这似乎是一个相当常见的请求,例如,请参阅 toml#592,并且 toml_edit
crate中有一个 示例,在这种情况下似乎不起作用,因为
items
是一个 ArrayOfTables
.
我尝试过以下访客
impl VisitMut for TestVisitor {
fn visit_table_mut(&mut self, node: &mut Table) {
visit_table_mut(self, node);
node.set_implicit(true);
}
fn visit_table_like_kv_mut(&mut self, mut key: KeyMut<'_>, node: &mut Item) {
println!("This is hit: {:?} -> {node:?}", key.get());
if let Item::Table(table) = node {
println!("This is never hit, as its an ArrayOfTables");
let table = std::mem::replace(table, Table::new());
let inline_table = table.into_inline_table();
key.fmt();
*node = Item::Value(Value::InlineTable(inline_table));
}
// Recurse further into the document tree.
visit_table_like_kv_mut(self, key, node);
}
fn visit_array_mut(&mut self, node: &mut Array) {
node.fmt();
}
}
但这似乎跳过了
Table
内的ArrayOfTables
,所以if
永远不会被击中。输出是:
This is hit: "items" -> ArrayOfTables(...)
This is hit: "a" -> Value(String(...)
// etc - the tables are never visited
我将如何构建一个可以将
items
中的任何表转换为 InlineTable
的访问者?似乎 VisitMut
中的所有其他方法都采用 Table
,所以我无法将它们转变为 InlineTable
如示例所要求。
好的,所以看看这两种不同的 TOML 格式实际上以不同的方式解析。
[[items]]
a = "testa1"
b = "testb1"
解析为
Item::ArrayOfTables
,仅接受 Table
作为其项目,而:
items = [
{ a = "testa1", b = "testb1" },
# etc
]
解析为
Item::Value(Value::Array(...))
,其中元素为 Value::InlineTable
。这意味着像下面这样的访客似乎会成功。
struct TestVisitor;
// I could be wrong here, but it seems I have to use the `kv` variant here
// because `visit_table_like_mut` has `node: &mut dyn toml_edit::TableLike`
// as the second argument, which `ArrayOfTables` doesn't seem to impl
impl VisitMut for TestVisitor {
fn visit_table_like_kv_mut(&mut self, key: KeyMut<'_>, node: &mut Item) {
if let Item::ArrayOfTables(tables) = node {
let new_tables = tables
.iter_mut()
.map(|t| {
let table = std::mem::replace(t, Table::new());
Value::InlineTable(table.into_inline_table())
})
.collect::<Vec<_>>();
*node = Item::Value(Value::Array(Array::from_iter(new_tables.iter())));
}
// Recurse further into the document tree.
visit_table_like_kv_mut(self, key, node);
}
}
我可以这样使用:
impl Test {
pub fn to_toml(&self) -> anyhow::Result<String> {
let raw_string = toml::to_string(self)?;
let mut doc = raw_string.parse::<DocumentMut>()?;
let mut visitor = TestVisitor;
visitor.visit_document_mut(&mut doc);
Ok(doc.to_string())
}
}