pub trait FieldCodec {
// Required methods
fn encode_value(&self, source: Reader<'_>) -> Result<JsonValue>;
fn decode_value(
&self,
source: &JsonValue,
target: Builder<'_>,
) -> Result<()>;
// Provided method
fn decode_member(
&self,
source: &JsonValue,
target: Builder<'_>,
field: Field,
) -> Result<()> { ... }
}Expand description
A custom JSON representation for a single Cap’n Proto value.
Implement this to override how a field, or every value of a given type, is
converted to and from JSON. It is required for AnyPointer and interface
fields, which carry no schema the codec could drive itself; it is merely
useful for everything else, when the default mapping is not the shape you
want on the wire.
A FieldCodec is attached to a Codec by one of
with_field_override,
with_type_override or
with_named_codec.
§Implementing
The trait has two required methods and one that you will need to override more often than its default suggests:
encode_valueis handed the Cap’n Proto value and returns theJsonValueto write in its place.decode_valueis handed a parsedJsonValueand a builder already positioned at the target value, and populates it.decode_memberis handed the parent struct builder plus the field to write, and so gets to decide how the target is created.
Which of the two decode methods is called depends on how the codec is bound — see the note below.
For simple cases a pair of closures is easier than a named type; see
make_field_codec.
use capnp_json::{FieldCodec, JsonValue};
/// Encodes a struct with `seconds`/`nanos` fields as a single number.
struct Timestamp;
impl FieldCodec for Timestamp {
fn encode_value(
&self,
source: capnp::dynamic_value::Reader<'_>,
) -> capnp::Result<JsonValue> {
let source: capnp::dynamic_struct::Reader<'_> = source.downcast();
let seconds: i64 = source.get_named("seconds")?.downcast();
let nanos: i64 = source.get_named("nanos")?.downcast();
Ok(JsonValue::Number(seconds as f64 + nanos as f64 / 1e9))
}
fn decode_value(
&self,
source: &JsonValue,
target: capnp::dynamic_value::Builder<'_>,
) -> capnp::Result<()> {
let JsonValue::Number(value) = source else {
return Err(capnp::Error::failed("expected a number".into()));
};
let mut target: capnp::dynamic_struct::Builder<'_> = target.downcast();
target.set_named("seconds", (value.trunc() as i64).into())?;
target.set_named("nanos", ((value.fract() * 1e9) as i64).into())?;
Ok(())
}
}§Which decode method is called
When the codec is bound to a field — via with_field_override,
with_type_override, or $Rust.codec on a field —
decode_member is called. When it is bound to
a struct type via $Rust.codec on the struct declaration,
decode_value is called with a builder for
that struct.
The default decode_member initialises the field and delegates to
decode_value. That works for struct, list and AnyPointer fields, but
fails for primitive, text, data and enum fields, because those cannot
be initialised. If your codec targets one of those, override
decode_member and use set on the parent builder instead:
fn decode_member(
&self,
source: &JsonValue,
mut target: capnp::dynamic_struct::Builder<'_>,
field: capnp::schema::Field,
) -> capnp::Result<()> {
let JsonValue::Number(value) = source else {
return Err(capnp::Error::failed("expected a number".into()));
};
target.set(field, (*value as i32).into())
}Required Methods§
Sourcefn encode_value(&self, source: Reader<'_>) -> Result<JsonValue>
fn encode_value(&self, source: Reader<'_>) -> Result<JsonValue>
Convert a Cap’n Proto value into the JSON that should stand for it.
source is the value being encoded: the field’s value when the codec is
bound to a field, or the struct itself when bound to a struct type. For
a field of a list type this is called once per element, with source
being the element.
The returned JsonValue is serialised by this crate, so no escaping
or quoting is needed. Returning JsonValue::DataBuffer is an error.
Sourcefn decode_value(&self, source: &JsonValue, target: Builder<'_>) -> Result<()>
fn decode_value(&self, source: &JsonValue, target: Builder<'_>) -> Result<()>
Populate an already-created Cap’n Proto value from JSON.
target is a builder for the value itself, not for its parent; use
decode_member if you need to create the
value rather than fill it in.
This is the method called for codecs bound to a struct type via
$Rust.codec, and — through the default decode_member — for codecs
bound to struct, list and AnyPointer fields.
Provided Methods§
Sourcefn decode_member(
&self,
source: &JsonValue,
target: Builder<'_>,
field: Field,
) -> Result<()>
fn decode_member( &self, source: &JsonValue, target: Builder<'_>, field: Field, ) -> Result<()>
Write one field of a struct from JSON.
Called when this codec is bound to a field. target is the containing
struct’s builder and field identifies the field to write, so an
implementation controls how the value is created — by init for
pointer-typed fields, or by set for everything else.
This is only called when the field is actually present in the JSON object; an absent field is left at its default.
The default implementation initialises the field and forwards to
decode_value, which is only valid for
struct, list and AnyPointer fields — see
the note on the trait.
Dyn Compatibility§
This trait is dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety".
Implementations on Foreign Types§
Source§impl<F, G> FieldCodec for (F, G)
A pair of closures (encode, decode) is a FieldCodec. Usually reached
through make_field_codec rather than written out.
impl<F, G> FieldCodec for (F, G)
A pair of closures (encode, decode) is a FieldCodec. Usually reached
through make_field_codec rather than written out.
Source§impl<T: FieldCodec + ?Sized> FieldCodec for &T
Lets a &T be used wherever a FieldCodec is expected, so one codec
instance can be shared between several Codecs. Every method is
forwarded, including decode_member, so a
codec behaves the same by reference as it does by value.
impl<T: FieldCodec + ?Sized> FieldCodec for &T
Lets a &T be used wherever a FieldCodec is expected, so one codec
instance can be shared between several Codecs. Every method is
forwarded, including decode_member, so a
codec behaves the same by reference as it does by value.