Skip to main content

soma_gateway/gateway/
params.rs

1use 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;