allora_runtime/spec/
http_inbound_adapter_spec_yaml.rs1use crate::error::{Error, Result};
42use crate::spec::version::validate_version;
43use crate::spec::HttpInboundAdapterSpec;
44use serde_yaml::Value as YamlValue;
45
46pub struct HttpInboundAdapterSpecYamlParser;
47
48impl HttpInboundAdapterSpecYamlParser {
49 pub fn parse_value(yaml: &YamlValue) -> Result<HttpInboundAdapterSpec> {
50 let _v = validate_version(yaml)?;
51 let root = yaml
52 .get("http-inbound-adapter")
53 .ok_or_else(|| Error::serialization("missing 'http-inbound-adapter'"))?;
54 if !root.is_mapping() {
55 return Err(Error::serialization(
56 "'http-inbound-adapter' must be a mapping",
57 ));
58 }
59 let host_val = root
61 .get("host")
62 .ok_or_else(|| Error::serialization("http-inbound-adapter.host required"))?;
63 let host_str = host_val
64 .as_str()
65 .ok_or_else(|| Error::serialization("http-inbound-adapter.host must be string"))?;
66 if host_str.is_empty() {
67 return Err(Error::serialization(
68 "http-inbound-adapter.host must not be empty",
69 ));
70 }
71 let port_val = root
73 .get("port")
74 .ok_or_else(|| Error::serialization("http-inbound-adapter.port required"))?;
75 let port_num = port_val
76 .as_u64()
77 .ok_or_else(|| Error::serialization("http-inbound-adapter.port must be integer"))?;
78 if port_num == 0 || port_num > 65535 {
79 return Err(Error::serialization(
80 "http-inbound-adapter.port out of range (1-65535)",
81 ));
82 }
83 let port_u16 = port_num as u16;
84 let path_val = root
86 .get("path")
87 .ok_or_else(|| Error::serialization("http-inbound-adapter.path required"))?;
88 let path_str = path_val
89 .as_str()
90 .ok_or_else(|| Error::serialization("http-inbound-adapter.path must be string"))?;
91 if path_str.is_empty() {
92 return Err(Error::serialization(
93 "http-inbound-adapter.path must not be empty",
94 ));
95 }
96 if !path_str.starts_with('/') {
97 return Err(Error::serialization(
98 "http-inbound-adapter.path must start with '/'",
99 ));
100 }
101 let methods_val = root
103 .get("methods")
104 .ok_or_else(|| Error::serialization("http-inbound-adapter.methods required"))?;
105 if !methods_val.is_sequence() {
106 return Err(Error::serialization(
107 "http-inbound-adapter.methods must be a sequence",
108 ));
109 }
110 let seq = methods_val.as_sequence().unwrap();
111 if seq.is_empty() {
112 return Err(Error::serialization(
113 "http-inbound-adapter.methods sequence must not be empty",
114 ));
115 }
116 const ALLOWED: &[&str] = &["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS", "HEAD"];
117 let mut methods: Vec<String> = Vec::with_capacity(seq.len());
118 for m in seq {
119 let ms = m.as_str().ok_or_else(|| {
120 Error::serialization("http-inbound-adapter.method must be string")
121 })?;
122 if !ALLOWED.contains(&ms) {
123 return Err(Error::serialization(format!(
124 "unsupported http-inbound-adapter.method '{ms}'"
125 )));
126 }
127 methods.push(ms.to_string());
128 }
129 let req_val = root
131 .get("request-channel")
132 .ok_or_else(|| Error::serialization("http-inbound-adapter.request-channel required"))?;
133 let req_str = req_val.as_str().ok_or_else(|| {
134 Error::serialization("http-inbound-adapter.request-channel must be string")
135 })?;
136 if req_str.is_empty() {
137 return Err(Error::serialization(
138 "http-inbound-adapter.request-channel must not be empty",
139 ));
140 }
141 let reply_opt = root
143 .get("reply-channel")
144 .and_then(|v| v.as_str())
145 .map(|s| s.to_string());
146 if let Some(ref rc) = reply_opt {
147 if rc.is_empty() {
148 return Err(Error::serialization(
149 "http-inbound-adapter.reply-channel must not be empty",
150 ));
151 }
152 }
153 let id_opt = root
155 .get("id")
156 .and_then(|v| v.as_str())
157 .map(|s| s.to_string());
158 if let Some(ref idv) = id_opt {
159 if idv.is_empty() {
160 return Err(Error::serialization(
161 "http-inbound-adapter.id must not be empty",
162 ));
163 }
164 }
165 Ok(match (id_opt, reply_opt) {
166 (Some(id), Some(reply)) => HttpInboundAdapterSpec::with_id_reply(
167 id, host_str, port_u16, path_str, methods, req_str, reply,
168 ),
169 (Some(id), None) => {
170 HttpInboundAdapterSpec::with_id(id, host_str, port_u16, path_str, methods, req_str)
171 }
172 (None, Some(reply)) => HttpInboundAdapterSpec::with_reply(
173 host_str, port_u16, path_str, methods, req_str, reply,
174 ),
175 (None, None) => {
176 HttpInboundAdapterSpec::new(host_str, port_u16, path_str, methods, req_str)
177 }
178 })
179 }
180 pub fn parse_str(raw: &str) -> Result<HttpInboundAdapterSpec> {
181 let val: YamlValue = serde_yaml::from_str(raw)
182 .map_err(|e| Error::serialization(format!("yaml parse error: {e}")))?;
183 Self::parse_value(&val)
184 }
185}