unc_jsonrpc_client/
header.rs

1//! Client headers.
2//!
3//! This module includes everything you need to build valid header entries.
4
5use std::marker::PhantomData;
6
7pub use reqwest::header::{HeaderName, HeaderValue, InvalidHeaderValue, ToStrError};
8
9/// [`HeaderEntry`] attribute identifying those that have been prevalidated.
10///
11/// The specification of a header entry identified by this discriminant doesn't return a [`Result`].
12///
13/// ### Example
14///
15/// This example adds the header name `custom-header` and value `custom:some-value`.
16///
17/// ```
18/// use unc_jsonrpc_client::{
19///     header::{HeaderEntry, HeaderValue, Prevalidated},
20///     methods, JsonRpcClient,
21/// };
22///
23/// struct CustomHeader(HeaderValue);
24///
25/// impl HeaderEntry<Prevalidated> for CustomHeader {
26///     type HeaderName = &'static str;
27///     type HeaderValue = HeaderValue;
28///
29///     fn header_name(&self) -> &Self::HeaderName {
30///         &"custom-header"
31///     }
32///
33///     fn header_pair(self) -> (Self::HeaderName, Self::HeaderValue) {
34///         ("custom-header", self.0)
35///     }
36/// }
37/// # #[tokio::main]
38/// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
39///
40/// let header_value = HeaderValue::try_from("custom:some-value")?; // <- error handling here
41///
42/// let client = JsonRpcClient::connect("https://rpc.testnet.unc.org").header(CustomHeader(header_value));
43/// # Ok(())
44/// # }
45pub struct Prevalidated {
46    _priv: (),
47}
48
49/// [`HeaderEntry`] attribute identifying those that need to be validated.
50///
51/// The specification of a header entry identified by this discriminant will return a [`Result`].
52///
53/// ### Example
54///
55/// This example adds the header name `custom-header` and value `custom:some-value`.
56///
57/// ```
58/// # use std::{fmt, error::Error};
59/// use unc_jsonrpc_client::{
60///     header::{HeaderEntry, HeaderValue, Postvalidated},
61///     methods, JsonRpcClient,
62/// };
63///
64/// struct CustomValue(&'static str);
65///
66/// struct CustomHeader(CustomValue);
67///
68/// # #[derive(Debug)]
69/// struct CustomError;
70/// # impl Error for CustomError {}
71/// # impl fmt::Display for CustomError {
72/// #     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
73/// #         write!(f, "custom error")
74/// #     }
75/// # }
76///
77/// impl TryFrom<CustomValue> for HeaderValue {
78///     type Error = CustomError;
79///
80///     fn try_from(v: CustomValue) -> Result<Self, Self::Error> {
81///         HeaderValue::try_from(format!("custom:{}", v.0)).map_err(|_| CustomError)
82///     }
83/// }
84///
85/// impl HeaderEntry<Postvalidated<CustomError>> for CustomHeader {
86///     type HeaderName = &'static str;
87///     type HeaderValue = CustomValue;
88///
89///     fn header_name(&self) -> &Self::HeaderName {
90///         &"custom-header"
91///     }
92///
93///     fn header_pair(self) -> (Self::HeaderName, Self::HeaderValue) {
94///         ("custom-header", self.0)
95///     }
96/// }
97/// # #[tokio::main]
98/// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
99///
100/// let client = JsonRpcClient::connect("https://rpc.testnet.unc.org")
101///     .header(CustomHeader(CustomValue("some-value")))?; // <- error handling here
102/// # Ok(())
103/// # }
104pub struct Postvalidated<E> {
105    _priv: PhantomData<E>,
106}
107
108/// Trait for identifying valid header entries.
109///
110/// Header entries are distinguished by their discrimimants, (See [HeaderEntryDiscriminant]).
111pub trait HeaderEntry<D = Prevalidated>: Sized
112where
113    D: HeaderEntryDiscriminant<Self>,
114{
115    type HeaderName;
116    type HeaderValue;
117
118    fn header_name(&self) -> &Self::HeaderName;
119    fn header_pair(self) -> (Self::HeaderName, Self::HeaderValue);
120}
121
122pub use discriminant::HeaderEntryDiscriminant;
123mod discriminant {
124    use reqwest::header::IntoHeaderName;
125
126    use super::{super::JsonRpcClient, HeaderEntry, HeaderValue, Postvalidated, Prevalidated};
127
128    pub trait Sealed {}
129
130    /// Trait for defining a [`HeaderEntry`]'s application on a client.
131    pub trait HeaderEntryDiscriminant<H>: Sealed {
132        type Output;
133
134        fn apply(client: JsonRpcClient, entry: H) -> Self::Output;
135    }
136
137    impl Sealed for Prevalidated {}
138    impl<T> HeaderEntryDiscriminant<T> for Prevalidated
139    where
140        T: HeaderEntry<Self, HeaderValue = HeaderValue>,
141        T::HeaderName: IntoHeaderName,
142    {
143        type Output = JsonRpcClient;
144
145        fn apply(mut client: JsonRpcClient, entry: T) -> Self::Output {
146            let (k, v) = entry.header_pair();
147            client.headers.insert(k, v);
148            client
149        }
150    }
151
152    impl<E> Sealed for Postvalidated<E> {}
153    impl<T, E> HeaderEntryDiscriminant<T> for Postvalidated<E>
154    where
155        T: HeaderEntry<Self>,
156        T::HeaderName: IntoHeaderName,
157        T::HeaderValue: TryInto<HeaderValue, Error = E>,
158    {
159        type Output = Result<JsonRpcClient, E>;
160
161        fn apply(mut client: JsonRpcClient, entry: T) -> Self::Output {
162            let (k, v) = entry.header_pair();
163            client.headers.insert(k, v.try_into()?);
164            Ok(client)
165        }
166    }
167
168    impl<N: IntoHeaderName> HeaderEntry<Prevalidated> for (N, HeaderValue) {
169        type HeaderName = N;
170        type HeaderValue = HeaderValue;
171
172        fn header_name(&self) -> &Self::HeaderName {
173            &self.0
174        }
175
176        fn header_pair(self) -> (Self::HeaderName, Self::HeaderValue) {
177            self
178        }
179    }
180
181    impl<N, V> HeaderEntry<Postvalidated<V::Error>> for (N, V)
182    where
183        N: IntoHeaderName,
184        V: TryInto<HeaderValue>,
185    {
186        type HeaderName = N;
187        type HeaderValue = V;
188
189        fn header_name(&self) -> &Self::HeaderName {
190            &self.0
191        }
192
193        fn header_pair(self) -> (Self::HeaderName, Self::HeaderValue) {
194            self
195        }
196    }
197}