soma_gateway/gateway/
params.rs1use std::collections::BTreeMap;
2
3use serde_json::{Map, Value};
4use thiserror::Error;
5
6use crate::config::UpstreamConfig;
7
8#[derive(Debug, Error, PartialEq, Eq)]
9pub enum ParamsError {
10 #[error("params must be a JSON object")]
11 MustBeObject,
12 #[error("field `{0}` is required")]
13 MissingField(&'static str),
14 #[error("field `{0}` must be a string")]
15 StringField(&'static str),
16 #[error("field `{0}` must be an array of strings")]
17 StringArrayField(&'static str),
18 #[error("field `{0}` must be an object with string values")]
19 StringMapField(&'static str),
20}
21
22pub fn object_params(params: &Value) -> Result<&Map<String, Value>, ParamsError> {
23 params.as_object().ok_or(ParamsError::MustBeObject)
24}
25
26pub fn string_param(
27 params: &Map<String, Value>,
28 field: &'static str,
29) -> Result<Option<String>, ParamsError> {
30 params
31 .get(field)
32 .map(|value| {
33 value
34 .as_str()
35 .map(ToOwned::to_owned)
36 .ok_or(ParamsError::StringField(field))
37 })
38 .transpose()
39}
40
41pub fn upstream_config_from_params(params: &Value) -> Result<UpstreamConfig, ParamsError> {
42 let params = object_params(params)?;
43 let mut config = parsed_upstream_config(params, required_string_param(params, "name")?)?;
44 config.proxy_resources = params
45 .get("proxy_resources")
46 .and_then(Value::as_bool)
47 .unwrap_or(true);
48 config.proxy_prompts = params
49 .get("proxy_prompts")
50 .and_then(Value::as_bool)
51 .unwrap_or(true);
52 Ok(config)
53}
54
55pub fn test_upstream_config_from_params(params: &Value) -> Result<UpstreamConfig, ParamsError> {
56 let params = object_params(params)?;
57 let name = string_param(params, "name")?.unwrap_or_else(|| "test".to_owned());
58 parsed_upstream_config(params, name)
59}
60
61fn parsed_upstream_config(
62 params: &Map<String, Value>,
63 name: String,
64) -> Result<UpstreamConfig, ParamsError> {
65 Ok(UpstreamConfig {
66 name,
67 url: string_param(params, "url")?,
68 command: string_param(params, "command")?,
69 args: string_array_param(params, "args")?.unwrap_or_default(),
70 env: env_param(params)?,
71 ..UpstreamConfig::default()
72 })
73}
74
75pub fn required_string_param(
76 params: &Map<String, Value>,
77 field: &'static str,
78) -> Result<String, ParamsError> {
79 string_param(params, field)?.ok_or(ParamsError::MissingField(field))
80}
81
82fn string_array_param(
83 params: &Map<String, Value>,
84 field: &'static str,
85) -> Result<Option<Vec<String>>, ParamsError> {
86 params
87 .get(field)
88 .map(|value| {
89 value
90 .as_array()
91 .ok_or(ParamsError::StringArrayField(field))?
92 .iter()
93 .map(|item| {
94 item.as_str()
95 .map(ToOwned::to_owned)
96 .ok_or(ParamsError::StringArrayField(field))
97 })
98 .collect()
99 })
100 .transpose()
101}
102
103fn env_param(params: &Map<String, Value>) -> Result<BTreeMap<String, String>, ParamsError> {
104 params
105 .get("env")
106 .map(|value| {
107 value
108 .as_object()
109 .ok_or(ParamsError::StringMapField("env"))?
110 .iter()
111 .map(|(key, value)| {
112 value
113 .as_str()
114 .map(|value| (key.clone(), value.to_owned()))
115 .ok_or(ParamsError::StringMapField("env"))
116 })
117 .collect()
118 })
119 .transpose()
120 .map(|value| value.unwrap_or_default())
121}
122
123#[cfg(test)]
124#[path = "params_tests.rs"]
125mod tests;