1use serde::{Deserialize, Serialize};
2use spin_http_routes::HttpTriggerRouteConfig;
3
4#[derive(Clone, Debug, Deserialize, Serialize)]
6#[serde(deny_unknown_fields)]
7pub struct HttpTriggerConfig {
8 pub component: Option<String>,
10 pub static_response: Option<StaticResponse>,
12 pub route: HttpTriggerRouteConfig,
14 #[serde(default)]
16 pub executor: Option<HttpExecutorType>,
17}
18
19impl HttpTriggerConfig {
20 pub fn lookup_key(&self, trigger_id: &str) -> anyhow::Result<crate::routes::TriggerLookupKey> {
21 match (&self.component, &self.static_response) {
22 (None, None) => Err(anyhow::anyhow!("Triggers must specify either component or static_response - {trigger_id} has neither")),
23 (Some(_), Some(_)) => Err(anyhow::anyhow!("Triggers must specify either component or static_response - {trigger_id} has both")),
24 (Some(c), None) => Ok(crate::routes::TriggerLookupKey::Component(c.to_string())),
25 (None, Some(_)) => Ok(crate::routes::TriggerLookupKey::Trigger(trigger_id.to_string())),
26 }
27 }
28}
29
30#[derive(Clone, Debug, Default, Deserialize, Serialize)]
36#[serde(deny_unknown_fields, rename_all = "lowercase", tag = "type")]
37pub enum HttpExecutorType {
38 #[default]
42 #[serde(alias = "spin")]
43 Http,
44 Wagi(WagiTriggerConfig),
46}
47
48#[derive(Clone, Debug, Deserialize, Serialize)]
50#[serde(default, deny_unknown_fields)]
51pub struct WagiTriggerConfig {
52 #[serde(skip_serializing)]
54 pub entrypoint: String,
55
56 pub argv: String,
66}
67
68impl Default for WagiTriggerConfig {
69 fn default() -> Self {
70 const WAGI_DEFAULT_ENTRYPOINT: &str = "_start";
72 const WAGI_DEFAULT_ARGV: &str = "${SCRIPT_NAME} ${ARGS}";
73
74 Self {
75 entrypoint: WAGI_DEFAULT_ENTRYPOINT.to_owned(),
76 argv: WAGI_DEFAULT_ARGV.to_owned(),
77 }
78 }
79}
80
81#[derive(Clone, Debug, Deserialize, Serialize)]
84pub struct StaticResponse {
85 #[serde(default)]
86 status_code: Option<u16>,
87 #[serde(default)]
88 headers: indexmap::IndexMap<String, String>,
89 #[serde(default)]
90 body: Option<String>,
91}
92
93impl StaticResponse {
94 pub fn status(&self) -> u16 {
95 self.status_code.unwrap_or(200)
96 }
97
98 pub fn headers(&self) -> impl Iterator<Item = (&String, &String)> {
99 self.headers.iter()
100 }
101
102 pub fn body(&self) -> Option<&String> {
103 self.body.as_ref()
104 }
105}
106
107#[cfg(test)]
108mod tests {
109 use super::*;
110
111 #[test]
112 fn wagi_config_smoke_test() {
113 let HttpExecutorType::Wagi(config) = toml::toml! { type = "wagi" }.try_into().unwrap()
114 else {
115 panic!("wrong type");
116 };
117 assert_eq!(config.entrypoint, "_start");
118 assert_eq!(config.argv, "${SCRIPT_NAME} ${ARGS}");
119 }
120}