1use anyhow::Result;
2use base64::Engine;
3use serde_json::Value;
4
5use crate::schema::ResultDecode;
6
7#[derive(Debug, Clone)]
8pub enum DecodedBody {
9 Json(Value),
10 Text(String),
11 Html(String),
12 Xml(String),
13 Binary(Vec<u8>),
14}
15
16impl DecodedBody {
17 pub fn as_json(&self) -> Option<&Value> {
18 match self {
19 DecodedBody::Json(v) => Some(v),
20 _ => None,
21 }
22 }
23
24 pub fn as_text(&self) -> Option<&str> {
25 match self {
26 DecodedBody::Text(v) | DecodedBody::Html(v) | DecodedBody::Xml(v) => Some(v),
27 _ => None,
28 }
29 }
30
31 pub fn to_json_value(&self) -> Value {
32 match self {
33 DecodedBody::Json(v) => v.clone(),
34 DecodedBody::Text(v) => Value::String(v.clone()),
35 DecodedBody::Html(v) => Value::String(v.clone()),
36 DecodedBody::Xml(v) => Value::String(v.clone()),
37 DecodedBody::Binary(v) => {
38 Value::String(base64::engine::general_purpose::STANDARD.encode(v))
39 }
40 }
41 }
42}
43
44pub fn decode_response(
45 mode: ResultDecode,
46 content_type: Option<&str>,
47 bytes: &[u8],
48) -> Result<DecodedBody> {
49 let inferred_mode = match mode {
50 ResultDecode::Auto => infer_mode(content_type, bytes),
51 explicit => explicit,
52 };
53
54 let decoded = match inferred_mode {
55 ResultDecode::Auto | ResultDecode::Text => {
56 DecodedBody::Text(String::from_utf8_lossy(bytes).to_string())
57 }
58 ResultDecode::Json => {
59 let value: Value = serde_json::from_slice(bytes)?;
60 DecodedBody::Json(value)
61 }
62 ResultDecode::Html => DecodedBody::Html(String::from_utf8_lossy(bytes).to_string()),
63 ResultDecode::Xml => DecodedBody::Xml(String::from_utf8_lossy(bytes).to_string()),
64 ResultDecode::Binary => DecodedBody::Binary(bytes.to_vec()),
65 };
66
67 Ok(decoded)
68}
69
70fn infer_mode(content_type: Option<&str>, body: &[u8]) -> ResultDecode {
71 if let Some(ct) = content_type {
72 let lower = ct.to_ascii_lowercase();
73 if lower.contains("application/json") || lower.ends_with("+json") {
74 return ResultDecode::Json;
75 }
76 if lower.contains("text/html") {
77 return ResultDecode::Html;
78 }
79 if lower.contains("xml") {
80 return ResultDecode::Xml;
81 }
82 if lower.starts_with("text/") {
83 return ResultDecode::Text;
84 }
85 }
86
87 if serde_json::from_slice::<Value>(body).is_ok() {
88 ResultDecode::Json
89 } else {
90 ResultDecode::Text
91 }
92}