1use crate::types::{ContentRange, Patch, Version};
4use bytes::Bytes;
5use std::collections::BTreeMap;
6
7#[cfg(feature = "server")]
8use axum::{
9 body::Body,
10 http::{header, HeaderValue, StatusCode},
11 response::{IntoResponse, Response},
12};
13
14#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
15pub struct Update {
16 pub version: Vec<Version>,
18 pub parents: Vec<Version>,
20 pub current_version: Option<Vec<Version>>,
22 pub merge_type: Option<String>,
24 pub patches: Option<Vec<Patch>>,
26 pub body: Option<Bytes>,
28 pub content_range: Option<ContentRange>,
30 pub content_type: Option<String>,
32 pub status: u16,
34 pub extra_headers: BTreeMap<String, String>,
36 pub url: Option<String>,
38}
39
40#[cfg(feature = "fuzzing")]
41impl<'a> arbitrary::Arbitrary<'a> for Update {
42 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
43 Ok(Update {
44 version: u.arbitrary()?,
45 parents: u.arbitrary()?,
46 current_version: u.arbitrary()?,
47 merge_type: u.arbitrary()?,
48 patches: u.arbitrary()?,
49 body: {
50 let v: Option<Vec<u8>> = u.arbitrary()?;
51 v.map(bytes::Bytes::from)
52 },
53 content_range: u.arbitrary()?,
54 content_type: u.arbitrary()?,
55 status: u.arbitrary()?,
56 extra_headers: u.arbitrary()?,
57 url: u.arbitrary()?,
58 })
59 }
60}
61
62impl Update {
63 #[must_use]
65 pub fn snapshot(version: Version, body: impl Into<Bytes>) -> Self {
66 Update {
67 version: vec![version],
68 parents: vec![],
69 current_version: None,
70 merge_type: None,
71 patches: None,
72 body: Some(body.into()),
73 content_range: None,
74 content_type: None,
75 status: 200,
76 extra_headers: BTreeMap::new(),
77 url: None,
78 }
79 }
80
81 #[must_use]
83 pub fn patched(version: Version, patches: Vec<Patch>) -> Self {
84 Update {
85 version: vec![version],
86 parents: vec![],
87 current_version: None,
88 merge_type: None,
89 patches: Some(patches),
90 body: None,
91 content_range: None,
92 content_type: None,
93 status: 200,
94 url: None,
95 extra_headers: BTreeMap::new(),
96 }
97 }
98
99 #[inline]
100 #[must_use]
101 pub fn is_snapshot(&self) -> bool {
102 self.body.is_some()
103 }
104
105 #[inline]
106 #[must_use]
107 pub fn is_patched(&self) -> bool {
108 self.patches.is_some()
109 }
110
111 #[inline]
112 #[must_use]
113 pub fn primary_version(&self) -> Option<&Version> {
114 self.version.first()
115 }
116
117 #[inline]
118 #[must_use]
119 pub fn body_str(&self) -> Option<&str> {
120 self.body.as_ref().and_then(|b| std::str::from_utf8(b).ok())
121 }
122
123 #[must_use]
124 pub fn subscription_snapshot(version: Version, body: impl Into<Bytes>) -> Self {
125 Update::snapshot(version, body).with_status(209)
126 }
127
128 #[must_use]
129 pub fn subscription_patched(version: Version, patches: Vec<Patch>) -> Self {
130 Update::patched(version, patches).with_status(209)
131 }
132
133 #[must_use]
134 pub fn with_parent(mut self, parent: Version) -> Self {
135 self.parents.push(parent);
136 self
137 }
138
139 #[must_use]
140 pub fn with_parents(mut self, parents: Vec<Version>) -> Self {
141 self.parents.extend(parents);
142 self
143 }
144
145 #[must_use]
146 pub fn with_current_version(mut self, version: Version) -> Self {
147 if self.current_version.is_none() {
148 self.current_version = Some(Vec::new());
149 }
150 if let Some(ref mut versions) = self.current_version {
151 versions.push(version);
152 }
153 self
154 }
155
156 #[must_use]
157 pub fn with_merge_type(mut self, merge_type: impl Into<String>) -> Self {
158 self.merge_type = Some(merge_type.into());
159 self
160 }
161
162 #[must_use]
163 pub fn with_content_range(mut self, content_range: ContentRange) -> Self {
164 self.content_range = Some(content_range);
165 self
166 }
167
168 #[must_use]
169 pub fn with_content_type(mut self, content_type: impl Into<String>) -> Self {
170 self.content_type = Some(content_type.into());
171 self
172 }
173
174 #[must_use]
175 pub fn with_status(mut self, status: u16) -> Self {
176 self.status = status;
177 self
178 }
179
180 #[must_use]
181 pub fn with_header(mut self, name: impl Into<String>, value: impl Into<String>) -> Self {
182 self.extra_headers.insert(name.into(), value.into());
183 self
184 }
185
186 #[must_use]
187 pub fn to_json(&self) -> serde_json::Value {
188 let mut obj = serde_json::Map::new();
189
190 obj.insert(
191 "version".to_string(),
192 serde_json::Value::Array(self.version.iter().map(|v| v.to_json()).collect()),
193 );
194
195 obj.insert(
196 "parents".to_string(),
197 serde_json::Value::Array(self.parents.iter().map(|v| v.to_json()).collect()),
198 );
199
200 if let Some(body) = &self.body {
201 obj.insert(
202 "body".to_string(),
203 serde_json::Value::String(String::from_utf8_lossy(body).into_owned()),
204 );
205 }
206
207 if let Some(merge_type) = &self.merge_type {
208 obj.insert(
209 "merge_type".to_string(),
210 serde_json::Value::String(merge_type.clone()),
211 );
212 }
213
214 serde_json::Value::Object(obj)
215 }
216}
217
218#[cfg(feature = "server")]
219impl IntoResponse for Update {
220 fn into_response(self) -> Response {
221 let mut response_builder = UpdateResponse::new(self.status);
222
223 if !self.version.is_empty() {
224 response_builder = response_builder.with_version(self.version.clone());
225 }
226
227 if !self.parents.is_empty() {
228 response_builder = response_builder.with_parents(self.parents.clone());
229 }
230
231 if let Some(current_version) = &self.current_version {
232 response_builder = response_builder.with_current_version(current_version.clone());
233 }
234
235 if let Some(content_type) = &self.content_type {
236 response_builder = response_builder.with_header(
237 header::CONTENT_TYPE.as_str().to_string(),
238 content_type.clone(),
239 );
240 } else if self.patches.is_some() {
241 response_builder = response_builder.with_header(
242 header::CONTENT_TYPE.as_str().to_string(),
243 crate::protocol::constants::media_types::BRAID_PATCH.to_string(),
244 );
245 }
246
247 for (key, value) in &self.extra_headers {
248 response_builder = response_builder.with_header(key.clone(), value.clone());
249 }
250
251 if let Some(body) = &self.body {
252 response_builder = response_builder.with_body(body.clone());
253 } else if let Some(patches) = &self.patches {
254 let patches_str = patches.len().to_string();
255 response_builder = response_builder.with_header(
256 crate::protocol::constants::headers::PATCHES
257 .as_str()
258 .to_string(),
259 patches_str,
260 );
261
262 if patches.len() == 1 {
263 let patch = &patches[0];
264 let content_range = format!("{} {}", patch.unit, patch.range);
265 response_builder = response_builder.with_header(
266 crate::protocol::constants::headers::CONTENT_RANGE
267 .as_str()
268 .to_string(),
269 content_range,
270 );
271 response_builder = response_builder.with_body(patch.content.clone());
272 } else if patches.len() > 1 {
273 let mut multi_body = bytes::BytesMut::new();
274 for patch in patches {
275 use bytes::BufMut;
276 let patch_headers = format!(
277 "Content-Length: {}\r\nContent-Range: {} {}\r\n\r\n",
278 patch.len(),
279 patch.unit,
280 patch.range
281 );
282 multi_body.put_slice(patch_headers.as_bytes());
283 multi_body.put_slice(&patch.content);
284 multi_body.put_slice(b"\r\n");
285 }
286 response_builder = response_builder.with_body(multi_body.freeze());
287 }
288 }
289
290 response_builder.build()
291 }
292}
293
294#[cfg(feature = "server")]
295pub struct UpdateResponse {
296 status: u16,
297 headers: BTreeMap<String, String>,
298 body: Option<Bytes>,
299}
300
301#[cfg(feature = "server")]
302impl UpdateResponse {
303 pub fn new(status: u16) -> Self {
304 UpdateResponse {
305 status,
306 headers: BTreeMap::new(),
307 body: None,
308 }
309 }
310
311 pub fn with_version(mut self, versions: Vec<Version>) -> Self {
312 let version_str = crate::protocol::format_version_header(&versions);
313 self.headers.insert(
314 crate::protocol::constants::headers::VERSION
315 .as_str()
316 .to_string(),
317 version_str,
318 );
319 self
320 }
321
322 pub fn with_parents(mut self, parents: Vec<Version>) -> Self {
323 let parents_str = crate::protocol::format_version_header(&parents);
324 self.headers.insert(
325 crate::protocol::constants::headers::PARENTS
326 .as_str()
327 .to_string(),
328 parents_str,
329 );
330 self
331 }
332
333 pub fn with_current_version(mut self, versions: Vec<Version>) -> Self {
334 let current_version_str = crate::protocol::format_version_header(&versions);
335 self.headers.insert(
336 crate::protocol::constants::headers::CURRENT_VERSION
337 .as_str()
338 .to_string(),
339 current_version_str,
340 );
341 self
342 }
343
344 pub fn with_body(mut self, body: impl Into<Bytes>) -> Self {
345 self.body = Some(body.into());
346 self
347 }
348
349 pub fn with_header(mut self, key: String, value: String) -> Self {
350 self.headers.insert(key, value);
351 self
352 }
353
354 pub fn build(self) -> Response {
355 let mut response = match self.status {
356 200 => Response::builder().status(StatusCode::OK),
357 209 => Response::builder().status(StatusCode::from_u16(209).unwrap()),
358 404 => Response::builder().status(StatusCode::NOT_FOUND),
359 500 => Response::builder().status(StatusCode::INTERNAL_SERVER_ERROR),
360 _ => Response::builder().status(StatusCode::from_u16(self.status).unwrap()),
361 };
362
363 for (key, value) in &self.headers {
364 if let Ok(header_value) = value.parse::<HeaderValue>() {
365 response = response.header(key, header_value);
366 }
367 }
368
369 if let Some(body) = self.body {
370 response
371 .header(header::CONTENT_LENGTH, body.len())
372 .body(Body::from(body))
373 .unwrap_or_else(|_| Response::default())
374 } else {
375 response
376 .body(Body::empty())
377 .unwrap_or_else(|_| Response::default())
378 }
379 }
380}
381
382impl Default for Update {
383 fn default() -> Self {
384 Update {
385 version: vec![],
386 parents: vec![],
387 current_version: None,
388 merge_type: None,
389 patches: None,
390 body: None,
391 content_range: None,
392 content_type: None,
393 status: 200,
394 extra_headers: BTreeMap::new(),
395 url: None,
396 }
397 }
398}
399
400#[cfg(test)]
401mod tests {
402 use super::*;
403
404 #[test]
405 fn test_update_snapshot() {
406 let update = Update::snapshot(Version::new("v1"), "body");
407 assert_eq!(update.version.len(), 1);
408 assert!(update.body.is_some());
409 assert!(update.patches.is_none());
410 assert!(update.is_snapshot());
411 assert!(!update.is_patched());
412 }
413
414 #[test]
415 fn test_update_patched() {
416 let update = Update::patched(Version::new("v1"), vec![Patch::json(".field", "value")]);
417 assert_eq!(update.version.len(), 1);
418 assert!(update.patches.is_some());
419 assert!(update.body.is_none());
420 assert!(update.is_patched());
421 assert!(!update.is_snapshot());
422 }
423
424 #[test]
425 fn test_update_builder() {
426 let update = Update::snapshot(Version::new("v1"), "body")
427 .with_parent(Version::new("v0"))
428 .with_merge_type("diamond");
429 assert_eq!(update.parents.len(), 1);
430 assert_eq!(update.merge_type, Some("diamond".to_string()));
431 }
432
433 #[test]
434 fn test_primary_version() {
435 let update = Update::snapshot(Version::new("v1"), "body");
436 assert_eq!(update.primary_version(), Some(&Version::new("v1")));
437 }
438
439 #[test]
440 fn test_body_str() {
441 let update = Update::snapshot(Version::new("v1"), "hello");
442 assert_eq!(update.body_str(), Some("hello"));
443 }
444
445 #[test]
446 fn test_with_parents() {
447 let update = Update::snapshot(Version::new("v3"), "merged")
448 .with_parents(vec![Version::new("v1"), Version::new("v2")]);
449 assert_eq!(update.parents.len(), 2);
450 }
451
452 #[test]
453 fn test_with_header() {
454 let update = Update::snapshot(Version::new("v1"), "data").with_header("X-Custom", "value");
455 assert_eq!(
456 update.extra_headers.get("X-Custom"),
457 Some(&"value".to_string())
458 );
459 }
460
461 #[test]
462 fn test_to_json() {
463 let update = Update::snapshot(Version::new("v1"), "data")
464 .with_parent(Version::new("v0"))
465 .with_merge_type("diamond");
466 let json = update.to_json();
467 assert!(json.get("version").is_some());
468 assert!(json.get("parents").is_some());
469 assert!(json.get("body").is_some());
470 assert!(json.get("merge_type").is_some());
471 }
472
473 #[test]
474 fn test_default() {
475 let update = Update::default();
476 assert!(update.version.is_empty());
477 assert!(update.parents.is_empty());
478 assert_eq!(update.status, 200);
479 }
480
481 #[test]
482 fn test_subscription_snapshot() {
483 let update = Update::subscription_snapshot(Version::new("v1"), "data");
484 assert_eq!(update.status, 209);
485 assert!(update.is_snapshot());
486 assert!(!update.is_patched());
487 }
488
489 #[test]
490 fn test_subscription_patched() {
491 let update =
492 Update::subscription_patched(Version::new("v2"), vec![Patch::json(".field", "value")]);
493 assert_eq!(update.status, 209);
494 assert!(update.is_patched());
495 assert!(!update.is_snapshot());
496 }
497
498 #[test]
499 fn test_subscription_with_parents() {
500 let update = Update::subscription_snapshot(Version::new("v2"), "data")
501 .with_parent(Version::new("v1"));
502 assert_eq!(update.status, 209);
503 assert_eq!(update.parents.len(), 1);
504 }
505
506 #[test]
507 fn test_zero_length_body() {
508 let update = Update::snapshot(Version::new("v1"), "");
510 assert!(update.body.is_some());
511 assert_eq!(update.body.as_ref().unwrap().len(), 0);
512 }
513
514 #[test]
515 fn test_zero_length_subscription() {
516 let update = Update::subscription_snapshot(Version::new("v1"), Bytes::new());
518 assert_eq!(update.status, 209);
519 assert!(update.body.is_some());
520 assert_eq!(update.body.as_ref().unwrap().len(), 0);
521 }
522}