Skip to main content

Raw

Struct Raw 

Source
pub struct Raw<'de>(/* private fields */);
Expand description

One JSON value, kept as the text that spelled it.

The span is exactly one value with no whitespace on either side, which is what lets a Raw stand anywhere a value stands: a member of an object, an element of an array, or a whole document on its own.

Reading borrows out of the input under the default policy: the value is stepped over rather than decoded, nothing inside it is converted, and the field is a subslice of the document. Writing is then a copy of that run of bytes. The owning form exists for the value that has to outlive its document, through into_owned, and for the span that had a comment taken out of it and so is no longer any run of the input; see the Read impl for both, and for exactly which spans those are.

The type deliberately has no From<&str>, no From<String> and no FromStr. Each would be a conversion that looks total and is not: new and from_string can fail, and the unchecked ways in are new_unchecked and from_string_unchecked, spelled that way so that a span nobody validated is visible at the call site rather than hidden behind an into().

Implementations§

Source§

impl<'de> Raw<'de>

Source

pub fn new(s: &'de str) -> Result<Self>

Take the one JSON value s spells, checking that it really is one.

The check is everything from_str checks reading s into a Value under Standard, except the number grammar, and it refuses what that read refuses with the same code at the same offset. The value has to be complete and well formed, with no control character inside a string and no escape in one, key or value, that the string reader would not decode: \q, a \u that is not four hex digits, and a surrogate without its other half all fail. And it has to be the only thing in s. Trailing content fails rather than being stored with the value, because a Raw carrying a tail would write that tail back out into the middle of whatever document it lands in and break it.

use structio::{ErrorCode, json::Raw};

assert!(Raw::new(r#"{"a":[1,2]}"#).is_ok());
assert_eq!(Raw::new(r#"{"a":}"#).unwrap_err().code, ErrorCode::UnexpectedCharacter);
assert_eq!(Raw::new("1 2").unwrap_err().code, ErrorCode::TrailingContent);
assert_eq!(Raw::new(r#""\q""#).unwrap_err().code, ErrorCode::InvalidEscape);
assert_eq!(Raw::new(r#""\ud800""#).unwrap_err().code, ErrorCode::InvalidSurrogate);

Whitespace on either side is dropped rather than refused, so " 1 " and "1" are the same Raw. Keeping it would put the caller’s indentation inside a document laid out by someone else.

Nothing is decoded to be checked: an escape is refused or let through and the span keeps it as it was spelled. So this settles that s is one value rather than that it is a value this crate would have produced, and the number grammar is the one place the two differ. A number is stepped over by its alphabet rather than held to the grammar, exactly as prettify steps over one, so 01 is accepted and stored as it was written. Holding it to the grammar would cost every well-formed number in every forwarded body to move a rejection ahead of the reader that will make it anyway, and that reader is the one that knows what the value was supposed to be.

Source

pub fn new_unchecked(s: &'de str) -> Self

Take s as one JSON value without looking at it.

The validity of the output document is yours, in the same way it is yours when you reach for Writer::raw: whatever s holds becomes part of whatever document this Raw is written into, verbatim and unexamined. Hand it something that is not one complete JSON value and you get output that is not JSON, at the position where the value should have been.

There is no unsafe here and nothing unsound to be caused: the type holds a &str either way, and every path through it stays in safe code. unchecked is about the document, not about memory. What the call buys is the walk new makes, which is worth something for a span that some earlier step already proved out: a value copied from another Raw, or a body a schema-aware layer has already parsed.

A span this accepted and new would not still behaves the same under every write policy. See the Write impl.

Source

pub fn as_str(&self) -> &str

The value’s text, as it will be written.

The span itself, not a decoded value: a Raw holding a JSON string has the quotes and the escapes in here, because those are part of how the value was spelled and this type is about the spelling. Reading the value as a value means declaring a type for it and parsing this text with from_str, which is a second pass and is meant to look like one.

Source

pub fn into_owned(self) -> Raw<'static>

Cut the borrow, copying the span if it is still one.

The way out of 'de for a value that has to outlive the document it came from: a request body parked on a queue, or a params held until the worker that will forward it is free. A span that is already owned, which is what reading a value carrying a comment under ALLOW_COMMENTS produces, moves without copying.

use structio::json::Raw;

let owned = {
    let text = String::from(r#"[1,2,3]"#);
    Raw::new(&text).unwrap().into_owned()
};
assert_eq!(owned.as_str(), "[1,2,3]");
Source§

impl Raw<'static>

Source

pub fn from_string(s: String) -> Result<Self>

Take the one JSON value s spells, checking that it really is one, without copying it.

The owning counterpart to new, and the way in for text this program produced rather than read: a body assembled from parts, or a value some other layer already rendered. The String becomes the span, so nothing is reallocated and the span is never copied out of the buffer it arrived in. Reaching for new(&s) and then into_owned instead copies a buffer the caller already owns.

The check and the trimming are exactly new’s, being the same walk: one complete value, nothing after it, and no whitespace on either side of what is kept. Trimming shifts bytes down inside s.

What the Raw then holds is the caller’s whole allocation, not just the span: a megabyte buffer that was built up and whittled down to one short value keeps the megabyte for as long as the Raw lives. Where that matters, new(&s)?.into_owned() is the call that pays a copy to allocate the span exactly.

use structio::json::Raw;

let text = format!(r#"{{"id":{}}}"#, 7);
let raw = Raw::from_string(text).unwrap();
assert_eq!(raw.as_str(), r#"{"id":7}"#);

s is consumed either way: a value that fails the check is dropped rather than handed back, because Error carries a code and a position and is not a place to park a buffer. Where the text has to survive its own rejection, check it with new first.

Source

pub fn from_string_unchecked(s: String) -> Self

Take s as one JSON value without looking at it, and without copying it.

new_unchecked for text this program owns, on the same terms: the validity of the output document is yours, and whatever s holds goes verbatim into whatever document this Raw lands in. Like that one and unlike from_string it does not trim, so whitespace around the value is part of the span and is written with it.

An empty String is the case to watch, being the one a builder that never got filled hands over. It writes nothing at all, which truncates the member it stands in to {"params":}; see default for why that is the hole the defaulted value exists to close.

Trait Implementations§

Source§

impl<'de> Clone for Raw<'de>

Source§

fn clone(&self) -> Self

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl<'de> Debug for Raw<'de>

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for Raw<'_>

Source§

fn default() -> Self

The literal null, borrowed.

Not the empty string, which is what a newtype over Cow<str> defaults to on its own and what would make Default quietly able to break a document. A Raw writes its span verbatim, so an empty span writes nothing at all and a struct whose payload was merely never filled comes out as {"payload":}: not a document any reader will accept, and not one this crate can otherwise produce. Glaze’s raw-JSON type has exactly that hole, and reproducing it here would mean a field that is safe to fill and dangerous to leave alone, which is the wrong way round for the member a schema says is optional.

null closes it and costs nothing to close: it is one complete value like any other, it is the JSON for the member that has no value, and it is what is_null answers to, so a defaulted Raw is also the member SKIP_NULL leaves out entirely.

let envelope = Envelope { id: 1, ..Default::default() };
assert_eq!(structio::to_string(&envelope), r#"{"id":1,"payload":null}"#);
Source§

impl Display for Raw<'_>

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

The span, exactly as as_str gives it and exactly as a compact write emits it. Under PRETTY a write lays the span out at the depth it sits at, which Display has no enclosing document to do.

{:#} is the same text rather than the laid-out one. Value prettifies under the alternate flag, but it is a tree and cannot be holding anything it could not lay out. A Raw can: new_unchecked takes a span nobody walked, and laying that one out fails. Display has nowhere to report a failure and would have to swallow it, so laying a span out stays prettify, which is asked for by name and returns a Result.

use structio::json::Raw;

let raw = Raw::new(r#""a\nb""#).unwrap();
assert_eq!(raw.to_string(), r#""a\nb""#);
Source§

impl<'de> Eq for Raw<'de>

Source§

impl<'de> PartialEq for Raw<'de>

Source§

fn eq(&self, other: &Self) -> bool

Equality operator ==. Read more
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Inequality operator !=. Read more
Source§

impl<'de> Read<'de> for Raw<'de>

Borrow the value’s bytes straight out of the document.

The span is found by stepping over the value rather than by reading it, so a Raw member costs a parse roughly what an unknown key costs: one walk to find where the value ends, and then a slice of the input between the two offsets that walk left. Nothing inside the value is converted, nothing is copied, and under the default policy nothing is allocated, the field being a subslice of the input.

The walk checks what Raw::new checks, so a document is refused or not whatever type its value lands in, the number grammar aside. The one thing it does that skipping an unknown key does not is decode each escape it meets and throw the character away, because a skipped value is discarded and this one is kept and written out again. Only a string holding a backslash pays for that.

Under ALLOW_COMMENTS the span may hold // and /* */. The document was allowed to carry those; the output is not, a comment being no part of what any writer here can emit, and a forwarded body carrying one would be refused by the next plain JSON reader that saw it. So that policy, and only that policy, searches the span for a /, and runs it through the minifier, which already strips comments under it, only when it finds one.

What that means for the bytes, exactly. A span with no / in it holds no comment, so it is borrowed just as the default policy borrows it: the same bytes, interior whitespace and all. A span with a / in it is minified into an owned string, which moves the whitespace between its tokens but never the tokens themselves, so key order, number spellings and escapes still come through as the document spelled them. The search is conservative in the direction that costs nothing: a / inside a string, as in "http://x", buys a minify the span did not need, while a / that is absent is proof there was nothing to strip.

The test is O::ALLOW_COMMENTS, a compile-time constant, so the default policy compiles to the borrow with neither the search nor the stripping present.

Source§

fn read<O: Options>(&mut self, p: &mut Parser<'de, O>) -> Result<(), ErrorCode>

Parse into self, from the cursor’s current position. Read more
Source§

impl<'de> StructuralPartialEq for Raw<'de>

Source§

impl Write for Raw<'_>

Write the span back out.

Compact, which is the default, that is one copy of the bytes that arrived and nothing else. Byte-for-byte preservation is the whole point of the type, so the bytes are not looked at, let alone reformatted.

Under PRETTY they are laid out again at the writer’s current depth, through the same walk prettify uses. A forwarded value is usually the one part of a document that did not come from a writer here, and emitting it verbatim would wedge an unindented blob between indented neighbours, which is what Glaze does and is jarring exactly where a human is reading. Reusing that walk rather than writing a second one is also what keeps the promise its module docs make: the output is byte-identical to what writing the same data under the same policy produces, because there is one set of layout rules rather than two. Tokens are still copied through as the input spelled them, so only the whitespace between them is this crate’s and the passthrough guarantee survives the layout.

A span that is not one complete JSON value can only come from new_unchecked, and is written verbatim under either policy. write returns () and has no way to report the problem, so the choice is between laying out as much as parses and emitting what the caller supplied; emitting it is what the compact path would have done, which makes an invalid span behave the same way whichever policy is in force. The layout walk therefore settles the span’s structure before it emits a byte, rather than starting and discovering the problem halfway: a writer has no way to take output back, so a fallback after a partial layout would write the value twice.

Source§

fn is_null(&self) -> bool

Whether the span is the literal null.

Write::is_null is about absence rather than about bytes, and a Raw is the one type here where the two questions have the same answer: it holds no value of its own, only the text of one, so what it means under SKIP_NULL is whatever the value it stands for would have meant. A forwarded null is a forwarded absence.

The comparison is against the span exactly, which new guarantees carries no surrounding whitespace. A Raw built by new_unchecked out of " null " is not absent, for the same reason it is not the value new would have stored.

Source§

fn write<O: Options>(&self, w: &mut Writer<'_, O>)

Auto Trait Implementations§

§

impl<'de> Freeze for Raw<'de>

§

impl<'de> RefUnwindSafe for Raw<'de>

§

impl<'de> Send for Raw<'de>

§

impl<'de> Sync for Raw<'de>

§

impl<'de> Unpin for Raw<'de>

§

impl<'de> UnsafeUnpin for Raw<'de>

§

impl<'de> UnwindSafe for Raw<'de>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T> ToString for T
where T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.