LCOV - code coverage report
Current view: top level - libs/proxy/json/src - lib.rs (source / functions) Coverage Total Hit
Test: 1e20c4f2b28aa592527961bb32170ebbd2c9172f.info Lines: 97.8 % 182 178
Test Date: 2025-07-16 12:29:03 Functions: 72.0 % 50 36

            Line data    Source code
       1              : //! A JSON serialization lib, designed for more flexibility than `serde_json` offers.
       2              : //!
       3              : //! Features:
       4              : //!
       5              : //! ## Dynamic construction
       6              : //!
       7              : //! Sometimes you have dynamic values you want to serialize, that are not already in a serde-aware model like a struct or a Vec etc.
       8              : //! To achieve this with serde, you need to implement a lot of different traits on a lot of different new-types.
       9              : //! Because of this, it's often easier to give-in and pull all the data into a serde-aware model (`serde_json::Value` or some intermediate struct),
      10              : //! but that is often not very efficient.
      11              : //!
      12              : //! This crate allows full control over the JSON encoding without needing to implement any extra traits. Just call the
      13              : //! relevant functions, and it will guarantee a correctly encoded JSON value.
      14              : //!
      15              : //! ## Async construction
      16              : //!
      17              : //! Similar to the above, sometimes the values arrive asynchronously. Often collecting those values in memory
      18              : //! is more expensive than writing them as JSON, since the overheads of `Vec` and `String` is much higher, however
      19              : //! there are exceptions.
      20              : //!
      21              : //! Serializing to JSON all in one go is also more CPU intensive and can cause lag spikes,
      22              : //! whereas serializing values incrementally spreads out the CPU load and reduces lag.
      23              : //!
      24              : //! ## Examples
      25              : //!
      26              : //! To represent the following JSON as a compact string
      27              : //!
      28              : //! ```json
      29              : //! {
      30              : //!   "results": {
      31              : //!     "rows": [
      32              : //!       {
      33              : //!         "id": 1,
      34              : //!         "value": null
      35              : //!       },
      36              : //!       {
      37              : //!         "id": 2,
      38              : //!         "value": "hello"
      39              : //!       }
      40              : //!     ]
      41              : //!   }
      42              : //! }
      43              : //! ```
      44              : //!
      45              : //! We can use the following code:
      46              : //!
      47              : //! ```
      48              : //! // create the outer object
      49              : //! let s = json::value_to_string!(|v| json::value_as_object!(|v| {
      50              : //!     // create an entry with key "results" and start an object value associated with it.
      51              : //!     let results = v.key("results");
      52              : //!     json::value_as_object!(|results| {
      53              : //!         // create an entry with key "rows" and start an list value associated with it.
      54              : //!         let rows = results.key("rows");
      55              : //!         json::value_as_list!(|rows| {
      56              : //!             // create a list entry and start an object value associated with it.
      57              : //!             let row = rows.entry();
      58              : //!             json::value_as_object!(|row| {
      59              : //!                 // add entry "id": 1
      60              : //!                 row.entry("id", 1);
      61              : //!                 // add entry "value": null
      62              : //!                 row.entry("value", json::Null);
      63              : //!             });
      64              : //!
      65              : //!             // create a list entry and start an object value associated with it.
      66              : //!             let row = rows.entry();
      67              : //!             json::value_as_object!(|row| {
      68              : //!                 // add entry "id": 2
      69              : //!                 row.entry("id", 2);
      70              : //!                 // add entry "value": "hello"
      71              : //!                 row.entry("value", "hello");
      72              : //!             });
      73              : //!         });
      74              : //!     });
      75              : //! }));
      76              : //!
      77              : //! assert_eq!(s, r#"{"results":{"rows":[{"id":1,"value":null},{"id":2,"value":"hello"}]}}"#);
      78              : //! ```
      79              : 
      80              : mod macros;
      81              : mod str;
      82              : mod value;
      83              : 
      84              : pub use value::{Null, ValueEncoder};
      85              : 
      86              : #[must_use]
      87              : /// Serialize a single json value.
      88              : pub struct ValueSer<'buf> {
      89              :     buf: &'buf mut Vec<u8>,
      90              :     start: usize,
      91              : }
      92              : 
      93              : impl<'buf> ValueSer<'buf> {
      94              :     /// Create a new json value serializer.
      95           40 :     pub fn new(buf: &'buf mut Vec<u8>) -> Self {
      96           40 :         Self { buf, start: 0 }
      97           40 :     }
      98              : 
      99              :     /// Borrow the underlying buffer
     100            2 :     pub fn as_buffer(&self) -> &[u8] {
     101            2 :         self.buf
     102            2 :     }
     103              : 
     104              :     #[inline]
     105           94 :     pub fn value(self, e: impl ValueEncoder) {
     106           94 :         e.encode(self);
     107           18 :     }
     108              : 
     109              :     /// Write raw bytes to the buf. This must be already JSON encoded.
     110              :     #[inline]
     111           31 :     pub fn write_raw_json(self, data: &[u8]) {
     112           31 :         self.buf.extend_from_slice(data);
     113           31 :         self.finish();
     114            5 :     }
     115              : 
     116              :     /// Start a new object serializer.
     117              :     #[inline]
     118            3 :     pub fn object(self) -> ObjectSer<'buf> {
     119            3 :         ObjectSer::new(self)
     120            3 :     }
     121              : 
     122              :     /// Start a new list serializer.
     123              :     #[inline]
     124            2 :     pub fn list(self) -> ListSer<'buf> {
     125            2 :         ListSer::new(self)
     126            2 :     }
     127              : 
     128              :     /// Finish the value ser.
     129              :     #[inline]
     130          148 :     fn finish(self) {
     131              :         // don't trigger the drop handler which triggers a rollback.
     132              :         // this won't cause memory leaks because `ValueSet` owns no allocations.
     133          148 :         std::mem::forget(self);
     134           26 :     }
     135              : }
     136              : 
     137              : impl Drop for ValueSer<'_> {
     138            1 :     fn drop(&mut self) {
     139            1 :         self.buf.truncate(self.start);
     140            1 :     }
     141              : }
     142              : 
     143              : #[must_use]
     144              : /// Serialize a json object.
     145              : pub struct ObjectSer<'buf> {
     146              :     value: ValueSer<'buf>,
     147              :     start: usize,
     148              : }
     149              : 
     150              : impl<'buf> ObjectSer<'buf> {
     151              :     /// Start a new object serializer.
     152              :     #[inline]
     153            5 :     pub fn new(value: ValueSer<'buf>) -> Self {
     154            5 :         value.buf.push(b'{');
     155            5 :         let start = value.buf.len();
     156            5 :         Self { value, start }
     157            5 :     }
     158              : 
     159              :     /// Borrow the underlying buffer
     160            0 :     pub fn as_buffer(&self) -> &[u8] {
     161            0 :         self.value.as_buffer()
     162            0 :     }
     163              : 
     164              :     /// Start a new object entry with the given string key, returning a [`ValueSer`] for the associated value.
     165              :     #[inline]
     166            8 :     pub fn key(&mut self, key: impl KeyEncoder) -> ValueSer<'_> {
     167            8 :         key.write_key(self)
     168            8 :     }
     169              : 
     170              :     /// Write an entry (key-value pair) to the object.
     171              :     #[inline]
     172            6 :     pub fn entry(&mut self, key: impl KeyEncoder, val: impl ValueEncoder) {
     173            6 :         self.key(key).value(val);
     174            6 :     }
     175              : 
     176              :     #[inline]
     177            8 :     fn entry_inner(&mut self, f: impl FnOnce(&mut Vec<u8>)) -> ValueSer<'_> {
     178              :         // track before the separator so we the value is rolled back it also removes the separator.
     179            8 :         let start = self.value.buf.len();
     180              : 
     181              :         // push separator if necessary
     182            8 :         if self.value.buf.len() > self.start {
     183            3 :             self.value.buf.push(b',');
     184            5 :         }
     185              :         // push key
     186            8 :         f(self.value.buf);
     187              :         // push value separator
     188            8 :         self.value.buf.push(b':');
     189              : 
     190              :         // return value writer.
     191            8 :         ValueSer {
     192            8 :             buf: self.value.buf,
     193            8 :             start,
     194            8 :         }
     195            8 :     }
     196              : 
     197              :     /// Reset the buffer back to before this object was started.
     198              :     #[inline]
     199            1 :     pub fn rollback(self) -> ValueSer<'buf> {
     200              :         // Do not fully reset the value, only reset it to before the `{`.
     201              :         // This ensures any `,` before this value are not clobbered.
     202            1 :         self.value.buf.truncate(self.start - 1);
     203            1 :         self.value
     204            1 :     }
     205              : 
     206              :     /// Finish the object ser.
     207              :     #[inline]
     208            4 :     pub fn finish(self) {
     209            4 :         self.value.buf.push(b'}');
     210            4 :         self.value.finish();
     211            4 :     }
     212              : }
     213              : 
     214              : pub trait KeyEncoder {
     215              :     fn write_key<'a>(self, obj: &'a mut ObjectSer) -> ValueSer<'a>;
     216              : }
     217              : 
     218              : #[must_use]
     219              : /// Serialize a json object.
     220              : pub struct ListSer<'buf> {
     221              :     value: ValueSer<'buf>,
     222              :     start: usize,
     223              : }
     224              : 
     225              : impl<'buf> ListSer<'buf> {
     226              :     /// Start a new list serializer.
     227              :     #[inline]
     228           45 :     pub fn new(value: ValueSer<'buf>) -> Self {
     229           45 :         value.buf.push(b'[');
     230           45 :         let start = value.buf.len();
     231           45 :         Self { value, start }
     232            6 :     }
     233              : 
     234              :     /// Borrow the underlying buffer
     235            2 :     pub fn as_buffer(&self) -> &[u8] {
     236            2 :         self.value.as_buffer()
     237            2 :     }
     238              : 
     239              :     /// Write an value to the list.
     240              :     #[inline]
     241            1 :     pub fn push(&mut self, val: impl ValueEncoder) {
     242            1 :         self.entry().value(val);
     243            1 :     }
     244              : 
     245              :     /// Start a new value entry in this list.
     246              :     #[inline]
     247          101 :     pub fn entry(&mut self) -> ValueSer<'_> {
     248              :         // track before the separator so we the value is rolled back it also removes the separator.
     249          101 :         let start = self.value.buf.len();
     250              : 
     251              :         // push separator if necessary
     252          101 :         if self.value.buf.len() > self.start {
     253           56 :             self.value.buf.push(b',');
     254           56 :         }
     255              : 
     256              :         // return value writer.
     257          101 :         ValueSer {
     258          101 :             buf: self.value.buf,
     259          101 :             start,
     260          101 :         }
     261           11 :     }
     262              : 
     263              :     /// Reset the buffer back to before this object was started.
     264              :     #[inline]
     265            1 :     pub fn rollback(self) -> ValueSer<'buf> {
     266              :         // Do not fully reset the value, only reset it to before the `[`.
     267              :         // This ensures any `,` before this value are not clobbered.
     268            1 :         self.value.buf.truncate(self.start - 1);
     269            1 :         self.value
     270            1 :     }
     271              : 
     272              :     /// Finish the object ser.
     273              :     #[inline]
     274           43 :     pub fn finish(self) {
     275           43 :         self.value.buf.push(b']');
     276           43 :         self.value.finish();
     277            4 :     }
     278              : }
     279              : 
     280              : #[cfg(test)]
     281              : mod tests {
     282              :     use crate::{Null, ValueSer};
     283              : 
     284              :     #[test]
     285            1 :     fn object() {
     286            1 :         let mut buf = vec![];
     287            1 :         let mut object = ValueSer::new(&mut buf).object();
     288            1 :         object.entry("foo", "bar");
     289            1 :         object.entry("baz", Null);
     290            1 :         object.finish();
     291              : 
     292            1 :         assert_eq!(buf, br#"{"foo":"bar","baz":null}"#);
     293            1 :     }
     294              : 
     295              :     #[test]
     296            1 :     fn list() {
     297            1 :         let mut buf = vec![];
     298            1 :         let mut list = ValueSer::new(&mut buf).list();
     299            1 :         list.entry().value("bar");
     300            1 :         list.entry().value(Null);
     301            1 :         list.finish();
     302              : 
     303            1 :         assert_eq!(buf, br#"["bar",null]"#);
     304            1 :     }
     305              : 
     306              :     #[test]
     307            1 :     fn object_macro() {
     308            1 :         let res = crate::value_to_string!(|obj| {
     309            1 :             crate::value_as_object!(|obj| {
     310            1 :                 obj.entry("foo", "bar");
     311            1 :                 obj.entry("baz", Null);
     312            1 :             })
     313            1 :         });
     314              : 
     315            1 :         assert_eq!(res, r#"{"foo":"bar","baz":null}"#);
     316            1 :     }
     317              : 
     318              :     #[test]
     319            1 :     fn list_macro() {
     320            1 :         let res = crate::value_to_string!(|list| {
     321            1 :             crate::value_as_list!(|list| {
     322            1 :                 list.entry().value("bar");
     323            1 :                 list.entry().value(Null);
     324            1 :             })
     325            1 :         });
     326              : 
     327            1 :         assert_eq!(res, r#"["bar",null]"#);
     328            1 :     }
     329              : 
     330              :     #[test]
     331            1 :     fn rollback_on_drop() {
     332            1 :         let res = crate::value_to_string!(|list| {
     333            1 :             crate::value_as_list!(|list| {
     334            1 :                 list.entry().value("bar");
     335              : 
     336              :                 'cancel: {
     337            1 :                     let nested_list = list.entry();
     338            1 :                     crate::value_as_list!(|nested_list| {
     339            1 :                         nested_list.entry().value(1);
     340              : 
     341            1 :                         assert_eq!(nested_list.as_buffer(), br#"["bar",[1"#);
     342            1 :                         if true {
     343            1 :                             break 'cancel;
     344            0 :                         }
     345              :                     })
     346              :                 }
     347              : 
     348            1 :                 assert_eq!(list.as_buffer(), br#"["bar""#);
     349              : 
     350            1 :                 list.entry().value(Null);
     351              :             })
     352              :         });
     353              : 
     354            1 :         assert_eq!(res, r#"["bar",null]"#);
     355            1 :     }
     356              : 
     357              :     #[test]
     358            1 :     fn rollback_object() {
     359            1 :         let res = crate::value_to_string!(|obj| {
     360            1 :             crate::value_as_object!(|obj| {
     361            1 :                 let entry = obj.key("1");
     362            1 :                 entry.value(1_i32);
     363            1 : 
     364            1 :                 let entry = obj.key("2");
     365            1 :                 let entry = {
     366            1 :                     let mut nested_obj = entry.object();
     367            1 :                     nested_obj.entry("foo", "bar");
     368            1 :                     nested_obj.rollback()
     369            1 :                 };
     370            1 : 
     371            1 :                 entry.value(2_i32);
     372            1 :             })
     373            1 :         });
     374              : 
     375            1 :         assert_eq!(res, r#"{"1":1,"2":2}"#);
     376            1 :     }
     377              : 
     378              :     #[test]
     379            1 :     fn rollback_list() {
     380            1 :         let res = crate::value_to_string!(|list| {
     381            1 :             crate::value_as_list!(|list| {
     382            1 :                 let entry = list.entry();
     383            1 :                 entry.value(1_i32);
     384            1 : 
     385            1 :                 let entry = list.entry();
     386            1 :                 let entry = {
     387            1 :                     let mut nested_list = entry.list();
     388            1 :                     nested_list.push("foo");
     389            1 :                     nested_list.rollback()
     390            1 :                 };
     391            1 : 
     392            1 :                 entry.value(2_i32);
     393            1 :             })
     394            1 :         });
     395              : 
     396            1 :         assert_eq!(res, r#"[1,2]"#);
     397            1 :     }
     398              : 
     399              :     #[test]
     400            1 :     fn string_escaping() {
     401            1 :         let mut buf = vec![];
     402            1 :         let mut object = ValueSer::new(&mut buf).object();
     403              : 
     404            1 :         let key = "hello";
     405            1 :         let value = "\n world";
     406              : 
     407            1 :         object.entry(format_args!("{key:?}"), value);
     408            1 :         object.finish();
     409              : 
     410            1 :         assert_eq!(buf, br#"{"\"hello\"":"\n world"}"#);
     411            1 :     }
     412              : }
        

Generated by: LCOV version 2.1-beta