ctoolbox/formats/
multipart.rs

1//! Multipart form utilities. The meat of this is the `build_multipart`
2//! function. The serialization code was needed to make it be able to tell a
3//! `Vec<u8>` apart from other arrays. `Vec<u8>` are specifically assumed to be
4//! file uploads.
5use anyhow::{Result, anyhow};
6use reqwest::blocking::multipart::Part;
7
8use crate::utilities::reader::reader_to_bytes;
9use crate::utilities::serde_value::to_html_form_string;
10use serde::Serialize;
11
12/// Internal captured representation of a struct field for multipart building.
13enum CapturedField {
14    Bytes(Vec<u8>),
15    Array(Vec<serde_json::Value>),
16    Scalar(serde_json::Value),
17}
18
19/// Internal serializer error used to implement `serde::ser::Error` without panicking.
20#[derive(Debug)]
21struct CaptureError(anyhow::Error);
22
23impl CaptureError {
24    pub fn custom(msg: impl std::fmt::Display) -> Self {
25        CaptureError(anyhow!(msg.to_string()))
26    }
27}
28
29impl std::fmt::Display for CaptureError {
30    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
31        self.0.fmt(f)
32    }
33}
34
35impl std::error::Error for CaptureError {}
36
37impl serde::ser::Error for CaptureError {
38    fn custom<T: std::fmt::Display>(msg: T) -> Self {
39        CaptureError(anyhow::anyhow!(msg.to_string()))
40    }
41}
42
43use serde::ser::{SerializeSeq, SerializeStruct, Serializer};
44
45/// Serializer that captures a struct into a key -> `CapturedField` map.
46struct TopSerializer<'a> {
47    out: &'a mut std::collections::BTreeMap<String, CapturedField>,
48}
49
50impl<'a> Serializer for TopSerializer<'a> {
51    type Ok = ();
52    type Error = CaptureError;
53    type SerializeSeq = serde::ser::Impossible<(), CaptureError>; // Changed to avoid type mismatch since sequences are not supported at top-level
54    type SerializeTuple = serde::ser::Impossible<(), CaptureError>;
55    type SerializeTupleStruct = serde::ser::Impossible<(), CaptureError>;
56    type SerializeTupleVariant = serde::ser::Impossible<(), CaptureError>;
57    type SerializeMap = serde::ser::Impossible<(), CaptureError>;
58    type SerializeStruct = StructCapture<'a>;
59    type SerializeStructVariant = serde::ser::Impossible<(), CaptureError>;
60
61    fn serialize_struct(
62        self,
63        _name: &'static str,
64        _len: usize,
65    ) -> Result<Self::SerializeStruct, Self::Error> {
66        Ok(StructCapture { out: self.out })
67    }
68
69    // All other entry points at top-level are unsupported; we expect a struct.
70    fn serialize_bool(self, _v: bool) -> Result<Self::Ok, Self::Error> {
71        Err(CaptureError::custom("expected struct at top-level"))
72    }
73    fn serialize_i8(self, _v: i8) -> Result<Self::Ok, Self::Error> {
74        Err(CaptureError::custom("expected struct at top-level"))
75    }
76    fn serialize_i16(self, _v: i16) -> Result<Self::Ok, Self::Error> {
77        Err(CaptureError::custom("expected struct at top-level"))
78    }
79    fn serialize_i32(self, _v: i32) -> Result<Self::Ok, Self::Error> {
80        Err(CaptureError::custom("expected struct at top-level"))
81    }
82    fn serialize_i64(self, _v: i64) -> Result<Self::Ok, Self::Error> {
83        Err(CaptureError::custom("expected struct at top-level"))
84    }
85    fn serialize_u8(self, _v: u8) -> Result<Self::Ok, Self::Error> {
86        Err(CaptureError::custom("expected struct at top-level"))
87    }
88    fn serialize_u16(self, _v: u16) -> Result<Self::Ok, Self::Error> {
89        Err(CaptureError::custom("expected struct at top-level"))
90    }
91    fn serialize_u32(self, _v: u32) -> Result<Self::Ok, Self::Error> {
92        Err(CaptureError::custom("expected struct at top-level"))
93    }
94    fn serialize_u64(self, _v: u64) -> Result<Self::Ok, Self::Error> {
95        Err(CaptureError::custom("expected struct at top-level"))
96    }
97    fn serialize_f32(self, _v: f32) -> Result<Self::Ok, Self::Error> {
98        Err(CaptureError::custom("expected struct at top-level"))
99    }
100    fn serialize_f64(self, _v: f64) -> Result<Self::Ok, Self::Error> {
101        Err(CaptureError::custom("expected struct at top-level"))
102    }
103    fn serialize_char(self, _v: char) -> Result<Self::Ok, Self::Error> {
104        Err(CaptureError::custom("expected struct at top-level"))
105    }
106    fn serialize_str(self, _v: &str) -> Result<Self::Ok, Self::Error> {
107        Err(CaptureError::custom("expected struct at top-level"))
108    }
109    fn serialize_bytes(self, _v: &[u8]) -> Result<Self::Ok, Self::Error> {
110        Err(CaptureError::custom("expected struct at top-level"))
111    }
112    fn serialize_none(self) -> Result<Self::Ok, Self::Error> {
113        Err(CaptureError::custom("expected struct at top-level"))
114    }
115    fn serialize_some<T: ?Sized + Serialize>(
116        self,
117        _value: &T,
118    ) -> Result<Self::Ok, Self::Error> {
119        Err(CaptureError::custom("expected struct at top-level"))
120    }
121    fn serialize_unit(self) -> Result<Self::Ok, Self::Error> {
122        Err(CaptureError::custom("expected struct at top-level"))
123    }
124    fn serialize_unit_struct(
125        self,
126        _name: &'static str,
127    ) -> Result<Self::Ok, Self::Error> {
128        Err(CaptureError::custom("expected struct at top-level"))
129    }
130    fn serialize_unit_variant(
131        self,
132        _name: &'static str,
133        _variant_index: u32,
134        _variant: &'static str,
135    ) -> Result<Self::Ok, Self::Error> {
136        Err(CaptureError::custom("expected struct at top-level"))
137    }
138    fn serialize_newtype_struct<T: ?Sized + Serialize>(
139        self,
140        _name: &'static str,
141        _value: &T,
142    ) -> Result<Self::Ok, Self::Error> {
143        Err(CaptureError::custom("expected struct at top-level"))
144    }
145    fn serialize_newtype_variant<T: ?Sized + Serialize>(
146        self,
147        _name: &'static str,
148        _variant_index: u32,
149        _variant: &'static str,
150        _value: &T,
151    ) -> Result<Self::Ok, Self::Error> {
152        Err(CaptureError::custom("expected struct at top-level"))
153    }
154    fn serialize_seq(
155        self,
156        _len: Option<usize>,
157    ) -> Result<Self::SerializeSeq, Self::Error> {
158        Err(CaptureError::custom("expected struct at top-level"))
159    }
160    fn serialize_tuple(
161        self,
162        _len: usize,
163    ) -> Result<Self::SerializeTuple, Self::Error> {
164        Err(CaptureError::custom("expected struct at top-level"))
165    }
166    fn serialize_tuple_struct(
167        self,
168        _name: &'static str,
169        _len: usize,
170    ) -> Result<Self::SerializeTupleStruct, Self::Error> {
171        Err(CaptureError::custom("expected struct at top-level"))
172    }
173    fn serialize_tuple_variant(
174        self,
175        _name: &'static str,
176        _variant_index: u32,
177        _variant: &'static str,
178        _len: usize,
179    ) -> Result<Self::SerializeTupleVariant, Self::Error> {
180        Err(CaptureError::custom("expected struct at top-level"))
181    }
182    fn serialize_map(
183        self,
184        _len: Option<usize>,
185    ) -> Result<Self::SerializeMap, Self::Error> {
186        Err(CaptureError::custom("expected struct at top-level"))
187    }
188    fn serialize_struct_variant(
189        self,
190        _name: &'static str,
191        _variant_index: u32,
192        _variant: &'static str,
193        _len: usize,
194    ) -> Result<Self::SerializeStructVariant, Self::Error> {
195        Err(CaptureError::custom("expected struct at top-level"))
196    }
197}
198
199struct StructCapture<'a> {
200    out: &'a mut std::collections::BTreeMap<String, CapturedField>,
201}
202
203impl SerializeStruct for StructCapture<'_> {
204    type Ok = ();
205    type Error = CaptureError;
206
207    fn serialize_field<T: ?Sized + Serialize>(
208        &mut self,
209        key: &'static str,
210        value: &T,
211    ) -> Result<(), Self::Error> {
212        let captured = FieldSerializer::capture(value)?;
213        self.out.insert(key.to_string(), captured);
214        Ok(())
215    }
216
217    fn end(self) -> Result<Self::Ok, Self::Error> {
218        Ok(())
219    }
220}
221
222/// Serializer that captures a single field into a `CapturedField`.
223struct FieldSerializer;
224
225impl FieldSerializer {
226    fn capture<T: ?Sized + Serialize>(
227        value: &T,
228    ) -> Result<CapturedField, CaptureError> {
229        value.serialize(Self)
230    }
231}
232
233impl Serializer for FieldSerializer {
234    type Ok = CapturedField;
235    type Error = CaptureError;
236    type SerializeSeq = SeqCapture;
237    type SerializeTuple = serde::ser::Impossible<CapturedField, CaptureError>;
238    type SerializeTupleStruct =
239        serde::ser::Impossible<CapturedField, CaptureError>;
240    type SerializeTupleVariant =
241        serde::ser::Impossible<CapturedField, CaptureError>;
242    type SerializeMap = serde::ser::Impossible<CapturedField, CaptureError>;
243    type SerializeStruct = serde::ser::Impossible<CapturedField, CaptureError>;
244    type SerializeStructVariant =
245        serde::ser::Impossible<CapturedField, CaptureError>;
246
247    fn serialize_bool(self, v: bool) -> Result<Self::Ok, Self::Error> {
248        Ok(CapturedField::Scalar(serde_json::Value::Bool(v)))
249    }
250    fn serialize_i8(self, v: i8) -> Result<Self::Ok, Self::Error> {
251        Ok(CapturedField::Scalar(serde_json::Value::Number(v.into())))
252    }
253    fn serialize_i16(self, v: i16) -> Result<Self::Ok, Self::Error> {
254        Ok(CapturedField::Scalar(serde_json::Value::Number(v.into())))
255    }
256    fn serialize_i32(self, v: i32) -> Result<Self::Ok, Self::Error> {
257        Ok(CapturedField::Scalar(serde_json::Value::Number(v.into())))
258    }
259    fn serialize_i64(self, v: i64) -> Result<Self::Ok, Self::Error> {
260        Ok(CapturedField::Scalar(serde_json::Value::Number(v.into())))
261    }
262    fn serialize_i128(self, v: i128) -> Result<Self::Ok, Self::Error> {
263        Ok(CapturedField::Scalar(serde_json::Value::Number(v.into())))
264    }
265    fn serialize_u8(self, v: u8) -> Result<Self::Ok, Self::Error> {
266        Ok(CapturedField::Scalar(serde_json::Value::Number(v.into())))
267    }
268    fn serialize_u16(self, v: u16) -> Result<Self::Ok, Self::Error> {
269        Ok(CapturedField::Scalar(serde_json::Value::Number(v.into())))
270    }
271    fn serialize_u32(self, v: u32) -> Result<Self::Ok, Self::Error> {
272        Ok(CapturedField::Scalar(serde_json::Value::Number(v.into())))
273    }
274    fn serialize_u64(self, v: u64) -> Result<Self::Ok, Self::Error> {
275        Ok(CapturedField::Scalar(serde_json::Value::Number(v.into())))
276    }
277    fn serialize_u128(self, v: u128) -> Result<Self::Ok, Self::Error> {
278        Ok(CapturedField::Scalar(serde_json::Value::Number(v.into())))
279    }
280    fn serialize_f32(self, v: f32) -> Result<Self::Ok, Self::Error> {
281        let n = serde_json::Number::from_f64(f64::from(v))
282            .ok_or_else(|| CaptureError::custom("invalid f32"))?;
283        Ok(CapturedField::Scalar(serde_json::Value::Number(n)))
284    }
285    fn serialize_f64(self, v: f64) -> Result<Self::Ok, Self::Error> {
286        let n = serde_json::Number::from_f64(v)
287            .ok_or_else(|| CaptureError::custom("invalid f64"))?;
288        Ok(CapturedField::Scalar(serde_json::Value::Number(n)))
289    }
290    fn serialize_char(self, v: char) -> Result<Self::Ok, Self::Error> {
291        Ok(CapturedField::Scalar(serde_json::Value::String(
292            v.to_string(),
293        )))
294    }
295    fn serialize_str(self, v: &str) -> Result<Self::Ok, Self::Error> {
296        Ok(CapturedField::Scalar(serde_json::Value::String(
297            v.to_string(),
298        )))
299    }
300    fn serialize_bytes(self, v: &[u8]) -> Result<Self::Ok, Self::Error> {
301        Ok(CapturedField::Bytes(v.to_vec()))
302    }
303    fn serialize_none(self) -> Result<Self::Ok, Self::Error> {
304        Ok(CapturedField::Scalar(serde_json::Value::Null))
305    }
306    fn serialize_some<T: ?Sized + Serialize>(
307        self,
308        value: &T,
309    ) -> Result<Self::Ok, Self::Error> {
310        // Represent Option<T>::Some as the inner value
311        FieldSerializer::capture(value)
312    }
313    fn serialize_unit(self) -> Result<Self::Ok, Self::Error> {
314        Ok(CapturedField::Scalar(serde_json::Value::Null))
315    }
316    fn serialize_unit_struct(
317        self,
318        _name: &'static str,
319    ) -> Result<Self::Ok, Self::Error> {
320        Ok(CapturedField::Scalar(serde_json::Value::Null))
321    }
322    fn serialize_unit_variant(
323        self,
324        _name: &'static str,
325        _variant_index: u32,
326        variant: &'static str,
327    ) -> Result<Self::Ok, Self::Error> {
328        Ok(CapturedField::Scalar(serde_json::Value::String(
329            variant.to_string(),
330        )))
331    }
332    fn serialize_newtype_struct<T: ?Sized + Serialize>(
333        self,
334        _name: &'static str,
335        value: &T,
336    ) -> Result<Self::Ok, Self::Error> {
337        FieldSerializer::capture(value)
338    }
339    fn serialize_newtype_variant<T: ?Sized + Serialize>(
340        self,
341        _name: &'static str,
342        _variant_index: u32,
343        variant: &'static str,
344        value: &T,
345    ) -> Result<Self::Ok, Self::Error> {
346        // Represent as a simple map { variant: value } as a scalar JSON object
347        let mut map = serde_json::Map::new();
348        map.insert(
349            variant.to_string(),
350            serde_json::to_value(value)
351                .map_err(|e| CaptureError(anyhow!(e)))?,
352        );
353        Ok(CapturedField::Scalar(serde_json::Value::Object(map)))
354    }
355    fn serialize_seq(
356        self,
357        _len: Option<usize>,
358    ) -> Result<Self::SerializeSeq, Self::Error> {
359        Ok(SeqCapture { elems: Vec::new() })
360    }
361    fn serialize_tuple(
362        self,
363        _len: usize,
364    ) -> Result<Self::SerializeTuple, Self::Error> {
365        Err(CaptureError::custom("tuple not supported"))
366    }
367    fn serialize_tuple_struct(
368        self,
369        _name: &'static str,
370        _len: usize,
371    ) -> Result<Self::SerializeTupleStruct, Self::Error> {
372        Err(CaptureError::custom("tuple struct not supported"))
373    }
374    fn serialize_tuple_variant(
375        self,
376        _name: &'static str,
377        _variant_index: u32,
378        _variant: &'static str,
379        _len: usize,
380    ) -> Result<Self::SerializeTupleVariant, Self::Error> {
381        Err(CaptureError::custom("tuple variant not supported"))
382    }
383    fn serialize_map(
384        self,
385        _len: Option<usize>,
386    ) -> Result<Self::SerializeMap, Self::Error> {
387        // Fallback: capture as scalar JSON object
388        Err(CaptureError::custom(
389            "map capture at field level not supported",
390        ))
391    }
392    fn serialize_struct(
393        self,
394        _name: &'static str,
395        _len: usize,
396    ) -> Result<Self::SerializeStruct, Self::Error> {
397        Err(CaptureError::custom("nested struct capture not supported"))
398    }
399    fn serialize_struct_variant(
400        self,
401        _name: &'static str,
402        _variant_index: u32,
403        _variant: &'static str,
404        _len: usize,
405    ) -> Result<Self::SerializeStructVariant, Self::Error> {
406        Err(CaptureError::custom("struct variant not supported"))
407    }
408}
409
410enum SeqElem {
411    U8(u8),
412    Other(serde_json::Value),
413}
414
415struct SeqCapture {
416    elems: Vec<SeqElem>,
417}
418
419impl SerializeSeq for SeqCapture {
420    type Ok = CapturedField;
421    type Error = CaptureError;
422
423    fn serialize_element<T: ?Sized + Serialize>(
424        &mut self,
425        value: &T,
426    ) -> Result<(), Self::Error> {
427        // Capture each element, distinguishing u8 from everything else.
428        value.serialize(ElemCapture {
429            elems: &mut self.elems,
430        })
431    }
432
433    fn end(self) -> Result<Self::Ok, Self::Error> {
434        // If all elements are U8, treat the whole sequence as bytes; else as a JSON array.
435        if self.elems.iter().all(|e| matches!(e, SeqElem::U8(_))) {
436            let bytes = self
437                .elems
438                .into_iter()
439                .map(|e| match e {
440                    SeqElem::U8(b) => b,
441                    _ => 0,
442                })
443                .collect::<Vec<u8>>();
444            Ok(CapturedField::Bytes(bytes))
445        } else {
446            let arr = self
447                .elems
448                .into_iter()
449                .map(|e| match e {
450                    SeqElem::U8(b) => serde_json::Value::Number(b.into()),
451                    SeqElem::Other(v) => v,
452                })
453                .collect::<Vec<_>>();
454            Ok(CapturedField::Array(arr))
455        }
456    }
457}
458
459struct ElemCapture<'a> {
460    elems: &'a mut Vec<SeqElem>,
461}
462
463impl Serializer for ElemCapture<'_> {
464    type Ok = ();
465    type Error = CaptureError;
466    type SerializeSeq = serde::ser::Impossible<(), CaptureError>;
467    type SerializeTuple = serde::ser::Impossible<(), CaptureError>;
468    type SerializeTupleStruct = serde::ser::Impossible<(), CaptureError>;
469    type SerializeTupleVariant = serde::ser::Impossible<(), CaptureError>;
470    type SerializeMap = serde::ser::Impossible<(), CaptureError>;
471    type SerializeStruct = serde::ser::Impossible<(), CaptureError>;
472    type SerializeStructVariant = serde::ser::Impossible<(), CaptureError>;
473
474    fn serialize_u8(self, v: u8) -> Result<Self::Ok, Self::Error> {
475        self.elems.push(SeqElem::U8(v));
476        Ok(())
477    }
478
479    // For all other primitives, push as a JSON value for later to_html_form_string conversion.
480    fn serialize_bool(self, v: bool) -> Result<Self::Ok, Self::Error> {
481        self.elems.push(SeqElem::Other(serde_json::Value::Bool(v)));
482        Ok(())
483    }
484    fn serialize_i8(self, v: i8) -> Result<Self::Ok, Self::Error> {
485        self.elems
486            .push(SeqElem::Other(serde_json::Value::Number(v.into())));
487        Ok(())
488    }
489    fn serialize_i16(self, v: i16) -> Result<Self::Ok, Self::Error> {
490        self.elems
491            .push(SeqElem::Other(serde_json::Value::Number(v.into())));
492        Ok(())
493    }
494    fn serialize_i32(self, v: i32) -> Result<Self::Ok, Self::Error> {
495        self.elems
496            .push(SeqElem::Other(serde_json::Value::Number(v.into())));
497        Ok(())
498    }
499    fn serialize_i64(self, v: i64) -> Result<Self::Ok, Self::Error> {
500        self.elems
501            .push(SeqElem::Other(serde_json::Value::Number(v.into())));
502        Ok(())
503    }
504    fn serialize_u16(self, v: u16) -> Result<Self::Ok, Self::Error> {
505        self.elems
506            .push(SeqElem::Other(serde_json::Value::Number(v.into())));
507        Ok(())
508    }
509    fn serialize_u32(self, v: u32) -> Result<Self::Ok, Self::Error> {
510        self.elems
511            .push(SeqElem::Other(serde_json::Value::Number(v.into())));
512        Ok(())
513    }
514    fn serialize_u64(self, v: u64) -> Result<Self::Ok, Self::Error> {
515        self.elems
516            .push(SeqElem::Other(serde_json::Value::Number(v.into())));
517        Ok(())
518    }
519    fn serialize_f32(self, v: f32) -> Result<Self::Ok, Self::Error> {
520        let n = serde_json::Number::from_f64(f64::from(v))
521            .ok_or_else(|| CaptureError::custom("invalid f32"))?;
522        self.elems
523            .push(SeqElem::Other(serde_json::Value::Number(n)));
524        Ok(())
525    }
526    fn serialize_f64(self, v: f64) -> Result<Self::Ok, Self::Error> {
527        let n = serde_json::Number::from_f64(v)
528            .ok_or_else(|| CaptureError::custom("invalid f64"))?;
529        self.elems
530            .push(SeqElem::Other(serde_json::Value::Number(n)));
531        Ok(())
532    }
533    fn serialize_char(self, v: char) -> Result<Self::Ok, Self::Error> {
534        self.elems
535            .push(SeqElem::Other(serde_json::Value::String(v.to_string())));
536        Ok(())
537    }
538    fn serialize_str(self, v: &str) -> Result<Self::Ok, Self::Error> {
539        self.elems
540            .push(SeqElem::Other(serde_json::Value::String(v.to_string())));
541        Ok(())
542    }
543    fn serialize_bytes(self, v: &[u8]) -> Result<Self::Ok, Self::Error> {
544        // Represent as JSON array of numbers when used as element; bytes here is uncommon.
545        let arr = v
546            .iter()
547            .copied()
548            .map(|b| serde_json::Value::Number(b.into()))
549            .collect();
550        self.elems
551            .push(SeqElem::Other(serde_json::Value::Array(arr)));
552        Ok(())
553    }
554    fn serialize_none(self) -> Result<Self::Ok, Self::Error> {
555        self.elems.push(SeqElem::Other(serde_json::Value::Null));
556        Ok(())
557    }
558    fn serialize_some<T: ?Sized + Serialize>(
559        self,
560        value: &T,
561    ) -> Result<Self::Ok, Self::Error> {
562        // Represent as the inner value
563        value.serialize(self)
564    }
565    fn serialize_unit(self) -> Result<Self::Ok, Self::Error> {
566        self.elems.push(SeqElem::Other(serde_json::Value::Null));
567        Ok(())
568    }
569    fn serialize_unit_struct(
570        self,
571        _name: &'static str,
572    ) -> Result<Self::Ok, Self::Error> {
573        self.elems.push(SeqElem::Other(serde_json::Value::Null));
574        Ok(())
575    }
576    fn serialize_unit_variant(
577        self,
578        _name: &'static str,
579        _variant_index: u32,
580        variant: &'static str,
581    ) -> Result<Self::Ok, Self::Error> {
582        self.elems.push(SeqElem::Other(serde_json::Value::String(
583            variant.to_string(),
584        )));
585        Ok(())
586    }
587    fn serialize_newtype_struct<T: ?Sized + Serialize>(
588        self,
589        _name: &'static str,
590        value: &T,
591    ) -> Result<Self::Ok, Self::Error> {
592        // Serialize inner as JSON value
593        let v = serde_json::to_value(value)
594            .map_err(|e| CaptureError(anyhow!(e)))?;
595        self.elems.push(SeqElem::Other(v));
596        Ok(())
597    }
598    fn serialize_newtype_variant<T: ?Sized + Serialize>(
599        self,
600        _name: &'static str,
601        _variant_index: u32,
602        variant: &'static str,
603        value: &T,
604    ) -> Result<Self::Ok, Self::Error> {
605        let mut map = serde_json::Map::new();
606        map.insert(
607            variant.to_string(),
608            serde_json::to_value(value)
609                .map_err(|e| CaptureError(anyhow!(e)))?,
610        );
611        self.elems
612            .push(SeqElem::Other(serde_json::Value::Object(map)));
613        Ok(())
614    }
615    fn serialize_seq(
616        self,
617        _len: Option<usize>,
618    ) -> Result<Self::SerializeSeq, Self::Error> {
619        // Nested sequences as elements are uncommon; represent as error to keep implementation small.
620        Err(CaptureError::custom("nested sequences not supported"))
621    }
622    fn serialize_tuple(
623        self,
624        _len: usize,
625    ) -> Result<Self::SerializeTuple, Self::Error> {
626        Err(CaptureError::custom("tuple elements not supported"))
627    }
628    fn serialize_tuple_struct(
629        self,
630        _name: &'static str,
631        _len: usize,
632    ) -> Result<Self::SerializeTupleStruct, Self::Error> {
633        Err(CaptureError::custom("tuple struct elements not supported"))
634    }
635    fn serialize_tuple_variant(
636        self,
637        _name: &'static str,
638        _variant_index: u32,
639        _variant: &'static str,
640        _len: usize,
641    ) -> Result<Self::SerializeTupleVariant, Self::Error> {
642        Err(CaptureError::custom("tuple variant elements not supported"))
643    }
644    fn serialize_map(
645        self,
646        _len: Option<usize>,
647    ) -> Result<Self::SerializeMap, Self::Error> {
648        Err(CaptureError::custom("map elements not supported"))
649    }
650    fn serialize_struct(
651        self,
652        _name: &'static str,
653        _len: usize,
654    ) -> Result<Self::SerializeStruct, Self::Error> {
655        Err(CaptureError::custom("struct elements not supported"))
656    }
657    fn serialize_struct_variant(
658        self,
659        _name: &'static str,
660        _variant_index: u32,
661        _variant: &'static str,
662        _len: usize,
663    ) -> Result<Self::SerializeStructVariant, Self::Error> {
664        Err(CaptureError::custom(
665            "struct variant elements not supported",
666        ))
667    }
668}
669
670pub fn build_multipart<T: Serialize>(data: &T) -> Result<(Vec<u8>, String)> {
671    // Build multipart body
672    let mut multipart = reqwest::blocking::multipart::Form::new();
673
674    // Capture fields with type-aware Vec<u8> detection.
675    let mut fields = std::collections::BTreeMap::<String, CapturedField>::new();
676    data.serialize(TopSerializer { out: &mut fields })
677        .map_err(|e| anyhow!(e.to_string()))?;
678
679    for (k, captured) in fields {
680        match captured {
681            CapturedField::Bytes(bytes) => {
682                let bytes_part = Part::bytes(bytes);
683                multipart = multipart.part(k.clone(), bytes_part);
684            }
685            CapturedField::Array(arr) => {
686                for item in arr {
687                    let val = to_html_form_string(&item)?;
688                    if let Some(v_str) = val {
689                        multipart = multipart.text(k.clone(), v_str);
690                    }
691                    // Skip None values
692                }
693            }
694            CapturedField::Scalar(v) => {
695                // Convert serde_json::Value to a string representation suitable for HTML forms
696                let val = to_html_form_string(&v)?;
697                if val.is_none() {
698                    // Skip None values (e.g., unchecked checkboxes)
699                    continue;
700                }
701                if let Some(vs) = val {
702                    multipart = multipart.text(k, vs);
703                } else {
704                    return Err(anyhow!(
705                        "Unsupported value type for field {k}: {v:?}"
706                    ));
707                }
708            }
709        }
710    }
711
712    let boundary = multipart.boundary().to_owned();
713    let reader = multipart.into_reader();
714    let buffer = reader_to_bytes(reader)?;
715    let content_type = format!("multipart/form-data; boundary={boundary}");
716    Ok((buffer, content_type))
717}
718
719#[crate::ctb_test]
720fn test_build_multipart() {
721    #[derive(Serialize)]
722    struct TestData {
723        bytes_array: Vec<u8>,
724        admin_users: Vec<u64>,
725        fixed_port: u16,
726        server_address: String,
727    }
728
729    let data = TestData {
730        bytes_array: vec![1, 2, 3],
731        admin_users: vec![12, 13],
732        fixed_port: 5678,
733        server_address: "bar".to_string(),
734    };
735
736    let result = build_multipart(&data);
737    assert!(result.is_ok());
738    let (buffer, content_type) = result.unwrap();
739    assert!(content_type.starts_with("multipart/form-data; boundary="));
740    // The buffer should contain the fields; exact boundary varies, so check for presence
741    let buffer_str = String::from_utf8_lossy(&buffer);
742    assert!(
743        buffer_str.contains("\"bytes_array\"\r\n\r\n\x01\x02\x03"),
744        "Buffer not contains expected: {buffer_str}"
745    );
746    assert!(
747        buffer_str.contains("\"admin_users\"\r\n\r\n12"),
748        "Buffer not contains expected: {buffer_str}"
749    );
750    assert!(
751        buffer_str.contains("\"admin_users\"\r\n\r\n13"),
752        "Buffer not contains expected: {buffer_str}"
753    );
754    assert!(
755        buffer_str.contains("\"fixed_port\"\r\n\r\n5678"),
756        "Buffer not contains expected: {buffer_str}"
757    );
758    assert!(
759        buffer_str.contains("\"server_address\"\r\n\r\nbar"),
760        "Buffer not contains expected: {buffer_str}"
761    );
762}