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::{Config, Engine, Result, Store}; 15 use wasmtime_wasi::p2::add_to_linker_async; 16 use wasmtime_wasi::p2::{IoView, WasiCtx, WasiCtxBuilder, 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 IoView for ComponentRunStates { 28 fn table(&mut self) -> &mut ResourceTable { 29 &mut self.resource_table 30 } 31 } 32 impl WasiView for ComponentRunStates { 33 fn ctx(&mut self) -> &mut WasiCtx { 34 &mut self.wasi_ctx 35 } 36 } 37 38 impl ComponentRunStates { 39 pub fn new() -> Self { 40 // Create a WASI context and put it in a Store; all instances in the store 41 // share this context. `WasiCtxBuilder` provides a number of ways to 42 // configure what the target program will have access to. 43 ComponentRunStates { 44 wasi_ctx: WasiCtxBuilder::new().build(), 45 resource_table: ResourceTable::new(), 46 } 47 } 48 } 49 50 bindgen!({ 51 path: "./examples/resource-component/kv-store.wit", 52 world: "kv-database", 53 async: true, 54 with: { 55 "example:kv-store/kvdb/connection": Connection 56 }, 57 // Interactions with `ResourceTable` can possibly trap so enable the ability 58 // to return traps from generated functions. 59 trappable_imports: true, 60 }); 61 62 pub struct Connection { 63 pub storage: HashMap<String, String>, 64 } 65 66 impl KvDatabaseImports for ComponentRunStates { 67 async fn log(&mut self, msg: String) -> Result<(), wasmtime::Error> { 68 // provide host function to the component 69 println!("Log: {msg}"); 70 Ok(()) 71 } 72 } 73 74 impl example::kv_store::kvdb::Host for ComponentRunStates {} 75 76 impl example::kv_store::kvdb::HostConnection for ComponentRunStates { 77 async fn new(&mut self) -> Result<Resource<Connection>, wasmtime::Error> { 78 Ok(self.resource_table.push(Connection { 79 storage: HashMap::new(), 80 })?) 81 } 82 83 async fn get( 84 &mut self, 85 resource: Resource<Connection>, 86 key: String, 87 ) -> Result<Option<String>, wasmtime::Error> { 88 let connection = self.resource_table.get(&resource)?; 89 Ok(connection.storage.get(&key).cloned()) 90 } 91 92 async fn set( 93 &mut self, 94 resource: Resource<Connection>, 95 key: String, 96 value: String, 97 ) -> Result<()> { 98 let connection = self.resource_table.get_mut(&resource)?; 99 connection.storage.insert(key, value); 100 Ok(()) 101 } 102 103 async fn remove( 104 &mut self, 105 resource: Resource<Connection>, 106 key: String, 107 ) -> Result<Option<String>> { 108 let connection = self.resource_table.get_mut(&resource)?; 109 Ok(connection.storage.remove(&key)) 110 } 111 112 async fn clear(&mut self, resource: Resource<Connection>) -> Result<(), wasmtime::Error> { 113 let large_string = self.resource_table.get_mut(&resource)?; 114 large_string.storage.clear(); 115 Ok(()) 116 } 117 118 async fn drop(&mut self, resource: Resource<Connection>) -> Result<()> { 119 let _ = self.resource_table.delete(resource)?; 120 Ok(()) 121 } 122 } 123 124 #[tokio::main] 125 async fn main() -> Result<()> { 126 // Construct the wasm engine with async support enabled. 127 let mut config = Config::new(); 128 config.async_support(true); 129 let engine = Engine::new(&config)?; 130 let mut linker = Linker::new(&engine); 131 let state = ComponentRunStates::new(); 132 let mut store = Store::new(&engine, state); 133 134 KvDatabase::add_to_linker::<_, HasSelf<_>>(&mut linker, |s| s)?; 135 add_to_linker_async(&mut linker)?; 136 137 // Instantiate our component with the imports we've created, and run its function 138 let component = Component::from_file(&engine, "target/wasm32-wasip2/debug/guest_kvdb.wasm")?; 139 let bindings = KvDatabase::instantiate_async(&mut store, &component, &linker).await?; 140 let result = bindings 141 .call_replace_value(&mut store, "hello", "world") 142 .await?; 143 assert_eq!(result, None); 144 let result = bindings 145 .call_replace_value(&mut store, "hello", "wasmtime") 146 .await?; 147 assert_eq!(result, Some("world".to_string())); 148 Ok(()) 149 } 150