1use nu_protocol::Value;
2use std::collections::HashMap;
3use std::path::PathBuf;
4
5#[derive(Clone, Debug, serde::Serialize)]
6#[serde(untagged)]
7pub enum HeaderValue {
8 Single(String),
9 Multiple(Vec<String>),
10}
11
12#[derive(Clone, Debug, Default)]
14pub struct HttpResponseMeta {
15 pub status: Option<u16>,
16 pub headers: HashMap<String, HeaderValue>,
17}
18
19#[derive(Clone, Debug)]
21pub struct Response {
22 pub status: u16,
23 pub headers: HashMap<String, HeaderValue>,
24 pub body_type: ResponseBodyType,
25}
26
27#[derive(Clone, Debug)]
28pub enum ResponseBodyType {
29 Normal,
30 Static {
31 root: PathBuf,
32 path: String,
33 fallback: Option<String>,
34 },
35 ReverseProxy {
36 target_url: String,
37 headers: HashMap<String, HeaderValue>,
38 preserve_host: bool,
39 strip_prefix: Option<String>,
40 request_body: Vec<u8>,
41 query: Option<HashMap<String, String>>,
42 },
43}
44
45#[derive(Debug)]
46pub enum ResponseTransport {
47 Empty,
48 Full(Vec<u8>),
49 Stream(tokio::sync::mpsc::Receiver<Vec<u8>>),
50}
51
52pub fn value_to_json(value: &Value) -> serde_json::Value {
53 match value {
54 Value::Nothing { .. } => serde_json::Value::Null,
55 Value::Bool { val, .. } => serde_json::Value::Bool(*val),
56 Value::Int { val, .. } => serde_json::Value::Number((*val).into()),
57 Value::Float { val, .. } => serde_json::Number::from_f64(*val)
58 .map(serde_json::Value::Number)
59 .unwrap_or(serde_json::Value::Null),
60 Value::String { val, .. } => serde_json::Value::String(val.clone()),
61 Value::List { vals, .. } => {
62 serde_json::Value::Array(vals.iter().map(value_to_json).collect())
63 }
64 Value::Record { val, .. } => {
65 let mut map = serde_json::Map::new();
66 for (k, v) in val.iter() {
67 map.insert(k.clone(), value_to_json(v));
68 }
69 serde_json::Value::Object(map)
70 }
71 _ => todo!(),
72 }
73}
74
75pub fn extract_http_response_meta(
77 metadata: Option<&nu_protocol::PipelineMetadata>,
78) -> HttpResponseMeta {
79 let Some(meta) = metadata else {
80 return HttpResponseMeta::default();
81 };
82
83 let Some(http_response) = meta.custom.get("http.response") else {
84 return HttpResponseMeta::default();
85 };
86
87 let Ok(record) = http_response.as_record() else {
88 return HttpResponseMeta::default();
89 };
90
91 let status = record
92 .get("status")
93 .and_then(|v| v.as_int().ok())
94 .map(|v| v as u16);
95
96 let headers = record
97 .get("headers")
98 .and_then(|v| v.as_record().ok())
99 .map(|headers_record| {
100 let mut map = HashMap::new();
101 for (k, v) in headers_record.iter() {
102 let header_value = match v {
103 Value::String { val, .. } => HeaderValue::Single(val.clone()),
104 Value::List { vals, .. } => {
105 let strings: Vec<String> = vals
106 .iter()
107 .filter_map(|v| v.as_str().ok())
108 .map(|s| s.to_string())
109 .collect();
110 HeaderValue::Multiple(strings)
111 }
112 _ => continue,
113 };
114 map.insert(k.clone(), header_value);
115 }
116 map
117 })
118 .unwrap_or_default();
119
120 HttpResponseMeta { status, headers }
121}
122
123pub fn value_to_bytes(value: Value) -> Vec<u8> {
124 match value {
125 Value::Nothing { .. } => Vec::new(),
126 Value::String { val, .. } => val.into_bytes(),
127 Value::Int { val, .. } => val.to_string().into_bytes(),
128 Value::Float { val, .. } => val.to_string().into_bytes(),
129 Value::Binary { val, .. } => val,
130 Value::Bool { val, .. } => val.to_string().into_bytes(),
131
132 Value::Record { val, .. } if val.get("__html").is_some() => match val.get("__html") {
134 Some(Value::String { val, .. }) => val.clone().into_bytes(),
135 _ => Vec::new(),
136 },
137
138 Value::List { .. } | Value::Record { .. } => serde_json::to_string(&value_to_json(&value))
140 .unwrap_or_else(|_| String::new())
141 .into_bytes(),
142
143 _ => todo!("value_to_bytes: {:?}", value),
144 }
145}