-
Notifications
You must be signed in to change notification settings - Fork 131
Description
Summary
toml_edit
focuses on lossless editing, but when serializing Rust types into a DocumentMut
(or string), developers often need fine-grained control over the resulting layout—e.g., inline vs. multiline tables, array breaking rules, key ordering, comment injection, and whitespace. Today this requires manual post-processing of the produced DocumentMut
, which is brittle and verbose.
I’m proposing first-class formatting controls—either via attribute/derive macros or via a pluggable formatter API—so users can declaratively specify layout while still leveraging serde + toml_edit
’s lossless model.
Motivation & use cases
Keep certain small structs as inline tables (e.g., { x = 1, y = 2 }
) while keeping others expanded.
-
Force arrays to be inline or multiline (and wrap after N items).
-
Preserve or inject comments at field, table, or document level.
-
Control key ordering/grouping (e.g., “provider.* first, then strategy.*”).
-
Normalize spacing/blank lines between logical sections.
-
Enforce project-wide style without hand-editing the
DocumentMut
afterwards. -
These are common needs in configuration tooling and code generators.
Current behavior
Given (simplified):
#[derive(serde::Serialize)]
struct ProviderConfig {
read_only_rpc_urls: Vec<Vec<String>>,
write_only_rpc_url: String,
}
#[derive(serde::Serialize)]
struct AppConfig {
provider_config: ProviderConfig,
// ...
}
let doc = toml_edit::ser::to_document(&app_config).unwrap();
let s = doc.to_string(); // or to_string_in_original_order()
We get a reasonable TOML, but we cannot declaratively say:
-
“serialize provider_config inline”
-
“format this array multiline with one element per line”
-
“insert a comment above provider_config”
-
“place [dex_price_fetcher_config] before [provider_config]”
…without writing custom traversal and mutation code overDocumentMut
.
Workarounds (and why they’re not ideal)
-
Serialize to string and re-parse into
DocumentMut
, then mutate nodes viainline_table
,as_array_mut
, etc.
→ Works but is error-prone, verbose, and couples business logic to formatting internals. -
Custom
serde
newtypes with manualSerialize
impls.
→ Pushes formatting into ad-hoc serialization code and doesn’t scale for mixed inline/multiline requirements across a large schema.
Proposed solutions
Attribute / derive-based formatting hints
Allow users to annotate structs/fields to guide layout. Examples:
#[derive(Serialize)]
struct AppConfig {
/// Put a comment above this table
#[toml_edit(comment = "Provider endpoints and options")]
#[toml_edit(order = 10)]
provider_config: ProviderConfig,
/// Emit as an inline table
#[toml_edit(inline)]
network: Network,
}
#[derive(Serialize)]
struct ProviderConfig {
/// Multiline array, one element per line
#[toml_edit(array = "multiline", per_line = 1, trailing_comma = false)]
read_only_rpc_urls: Vec<Vec<String>>,
/// Keep inline (even if long)
#[toml_edit(inline)]
write_only_rpc_url: String,
}