Skip to main content

earl_core/
decode.rs

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}