1 //! Example of instantiating a WASIp2 component with the use of resource
2
3 /*
4 You can execute this example with:
5 cmake examples/
6 cargo run --example resource-component
7 */
8
9 use std::collections::HashMap;
10
11 use wasmtime::component::bindgen;
12 use wasmtime::component::{Component, Linker, ResourceTable};
13 use wasmtime::component::{HasSelf, Resource};
14 use wasmtime::{Engine, Result, Store};
15 use wasmtime_wasi::p2::add_to_linker_async;
16 use wasmtime_wasi::{WasiCtx, WasiCtxView, WasiView};
17
18 pub struct ComponentRunStates {
19 // These two are required basically as a standard way to enable the impl of IoView and
20 // WasiView.
21 // impl of WasiView is required by [`wasmtime_wasi::p2::add_to_linker_sync`]
22 pub wasi_ctx: WasiCtx,
23 pub resource_table: ResourceTable,
24 // You can add other custom host states if needed
25 }
26
27 impl WasiView for ComponentRunStates {
ctx(&mut self) -> WasiCtxView<'_>28 fn ctx(&mut self) -> WasiCtxView<'_> {
29 WasiCtxView {
30 ctx: &mut self.wasi_ctx,
31 table: &mut self.resource_table,
32 }
33 }
34 }
35
36 impl ComponentRunStates {
new() -> Self37 pub fn new() -> Self {
38 // Create a WASI context and put it in a Store; all instances in the store
39 // share this context. `WasiCtx` provides a number of ways to
40 // configure what the target program will have access to.
41 ComponentRunStates {
42 wasi_ctx: WasiCtx::builder().build(),
43 resource_table: ResourceTable::new(),
44 }
45 }
46 }
47
48 bindgen!({
49 path: "./examples/resource-component/kv-store.wit",
50 world: "kv-database",
51 // Interactions with `ResourceTable` can possibly trap so enable the ability
52 // to return traps from generated functions.
53 imports: { default: async | trappable },
54 exports: { default: async },
55 with: {
56 "example:kv-store/kvdb.connection": Connection
57 },
58 });
59
60 pub struct Connection {
61 pub storage: HashMap<String, String>,
62 }
63
64 impl KvDatabaseImports for ComponentRunStates {
log(&mut self, msg: String) -> Result<(), wasmtime::Error>65 async fn log(&mut self, msg: String) -> Result<(), wasmtime::Error> {
66 // provide host function to the component
67 println!("Log: {msg}");
68 Ok(())
69 }
70 }
71
72 impl example::kv_store::kvdb::Host for ComponentRunStates {}
73
74 impl example::kv_store::kvdb::HostConnection for ComponentRunStates {
new(&mut self) -> Result<Resource<Connection>, wasmtime::Error>75 async fn new(&mut self) -> Result<Resource<Connection>, wasmtime::Error> {
76 Ok(self.resource_table.push(Connection {
77 storage: HashMap::new(),
78 })?)
79 }
80
get( &mut self, resource: Resource<Connection>, key: String, ) -> Result<Option<String>, wasmtime::Error>81 async fn get(
82 &mut self,
83 resource: Resource<Connection>,
84 key: String,
85 ) -> Result<Option<String>, wasmtime::Error> {
86 let connection = self.resource_table.get(&resource)?;
87 Ok(connection.storage.get(&key).cloned())
88 }
89
set( &mut self, resource: Resource<Connection>, key: String, value: String, ) -> Result<()>90 async fn set(
91 &mut self,
92 resource: Resource<Connection>,
93 key: String,
94 value: String,
95 ) -> Result<()> {
96 let connection = self.resource_table.get_mut(&resource)?;
97 connection.storage.insert(key, value);
98 Ok(())
99 }
100
remove( &mut self, resource: Resource<Connection>, key: String, ) -> Result<Option<String>>101 async fn remove(
102 &mut self,
103 resource: Resource<Connection>,
104 key: String,
105 ) -> Result<Option<String>> {
106 let connection = self.resource_table.get_mut(&resource)?;
107 Ok(connection.storage.remove(&key))
108 }
109
clear(&mut self, resource: Resource<Connection>) -> Result<(), wasmtime::Error>110 async fn clear(&mut self, resource: Resource<Connection>) -> Result<(), wasmtime::Error> {
111 let large_string = self.resource_table.get_mut(&resource)?;
112 large_string.storage.clear();
113 Ok(())
114 }
115
drop(&mut self, resource: Resource<Connection>) -> Result<()>116 async fn drop(&mut self, resource: Resource<Connection>) -> Result<()> {
117 let _ = self.resource_table.delete(resource)?;
118 Ok(())
119 }
120 }
121
122 #[tokio::main]
main() -> Result<()>123 async fn main() -> Result<()> {
124 let engine = Engine::default();
125 let mut linker = Linker::new(&engine);
126 let state = ComponentRunStates::new();
127 let mut store = Store::new(&engine, state);
128
129 KvDatabase::add_to_linker::<_, HasSelf<_>>(&mut linker, |s| s)?;
130 add_to_linker_async(&mut linker)?;
131
132 // Instantiate our component with the imports we've created, and run its function
133 let component = Component::from_file(&engine, "target/wasm32-wasip2/debug/guest_kvdb.wasm")?;
134 let bindings = KvDatabase::instantiate_async(&mut store, &component, &linker).await?;
135 let result = bindings
136 .call_replace_value(&mut store, "hello", "world")
137 .await?;
138 assert_eq!(result, None);
139 let result = bindings
140 .call_replace_value(&mut store, "hello", "wasmtime")
141 .await?;
142 assert_eq!(result, Some("world".to_string()));
143 Ok(())
144 }
145