serde_object/
assistant.rs

1//! Side channels for providing more information than the Serde API lets through in-band.
2
3use {
4    crate::{Object, Visitor},
5    lazy_static::lazy_static,
6    serde::de::{self, DeserializeSeed as _},
7    std::{any::Any, borrow::Cow, marker::PhantomData, sync::Mutex},
8    wyz::Pipe as _,
9};
10
11#[cfg(feature = "assistant-extra")]
12pub mod extra {
13    use {super::VariantKind, linkme::distributed_slice};
14
15    /// Called between EnumAccess::variant_seed and one of VariantAccess's methods.
16    #[distributed_slice]
17    pub static ENUM_VARIANT_ASSISTS: [fn() -> Option<VariantKind>] = [..];
18
19    pub fn enum_variant_hint() -> Option<VariantKind> {
20        // If it were possible to get type-GUIDs for non-'static types, this would be doable a lot more nicely.
21        // Without that, we can't inspect the EnumAccess or VariantAccess involved safely, though.
22        // Unfortunately, the RFC for non-'static TypeIds was rejected (<https://github.com/rust-lang/rust/issues/41875>),
23        // so this is the best I can do.
24
25        for enum_variant_assist in ENUM_VARIANT_ASSISTS.iter() {
26            if let result @ Some(_) = enum_variant_assist() {
27                return result;
28            }
29        }
30        None
31    }
32}
33
34pub enum VariantKind {
35    Unit,
36    Newtype,
37    Tuple(usize),
38    Struct(Cow<'static, [Cow<'static, str>]>),
39}
40
41pub trait EnumAssistant {
42    fn variant_hint<E: de::Error>(&self, variant: &Object) -> Result<VariantKind, E>;
43}
44
45impl<T: EnumAssistant> EnumAssistant for &T {
46    #[inline(always)]
47    fn variant_hint<E: de::Error>(&self, variant: &Object) -> Result<VariantKind, E> {
48        <T as EnumAssistant>::variant_hint(self, variant)
49    }
50}
51
52pub struct Seed<Assistant: EnumAssistant + Clone>(pub Assistant);
53impl<'de, Assistant: EnumAssistant + Clone> de::DeserializeSeed<'de> for Seed<Assistant> {
54    type Value = Object<'de>;
55
56    fn deserialize<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
57    where
58        D: de::Deserializer<'de>,
59    {
60        deserializer.deserialize_any(Visitor(self.0))
61    }
62}
63
64pub struct Assisted<'a, Assistant: EnumAssistant + 'static>(Object<'a>, PhantomData<Assistant>);
65lazy_static! {
66    static ref ASSIST_ASSISTANT: Mutex<Option<&'static (dyn Any + Sync)>> = Mutex::default();
67}
68pub fn assist<'a, Assistant: EnumAssistant + Sync + 'static>(
69    assistant: Assistant, //TODO: Lower the lifetime requirement for this parameter type.
70    deserialize: impl FnOnce() -> Assisted<'a, Assistant>,
71) -> Object<'a> {
72    *ASSIST_ASSISTANT.lock().unwrap() =
73        Some(unsafe { extend_ref(&assistant) as &'static (dyn Any + Sync) });
74    let result = deserialize();
75    ASSIST_ASSISTANT.lock().unwrap().take(); // Make sure the assistant ref doesn't stick around.
76    result.0
77}
78
79impl<'de, Assistant: EnumAssistant> de::Deserialize<'de> for Assisted<'de, Assistant> {
80    fn deserialize<D>(
81        deserializer: D,
82    ) -> std::result::Result<Self, <D as serde::de::Deserializer<'de>>::Error>
83    where
84        D: de::Deserializer<'de>,
85    {
86        Ok(Self(
87            Seed(
88                ASSIST_ASSISTANT
89                    .lock()
90                    .unwrap()
91                    .take()
92                    .expect("Could not retrieve stored EnumAssistant.")
93                    .pipe(|a| Any::downcast_ref::<Assistant>(a))
94                    .expect("Received wrong type of assistant."),
95            )
96            .deserialize(deserializer)?,
97            PhantomData,
98        ))
99    }
100}
101
102unsafe fn extend_ref<T>(reference: &'_ T) -> &'static T {
103    std::mem::transmute(reference)
104}