Skip to main content

coil_wasm/invocation/
context.rs

1use std::collections::BTreeMap;
2
3use crate::error::WasmModelError;
4
5use super::InvocationInput;
6
7#[derive(Debug, Clone, PartialEq, Eq)]
8pub struct CustomerAppContext {
9    pub app_id: String,
10    pub tenant_id: Option<String>,
11    pub site_id: Option<String>,
12    pub brand_id: Option<String>,
13    pub locale: Option<String>,
14}
15
16impl CustomerAppContext {
17    pub fn new(app_id: impl Into<String>) -> Result<Self, WasmModelError> {
18        Ok(Self {
19            app_id: crate::validation::validate_token("app_id", app_id.into())?,
20            tenant_id: None,
21            site_id: None,
22            brand_id: None,
23            locale: None,
24        })
25    }
26
27    pub fn with_tenant_id(mut self, tenant_id: impl Into<String>) -> Result<Self, WasmModelError> {
28        self.tenant_id = Some(crate::validation::validate_token(
29            "tenant_id",
30            tenant_id.into(),
31        )?);
32        Ok(self)
33    }
34
35    pub fn with_site_id(mut self, site_id: impl Into<String>) -> Result<Self, WasmModelError> {
36        self.site_id = Some(crate::validation::validate_token(
37            "site_id",
38            site_id.into(),
39        )?);
40        Ok(self)
41    }
42
43    pub fn with_brand_id(mut self, brand_id: impl Into<String>) -> Result<Self, WasmModelError> {
44        self.brand_id = Some(crate::validation::validate_token(
45            "brand_id",
46            brand_id.into(),
47        )?);
48        Ok(self)
49    }
50
51    pub fn with_locale(mut self, locale: impl Into<String>) -> Result<Self, WasmModelError> {
52        self.locale = Some(crate::validation::validate_token("locale", locale.into())?);
53        Ok(self)
54    }
55}
56
57#[derive(Debug, Clone, Copy, PartialEq, Eq)]
58pub enum PrincipalKind {
59    Anonymous,
60    User,
61    ServiceAccount,
62}
63
64impl std::fmt::Display for PrincipalKind {
65    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
66        match self {
67            Self::Anonymous => f.write_str("anonymous"),
68            Self::User => f.write_str("user"),
69            Self::ServiceAccount => f.write_str("service_account"),
70        }
71    }
72}
73
74#[derive(Debug, Clone, PartialEq, Eq)]
75pub struct PrincipalRef {
76    pub kind: PrincipalKind,
77    pub id: Option<String>,
78}
79
80impl PrincipalRef {
81    pub fn anonymous() -> Self {
82        Self {
83            kind: PrincipalKind::Anonymous,
84            id: None,
85        }
86    }
87
88    pub fn user(id: impl Into<String>) -> Result<Self, WasmModelError> {
89        Ok(Self {
90            kind: PrincipalKind::User,
91            id: Some(crate::validation::validate_token(
92                "principal_id",
93                id.into(),
94            )?),
95        })
96    }
97
98    pub fn service_account(id: impl Into<String>) -> Result<Self, WasmModelError> {
99        Ok(Self {
100            kind: PrincipalKind::ServiceAccount,
101            id: Some(crate::validation::validate_token(
102                "principal_id",
103                id.into(),
104            )?),
105        })
106    }
107
108    pub fn validate(&self) -> Result<(), WasmModelError> {
109        match self.kind {
110            PrincipalKind::Anonymous => Ok(()),
111            PrincipalKind::User | PrincipalKind::ServiceAccount => {
112                if self.id.as_deref().is_some_and(|id| !id.is_empty()) {
113                    Ok(())
114                } else {
115                    Err(WasmModelError::PrincipalIdRequired { kind: self.kind })
116                }
117            }
118        }
119    }
120}
121
122#[derive(Debug, Clone, PartialEq, Eq)]
123pub struct TraceContext {
124    pub trace_id: String,
125    pub parent_span_id: Option<String>,
126    pub request_id: Option<String>,
127}
128
129impl TraceContext {
130    pub fn new(trace_id: impl Into<String>) -> Result<Self, WasmModelError> {
131        Ok(Self {
132            trace_id: crate::validation::validate_token("trace_id", trace_id.into())?,
133            parent_span_id: None,
134            request_id: None,
135        })
136    }
137
138    pub fn with_parent_span_id(
139        mut self,
140        parent_span_id: impl Into<String>,
141    ) -> Result<Self, WasmModelError> {
142        self.parent_span_id = Some(crate::validation::validate_token(
143            "parent_span_id",
144            parent_span_id.into(),
145        )?);
146        Ok(self)
147    }
148
149    pub fn with_request_id(
150        mut self,
151        request_id: impl Into<String>,
152    ) -> Result<Self, WasmModelError> {
153        self.request_id = Some(crate::validation::validate_token(
154            "request_id",
155            request_id.into(),
156        )?);
157        Ok(self)
158    }
159}
160
161#[derive(Debug, Clone, PartialEq, Eq)]
162pub struct InvocationContext {
163    pub customer_app: CustomerAppContext,
164    pub principal: PrincipalRef,
165    pub trace: TraceContext,
166    pub extension_config: BTreeMap<String, crate::manifest::ExtensionConfigValue>,
167    pub input: InvocationInput,
168}
169
170impl InvocationContext {
171    pub fn new(
172        customer_app: CustomerAppContext,
173        principal: PrincipalRef,
174        trace: TraceContext,
175        input: InvocationInput,
176    ) -> Self {
177        Self {
178            customer_app,
179            principal,
180            trace,
181            extension_config: BTreeMap::new(),
182            input,
183        }
184    }
185
186    pub fn with_config_value(
187        mut self,
188        key: impl Into<String>,
189        value: crate::manifest::ExtensionConfigValue,
190    ) -> Result<Self, WasmModelError> {
191        let key = crate::validation::validate_token("extension_config_key", key.into())?;
192        value.validate_for_key(&key)?;
193        self.extension_config.insert(key, value);
194        Ok(self)
195    }
196
197    pub fn validate(&self) -> Result<(), WasmModelError> {
198        self.principal.validate()?;
199        for (key, value) in &self.extension_config {
200            crate::validation::validate_token("extension_config_key", key.clone())?;
201            value.validate_for_key(key)?;
202        }
203        Ok(())
204    }
205}