toml-rs / toml

Rust TOML Parser
https://docs.rs/toml
Apache License 2.0
730 stars 107 forks source link

Replacing an `InlineTable` with a comment with a `Table` results in invalid syntax #691

Open nrabulinski opened 8 months ago

nrabulinski commented 8 months ago
use std::str::FromStr;

use toml_edit::{Document, Item, Table};

fn main() {
    let mut val = Document::from_str(
        r#"
    # hello i'm a comment
    foo = { bar = 1 }
    "#,
    )
    .unwrap();
    println!("before: {}", val.to_string());
    let mut new_foo = Table::new();
    new_foo.insert("bar", Item::Value(1.into()));
    new_foo.insert("baz", Item::Value(2.into()));
    *val.get_mut("foo").unwrap() = Item::Table(new_foo);
    println!("after: {}", val.to_string());
}

Expected output:

before:
    # hello i'm a comment
    foo = { bar = 1 }

after:
    # hello i'm a comment
    [foo]
    bar = 1
    baz = 2

Actual output:

before:
    # hello i'm a comment
    foo = { bar = 1 }

after: [
    # hello i'm a comment
    foo ]
bar = 1
baz = 2
nrabulinski commented 8 months ago

I tested it on multiple version and it seems like the bug has been here since forever, but I couldn't find any issue which would reference it

epage commented 8 months ago

This is another way of accomplishing the end result as in #267.

eemed commented 3 months ago

For others searching for a workaround: I set the comment to the table value instead of the key. Here I'm using the visitor while adding doc strings to the serialized toml.

impl VisitMut for Formatter {
  fn visit_table_like_kv_mut(&mut self, mut key: KeyMut<'_>, node: &mut Item) {
        if node.is_inline_table() {
            let item = std::mem::replace(node, Item::None);
            if let Ok(table) = item.into_table() {
                *node = Item::Table(table);
            }
        }
        ...

        if let Ok(doc) = doc {
            let mut comment = String::from("\n");
            for line in doc.lines() {
                let line = format!("# {line}\n");
                comment.push_str(&line);
            }

            match node {
                Item::Table(table) => {
                    // Set the comment to the table instead of key
                    let decor = table.decor_mut();
                    decor.set_prefix(comment);
                }
                _ => {
                    let decor = key.leaf_decor_mut();
                    decor.set_prefix(comment);
                }
            }
        }

        ...

    }
}

This produces output


# Comment
[section]