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}