Two new declarations: a field or a variant that answers to more than one name, and a one-field struct written as that field alone. Plus ReadOwned, the bound for a read out of a buffer you own, and a json::Raw that can be built from a String you already hold. No breaking changes.
[dependencies]
structio = "0.6"
# The derive is off by default:
structio = { version = "0.6", features = ["derive"] }Added
-
A field or a variant may answer to more than one name.
object!(Settings { timeout | "timeout_ms" })andtagged_enum!(Mode { Idle | "idle" }), repeatable, with#[structio(alias = "..")]as the derive's spelling. The declared name is the one written and an alias is only ever read, so adding one cannot change a byte the program writes: it is how a key or a variant is renamed without breaking the documents already written under the old spelling. A case rule leaves an alias alone, as it leaves an explicit key alone; a#[required]member is satisfied by any of its names; awrite_onlydeclaration refuses one, never reading.Keys::ALIASESandVariants::ALIASESare the new associated consts that carry it, both defaulted to empty, so a hand-written impl is unchanged. docs/schemas.md has the rest. -
transparent!, a one-field struct written as that field alone.UserId(7)is7, not[7]and not{"0":7}: reading and writing delegate to the field, with no object, no keys and no array around it. That is the declaration for the newtype that exists to keep twou64s apart in Rust and means nothing to either format, whicharray!writes as a one-element array and an object cannot write at all, having no name for its key to be.json_transparent!andbeve_transparent!narrow it to one format andwrite_onlyto one direction; it takes an adapter,transparent!(Timeout { 0 as Millis }), which is the whole newtype-around-a-foreign-type case in one line;#[structio(transparent)]is the derive's spelling.is_nullis forwarded in both formats, and BEVE's typed-array path deliberately is not, that one being a layout claim a declaration cannot make. docs/schemas.md has the rest. -
ReadOwned, the bound for parsing a value out of a buffer you own. At the crate root and per format, asReadWrite's read-side counterpart. A function that holds the document and hands aTback needsfor<'de> Read<'de> + Default: higher-ranked because a type may borrow out of the input and a caller owning the buffer cannot allow that, andDefaultbecause reading fills a value rather than constructing one. The crate spelled that pair out in ten of its own signatures, and a downstream extractor had to work it out from scratch;ReadOwnedis the name for it.from_strstill takesRead<'de>, tied to the input's lifetime so a borrowing type can be read, exactly as serde keepsDeserializeOwnedtofrom_reader. docs/schemas.md has the rest. -
json::Raw::from_stringandfrom_string_unchecked, the way in from aString. Text this program produced rather than read had no entry point:Raw::new_unchecked(&text).into_owned()copied a buffer the caller already owned, there being no way to hand theStringover. These take it, so the buffer becomes the span with nothing reallocated and the span never copied out of it,from_stringtrimming by shifting bytes inside it. The check and the trimming arenew's, being the same walk. TheRawthen holds the caller's whole allocation rather than just the span, sonew(&s)?.into_owned()is still the call for a long-lived value whittled out of a much larger buffer. A rejected value is dropped rather than handed back, so check withnewfirst where the text has to survive its own rejection. There is still noFrom<String>, for the reason there is noFrom<&str>. -
Displayforjson::Rawandjson::JsonStr.Rawdisplays its span, which is whatas_strgives and what a compact write emits, escapes and quotes included, since the type is about the spelling.{:#}is that same text rather than a laid-out one: a spannew_uncheckedaccepted may have no layout, andDisplayhas nowhere to report that, soprettifystays the named way to ask.JsonStrdisplays the string the document meant, with its escapes already resolved. -
Debug,Clone,PartialEq,EqandHashforjson::JsonStr. It had none, so a test could notassert_eq!on one or print one, and the keyError::key_inhands back could not be looked up in a set of the names a schema knows. Equality and hashing are the text rather than the variant: a key written"a"and one written"\u0061"are the same key, which deriving either would have denied.
Fixed
ReadWrite's doc example was missing+ Defaultand could not compile. It was markedignore, so nothing caught it. Now compiled, and the same example in theobject!docs already had the bound.Write's example, the crate's only otherignore, is compiled too.