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}