wick_trigger_wasm_command/
trigger.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
use std::collections::HashMap;
use std::fmt;
use std::sync::Arc;

use async_trait::async_trait;
use structured_output::StructuredOutput;
use tracing::Span;
use wasmtime::component::{Component, Linker};
use wasmtime::Store;
use wasmtime_wasi::preview2::{self, Table, WasiCtxBuilder};
use wick_config::config::{AppConfiguration, BoundIdentifier, TriggerDefinition};
use wick_runtime::Runtime;
use wick_trigger::resources::Resource;
use wick_trigger::Trigger;

use crate::state::{self, SimpleState};
use crate::Error;

#[derive(Default, Clone, Copy)]
#[allow(missing_debug_implementations)]
#[non_exhaustive]
pub struct WasmTrigger {}

#[async_trait]
impl Trigger for WasmTrigger {
  async fn run(
    &self,
    _name: String,
    _runtime: Runtime,
    _app_config: AppConfiguration,
    config: TriggerDefinition,
    _resources: Arc<HashMap<BoundIdentifier, Resource>>,
    parent_span: Span,
  ) -> Result<StructuredOutput, wick_trigger::Error> {
    let TriggerDefinition::WasmCommand(config) = config else {
      panic!("invalid trigger definition, expected WasmCommand configuraton");
    };
    let span = info_span!("trigger:wasm-command");
    span.follows_from(parent_span);
    span.in_scope(|| {
      debug!("config: {:?}", config);
    });

    let engine = wick_wasm_engine::wasm_engine();

    let module_bytes = config
      .reference()
      .bytes(&Default::default())
      .await
      .map_err(|e| Error::ComponentFetch(Box::new(e)))?;
    let component = Component::from_binary(engine, &module_bytes).map_err(Error::ComponentLoad)?;

    let mut linker = Linker::<SimpleState>::new(engine);

    wasmtime_wasi::preview2::command::add_to_linker(&mut linker).map_err(Error::Linker)?;

    let mut table = Table::new();

    let mut wasi = WasiCtxBuilder::new();
    wasi.inherit_stdio();
    let wasi = wasi.build(&mut table).map_err(Error::WasiBuild)?;
    let mut store = Store::new(engine, SimpleState { wasi, table });

    let (_bindings, instance) = state::generated::CommandTrigger::instantiate_async(&mut store, &component, &linker)
      .await
      .map_err(Error::Instantiation)?;

    let cmd = preview2::command::Command::new(&mut store, &instance).map_err(Error::WasiCommand)?;

    let exit = cmd
      .wasi_cli_run()
      .call_run(&mut store)
      .await
      .map_err(Error::CommandRun)?
      .map_or(false, |_| true);

    Ok(StructuredOutput::new("", serde_json::json!({ "success": exit })))
  }

  async fn shutdown_gracefully(self) -> Result<(), wick_trigger::Error> {
    Ok(())
  }

  async fn wait_for_done(&self) -> StructuredOutput {
    StructuredOutput::default()
  }
}

impl fmt::Display for WasmTrigger {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    write!(f, "Cli Trigger",)
  }
}