Skip to main content

sciforge_hub/api/dto/
request.rs

1//! Inbound computation request payload.
2//!
3//! [`ComputeRequest`] carries the domain, function name, and parameter
4//! map submitted by the caller.
5
6use std::collections::HashMap;
7
8/// Inbound computation request.
9#[derive(Debug, Clone)]
10pub struct ComputeRequest {
11    /// Target scientific domain.
12    pub domain: String,
13    /// Function to compute.
14    pub function: String,
15    /// Named parameters.
16    pub params: HashMap<String, ParamValue>,
17}
18
19/// Typed parameter value.
20#[derive(Debug, Clone)]
21pub enum ParamValue {
22    Scalar(f64),
23    Integer(i64),
24    Text(String),
25    Boolean(bool),
26    Array(Vec<f64>),
27}
28
29impl ComputeRequest {
30    /// Creates an empty request for the given domain and function.
31    pub fn new(domain: &str, function: &str) -> Self {
32        Self {
33            domain: domain.to_string(),
34            function: function.to_string(),
35            params: HashMap::new(),
36        }
37    }
38
39    /// Adds a scalar parameter.
40    pub fn with_scalar(mut self, key: &str, value: f64) -> Self {
41        self.params
42            .insert(key.to_string(), ParamValue::Scalar(value));
43        self
44    }
45
46    /// Adds an integer parameter.
47    pub fn with_integer(mut self, key: &str, value: i64) -> Self {
48        self.params
49            .insert(key.to_string(), ParamValue::Integer(value));
50        self
51    }
52
53    /// Adds a text parameter.
54    pub fn with_text(mut self, key: &str, value: &str) -> Self {
55        self.params
56            .insert(key.to_string(), ParamValue::Text(value.to_string()));
57        self
58    }
59
60    /// Adds a boolean parameter.
61    pub fn with_bool(mut self, key: &str, value: bool) -> Self {
62        self.params
63            .insert(key.to_string(), ParamValue::Boolean(value));
64        self
65    }
66
67    /// Adds an array parameter.
68    pub fn with_array(mut self, key: &str, value: Vec<f64>) -> Self {
69        self.params
70            .insert(key.to_string(), ParamValue::Array(value));
71        self
72    }
73
74    /// Returns the scalar value for `key`, if present.
75    pub fn get_scalar(&self, key: &str) -> Option<f64> {
76        match self.params.get(key) {
77            Some(ParamValue::Scalar(v)) => Some(*v),
78            _ => None,
79        }
80    }
81
82    /// Returns the integer value for `key`, if present.
83    pub fn get_integer(&self, key: &str) -> Option<i64> {
84        match self.params.get(key) {
85            Some(ParamValue::Integer(v)) => Some(*v),
86            _ => None,
87        }
88    }
89
90    /// Returns the array value for `key`, if present.
91    pub fn get_array(&self, key: &str) -> Option<&[f64]> {
92        match self.params.get(key) {
93            Some(ParamValue::Array(v)) => Some(v),
94            _ => None,
95        }
96    }
97
98    /// Attempts to parse a [`ComputeRequest`] from a JSON string.
99    pub fn from_json_str(json: &str) -> Option<Self> {
100        let mut domain = String::new();
101        let mut function = String::new();
102        for line in json.lines() {
103            let trimmed = line.trim().trim_matches(',');
104            if let Some(val) = trimmed
105                .strip_prefix(r#""domain""#)
106                .and_then(extract_json_string)
107            {
108                domain = val;
109            } else if let Some(val) = trimmed
110                .strip_prefix(r#""function""#)
111                .and_then(extract_json_string)
112            {
113                function = val;
114            }
115        }
116        if domain.is_empty() || function.is_empty() {
117            return None;
118        }
119        Some(Self::new(&domain, &function))
120    }
121}
122
123fn extract_json_string(s: &str) -> Option<String> {
124    let s = s.trim().trim_start_matches(':').trim();
125    let s = s.trim_matches('"').trim_matches(',');
126    if s.is_empty() {
127        None
128    } else {
129        Some(s.to_string())
130    }
131}