Skip to content

structio 0.4.0

Choose a tag to compare

@stephenberry stephenberry released this 04 Sep 15:56
· 17 commits to main since this release

#[derive(Structio)] behind an optional feature, a Value tree, and a late enum tag. One breaking change: a raw identifier's r# is no longer part of its key.

[dependencies]
structio = "0.4"

# The derive is off by default:
structio = { version = "0.4", features = ["derive"] }

Changed

  • A raw identifier's r# is no longer part of its key. A field or variant written r#type had the key r#type, because that is what stringify! hands the macro. It is now type, before any case rule runs, since the prefix is how Rust spells a name that collides with a keyword rather than part of the name: r#type is how you write a field for a "type" key. Both formats, fields and variants, derived and declared. An explicit "r#type" => field is a literal and is unchanged. Breaking for a declaration with a raw identifier and no explicit key, which now reads and writes a different key.

  • An internally tagged enum's tag no longer has to come first. tagged_enum!(.. as tag "kind") used to refuse an object whose first member was not the tag with ExpectedTag, which refused every document from a sorted-key writer the moment a member sorted before the tag. The reader now steps over the members before the tag, dispatches on it, reads the members after it, and then reads the ones it stepped over, nesting as deep as the payloads do. A tag that is first still costs one pass; the members before a late tag are walked twice, and a key on both sides of the tag keeps its earlier value. Required-field and unknown-key rules apply to the deferred members as to any other. An object with no tag at all is still ExpectedTag, reported against its first key.

Added

  • #[derive(Structio)], behind the derive feature. A front end to object!, array!, unit_enum! and tagged_enum!: it reads the type and emits the declaration, so a derived type and a declared type are the same impls. rename_all, tag, array, element, json, beve and crate on the type; rename, skip, required and with on a field; rename on a variant. Generics and their bounds are read off the type. The feature is off by default and the derive crate has no dependencies. docs/derive.md has the rest, including what later stages add.
  • BEVE containers reserve on the wire count. Reader::read_seq_counted and read_map_counted hand the element count to the caller before the first element, clipped to what the input could hold, and beve::cautious::<T> clips it again to a megabyte of T. Vec, VecDeque, HashMap and HashSet, adapted or not, reserve once instead of doubling up; a hostile count can waste at most that megabyte.
  • Value, a tree for a value with no declared type. Null, bool, number, string, array, object, with get, pointer/pointer_mut, the as_*/is_* accessors, Index/IndexMut by key or position, and the value! macro to build one. It reads and writes through both formats like any other type, so it can be a field of an object! declaration or a whole document; a BEVE typed array, complex run or matrix reads into the same shape beve_to_json writes. Number keeps whether it was an unsigned integer, a negative integer or a float, and writes a whole-valued float as 1.0 so the kind survives a trip through text. to_value and from_value move a declared type in and out, through JSON text. This is for the value nothing decodes, a register tree walked by path or a body forwarded unread, not a substitute for a declared type, and the crate's stance on that is unchanged.

Upgrading

The break is narrow. A declaration with a raw identifier and no explicit key changes the key it reads and writes: a field written r#type was keyed r#type and is now keyed type. Nothing else about a 0.3.2 declaration changes its bytes. If you were working around the old behaviour with "r#type" => r#type, that explicit key still means exactly what it says and will keep the old wire format.

The derive feature is additive and off by default, so a crate that does not enable it builds structio with no dependencies and no proc-macro, as before.