gestalt/public/
rest_mapping.rs1use serde_json::{Map, Value};
4
5use crate::public::generated::metadata::PublicField;
6use crate::rpc_support::{GestaltError, gestalt_error_code};
7
8pub fn substitute_path(
10 pattern: &str,
11 request: &Value,
12 path_fields: &[PublicField],
13) -> Result<String, GestaltError> {
14 let Some(object) = request.as_object() else {
15 return Ok(pattern.to_string());
16 };
17 let mut out = pattern.to_string();
18 for field in path_fields {
19 let token = format!("{{{}}}", field.name);
20 let value = object
21 .get(field.json_name)
22 .or_else(|| object.get(field.name))
23 .ok_or_else(|| {
24 GestaltError::new(
25 gestalt_error_code::INVALID_ARGUMENT,
26 format!("missing path parameter {}", field.name),
27 )
28 })?;
29 let segment = scalar_to_string(value).ok_or_else(|| {
30 GestaltError::new(
31 gestalt_error_code::INVALID_ARGUMENT,
32 format!("unsupported path parameter type for {}", field.name),
33 )
34 })?;
35 out = out.replace(&token, &urlencoding::encode(&segment));
36 }
37 Ok(out)
38}
39
40fn is_path_field(field: &str, path_fields: &[PublicField]) -> bool {
41 path_fields
42 .iter()
43 .any(|path_field| path_field.name == field || path_field.json_name == field)
44}
45
46fn retained_fields<'a>(
47 request: &'a Map<String, Value>,
48 path_fields: &[PublicField],
49) -> impl Iterator<Item = (&'a String, &'a Value)> {
50 request
51 .iter()
52 .filter(move |(key, value)| !value.is_null() && !is_path_field(key, path_fields))
53}
54
55pub fn build_query_pairs(
57 request: &Map<String, Value>,
58 path_fields: &[PublicField],
59) -> Vec<(String, String)> {
60 let mut pairs = Vec::new();
61 for (key, value) in retained_fields(request, path_fields) {
62 encode_query_value(key, value, &mut pairs);
63 }
64 pairs
65}
66
67pub fn build_body_map(
69 request: &Map<String, Value>,
70 path_fields: &[PublicField],
71) -> Map<String, Value> {
72 retained_fields(request, path_fields)
73 .map(|(key, value)| (key.clone(), value.clone()))
74 .collect()
75}
76
77fn encode_query_value(key: &str, value: &Value, out: &mut Vec<(String, String)>) {
78 match value {
79 Value::Null => {}
80 Value::Array(items) => {
81 for item in items {
82 encode_query_value(key, item, out);
83 }
84 }
85 Value::Object(nested) => {
86 for (nested_key, nested_value) in nested {
87 encode_query_value(&format!("{key}.{nested_key}"), nested_value, out);
88 }
89 }
90 _ => {
91 if let Some(text) = scalar_to_string(value) {
92 out.push((key.to_string(), text));
93 }
94 }
95 }
96}
97
98pub fn encode_query_string(pairs: &[(String, String)]) -> String {
100 pairs
101 .iter()
102 .map(|(key, value)| {
103 format!(
104 "{}={}",
105 urlencoding::encode(key),
106 urlencoding::encode(value)
107 )
108 })
109 .collect::<Vec<_>>()
110 .join("&")
111}
112
113fn scalar_to_string(value: &Value) -> Option<String> {
114 match value {
115 Value::String(text) => Some(text.clone()),
116 Value::Number(number) => Some(number.to_string()),
117 Value::Bool(flag) => Some(flag.to_string()),
118 _ => None,
119 }
120}