1use crate::error::RuntimeError;
8use crate::loaded::ExtensionId;
9use crate::runtime::ExtensionRuntime;
10use crate::types::{
11 CompileContext, Diagnostic, HostExtensionError, RoleError, RoleSpec, Severity, TargetKind,
12};
13
14const IFACE_BASE: &str = "greentic:extension-design/roles";
16const ROLES_VERSIONS: &[&str] = &["0.3.0", "0.2.0"];
18
19impl ExtensionRuntime {
20 pub fn list_roles(&self, ext_id: &str) -> Result<Vec<RoleSpec>, RuntimeError> {
29 use crate::host_bindings::exports::greentic::extension_design0_2_0::roles::RoleSpec as WitRoleSpec;
30
31 let loaded = self
32 .loaded()
33 .get(&ExtensionId(ext_id.to_string()))
34 .cloned()
35 .ok_or_else(|| RuntimeError::NotFound(ext_id.to_string()))?;
36
37 let (mut store, instance) = loaded
38 .build_store_and_instance(
39 self.engine(),
40 self.host_overrides().clone(),
41 &crate::host_ports::HostCallContext::default(),
42 )
43 .map_err(RuntimeError::Wasmtime)?;
44
45 let iface_idx = {
48 let mut found = None;
49 for &v in ROLES_VERSIONS {
50 let name = format!("{IFACE_BASE}@{v}");
51 if let Some(idx) = instance.get_export_index(&mut store, None, &name) {
52 found = Some(idx);
53 break;
54 }
55 }
56 match found {
57 Some(idx) => idx,
58 None => return Ok(Vec::new()),
59 }
60 };
61
62 let func_idx = instance
63 .get_export_index(&mut store, Some(&iface_idx), "list-roles")
64 .ok_or_else(|| {
65 RuntimeError::Wasmtime(anyhow::anyhow!(
66 "interface '{IFACE_BASE}' does not export 'list-roles'"
67 ))
68 })?;
69
70 let func = instance
71 .get_typed_func::<(), (Vec<WitRoleSpec>,)>(&mut store, &func_idx)
72 .map_err(|e| RuntimeError::Wasmtime(e.into()))?;
73
74 let (roles,) = func
75 .call(&mut store, ())
76 .map_err(|e| RuntimeError::Wasmtime(e.into()))?;
77
78 Ok(roles.into_iter().map(wit_role_spec_to_host).collect())
79 }
80
81 pub fn validate_role(
89 &self,
90 ext_id: &str,
91 name: &str,
92 entry_json: &str,
93 ) -> Result<Vec<Diagnostic>, RuntimeError> {
94 use crate::host_bindings::exports::greentic::extension_design0_2_0::roles::Diagnostic as WitDiagnostic;
95
96 let loaded = self
97 .loaded()
98 .get(&ExtensionId(ext_id.to_string()))
99 .cloned()
100 .ok_or_else(|| RuntimeError::NotFound(ext_id.to_string()))?;
101
102 let (mut store, instance) = loaded
103 .build_store_and_instance(
104 self.engine(),
105 self.host_overrides().clone(),
106 &crate::host_ports::HostCallContext::default(),
107 )
108 .map_err(RuntimeError::Wasmtime)?;
109
110 let (iface_idx, iface_name, _version) = crate::runtime::resolve_iface_versions(
111 &mut store,
112 &instance,
113 IFACE_BASE,
114 ROLES_VERSIONS,
115 )?;
116
117 let func_idx = instance
118 .get_export_index(&mut store, Some(&iface_idx), "validate-role")
119 .ok_or_else(|| {
120 RuntimeError::Wasmtime(anyhow::anyhow!(
121 "interface '{iface_name}' does not export 'validate-role'"
122 ))
123 })?;
124
125 let func = instance
126 .get_typed_func::<(String, String), (Vec<WitDiagnostic>,)>(&mut store, &func_idx)
127 .map_err(|e| RuntimeError::Wasmtime(e.into()))?;
128
129 let (diags,) = func
130 .call(&mut store, (name.to_string(), entry_json.to_string()))
131 .map_err(|e| RuntimeError::Wasmtime(e.into()))?;
132
133 Ok(diags.into_iter().map(wit_diagnostic_to_host).collect())
134 }
135
136 pub fn compile_role(
146 &self,
147 ext_id: &str,
148 name: &str,
149 target: TargetKind,
150 entry_json: &str,
151 ctx: Option<&CompileContext>,
152 ) -> Result<String, RoleError> {
153 let loaded = self
154 .loaded()
155 .get(&ExtensionId(ext_id.to_string()))
156 .cloned()
157 .ok_or_else(|| RoleError::UnknownRole(ext_id.to_string()))?;
158
159 let (mut store, instance) = loaded
160 .build_store_and_instance(
161 self.engine(),
162 self.host_overrides().clone(),
163 &crate::host_ports::HostCallContext::default(),
164 )
165 .map_err(|e| {
166 RoleError::Host(HostExtensionError::Internal(format!(
167 "instantiate '{ext_id}': {e}"
168 )))
169 })?;
170
171 let (iface_idx, iface_name, version) = crate::runtime::resolve_iface_versions(
172 &mut store,
173 &instance,
174 IFACE_BASE,
175 ROLES_VERSIONS,
176 )
177 .map_err(|e| {
178 RoleError::Host(HostExtensionError::Internal(format!(
179 "extension '{ext_id}' does not export '{IFACE_BASE}': {e}"
180 )))
181 })?;
182
183 let func_idx = instance
184 .get_export_index(&mut store, Some(&iface_idx), "compile-role")
185 .ok_or_else(|| {
186 RoleError::Host(HostExtensionError::Internal(format!(
187 "interface '{iface_name}' does not export 'compile-role'"
188 )))
189 })?;
190
191 if version == "0.3.0" {
192 use crate::host_bindings::design_v03::exports::greentic::extension_design0_3_0::roles::{
193 CompileContext as WitCompileContext, RoleError as WitRoleError,
194 TargetKind as WitTargetKind,
195 };
196
197 let func = instance
198 .get_typed_func::<
199 (String, WitTargetKind, String, Option<WitCompileContext>),
200 (Result<String, WitRoleError>,),
201 >(&mut store, &func_idx)
202 .map_err(|e| RoleError::Host(HostExtensionError::Internal(e.to_string())))?;
203
204 let wit_ctx = ctx.cloned().map(|c| WitCompileContext {
205 flow_entries_json: c.flow_entries_json,
206 flow_id: c.flow_id,
207 locale: c.locale,
208 });
209
210 let (result,) = func
211 .call(
212 &mut store,
213 (
214 name.to_string(),
215 target_to_wit_v03(target),
216 entry_json.to_string(),
217 wit_ctx,
218 ),
219 )
220 .map_err(|e| RoleError::Host(HostExtensionError::Internal(e.to_string())))?;
221
222 result.map_err(wit_role_error_to_host_v03)
223 } else {
224 use crate::host_bindings::exports::greentic::extension_design0_2_0::roles::{
225 CompileContext as WitCompileContext, RoleError as WitRoleError,
226 TargetKind as WitTargetKind,
227 };
228
229 let func = instance
230 .get_typed_func::<
231 (String, WitTargetKind, String, Option<WitCompileContext>),
232 (Result<String, WitRoleError>,),
233 >(&mut store, &func_idx)
234 .map_err(|e| RoleError::Host(HostExtensionError::Internal(e.to_string())))?;
235
236 let wit_ctx = ctx.cloned().map(|c| WitCompileContext {
237 flow_entries_json: c.flow_entries_json,
238 flow_id: c.flow_id,
239 locale: c.locale,
240 });
241
242 let (result,) = func
243 .call(
244 &mut store,
245 (
246 name.to_string(),
247 target_to_wit(target),
248 entry_json.to_string(),
249 wit_ctx,
250 ),
251 )
252 .map_err(|e| RoleError::Host(HostExtensionError::Internal(e.to_string())))?;
253
254 result.map_err(wit_role_error_to_host)
255 }
256 }
257}
258
259fn wit_role_spec_to_host(
260 s: crate::host_bindings::exports::greentic::extension_design0_2_0::roles::RoleSpec,
261) -> RoleSpec {
262 RoleSpec {
263 name: s.name,
264 description: s.description,
265 json_schema: s.json_schema,
266 target: target_from_wit(s.target),
267 schema_version: s.schema_version,
268 context_aware: s.context_aware,
269 }
270}
271
272fn wit_diagnostic_to_host(
273 d: crate::host_bindings::exports::greentic::extension_design0_2_0::roles::Diagnostic,
274) -> Diagnostic {
275 use crate::host_bindings::greentic::extension_base0_1_0::types::Severity as WitSeverity;
276 Diagnostic {
277 severity: match d.severity {
278 WitSeverity::Error => Severity::Error,
279 WitSeverity::Warning => Severity::Warning,
280 WitSeverity::Info => Severity::Info,
281 WitSeverity::Hint => Severity::Hint,
282 },
283 code: d.code,
284 message: d.message,
285 path: d.path,
286 }
287}
288
289fn wit_role_error_to_host(
294 e: crate::host_bindings::exports::greentic::extension_design0_2_0::roles::RoleError,
295) -> RoleError {
296 use crate::host_bindings::exports::greentic::extension_design0_2_0::roles::RoleError as WitRoleError;
297 match e {
298 WitRoleError::UnknownRole(s) => RoleError::UnknownRole(s),
299 WitRoleError::InvalidInput(diags) => {
300 RoleError::InvalidInput(diags.into_iter().map(wit_diagnostic_to_host).collect())
301 }
302 WitRoleError::CompileFailed(s) => RoleError::CompileFailed(s),
303 WitRoleError::TargetNotSupported(t) => RoleError::TargetNotSupported(target_from_wit(t)),
304 WitRoleError::VersionNotSupported(v) => RoleError::VersionNotSupported(v),
305 WitRoleError::Host(ee) => RoleError::Host(wit_extension_error_to_host(ee)),
306 }
307}
308
309fn wit_extension_error_to_host(
310 e: crate::host_bindings::exports::greentic::extension_design0_2_0::roles::ExtensionError,
311) -> HostExtensionError {
312 use crate::host_bindings::exports::greentic::extension_design0_2_0::roles::ExtensionError as WitErr;
313 match e {
314 WitErr::InvalidInput(s) => HostExtensionError::InvalidInput(s),
315 WitErr::MissingCapability(s) => HostExtensionError::MissingCapability(s),
316 WitErr::PermissionDenied(s) => HostExtensionError::PermissionDenied(s),
317 WitErr::Internal(s) => HostExtensionError::Internal(s),
318 }
319}
320
321fn target_to_wit(
322 t: TargetKind,
323) -> crate::host_bindings::exports::greentic::extension_design0_2_0::roles::TargetKind {
324 use crate::host_bindings::exports::greentic::extension_design0_2_0::roles::TargetKind as Wit;
325 match t {
326 TargetKind::AdaptiveCard => Wit::AdaptiveCard,
327 TargetKind::SlackBlockKit => Wit::SlackBlockKit,
328 TargetKind::TeamsCard => Wit::TeamsCard,
329 TargetKind::PlainText => Wit::PlainText,
330 }
331}
332
333fn target_from_wit(
334 t: crate::host_bindings::exports::greentic::extension_design0_2_0::roles::TargetKind,
335) -> TargetKind {
336 use crate::host_bindings::exports::greentic::extension_design0_2_0::roles::TargetKind as Wit;
337 match t {
338 Wit::AdaptiveCard => TargetKind::AdaptiveCard,
339 Wit::SlackBlockKit => TargetKind::SlackBlockKit,
340 Wit::TeamsCard => TargetKind::TeamsCard,
341 Wit::PlainText => TargetKind::PlainText,
342 }
343}
344
345fn wit_role_error_to_host_v03(
350 e: crate::host_bindings::design_v03::exports::greentic::extension_design0_3_0::roles::RoleError,
351) -> RoleError {
352 use crate::host_bindings::design_v03::exports::greentic::extension_design0_3_0::roles::RoleError as WitRoleError;
353 match e {
354 WitRoleError::UnknownRole(s) => RoleError::UnknownRole(s),
355 WitRoleError::InvalidInput(diags) => {
356 RoleError::InvalidInput(diags.into_iter().map(wit_diagnostic_to_host_v03).collect())
357 }
358 WitRoleError::CompileFailed(s) => RoleError::CompileFailed(s),
359 WitRoleError::TargetNotSupported(t) => {
360 RoleError::TargetNotSupported(target_from_wit_v03(t))
361 }
362 WitRoleError::VersionNotSupported(v) => RoleError::VersionNotSupported(v),
363 WitRoleError::Host(ee) => RoleError::Host(wit_extension_error_to_host_v03(ee)),
364 }
365}
366
367fn wit_extension_error_to_host_v03(
373 e: crate::host_bindings::design_v03::exports::greentic::extension_design0_3_0::roles::ExtensionError,
374) -> HostExtensionError {
375 use crate::host_bindings::design_v03::exports::greentic::extension_design0_3_0::roles::ExtensionError as WitErr;
376 match e {
377 WitErr::InvalidInput(s) => HostExtensionError::InvalidInput(s),
378 WitErr::MissingCapability(s) => HostExtensionError::MissingCapability(s),
379 WitErr::PermissionDenied(s) => HostExtensionError::PermissionDenied(s),
380 WitErr::NotFound(s) => HostExtensionError::NotFound(s),
381 WitErr::SchemaInvalid(s) => HostExtensionError::SchemaInvalid(s),
382 WitErr::Internal(s) => HostExtensionError::Internal(s),
383 }
384}
385
386fn wit_diagnostic_to_host_v03(
387 d: crate::host_bindings::design_v03::exports::greentic::extension_design0_3_0::roles::Diagnostic,
388) -> Diagnostic {
389 use crate::host_bindings::design_v03::greentic::extension_base0_2_0::types::Severity as WitSeverity;
390 Diagnostic {
391 severity: match d.severity {
392 WitSeverity::Error => Severity::Error,
393 WitSeverity::Warning => Severity::Warning,
394 WitSeverity::Info => Severity::Info,
395 WitSeverity::Hint => Severity::Hint,
396 },
397 code: d.code,
398 message: d.message,
399 path: d.path,
400 }
401}
402
403fn target_to_wit_v03(
404 t: TargetKind,
405) -> crate::host_bindings::design_v03::exports::greentic::extension_design0_3_0::roles::TargetKind {
406 use crate::host_bindings::design_v03::exports::greentic::extension_design0_3_0::roles::TargetKind as Wit;
407 match t {
408 TargetKind::AdaptiveCard => Wit::AdaptiveCard,
409 TargetKind::SlackBlockKit => Wit::SlackBlockKit,
410 TargetKind::TeamsCard => Wit::TeamsCard,
411 TargetKind::PlainText => Wit::PlainText,
412 }
413}
414
415fn target_from_wit_v03(
416 t: crate::host_bindings::design_v03::exports::greentic::extension_design0_3_0::roles::TargetKind,
417) -> TargetKind {
418 use crate::host_bindings::design_v03::exports::greentic::extension_design0_3_0::roles::TargetKind as Wit;
419 match t {
420 Wit::AdaptiveCard => TargetKind::AdaptiveCard,
421 Wit::SlackBlockKit => TargetKind::SlackBlockKit,
422 Wit::TeamsCard => TargetKind::TeamsCard,
423 Wit::PlainText => TargetKind::PlainText,
424 }
425}