Skip to main content

Writer

Struct Writer 

Source
pub struct Writer<'a, O: Options = Standard> { /* private fields */ }
Expand description

Accumulates JSON output.

The lifetime is the borrow of an io::Write sink, and is 'static for the ordinary in-memory writers built by Writer::new and friends.

O is the write policy, which decides at compile time whether the output is indented and whether null members are left out. It defaults to Standard, though a constructor cannot infer from that, so build one as Writer::<Standard>::new(). Trait implementations take &mut Writer<'_, O> and stay generic over it.

§Output only goes forwards

A writer cannot be rewound. There is no truncate, no checkpoint, no way to drop back to a mark, and the nesting depth is not readable either. That follows from what a writer is rather than being an omission. Writer::to_sink hands the front of the buffer to an io::Write as it fills, so a byte a caller wanted back may already be down a socket and beyond recall. A checkpoint could be honoured only by refusing to drain until it was released, which is buffering without a bound, and that is the one property the sink exists to provide.

The in-memory writer could offer it, and deliberately does not. A Write impl is handed &mut Writer<'_, O> and cannot tell the two apart, so an operation that worked on one and quietly held a stream’s whole output in memory on the other would be worse than no operation: the failure would show up as a machine running out of memory under load, in an impl whose author had no way to know it was doing that.

What this asks of an implementation is that it settle a question before emitting its first byte rather than part way through. A type that might have to write something other than what it started on walks its input first and commits afterwards. Raw is the worked example: it may have to fall back to emitting its span verbatim, so under Options::PRETTY a probe pass walks the span and settles whether it lays out at all, and only then does prettify_value_into emit anything. The probe costs a second walk over the span; a fallback discovered part way through the layout would write the value twice, and no arrangement of the code makes that recoverable.

Implementations§

Source§

impl<O: Options> Writer<'static, O>

Source

pub fn new() -> Self

Source

pub fn with_capacity(n: usize) -> Self

Source

pub fn from_vec(buf: Vec<u8>) -> Self

Reuse an existing buffer, discarding whatever it held.

Self::appending is the one that keeps it.

Source

pub fn appending(buf: Vec<u8>) -> Self

Write after what a buffer already holds, rather than over it.

Self::from_vec discards the contents; this keeps them and writes past them. A document that has to sit behind something – a protocol header, or the entries already written into a listing – then costs one buffer rather than a second buffer and a copy out of it. json::append is this with the writer kept out of sight, and beve::Writer::appending is the same constructor on the other format.

The bytes in front are not examined and need not be text, since what comes back from Self::into_vec is bytes. They are checked, rather than trusted, by Self::into_string, which is what makes appending onto the bytes of a String sound:

use structio::Standard;
use structio::json::{Write, Writer};

let out = String::from("[1,2,");
let mut w = Writer::<Standard>::appending(out.into_bytes());
3.write(&mut w);
let mut out = w.into_string();
out.push(']');

assert_eq!(out, "[1,2,3]");
Source§

impl<'a, O: Options> Writer<'a, O>

Source

pub fn to_sink(out: &'a mut dyn Write) -> Self

Write through to out, buffering DEFAULT_SINK_BUFFER bytes at a time.

The document is drained as it is produced, so peak memory is the buffer plus the largest single scalar written, not the size of the output. Writer::finish must be called to flush the tail and report any I/O error; crate::to_writer does both.

Source

pub fn to_sink_with_capacity(out: &'a mut dyn Write, capacity: usize) -> Self

Writer::to_sink with an explicit buffer size.

capacity is clamped up to one byte: draining always retains the last byte written, because it may still be a trailing comma awaiting overwrite by a closing brace.

Source

pub fn as_bytes(&self) -> &[u8] ⓘ

The bytes written so far, or with a sink, the bytes not yet drained.

With Writer::appending the bytes handed in are in front of them, those being in the buffer too. This is the whole buffer either way, which is what Writer::into_vec hands back.

Source

pub fn into_string(self) -> String

Take the output.

Everything written is valid UTF-8 by construction: string contents come from &str, numbers and escapes are ASCII, and Writer::push rejects non-ASCII bytes.

With a sink this returns only the undrained tail. Use Writer::finish instead.

§Panics

If the writer was built by Writer::appending over bytes that are not valid UTF-8. Those are the only bytes in the buffer this writer did not produce, so they are the only ones it has to check; a binary prefix is a perfectly good thing to append JSON behind, but it cannot come back out as a String. Take Writer::into_vec there instead.

Source

pub fn into_vec(self) -> Vec<u8> ⓘ

Source

pub fn len(&self) -> usize

Bytes currently buffered.

With a sink this counts only what has not been drained, matching Writer::as_bytes. A running total of the whole document is not offered because a failed drain would make it a lie. With Writer::appending it counts the bytes handed in as well, those being in the buffer.

Source

pub fn is_empty(&self) -> bool

Whether the buffer holds nothing.

Writer::len’s companion, and it counts what that counts: a writer built by Writer::appending over a buffer with something in it is not empty before it writes a byte of its own.

Source

pub fn finish(self) -> Result<()>

Drain the remaining bytes to the sink and report the first I/O error.

Without a sink this is Ok(()) and does nothing.

By value on purpose. A sink writer that keeps going after its tail has been flushed would emit a second document’s worth of bytes into the middle of the first, and asking twice whether the write succeeded has no second answer worth giving.

Source

pub fn push(&mut self, b: u8)

Append one ASCII byte.

§Panics

If b is not ASCII. Writer::into_string converts without re-validating, so the buffer has to stay valid UTF-8, and this is the only way safe code could break that. At every internal call site b is a literal, so the check folds away.

Source

pub fn raw(&mut self, s: &str)

Append a string verbatim, without quoting or escaping it.

Whatever s holds becomes part of the document as written, so keeping the result valid JSON is the caller’s job. Emitting a number literal is a supported use of this, and write_number_str is that use with the literal checked.

Source

pub fn write_object<T: WriteObject>(&mut self, value: &T)

Write a struct as a JSON object.

Members are written with an unconditional trailing comma, and the last one is overwritten with }. That removes the per-field “am I first” branch from the inner loop entirely.

The overwrite checks for that comma rather than assuming it. WriteObject is a safe trait that anyone may implement, so nothing guarantees write_fields wrote what it was asked to, and the buffer is handed out by Writer::into_string without revalidation.

Source

pub fn member<T: Write + ?Sized>(&mut self, prefix: &str, value: &T)

Write one "key":value, member. prefix is the pre-quoted key with its colon, built at compile time by the macro.

The prefix is written as it is given, quotes, colon and all, and nothing in it is escaped: the macro builds it from a Rust identifier, which has nothing JSON needs to escape. A key computed at runtime has no such guarantee, so pass it to Self::member_key and let this crate quote it rather than assembling the prefix by hand.

Under Options::SKIP_NULL a member holding nothing is not written at all, key included. The test is a constant plus a call that is false for all but a handful of types, so a policy that does not ask for it pays nothing and one that does pays a predictable branch on a value already in hand.

Source

pub fn member_with<A: WriteAs<T>, T: ?Sized>(&mut self, prefix: &str, value: &T)

Write one "key":value, member, the value through an adapter.

Self::member for a field whose declaration named an adapter, down to the trailing comma and to Options::SKIP_NULL, which asks WriteAs::is_null rather than the value itself. A member that writes itself would escape both.

A appears in no argument, so it is always turned up explicitly: w.member_with::<Millis, _>(prefix, value).

Source

pub fn member_key<T: Write + ?Sized>(&mut self, key: &str, value: &T)

Write one "key":value, member, the key given at runtime and quoted here.

Self::member takes a prefix already quoted and punctuated, which the macro assembles from a Rust identifier through quoted_key: a key with nothing in it for JSON to escape. A writer that discovers its keys as it goes has no such guarantee, and building the prefix itself means escaping by hand or emitting a document no reader will take. This takes the key alone and escapes it exactly as Self::write_str does.

Options::SKIP_NULL applies, as it does to any struct member. Where the key came from is not what the policy turns on: the boundary it draws is between a struct’s member and a map’s entry, and Self::write_keyed is the map.

use structio::json::{WriteObject, Writer};
use structio::{KeyMap, Keys, Options, Standard};

struct Walked<'a>(&'a [(String, u32)]);

impl Keys for Walked<'_> {
    // No declaration, so no static key set and no read half.
    const KEYS: &'static [&'static str] = &[];
    const MAP: &'static KeyMap = &KeyMap::build(Self::KEYS);
}

impl WriteObject for Walked<'_> {
    fn write_fields<O: Options>(&self, w: &mut Writer<'_, O>) {
        for (key, value) in self.0 {
            w.member_key(key, value);
        }
    }
}

let entries = [("a \"quoted\" key".to_owned(), 1)];
let mut w = Writer::<Standard>::new();
w.write_object(&Walked(&entries));
assert_eq!(w.into_string(), r#"{"a \"quoted\" key":1}"#);
Source

pub fn member_key_with<A: WriteAs<T>, T: ?Sized>( &mut self, key: &str, value: &T, )

Write one "key":value, member with a runtime key, the value through an adapter.

Self::member_key as Self::member_with is to Self::member: the key is escaped here and Options::SKIP_NULL asks WriteAs::is_null rather than the value itself.

A appears in no argument, so it is always turned up explicitly: w.member_key_with::<Millis, _>(key, value).

Source

pub fn write_tagged<T: Write + ?Sized>(&mut self, prefix: &str, value: &T)

Write an enum variant that carries a value: {"Name":value}.

prefix is the pre-quoted name with its colon, built at compile time by the macro, exactly as Self::member takes one. A variant carrying nothing is not written here at all: it is its own name, so it goes through Self::write_str.

Options::SKIP_NULL deliberately does not reach here. Dropping the member would leave {}, which names no variant and so is not a smaller spelling of this value but a different one.

Source

pub fn write_internally_tagged<T: WriteObject + ?Sized>( &mut self, prefix: &str, name: &str, value: &T, )

Write an internally tagged variant carrying a value: {"tag":"Name",...}, the payload’s own members following the tag.

prefix is the pre-quoted tag key with its colon and name the variant’s name, both built at compile time by the macro. The tag is written first because that is where the reader requires it: this crate reads in one pass, so a tag after the members it describes could not be used without a second look at them.

Options::SKIP_NULL reaches the payload’s members, which is what it means everywhere else, but not the tag: dropping that would leave an object naming no variant.

Source

pub fn write_array<T: WriteArray>(&mut self, value: &T)

Write a struct as a JSON array.

The bracket counterpart of Self::write_object, down to the trailing comma each element writes and the closing bracket that overwrites the last of them.

Source

pub fn element<T: Write + ?Sized>(&mut self, value: &T)

Write one value, element.

Options::SKIP_NULL deliberately does not reach here. Dropping a null from a sequence would shorten it and shift every index after it, which is a change to the data rather than to its presentation.

Source

pub fn write_seq<'i, T, I>(&mut self, items: I)
where T: Write + 'i, I: IntoIterator<Item = &'i T>,

Write a sequence as a JSON array.

Source

pub fn write_seq_with<'i, A, T, I>(&mut self, items: I)
where A: WriteAs<T>, T: 'i + ?Sized, I: IntoIterator<Item = &'i T>,

Write a sequence as a JSON array, each element through an adapter.

Self::write_seq for elements the adapter describes rather than their own Write impl. It is what Vec<A>’s WriteAs is built on, and it is public so that an adapter defined outside this crate can write a sequence whose elements another adapter describes; the bracket methods are pub(crate), so write_seq and this are the two ways to a JSON array. Elements described by neither trait still have to be turned into something that is.

Source

pub fn write_keyed<'i, K, V, I>(&mut self, entries: I)
where K: ToJsonKey + 'i, V: Write + 'i, I: IntoIterator<Item = (&'i K, &'i V)>,

Write a map as a JSON object.

Keys go through ToJsonKey, so numeric keys come out quoted, which is the only form JSON has for an object key.

Source

pub fn write_keyed_with<'i, KA, VA, K, V, I>(&mut self, entries: I)
where KA: WriteKeyAs<K>, VA: WriteAs<V>, K: 'i + ?Sized, V: 'i + ?Sized, I: IntoIterator<Item = (&'i K, &'i V)>,

Write a map as a JSON object, keys and values each through an adapter.

Self::write_keyed with both halves adapted, which is what HashMap<KA, VA>’s WriteAs is built on. Name Same for a half that wants the type’s own impl.

Source

pub fn write_bool(&mut self, v: bool)

Source

pub fn write_null(&mut self)

Source

pub fn write_u64(&mut self, v: u64)

Source

pub fn write_i64(&mut self, v: i64)

Source

pub fn write_u128(&mut self, v: u128)

Write a u128.

Values past u64::MAX are rare enough that the wide path is a plain loop rather than a table walk.

Source

pub fn write_i128_raw(&mut self, v: i128)

Write an i128, also used for the quoted-integer key path.

Source

pub fn write_f64(&mut self, v: f64)

Write an f64. NaN and infinity have no JSON form, so they are written as null, matching Glaze.

Source

pub fn write_f32(&mut self, v: f32)

Source

pub fn write_number_str(&mut self, s: &str)

Write a number already in its JSON form.

The other half of Parser::read_number_str, which is where the case for the pair is written out.

§Panics

Under debug_assertions, if s is not one JSON number literal. That is a bug in the caller rather than a condition to handle: the document is already being written, so there is nowhere for an error to go and nothing to do but publish something no reader will accept. Release builds append s unchecked, as raw does.

use structio::{Standard, json::Writer};

let mut w = Writer::<Standard>::new();
w.write_number_str("-1.2345678901234567890123e400");
assert_eq!(w.into_string(), "-1.2345678901234567890123e400");
Source

pub fn write_str(&mut self, s: &str)

Write a quoted, escaped JSON string.

The common case is a string with nothing to escape, and it is copied a word at a time as it is scanned: each eight bytes are stored into the spare capacity before the mask says whether they were clean, so a clean word costs a load, a store and the mask, and the only branch is the one that is never taken. Scanning first and copying the run afterwards, as this used to, ended in a memcpy whose length the compiler could not see, and for the short strings documents are full of the call cost more than the bytes. The stored word is counted into the length only as far as the first byte that needs escaping, so an overshoot lands in spare capacity and is overwritten by what follows.

Trait Implementations§

Source§

impl<O: Options> Default for Writer<'static, O>

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

§

impl<'a, O = Standard> !RefUnwindSafe for Writer<'a, O>

§

impl<'a, O = Standard> !Send for Writer<'a, O>

§

impl<'a, O = Standard> !Sync for Writer<'a, O>

§

impl<'a, O = Standard> !UnwindSafe for Writer<'a, O>

§

impl<'a, O> Freeze for Writer<'a, O>
where PhantomData<fn() -> O>: Freeze,

§

impl<'a, O> Unpin for Writer<'a, O>
where PhantomData<fn() -> O>: Unpin,

§

impl<'a, O> UnsafeUnpin for Writer<'a, O>
where PhantomData<fn() -> O>: UnsafeUnpin,

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> 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, 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.