forest/rpc/reflect/
util.rs

1// Copyright 2019-2025 ChainSafe Systems
2// SPDX-License-Identifier: Apache-2.0, MIT
3use std::fmt::Display;
4
5use serde::{Deserialize, Deserializer, de::Visitor, forward_to_deserialize_any};
6
7/// "Introspection" by tracing a [`Deserialize`] to see if it's [`Option`]-like.
8///
9/// This allows us to smoothly translate between rust functions that take an optional
10/// parameter, and the content descriptors in OpenRPC.
11pub trait Optional<'de>: Deserialize<'de> {
12    fn optional() -> bool {
13        #[derive(Default)]
14        struct DummyDeserializer;
15
16        #[derive(thiserror::Error, Debug)]
17        #[error("")]
18        struct DeserializeOptionWasCalled(bool);
19
20        impl serde::de::Error for DeserializeOptionWasCalled {
21            fn custom<T: Display>(_: T) -> Self {
22                Self(false)
23            }
24        }
25
26        impl<'de> Deserializer<'de> for DummyDeserializer {
27            type Error = DeserializeOptionWasCalled;
28
29            fn deserialize_any<V: Visitor<'de>>(self, _: V) -> Result<V::Value, Self::Error> {
30                Err(DeserializeOptionWasCalled(false))
31            }
32
33            fn deserialize_option<V: Visitor<'de>>(self, _: V) -> Result<V::Value, Self::Error> {
34                Err(DeserializeOptionWasCalled(true))
35            }
36
37            forward_to_deserialize_any! {
38                bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string
39                bytes byte_buf unit unit_struct newtype_struct seq tuple
40                tuple_struct map struct enum identifier ignored_any
41            }
42        }
43
44        let Err(DeserializeOptionWasCalled(optional)) = Self::deserialize(DummyDeserializer) else {
45            unreachable!("DummyDeserializer never returns Ok(..)")
46        };
47        optional
48    }
49    /// # Panics
50    /// - This is only safe to call if [`Optional::optional`] returns `true`.
51    fn unwrap_none() -> Self {
52        Self::deserialize(serde_json::Value::Null)
53            .expect("`null` json values should deserialize to a `None` for option-like types")
54    }
55}
56
57impl<'de, T> Optional<'de> for T where T: Deserialize<'de> {}