wekan_cli/client/
imports.rs1use std::{io::Write, marker::PhantomData};
3
4use serde::Serialize;
5use thiserror::Error;
6
7pub const MAX_IMPORT_JSON_BYTES: usize = 50 * 1024 * 1024;
8
9pub struct PreparedImportRequest<T> {
11 body: Vec<u8>,
12 _request: PhantomData<T>,
13}
14
15#[derive(Debug, Error)]
16#[error("encoded import request exceeds the 50 MiB JSON limit; no request was sent")]
17pub struct ImportEncodeError;
18
19impl<T: Serialize> PreparedImportRequest<T> {
20 pub fn new(request: &T) -> Result<Self, ImportEncodeError> {
21 let mut output = BoundedJson(Vec::new());
22 serde_json::to_writer(&mut output, request).map_err(|_| ImportEncodeError)?;
23 Ok(Self {
24 body: output.0,
25 _request: PhantomData,
26 })
27 }
28}
29
30impl<T> PreparedImportRequest<T> {
31 pub(super) fn into_body(self) -> Vec<u8> {
32 self.body
33 }
34}
35
36struct BoundedJson(Vec<u8>);
37
38impl Write for BoundedJson {
39 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
40 if buf.len() > MAX_IMPORT_JSON_BYTES - self.0.len() {
41 return Err(std::io::Error::other("import JSON exceeds 50 MiB"));
42 }
43 self.0.extend_from_slice(buf);
44 Ok(buf.len())
45 }
46
47 fn flush(&mut self) -> std::io::Result<()> {
48 Ok(())
49 }
50}
51
52#[cfg(test)]
53mod tests {
54 use super::*;
55
56 #[test]
57 fn final_encoding_limit_includes_json_escaping() {
58 let prepared = PreparedImportRequest::new(&"line\n").unwrap();
59 assert_eq!(prepared.body, br#""line\n""#);
60 assert!(PreparedImportRequest::new(&"\n".repeat(MAX_IMPORT_JSON_BYTES / 2)).is_err());
61 }
62}