rullst_core/client_contract/
policy.rs1use super::{
2 CURRENT_CLIENT_CONTRACT_VERSION, ClientContractError, ClientRequest, ClientVersionOffer,
3 ContractVersion, DEFAULT_CLIENT_CONTRACT_BODY_BYTES, MAX_CLIENT_CONTRACT_BODY_BYTES,
4 ServerFailure, ServerResponse,
5};
6use serde::Serialize;
7use serde::de::DeserializeOwned;
8use std::io::Write;
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12#[non_exhaustive]
13pub struct ClientContractPolicy {
14 minimum_version: ContractVersion,
15 current_version: ContractVersion,
16 max_body_bytes: usize,
17}
18
19impl ClientContractPolicy {
20 pub const fn new(
22 minimum_version: ContractVersion,
23 current_version: ContractVersion,
24 max_body_bytes: usize,
25 ) -> Result<Self, ClientContractError> {
26 if minimum_version.get() > current_version.get()
27 || max_body_bytes == 0
28 || max_body_bytes > MAX_CLIENT_CONTRACT_BODY_BYTES
29 {
30 return Err(ClientContractError::InvalidPolicy);
31 }
32 Ok(Self {
33 minimum_version,
34 current_version,
35 max_body_bytes,
36 })
37 }
38
39 pub const fn minimum_version(self) -> ContractVersion {
41 self.minimum_version
42 }
43
44 pub const fn current_version(self) -> ContractVersion {
46 self.current_version
47 }
48
49 pub const fn max_body_bytes(self) -> usize {
51 self.max_body_bytes
52 }
53
54 pub fn decode_offer(self, body: &[u8]) -> Result<ClientVersionOffer, ClientContractError> {
56 self.validate_body_size(body.len())?;
57 serde_json::from_slice(body).map_err(ClientContractError::InvalidJson)
58 }
59
60 pub fn encode_offer(self, offer: &ClientVersionOffer) -> Result<Vec<u8>, ClientContractError> {
62 self.encode_bounded(offer)
63 }
64
65 pub fn negotiate(
67 self,
68 offer: &ClientVersionOffer,
69 ) -> Result<ContractVersion, ClientContractError> {
70 offer
71 .supported_versions()
72 .iter()
73 .rev()
74 .copied()
75 .find(|version| *version >= self.minimum_version && *version <= self.current_version)
76 .ok_or(ClientContractError::NoMutualVersion)
77 }
78
79 pub const fn validate_version(
81 self,
82 version: ContractVersion,
83 ) -> Result<(), ClientContractError> {
84 if version.get() < self.minimum_version.get() || version.get() > self.current_version.get()
85 {
86 Err(ClientContractError::UnsupportedVersion {
87 received: version.get(),
88 minimum: self.minimum_version.get(),
89 current: self.current_version.get(),
90 })
91 } else {
92 Ok(())
93 }
94 }
95
96 pub fn decode_request<T>(self, body: &[u8]) -> Result<ClientRequest<T>, ClientContractError>
98 where
99 T: DeserializeOwned,
100 {
101 self.validate_body_size(body.len())?;
102 let request = serde_json::from_slice::<ClientRequest<T>>(body)
103 .map_err(ClientContractError::InvalidJson)?;
104 self.validate_version(request.version())?;
105 Ok(request)
106 }
107
108 pub fn encode_request<T>(
110 self,
111 request: &ClientRequest<T>,
112 ) -> Result<Vec<u8>, ClientContractError>
113 where
114 T: Serialize,
115 {
116 self.validate_version(request.version())?;
117 self.encode_bounded(request)
118 }
119
120 pub fn decode_response<T>(self, body: &[u8]) -> Result<ServerResponse<T>, ClientContractError>
122 where
123 T: DeserializeOwned,
124 {
125 self.validate_body_size(body.len())?;
126 let response = serde_json::from_slice::<ServerResponse<T>>(body)
127 .map_err(ClientContractError::InvalidJson)?;
128 self.validate_version(response.version())?;
129 Ok(response)
130 }
131
132 pub fn encode_response<T>(
134 self,
135 response: &ServerResponse<T>,
136 ) -> Result<Vec<u8>, ClientContractError>
137 where
138 T: Serialize,
139 {
140 self.validate_version(response.version())?;
141 self.encode_bounded(response)
142 }
143
144 pub fn decode_failure(self, body: &[u8]) -> Result<ServerFailure, ClientContractError> {
146 self.validate_body_size(body.len())?;
147 let response = serde_json::from_slice::<ServerFailure>(body)
148 .map_err(ClientContractError::InvalidJson)?;
149 self.validate_version(response.version())?;
150 Ok(response)
151 }
152
153 pub fn encode_failure(self, response: &ServerFailure) -> Result<Vec<u8>, ClientContractError> {
155 self.validate_version(response.version())?;
156 self.encode_bounded(response)
157 }
158
159 fn encode_bounded<T>(self, value: &T) -> Result<Vec<u8>, ClientContractError>
160 where
161 T: Serialize,
162 {
163 let mut writer = BoundedJsonWriter::new(self.max_body_bytes);
164 match serde_json::to_writer(&mut writer, value) {
165 Ok(()) => Ok(writer.into_bytes()),
166 Err(_) if writer.exceeded() => Err(ClientContractError::BodyTooLarge {
167 maximum: self.max_body_bytes,
168 }),
169 Err(error) => Err(ClientContractError::EncodeJson(error)),
170 }
171 }
172
173 const fn validate_body_size(self, received: usize) -> Result<(), ClientContractError> {
174 if received > self.max_body_bytes {
175 Err(ClientContractError::BodyTooLarge {
176 maximum: self.max_body_bytes,
177 })
178 } else {
179 Ok(())
180 }
181 }
182}
183
184struct BoundedJsonWriter {
185 bytes: Vec<u8>,
186 maximum: usize,
187 exceeded: bool,
188}
189
190impl BoundedJsonWriter {
191 fn new(maximum: usize) -> Self {
192 Self {
193 bytes: Vec::new(),
194 maximum,
195 exceeded: false,
196 }
197 }
198
199 fn exceeded(&self) -> bool {
200 self.exceeded
201 }
202
203 fn into_bytes(self) -> Vec<u8> {
204 self.bytes
205 }
206}
207
208impl Write for BoundedJsonWriter {
209 fn write(&mut self, buffer: &[u8]) -> std::io::Result<usize> {
210 let remaining = self.maximum.saturating_sub(self.bytes.len());
211 if buffer.len() > remaining {
212 self.exceeded = true;
213 return Err(std::io::Error::other("client contract body limit reached"));
214 }
215 self.bytes.extend_from_slice(buffer);
216 Ok(buffer.len())
217 }
218
219 fn flush(&mut self) -> std::io::Result<()> {
220 Ok(())
221 }
222}
223
224impl Default for ClientContractPolicy {
225 fn default() -> Self {
226 Self {
227 minimum_version: CURRENT_CLIENT_CONTRACT_VERSION,
228 current_version: CURRENT_CLIENT_CONTRACT_VERSION,
229 max_body_bytes: DEFAULT_CLIENT_CONTRACT_BODY_BYTES,
230 }
231 }
232}