1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
use serde_json::Value;
use std::{cell::RefCell, marker::PhantomData};

use serde::{Serialize, Serializer};

use crate::{
    schema::{SchemaGenerator, SchemaObject},
    Configurable, GenerateError, Metadata, ToValue,
};

/// Delegated serialization.
///
/// This adapter type lets us delegate the work of serializing an `I` by delegating it to `H`, where
/// `H` represents some sort of helper type (a la `serde_with`) that takes `I` and serializes it in
/// a specific way. This is a common pattern for using standard Rust types on the interior (such as
/// `std::time::Duration`) but (de)serializing them in more friendly/ergonomic forms, like `10s`,
/// which requires the use of custom (de)serialize functions or types.
///
/// Concretely, in the codegen, if a value has no mitigating configuration, we treat it as if it is
/// already `Serialize`, and use it directly. Otherwise, if specific attributes are in place that
/// indicate the use of an optional helper type, we construct `Delegated<I, H>` where `H` is the
/// value passed to `#[serde(with = "...")]`.
///
/// Astute readers may realize: "but isn't the value of `with` supposed to be a module path where a
/// custom `serialize` and/or `deserialize` function exist?", and they would be correct insofar as
/// that is what the `serde` documentation states. However, that value is used to construct a _path_
/// to a function to be called, which means that if you simply specify a type that is in scope, and
/// it has a public function called `serialize` and/or `deserialize`, you end up with a path like
/// `MyCustomType::serialize`, which is valid, and `serde` and `serde_with` use this fact to support
/// generating custom types that can be used for (de)serialization.
///
/// This means we do some extra work up front to avoid misclassifying usages of `#[serde(with =
/// "...")]` that are using module paths, and as such, this also means that those usages are not
/// supported.
pub struct Delegated<I, H> {
    input: I,
    _helper: PhantomData<fn(H)>,
}

// Adapter implementation for `serde_with` where the helper `H` is able to serialize `I`.
impl<I, H> Serialize for Delegated<I, serde_with::As<H>>
where
    H: serde_with::SerializeAs<I>,
{
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        H::serialize_as(&self.input, serializer)
    }
}

impl<I, H> From<I> for Delegated<I, serde_with::As<H>>
where
    H: serde_with::SerializeAs<I>,
{
    fn from(input: I) -> Self {
        Self {
            input,
            _helper: PhantomData,
        }
    }
}

// Passthrough implementation for any `H` which is `Configurable`.
impl<I, H> Configurable for Delegated<I, H>
where
    H: Configurable,
{
    fn referenceable_name() -> Option<&'static str> {
        // Forward to the underlying `H`.
        H::referenceable_name()
    }

    fn metadata() -> Metadata {
        // Forward to the underlying `H`.
        //
        // We have to convert from `Metadata` to `Metadata` which erases the default value,
        // notably, but delegated helpers should never actually have default values, so this is
        // essentially a no-op.
        H::metadata().convert()
    }

    fn validate_metadata(metadata: &Metadata) -> Result<(), GenerateError> {
        // Forward to the underlying `H`.
        //
        // We have to convert from `Metadata` to `Metadata` which erases the default value,
        // notably, but `serde_with` helpers should never actually have default values, so this is
        // essentially a no-op.
        let converted = metadata.convert();
        H::validate_metadata(&converted)
    }

    fn generate_schema(gen: &RefCell<SchemaGenerator>) -> Result<SchemaObject, GenerateError> {
        // Forward to the underlying `H`.
        H::generate_schema(gen)
    }
}

impl<I, H> ToValue for Delegated<I, H>
where
    H: Configurable,
    Delegated<I, H>: Serialize,
{
    fn to_value(&self) -> Value {
        serde_json::to_value(self).unwrap()
    }
}