Skip to main content

capnp_json/
lib.rs

1//! A [Cap'n Proto](https://capnproto.org) JSON codec, implementing the codec
2//! defined in [`json.capnp`].
3//!
4//! The wire format is compatible with the C++ `capnp::JsonCodec` that ships
5//! with Cap'n Proto: messages encoded by this crate can be decoded by the C++
6//! codec, and vice-versa. Encoding and decoding are driven entirely by the
7//! schema's runtime type information, so no per-type derive or code generation
8//! beyond `capnpc` is required.
9//!
10//! # Quick start
11//!
12//! [`to_json`] turns any struct reader into a JSON string, and [`from_json`]
13//! fills a struct builder from one:
14//!
15//! ```ignore
16//! use capnp::message;
17//! use capnp_json::{from_json, to_json};
18//!
19//! let mut builder = message::Builder::new_default();
20//! let root: my_schema_capnp::my_struct::Builder<'_> = builder.init_root();
21//! // ... populate `root` ...
22//!
23//! let json: String = to_json(root.reborrow_as_reader())?;
24//!
25//! let mut decoded = message::Builder::new_default();
26//! let decoded_root: my_schema_capnp::my_struct::Builder<'_> =
27//!   decoded.init_root();
28//! from_json(&json, decoded_root)?;
29//! ```
30//!
31//! Both are thin wrappers around [`Codec`], which is what you need as soon as
32//! you want custom encodings for particular fields or types — see
33//! [custom codecs](#custom-codecs) below.
34//!
35//! # Type mapping
36//!
37//! | Cap'n Proto | JSON |
38//! | --- | --- |
39//! | `Void` | `null` |
40//! | `Bool` | `true` / `false` |
41//! | `Int8`/`Int16`/`Int32`, `UInt8`/`UInt16`/`UInt32` | number |
42//! | `Int64`, `UInt64` | **string** holding a decimal integer |
43//! | `Float32`, `Float64` | number, except `NaN`, `Infinity` and `-Infinity`, which are strings |
44//! | `Text` | string |
45//! | `Data` | array of byte-valued numbers, unless `$Json.base64` or `$Json.hex` is applied |
46//! | enum | string naming the enumerant, or a number if the ordinal is not in the schema |
47//! | struct, group | object |
48//! | `List(T)` | array |
49//! | `AnyPointer`, interface | not representable; an error unless a custom codec is registered |
50//!
51//! 64-bit integers are strings because JSON numbers are IEEE-754 doubles and
52//! cannot represent the full 64-bit range exactly. When decoding, both the
53//! string and the number form are accepted for `Int64`/`UInt64`.
54//!
55//! Integer fields are range-checked on decode, as they are in C++: a number
56//! outside the field's range, or one with a fractional part, is an error
57//! rather than being silently clamped or truncated. `300` is not an `Int8`
58//! and `1.9` is not an `Int32`. The same applies to the elements of a `Data`
59//! array, which must be whole numbers in `[0, 255]`.
60//!
61//! Float fields are *not* range-checked, also matching C++: a magnitude too
62//! large for a `Float32` becomes an infinity. Enum ordinals given as numbers
63//! are not checked either.
64//!
65//! On encode, a field is omitted entirely when it is unset — for pointer
66//! fields (text, data, lists, structs) that means a null pointer. On decode, a
67//! field absent from the JSON is left at its schema default, and a JSON field
68//! that does not correspond to anything in the schema is ignored.
69//!
70//! A null pointer and an absent field are the same thing in Cap'n Proto, so a
71//! JSON `null` for a `Text`, `Data`, `List` or struct field means the field
72//! was not set, rather than that it holds an empty value; the field is left
73//! alone. `Void` is the exception, since `null` is its value rather than its
74//! absence, and `Float32`/`Float64` read `null` as `NaN`. For every other type
75//! `null` is an error. This is only true of *fields*: as a list element,
76//! `null` has to be a value, so `[null]` is an error for a list of text. All
77//! of this matches the C++ codec (`isPointerToJsonNull`), though that
78//! behaviour is on its main branch and not in any release yet.
79//!
80//! # JSON annotations
81//!
82//! To use any of the JSON annotations defined in [`json.capnp`], tell `capnpc`
83//! to resolve references to the annotation schema to this crate from your
84//! `build.rs`:
85//!
86//! ```ignore
87//! capnpc::CompilerCommand::new()
88//!     .crate_provides("capnp_json", [0x8ef99297a43a5e34])
89//!     .file("my_schema.capnp")
90//!     .run()
91//!     .expect("compiling schema");
92//! ```
93//!
94//! `0x8ef99297a43a5e34` is the file ID of `json.capnp`. The supported
95//! annotations are:
96//!
97//! - **`$Json.name("...")`** — use a different name for a field, enumerant,
98//!   group or union member in the JSON representation.
99//! - **`$Json.flatten()`** / **`$Json.flatten(prefix = "p.")`** — splice a
100//!   struct, group or union's members directly into the parent object rather
101//!   than nesting them, optionally prefixing each name.
102//!
103//!   Because a flattened field consumes no JSON nesting, flattening must
104//!   terminate: a struct cannot flatten a field of its own type, directly or
105//!   through a chain of other flattened fields and groups. [`validate_schema`]
106//!   checks this and reports it the way the C++ codec does; encoding and
107//!   decoding do not, since a schema is compile-time data and a cycle in one
108//!   is a build-time mistake rather than a property of any input. Left
109//!   unchecked, a cyclic schema is still rejected when decoded, but by the
110//!   recursion limit and with a less pointed message.
111//! - **`$Json.discriminator(name = "kind", valueName = "value")`** — encode
112//!   which member of a union is active as a sibling string field rather than
113//!   by the presence of the member's own key.
114//! - **`$Json.base64`** / **`$Json.hex`** — encode a `Data` field (or the
115//!   elements of a list of `Data`) as a Base64 or hex string instead of an
116//!   array of byte values. Applying both to one field is an error, as is
117//!   applying either to a field that is not `Data`.
118//!
119//! # Custom codecs
120//!
121//! Some things have no natural JSON form — `AnyPointer` and interface fields
122//! most obviously, but also domain types such as timestamps that you would
123//! rather see as an ISO-8601 string than as a struct. [`Codec`] lets you
124//! supply a [`FieldCodec`] for these, bound in one of three ways:
125//!
126//! - [`Codec::with_field_override`] — for one specific field of one specific
127//!   struct.
128//! - [`Codec::with_type_override`] — for every field of a given type.
129//! - [`Codec::with_named_codec`] — for every field or struct tagged
130//!   `$Rust.codec("name")` in the schema, which keeps the choice next to the
131//!   data it applies to.
132//!
133//! The `$Rust.codec` annotation is defined in this crate's own
134//! `rust-json.capnp` (file ID `0xf955e504bf781ac6`); see
135//! [`Codec::with_named_codec`] for the `build.rs` setup it needs.
136//!
137//! # Compatibility notes
138//!
139//! The following are known divergences from the C++ codec. They matter mostly
140//! when decoding input from an untrusted or third-party producer:
141//!
142//! - Input after the top-level value is ignored rather than rejected.
143//! - A `Data` element above 255 is rejected. C++ intends the same — its error
144//!   says "not an integer in [0, 255]" — but implements the bound as
145//!   `byte(x) == x`, whose out-of-range `double`-to-`byte` conversion is
146//!   undefined behaviour; in practice it accepts `256`, `300` and `511` and
147//!   stores them modulo 256. Being stricter cannot break round-tripping,
148//!   since the C++ encoder never emits such a value.
149//! - `\uXXXX` surrogate pairs are combined into the character they denote.
150//!   C++ decodes each escape separately and produces WTF-8 — the surrogates
151//!   encoded individually, which is not valid UTF-8 and which a Rust `String`
152//!   cannot hold — and notes as much in a TODO. Unpaired surrogates are
153//!   rejected here rather than replaced. This cannot affect round-tripping:
154//!   the C++ encoder writes non-BMP characters as literal UTF-8, never as
155//!   escapes.
156//! - Only `Int64`/`UInt64` accept the string form of an integer on decode;
157//!   C++ accepts it for every integer width.
158//! - Duplicate keys within one JSON object are rejected.
159//! - Floats are written in Rust's `Display` form, which never uses exponent
160//!   notation: `1e300` is emitted as 301 digits rather than as `1e300`. Both
161//!   parse back to the same value.
162//!
163//! Output is not pretty-printed, and the `Value` / `Call` / `raw` extensions
164//! from `json.capnp` are not implemented.
165//!
166//! [`json.capnp`]: https://github.com/capnproto/capnproto/blob/master/c%2B%2B/src/capnp/compat/json.capnp
167
168#![warn(missing_docs)]
169
170use std::collections::{BTreeMap, HashMap};
171
172mod data;
173mod decode;
174mod encode;
175mod validate;
176
177#[allow(missing_docs)]
178mod schema {
179  capnp::generated_code!(pub mod json_capnp);
180}
181
182// The generated code for schemas annotated with `$Rust.codec` refers to this
183// module by path, so it has to be public even though it is not part of the
184// hand-written API. See `Codec::with_named_codec`.
185#[allow(missing_docs)]
186mod rust_json_schema {
187  capnp::generated_code!(pub mod rust_json_capnp);
188}
189
190#[doc(hidden)]
191pub use rust_json_schema::rust_json_capnp;
192#[doc(hidden)]
193pub use schema::json_capnp;
194
195#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
196enum DataEncoding {
197  #[default]
198  Default,
199  Base64,
200  Hex,
201}
202
203struct EncodingOptions<'schema, 'prefix> {
204  prefix:        &'prefix std::borrow::Cow<'schema, str>,
205  name:          &'schema str,
206  field:         Option<capnp::schema::Field>,
207  flatten:       Option<json_capnp::flatten_options::Reader<'schema>>,
208  discriminator: Option<json_capnp::discriminator_options::Reader<'schema>>,
209  data_encoding: DataEncoding,
210  codec:         Option<&'schema str>,
211}
212
213impl Default for EncodingOptions<'_, '_> {
214  fn default() -> Self {
215    Self {
216      prefix:        &std::borrow::Cow::Borrowed(""),
217      name:          "",
218      field:         None,
219      flatten:       None,
220      discriminator: None,
221      data_encoding: DataEncoding::Default,
222      codec:         None,
223    }
224  }
225}
226
227impl<'schema, 'prefix> EncodingOptions<'schema, 'prefix> {
228  fn from_field(
229    prefix: &'prefix std::borrow::Cow<'schema, str>,
230    field: capnp::schema::Field,
231  ) -> capnp::Result<Self> {
232    let mut options = Self {
233      prefix,
234      name: field.get_proto().get_name()?.to_str()?,
235      field: Some(field),
236      flatten: None,
237      discriminator: None,
238      data_encoding: DataEncoding::Default,
239      codec: None,
240    };
241
242    for anno in field.get_annotations()?.iter() {
243      match anno.get_id() {
244        rust_json_capnp::codec::ID => {
245          options.codec = Some(
246            anno
247              .get_value()?
248              .downcast::<capnp::text::Reader>()
249              .to_str()?,
250          );
251        }
252        json_capnp::name::ID => {
253          options.name = anno
254            .get_value()?
255            .downcast::<capnp::text::Reader>()
256            .to_str()?;
257        }
258        json_capnp::base64::ID => {
259          if options.data_encoding != DataEncoding::Default {
260            return Err(capnp::Error::failed(
261                            "Cannot specify both base64 and hex annotations on the same field"
262                                .into(),
263                        ));
264          }
265          options.data_encoding = DataEncoding::Base64;
266        }
267        json_capnp::hex::ID => {
268          if options.data_encoding != DataEncoding::Default {
269            return Err(capnp::Error::failed(
270                            "Cannot specify both base64 and hex annotations on the same field"
271                                .into(),
272                        ));
273          }
274          options.data_encoding = DataEncoding::Hex;
275        }
276        json_capnp::flatten::ID => {
277          options.flatten = Some(
278            anno
279              .get_value()?
280              .downcast_struct::<json_capnp::flatten_options::Owned>(),
281          );
282        }
283        json_capnp::discriminator::ID => {
284          options.discriminator = Some(
285            anno
286              .get_value()?
287              .downcast_struct::<json_capnp::discriminator_options::Owned>(),
288          );
289        }
290        _ => {}
291      }
292    }
293    if options.data_encoding != DataEncoding::Default {
294      let mut element_type = field.get_type();
295      while let capnp::introspect::TypeVariant::List(sub_element_type) =
296        element_type.which()
297      {
298        element_type = sub_element_type;
299      }
300      if !matches!(element_type.which(), capnp::introspect::TypeVariant::Data) {
301        return Err(capnp::Error::failed(
302          "base64/hex annotation can only be applied to Data fields".into(),
303        ));
304      }
305    }
306    Ok(options)
307  }
308}
309
310/// Check that a schema can be represented as JSON at all.
311///
312/// At present this means checking that `$Json.flatten` terminates. A flattened
313/// field splices its members into the parent's JSON object instead of nesting
314/// them, so a struct that flattens a field of its own type — directly, or
315/// through a chain of other flattened fields and groups — describes an object
316/// of infinite width. The C++ codec rejects such a schema outright with
317/// "cyclic JSON flattening detected"; this function is how you ask for the
318/// same verdict.
319///
320/// `T` is the generated `Owned` type of the struct you encode or decode as the
321/// root. Every struct reachable from it is checked too — including through
322/// plain fields and list element types — so validating the root type covers
323/// the whole message.
324///
325/// # When to call this
326///
327/// Once, at startup or from a test — not per message. A schema is compile-time
328/// data: `capnpc` generates it and nothing can change it at runtime, so a
329/// cyclic flatten is a mistake in your `.capnp` file rather than a property of
330/// any particular input. Encoding and decoding therefore do *not* run this
331/// check, and paying for it on every call would be a permanent tax to
332/// re-discover a build-time bug.
333///
334/// ```ignore
335/// #[test]
336/// fn schema_is_json_encodable() {
337///   capnp_json::validate_schema::<my_schema_capnp::my_struct::Owned>()
338///     .expect("schema must be JSON-encodable");
339/// }
340/// ```
341///
342/// Skipping it is not dangerous, only less informative: a cyclic schema still
343/// gets rejected when decoded, by the recursion limit
344/// ([`CodecOptions::recursion_limit`]), just with a message that points at the
345/// depth rather than at the cycle.
346pub fn validate_schema<T: capnp::traits::OwnedStruct>() -> capnp::Result<()> {
347  // `OwnedStruct` is only implemented for struct types, so the other variants
348  // are unreachable; report rather than panic if that ever stops holding.
349  let capnp::introspect::TypeVariant::Struct(raw) = T::introspect().which()
350  else {
351    return Err(capnp::Error::failed(
352      "validate_schema requires a struct type".into(),
353    ));
354  };
355  validate::check_flattening_terminates(capnp::schema::StructSchema::new(raw))
356}
357
358/// Encode a Cap'n Proto struct as a JSON string.
359///
360/// `reader` accepts anything that converts into a
361/// [`capnp::dynamic_value::Reader`] — typically a struct reader obtained from
362/// `message::Reader::get_root()` or `message::Builder::reborrow_as_reader()`.
363/// The value must be a struct, since the top-level JSON value is an object.
364///
365/// The mapping from Cap'n Proto values to JSON, and the effect of the
366/// `$Json.*` annotations, are described in the
367/// [module documentation](crate#type-mapping); all of it matches the C++
368/// `capnp::JsonCodec`. The output is compact, with no insignificant
369/// whitespace.
370///
371/// This is [`Codec::new().encode(reader)`](Codec::encode). Use a [`Codec`]
372/// directly to register custom [`FieldCodec`]s — which is required for
373/// messages containing `AnyPointer` or interface fields.
374///
375/// ```ignore
376/// let json: String = capnp_json::to_json(root.reborrow_as_reader())?;
377/// ```
378pub fn to_json<'msg>(
379  reader: impl Into<capnp::dynamic_value::Reader<'msg>>,
380) -> capnp::Result<String> {
381  Codec::new().encode(reader)
382}
383
384/// Decode a JSON string into a Cap'n Proto struct builder.
385///
386/// `builder` accepts anything that converts into a
387/// [`capnp::dynamic_value::Builder`]; it must be a struct builder, since JSON
388/// objects map to Cap'n Proto structs. The value mapping and annotations are
389/// the same as for [`to_json`].
390///
391/// Fields absent from the JSON keep whatever `builder` already holds, and
392/// JSON fields with no counterpart in the schema are ignored.
393///
394/// Returns an error if `json` is malformed, if the top-level JSON value is
395/// not an object, or if any field's value cannot be coerced to its declared
396/// Cap'n Proto type. On error, `builder` may have been partially populated.
397///
398/// This is [`Codec::new().decode(json, builder)`](Codec::decode). Read
399/// [the compatibility notes](crate#compatibility-notes) before decoding input
400/// you do not control.
401///
402/// ```ignore
403/// capnp_json::from_json(&json, root)?;
404/// ```
405pub fn from_json<'segments>(
406  json: &str,
407  builder: impl Into<capnp::dynamic_value::Builder<'segments>>,
408) -> capnp::Result<()> {
409  Codec::new().decode(json, builder)
410}
411
412/// An in-memory JSON value.
413///
414/// This is the currency of the [`FieldCodec`] trait: a codec produces a
415/// `JsonValue` when encoding and is handed one when decoding. The codec itself
416/// never deals with JSON syntax — serialising and parsing are handled by this
417/// crate.
418///
419/// ```
420/// use capnp_json::JsonValue;
421///
422/// let value = JsonValue::Array(vec![
423///   JsonValue::String("hello".into()),
424///   JsonValue::Number(42.0),
425///   JsonValue::Null,
426/// ]);
427/// assert_eq!(value, value.clone());
428/// ```
429///
430/// # Numbers
431///
432/// [`Number`](JsonValue::Number) is an `f64`, matching JSON's own numeric
433/// model. A codec that needs the full 64-bit integer range should encode to
434/// [`String`](JsonValue::String), which is what this crate does for `Int64`
435/// and `UInt64` fields.
436///
437/// # Object ordering
438///
439/// [`Object`](JsonValue::Object) is a [`BTreeMap`], so its members are written
440/// in sorted key order. JSON objects are unordered by definition, so no order
441/// is more correct than another, but a *deterministic* one matters: the same
442/// value must encode to the same bytes every time, or golden-file tests,
443/// response caching and anything signing the output stop working. Sorted order
444/// is also the canonical form specified by RFC 8785.
445///
446/// Members of *schema* structs never pass through this map — they are written
447/// in schema declaration order — so this affects only the objects a custom
448/// [`FieldCodec`] builds.
449///
450/// Duplicate keys are rejected when parsing, so an `Object` decoded by this
451/// crate never loses a member.
452// FIXME: The String valued below could be Cow<'input, str> as they only really
453// need to be allocated if the input contains escaped characters. That would be
454// a little more tricky lower down, but not by a lot.
455#[derive(Debug, Clone, PartialEq)]
456pub enum JsonValue {
457  /// JSON `null`. Also the encoding of a Cap'n Proto `Void`.
458  Null,
459  /// JSON `true` or `false`.
460  Boolean(bool),
461  /// A JSON number. See [the note on numbers](JsonValue#numbers).
462  Number(f64),
463  /// A JSON string, already unescaped.
464  String(String),
465  /// A JSON array.
466  Array(Vec<JsonValue>),
467  /// A JSON object. See [the note on ordering](JsonValue#object-ordering).
468  Object(BTreeMap<String, JsonValue>),
469
470  /// Internal scratch space used while decoding `Data` fields; not part of
471  /// the JSON data model.
472  ///
473  /// Decoding a `Data` field has to hand out a `capnp::data::Reader`
474  /// borrowing from somewhere, and the decoded bytes have no other home, so
475  /// they are parked in the [`JsonValue`] being decoded. A [`FieldCodec`]
476  /// will never be given this variant and should never construct one; treat
477  /// it as an unreachable case.
478  // FIXME: Remove this from the public type and use a wrapper inside decode
479  #[doc(hidden)]
480  DataBuffer(Vec<u8>),
481}
482
483/// A custom JSON representation for a single Cap'n Proto value.
484///
485/// Implement this to override how a field, or every value of a given type, is
486/// converted to and from JSON. It is *required* for `AnyPointer` and interface
487/// fields, which carry no schema the codec could drive itself; it is merely
488/// useful for everything else, when the default mapping is not the shape you
489/// want on the wire.
490///
491/// A `FieldCodec` is attached to a [`Codec`] by one of
492/// [`with_field_override`](Codec::with_field_override),
493/// [`with_type_override`](Codec::with_type_override) or
494/// [`with_named_codec`](Codec::with_named_codec).
495///
496/// # Implementing
497///
498/// The trait has two required methods and one that you will need to override
499/// more often than its default suggests:
500///
501/// - [`encode_value`](FieldCodec::encode_value) is handed the Cap'n Proto
502///   value and returns the [`JsonValue`] to write in its place.
503/// - [`decode_value`](FieldCodec::decode_value) is handed a parsed
504///   [`JsonValue`] and a builder already positioned at the target value, and
505///   populates it.
506/// - [`decode_member`](FieldCodec::decode_member) is handed the *parent*
507///   struct builder plus the field to write, and so gets to decide how the
508///   target is created.
509///
510/// Which of the two decode methods is called depends on how the codec is
511/// bound — see [the note below](#which-decode-method-is-called).
512///
513/// For simple cases a pair of closures is easier than a named type; see
514/// [`make_field_codec`].
515///
516/// ```
517/// use capnp_json::{FieldCodec, JsonValue};
518///
519/// /// Encodes a struct with `seconds`/`nanos` fields as a single number.
520/// struct Timestamp;
521///
522/// impl FieldCodec for Timestamp {
523///   fn encode_value(
524///     &self,
525///     source: capnp::dynamic_value::Reader<'_>,
526///   ) -> capnp::Result<JsonValue> {
527///     let source: capnp::dynamic_struct::Reader<'_> = source.downcast();
528///     let seconds: i64 = source.get_named("seconds")?.downcast();
529///     let nanos: i64 = source.get_named("nanos")?.downcast();
530///     Ok(JsonValue::Number(seconds as f64 + nanos as f64 / 1e9))
531///   }
532///
533///   fn decode_value(
534///     &self,
535///     source: &JsonValue,
536///     target: capnp::dynamic_value::Builder<'_>,
537///   ) -> capnp::Result<()> {
538///     let JsonValue::Number(value) = source else {
539///       return Err(capnp::Error::failed("expected a number".into()));
540///     };
541///     let mut target: capnp::dynamic_struct::Builder<'_> = target.downcast();
542///     target.set_named("seconds", (value.trunc() as i64).into())?;
543///     target.set_named("nanos", ((value.fract() * 1e9) as i64).into())?;
544///     Ok(())
545///   }
546/// }
547/// ```
548///
549/// # Which decode method is called
550///
551/// When the codec is bound to a *field* — via `with_field_override`,
552/// `with_type_override`, or `$Rust.codec` on a field —
553/// [`decode_member`](FieldCodec::decode_member) is called. When it is bound to
554/// a *struct type* via `$Rust.codec` on the struct declaration,
555/// [`decode_value`](FieldCodec::decode_value) is called with a builder for
556/// that struct.
557///
558/// The default `decode_member` initialises the field and delegates to
559/// `decode_value`. That works for struct, list and `AnyPointer` fields, but
560/// **fails for primitive, text, data and enum fields**, because those cannot
561/// be `init`ialised. If your codec targets one of those, override
562/// `decode_member` and use `set` on the parent builder instead:
563///
564/// ```
565/// # use capnp_json::{FieldCodec, JsonValue};
566/// # struct Celsius;
567/// # impl FieldCodec for Celsius {
568/// #   fn encode_value(&self, _: capnp::dynamic_value::Reader<'_>)
569/// #     -> capnp::Result<JsonValue> { Ok(JsonValue::Null) }
570/// #   fn decode_value(&self, _: &JsonValue, _: capnp::dynamic_value::Builder<'_>)
571/// #     -> capnp::Result<()> { Ok(()) }
572/// fn decode_member(
573///   &self,
574///   source: &JsonValue,
575///   mut target: capnp::dynamic_struct::Builder<'_>,
576///   field: capnp::schema::Field,
577/// ) -> capnp::Result<()> {
578///   let JsonValue::Number(value) = source else {
579///     return Err(capnp::Error::failed("expected a number".into()));
580///   };
581///   target.set(field, (*value as i32).into())
582/// }
583/// # }
584/// ```
585pub trait FieldCodec {
586  /// Convert a Cap'n Proto value into the JSON that should stand for it.
587  ///
588  /// `source` is the value being encoded: the field's value when the codec is
589  /// bound to a field, or the struct itself when bound to a struct type. For
590  /// a field of a list type this is called once per element, with `source`
591  /// being the element.
592  ///
593  /// The returned [`JsonValue`] is serialised by this crate, so no escaping
594  /// or quoting is needed. Returning [`JsonValue::DataBuffer`] is an error.
595  fn encode_value(
596    &self,
597    source: capnp::dynamic_value::Reader<'_>,
598  ) -> capnp::Result<JsonValue>;
599
600  /// Populate an already-created Cap'n Proto value from JSON.
601  ///
602  /// `target` is a builder for the value itself, not for its parent; use
603  /// [`decode_member`](FieldCodec::decode_member) if you need to create the
604  /// value rather than fill it in.
605  ///
606  /// This is the method called for codecs bound to a struct type via
607  /// `$Rust.codec`, and — through the default `decode_member` — for codecs
608  /// bound to struct, list and `AnyPointer` fields.
609  fn decode_value(
610    &self,
611    source: &JsonValue,
612    target: capnp::dynamic_value::Builder<'_>,
613  ) -> capnp::Result<()>;
614
615  /// Write one field of a struct from JSON.
616  ///
617  /// Called when this codec is bound to a field. `target` is the *containing*
618  /// struct's builder and `field` identifies the field to write, so an
619  /// implementation controls how the value is created — by `init` for
620  /// pointer-typed fields, or by `set` for everything else.
621  ///
622  /// This is only called when the field is actually present in the JSON
623  /// object; an absent field is left at its default.
624  ///
625  /// The default implementation initialises the field and forwards to
626  /// [`decode_value`](FieldCodec::decode_value), which is only valid for
627  /// struct, list and `AnyPointer` fields — see
628  /// [the note on the trait](FieldCodec#which-decode-method-is-called).
629  fn decode_member(
630    &self,
631    source: &JsonValue,
632    target: capnp::dynamic_struct::Builder<'_>,
633    field: capnp::schema::Field,
634  ) -> capnp::Result<()> {
635    self.decode_value(source, target.init(field)?)
636  }
637}
638
639/// Lets a `&T` be used wherever a [`FieldCodec`] is expected, so one codec
640/// instance can be shared between several [`Codec`]s. Every method is
641/// forwarded, including [`decode_member`](FieldCodec::decode_member), so a
642/// codec behaves the same by reference as it does by value.
643impl<T: FieldCodec + ?Sized> FieldCodec for &T {
644  fn encode_value(
645    &self,
646    source: capnp::dynamic_value::Reader<'_>,
647  ) -> capnp::Result<JsonValue> {
648    (**self).encode_value(source)
649  }
650  fn decode_value(
651    &self,
652    source: &JsonValue,
653    target: capnp::dynamic_value::Builder<'_>,
654  ) -> capnp::Result<()> {
655    (**self).decode_value(source, target)
656  }
657  fn decode_member(
658    &self,
659    source: &JsonValue,
660    target: capnp::dynamic_struct::Builder<'_>,
661    field: capnp::schema::Field,
662  ) -> capnp::Result<()> {
663    (**self).decode_member(source, target, field)
664  }
665}
666
667/// A pair of closures `(encode, decode)` is a [`FieldCodec`]. Usually reached
668/// through [`make_field_codec`] rather than written out.
669// implement FieldCodec for any (fn, fn) pair that matches the signature
670impl<F, G> FieldCodec for (F, G)
671where
672  F: Fn(capnp::dynamic_value::Reader<'_>) -> capnp::Result<JsonValue>,
673  G: Fn(&JsonValue, capnp::dynamic_value::Builder<'_>) -> capnp::Result<()>,
674{
675  fn encode_value(
676    &self,
677    source: capnp::dynamic_value::Reader<'_>,
678  ) -> capnp::Result<JsonValue> {
679    (self.0)(source)
680  }
681  fn decode_value(
682    &self,
683    source: &JsonValue,
684    target: capnp::dynamic_value::Builder<'_>,
685  ) -> capnp::Result<()> {
686    (self.1)(source, target)
687  }
688}
689
690/// Build a [`FieldCodec`] from an encoder and a decoder closure.
691///
692/// A shorthand for the cases that do not need a named type — the two closures
693/// correspond to [`FieldCodec::encode_value`] and
694/// [`FieldCodec::decode_value`]. The resulting codec uses the default
695/// [`decode_member`](FieldCodec::decode_member), so it is only suitable for
696/// struct, list and `AnyPointer` fields; for a primitive, text, data or enum
697/// field, implement [`FieldCodec`] directly and override `decode_member`.
698///
699/// ```
700/// use capnp_json::{make_field_codec, JsonValue};
701///
702/// // Represent a struct's `text` field as the whole JSON value.
703/// let codec = make_field_codec(
704///   |source: capnp::dynamic_value::Reader<'_>| {
705///     let source: capnp::dynamic_struct::Reader<'_> = source.downcast();
706///     let text: capnp::text::Reader<'_> = source.get_named("text")?.downcast();
707///     Ok(JsonValue::String(text.to_str()?.to_owned()))
708///   },
709///   |source: &JsonValue, target: capnp::dynamic_value::Builder<'_>| {
710///     let JsonValue::String(text) = source else {
711///       return Err(capnp::Error::failed("expected a string".into()));
712///     };
713///     let mut target: capnp::dynamic_struct::Builder<'_> = target.downcast();
714///     target.set_named("text", text.as_str().into())
715///   },
716/// );
717/// # let _ = codec;
718/// ```
719pub fn make_field_codec<'env>(
720  encode_fn: impl Fn(capnp::dynamic_value::Reader<'_>) -> capnp::Result<JsonValue>
721    + 'env,
722  decode_fn: impl Fn(&JsonValue, capnp::dynamic_value::Builder<'_>) -> capnp::Result<()>
723    + 'env,
724) -> impl FieldCodec + 'env {
725  (encode_fn, decode_fn)
726}
727
728/// Encoding and decoding options for a [`Codec`].
729///
730/// Construct with [`Default`] and adjust what you need, so that options added
731/// in future versions keep their defaults:
732///
733/// ```
734/// use capnp_json::{Codec, CodecOptions};
735///
736/// let codec = Codec::new_with_options(CodecOptions {
737///   recursion_limit: 32,
738///   ..Default::default()
739/// });
740/// # let _ = codec;
741/// ```
742#[derive(Debug, Clone, PartialEq, Eq)]
743pub struct CodecOptions {
744  /// How deeply decoding will recurse before giving up. Defaults to 64,
745  /// matching the C++ codec's `maxNestingDepth`.
746  ///
747  /// Decoding is recursive, so without a bound, deeply nested input exhausts
748  /// the stack and aborts the process — which is not a catchable error in
749  /// Rust. The limit turns that into an ordinary `Err`.
750  ///
751  /// It bounds two things: the nesting depth of the JSON itself, and the
752  /// depth of the walk over the schema. The second is not implied by the
753  /// first, because a struct that flattens a field of its own type recurses
754  /// on the schema without descending into the JSON at all.
755  ///
756  /// A limit of `N` admits `N` nested arrays or objects. Scalars do not count
757  /// against it, so the boundary is the same one the C++ codec applies at the
758  /// same numeric setting.
759  ///
760  /// **Raising this reintroduces the crash it exists to prevent.** The safe
761  /// ceiling depends on your build profile and the stack size of the thread
762  /// doing the decoding; measured on a 1 MiB stack, decoding survived depth
763  /// 1600 in release but overflowed at depth 200 in debug. The default is
764  /// comfortably safe in both. Lowering it is always safe.
765  pub recursion_limit: usize,
766}
767
768impl Default for CodecOptions {
769  fn default() -> Self {
770    Self {
771      recursion_limit: 64,
772    }
773  }
774}
775
776/// A JSON codec for Cap'n Proto messages.
777///
778/// A `Codec` holds the custom [`FieldCodec`]s to apply while encoding and
779/// decoding. If you need none, [`to_json`] and [`from_json`] are equivalent to
780/// `Codec::new().encode(..)` and `Codec::new().decode(..)` and are more
781/// convenient.
782///
783/// A `Codec` is built up with the `with_*` methods, which consume and return
784/// it:
785///
786/// ```
787/// use capnp_json::{make_field_codec, Codec, JsonValue};
788///
789/// let codec = Codec::new().with_named_codec(
790///   "shouty",
791///   make_field_codec(
792///     |source: capnp::dynamic_value::Reader<'_>| {
793///       let text: capnp::text::Reader<'_> = source.downcast();
794///       Ok(JsonValue::String(text.to_str()?.to_uppercase()))
795///     },
796///     |_source: &JsonValue, _target: capnp::dynamic_value::Builder<'_>| Ok(()),
797///   ),
798/// );
799/// # let _ = codec;
800/// ```
801///
802/// The `'env` lifetime is that of the data borrowed by the registered codecs;
803/// for codecs that own everything they use, it is `'static`.
804///
805/// # Reuse and threads
806///
807/// Encoding and decoding take `&self` and keep no state between calls, so one
808/// `Codec` serves any number of messages and building one is cheap. Reuse it
809/// if it is convenient; nothing is lost by not doing so.
810///
811/// A `Codec` is neither `Send` nor `Sync`, so give each thread its own. In
812/// async code that means building one where it is used rather than holding it
813/// in shared state: `Codec::new()` costs a handful of nanoseconds, so the only
814/// reason to keep one alive is the custom [`FieldCodec`]s registered on it. If
815/// you need those in shared state, store something `Send + Sync` that builds
816/// the codec — a factory closure — and call it per request, or keep one in a
817/// [`thread_local!`](std::thread_local).
818///
819/// This is not a bound that could simply be added. It comes from the schema
820/// types: `field_overrides` is keyed on [`capnp::schema::Field`], which holds
821/// readers over the generated schema data, and those hold raw pointers. The
822/// same is true of [`capnp::dynamic_value::Reader`] and its `Builder`, so the
823/// *values* being encoded are `!Send` too — an encode or decode is inherently
824/// one synchronous stretch, and a `Send` `Codec` would only ever buy the
825/// ability to store one, never to await part-way through.
826///
827/// # Which codec wins
828///
829/// At most one [`FieldCodec`] applies to any given value. When encoding or
830/// decoding a struct field, the first match of the following is used:
831///
832/// 1. a `$Rust.codec("name")` annotation on the field, resolved against the
833///    names registered with [`with_named_codec`](Codec::with_named_codec);
834/// 2. a [`with_field_override`](Codec::with_field_override) registered for
835///    exactly that field;
836/// 3. a [`with_type_override`](Codec::with_type_override) registered for the
837///    field's declared type.
838///
839/// If none matches and the value is a struct, a `$Rust.codec("name")`
840/// annotation on the *struct's own declaration* is used if one is present.
841///
842/// A `$Rust.codec` name that is not registered is silently ignored and the
843/// default encoding applies.
844///
845/// # Scope of overrides
846///
847/// Both overrides are matched against a *field*, which has two consequences
848/// worth knowing:
849///
850/// - Neither applies to the root value passed to [`encode`](Codec::encode) or
851///   [`decode`](Codec::decode), since that is not reached through a field.
852///   Use a `$Rust.codec` annotation on the struct declaration for that.
853/// - For a field of type `List(T)`, a type override must be registered for
854///   `List(T)` rather than for `T` — one registered for `T` will not fire for
855///   the elements. A `$Rust.codec` annotation on a list field behaves the
856///   other way round: it is applied to each element in turn.
857pub struct Codec<'env> {
858  field_overrides: HashMap<capnp::schema::Field, Box<dyn FieldCodec + 'env>>,
859  type_overrides:  HashMap<capnp::introspect::Type, Box<dyn FieldCodec + 'env>>,
860  registry:        HashMap<String, Box<dyn FieldCodec + 'env>>,
861
862  options: CodecOptions,
863}
864
865impl<'env> Codec<'env> {
866  /// Create a codec with no custom [`FieldCodec`]s registered.
867  ///
868  /// The result encodes and decodes exactly as [`to_json`] and [`from_json`]
869  /// do.
870  pub fn new() -> Self {
871    Self::new_with_options(CodecOptions::default())
872  }
873
874  /// Create a codec with no custom [`FieldCodec`]s registered, and the given
875  /// [`CodecOptions`].
876  ///
877  /// Equivalent to [`new`](Codec::new) other than the options; read
878  /// [`CodecOptions::recursion_limit`] before raising the recursion limit.
879  pub fn new_with_options(options: CodecOptions) -> Self {
880    Self {
881      field_overrides: HashMap::new(),
882      type_overrides: HashMap::new(),
883      registry: HashMap::new(),
884      options,
885    }
886  }
887
888  /// Use `codec` for one specific field of one specific struct type.
889  ///
890  /// `field` is obtained from the field's containing
891  /// [`StructSchema`](capnp::schema::StructSchema), which in turn comes from
892  /// the generated `Owned` type:
893  ///
894  /// ```ignore
895  /// use capnp::introspect::Introspect;
896  ///
897  /// let capnp::introspect::TypeVariant::Struct(schema) =
898  ///   my_schema_capnp::my_struct::Owned::introspect().which()
899  /// else {
900  ///   unreachable!("my_struct is a struct");
901  /// };
902  /// let field = capnp::schema::StructSchema::new(schema)
903  ///   .get_field_by_name("myField")?;
904  ///
905  /// let codec = Codec::new().with_field_override(field, MyFieldCodec);
906  /// ```
907  ///
908  /// This is the most specific binding and takes precedence over
909  /// [`with_type_override`](Codec::with_type_override); see
910  /// [which codec wins](Codec#which-codec-wins). Registering a second codec
911  /// for the same field replaces the first.
912  pub fn with_field_override(
913    mut self,
914    field: capnp::schema::Field,
915    codec: impl FieldCodec + 'env,
916  ) -> Self {
917    self.field_overrides.insert(field, Box::new(codec));
918    self
919  }
920
921  /// Use `codec` for every field whose declared type is `typ`.
922  ///
923  /// The type comes from the generated `Owned` type of whatever you want to
924  /// override:
925  ///
926  /// ```ignore
927  /// use capnp::introspect::Introspect;
928  ///
929  /// let codec = Codec::new()
930  ///   .with_type_override(my_schema_capnp::timestamp::Owned::introspect(), Timestamp);
931  /// ```
932  ///
933  /// Matching is on the field's *declared* type, so read
934  /// [the note on scope](Codec#scope-of-overrides) before using this for
935  /// list element types. Registering a second codec for the same type
936  /// replaces the first.
937  pub fn with_type_override(
938    mut self,
939    typ: capnp::introspect::Type,
940    codec: impl FieldCodec + 'env,
941  ) -> Self {
942    self.type_overrides.insert(typ, Box::new(codec));
943    self
944  }
945
946  /// Register `codec` under `name`, for use by `$Rust.codec` annotations.
947  ///
948  /// This puts the choice of representation in the schema, next to the data
949  /// it describes, rather than in the code that builds the [`Codec`]:
950  ///
951  /// ```capnp
952  /// using Rust = import "/rust-json.capnp";
953  ///
954  /// struct Reading {
955  ///   takenAt @0 :Int64 $Rust.codec("iso8601");
956  /// }
957  ///
958  /// struct Duration $Rust.codec("iso8601-duration") {
959  ///   seconds @0 :Int64;
960  /// }
961  /// ```
962  ///
963  /// ```ignore
964  /// let codec = Codec::new().with_named_codec("iso8601", Iso8601);
965  /// ```
966  ///
967  /// The annotation may be applied to a field or to a struct declaration. On
968  /// a field it takes precedence over both override maps; on a struct it
969  /// applies wherever a value of that struct type is encoded, including at
970  /// the root. A name with no matching registration is ignored and the
971  /// default encoding is used, so a codec registered under the wrong name
972  /// fails silently rather than loudly.
973  ///
974  /// The annotation is declared in this crate's `rust-json.capnp`. Copy that
975  /// file somewhere on your schema import path, and point `capnpc` at this
976  /// crate for its file ID:
977  ///
978  /// ```ignore
979  /// capnpc::CompilerCommand::new()
980  ///     .crate_provides("capnp_json", [0xf955e504bf781ac6])
981  ///     .file("my_schema.capnp")
982  ///     .run()
983  ///     .expect("compiling schema");
984  /// ```
985  ///
986  /// If the schema also uses the `$Json.*` annotations, list both file IDs:
987  /// `[0x8ef99297a43a5e34, 0xf955e504bf781ac6]`.
988  pub fn with_named_codec(
989    mut self,
990    name: impl Into<String>,
991    codec: impl FieldCodec + 'env,
992  ) -> Self {
993    self.registry.insert(name.into(), Box::new(codec));
994    self
995  }
996
997  /// Encode a Cap'n Proto struct as a JSON string.
998  ///
999  /// The value mapping is described in the
1000  /// [module documentation](crate#type-mapping). Returns an error if `reader`
1001  /// is not a struct, if the message contains an `AnyPointer` or interface
1002  /// field with no codec registered for it, or if a registered
1003  /// [`FieldCodec`] fails.
1004  ///
1005  /// Use [`encode_to`](Codec::encode_to) to write into an existing buffer or
1006  /// stream instead of allocating a `String`.
1007  pub fn encode<'msg>(
1008    &self,
1009    reader: impl Into<capnp::dynamic_value::Reader<'msg>>,
1010  ) -> capnp::Result<String> {
1011    let mut writer = std::io::Cursor::new(Vec::with_capacity(4096));
1012    self.encode_to(&mut writer, reader)?;
1013    String::from_utf8(writer.into_inner()).map_err(|e| {
1014      capnp::Error::failed(format!(
1015        "Failed to convert JSON bytes to string: {}",
1016        e
1017      ))
1018    })
1019  }
1020
1021  /// Encode a Cap'n Proto struct as JSON, writing it to `writer`.
1022  ///
1023  /// Behaves as [`encode`](Codec::encode) but streams the output, so it does
1024  /// not hold the whole document in memory. The JSON is written as UTF-8.
1025  ///
1026  /// Output is written in many small pieces, so wrap unbuffered destinations
1027  /// such as `File` or `TcpStream` in a [`BufWriter`](std::io::BufWriter).
1028  ///
1029  /// If an error occurs part-way through, a partial document will already
1030  /// have been written.
1031  pub fn encode_to<'msg, W: std::io::Write>(
1032    &self,
1033    writer: &mut W,
1034    reader: impl Into<capnp::dynamic_value::Reader<'msg>>,
1035  ) -> capnp::Result<()> {
1036    let capnp::dynamic_value::Reader::Struct(reader) = reader.into() else {
1037      return Err(capnp::Error::failed(
1038        "Top-level value must be a struct".into(),
1039      ));
1040    };
1041    encode::serialize_json_to(self, writer, reader)
1042  }
1043
1044  /// Decode a JSON string into a Cap'n Proto struct builder.
1045  ///
1046  /// The top-level JSON value must be an object, and `builder` must therefore
1047  /// be a struct builder. Fields absent from the JSON keep the values already
1048  /// in `builder`; JSON fields with no counterpart in the schema are ignored.
1049  /// `builder` is not cleared first, so decoding into a builder that has
1050  /// already been populated merges into it.
1051  ///
1052  /// Returns an error if `json` is malformed, if a value cannot be coerced to
1053  /// its field's declared type, or if a registered [`FieldCodec`] fails. On
1054  /// error, `builder` may have been partially populated.
1055  ///
1056  /// Read [the compatibility notes](crate#compatibility-notes) before
1057  /// decoding input you do not control — in particular, deeply nested JSON
1058  /// can exhaust the stack.
1059  pub fn decode<'segments>(
1060    &self,
1061    json: &str,
1062    builder: impl Into<capnp::dynamic_value::Builder<'segments>>,
1063  ) -> capnp::Result<()> {
1064    let capnp::dynamic_value::Builder::Struct(builder) = builder.into() else {
1065      return Err(capnp::Error::failed(
1066        "Top-level JSON value must be an object".into(),
1067      ));
1068    };
1069    decode::parse(self, json, builder)
1070  }
1071}
1072
1073impl Default for Codec<'_> {
1074  fn default() -> Self {
1075    Self::new()
1076  }
1077}