diff --git a/Cargo.lock b/Cargo.lock index b2b86c8..394379d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1995,6 +1995,13 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "kv-probe-component" +version = "0.1.0" +dependencies = [ + "wit-bindgen 0.41.0", +] + [[package]] name = "lazy_static" version = "1.5.0" diff --git a/Cargo.toml b/Cargo.toml index 58ce0f3..fcec5ea 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,7 @@ members = [ "components/fault", "components/spin", "components/clock-probe", + "components/kv-probe", "components/wasi-clock-probe", ] default-members = ["crates/wasmeld-runtime"] diff --git a/components/kv-probe/Cargo.toml b/components/kv-probe/Cargo.toml new file mode 100644 index 0000000..6f9d37c --- /dev/null +++ b/components/kv-probe/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "kv-probe-component" +version = "0.1.0" +edition.workspace = true +rust-version.workspace = true +license.workspace = true + +[package.metadata.wasmeld] +id = "kv-probe" +world = "component:kv-probe/kv-probe-component@0.1.0" + +[lib] +crate-type = ["cdylib"] + +[dependencies] +wit-bindgen.workspace = true diff --git a/components/kv-probe/src/lib.rs b/components/kv-probe/src/lib.rs new file mode 100644 index 0000000..a5023db --- /dev/null +++ b/components/kv-probe/src/lib.rs @@ -0,0 +1,63 @@ +mod bindings { + wit_bindgen::generate!({ + path: "wit", + world: "kv-probe-component", + generate_all, + }); +} + +use bindings::wasmeld::kv::store; + +struct KvProbe; + +impl bindings::Guest for KvProbe { + fn init(_config: Vec) -> Result<(), bindings::ServiceError> { + Ok(()) + } + + fn invoke(input: Vec) -> Result, bindings::ServiceError> { + if let Some(key) = input.strip_prefix(b"get:") { + let key = parse_key(key)?; + return store::get(&key) + .map(|value| value.unwrap_or_default()) + .map_err(kv_error); + } + + if let Some(command) = input.strip_prefix(b"set:") { + let separator = command + .iter() + .position(|byte| *byte == b':') + .ok_or_else(|| { + bindings::ServiceError::CallFailed( + "set command must be set::".to_owned(), + ) + })?; + let key = parse_key(&command[..separator])?; + let value = command[separator + 1..].to_vec(); + store::set(&key, &value).map_err(kv_error)?; + return Ok(Vec::new()); + } + + if let Some(key) = input.strip_prefix(b"delete:") { + let key = parse_key(key)?; + store::delete(&key).map_err(kv_error)?; + return Ok(Vec::new()); + } + + Err(bindings::ServiceError::CallFailed( + "expected get:, set::, or delete:".to_owned(), + )) + } +} + +fn parse_key(bytes: &[u8]) -> Result { + core::str::from_utf8(bytes) + .map(str::to_owned) + .map_err(|_| bindings::ServiceError::CallFailed("key must be valid UTF-8".to_owned())) +} + +fn kv_error(error: store::KvError) -> bindings::ServiceError { + bindings::ServiceError::CallFailed(format!("KV operation failed: {error:?}")) +} + +bindings::export!(KvProbe with_types_in bindings); diff --git a/components/kv-probe/wasmeld.toml b/components/kv-probe/wasmeld.toml new file mode 100644 index 0000000..37333ee --- /dev/null +++ b/components/kv-probe/wasmeld.toml @@ -0,0 +1,11 @@ +schema_version = 1 + +[dependencies] +"wasmeld:kv" = "0.1.0" +"wasmeld:service" = "0.1.0" + +[replace."wasmeld:kv"] +path = "../../wit/kv" + +[replace."wasmeld:service"] +path = "../../wit/service" diff --git a/components/kv-probe/wit.lock b/components/kv-probe/wit.lock new file mode 100644 index 0000000..d0f2c2f --- /dev/null +++ b/components/kv-probe/wit.lock @@ -0,0 +1,15 @@ +schema_version = 1 + +[[package]] +name = "wasmeld:kv" +version = "0.1.0" +source = "path+../../wit/kv" +sha256 = "6dc3a84dc03c7104aa8b305518466c95e4440e1031c4bef3b59843f40f24d0fb" +replaced = true + +[[package]] +name = "wasmeld:service" +version = "0.1.0" +source = "path+../../wit/service" +sha256 = "d5497307bbcd1e159f7707f385b488a6f2e26362d5256d5bc1080ac603a51305" +replaced = true diff --git a/components/kv-probe/wit/world.wit b/components/kv-probe/wit/world.wit new file mode 100644 index 0000000..d7f9b72 --- /dev/null +++ b/components/kv-probe/wit/world.wit @@ -0,0 +1,6 @@ +package component:kv-probe@0.1.0; + +world kv-probe-component { + include wasmeld:service/service-component@0.1.0; + import wasmeld:kv/store@0.1.0; +}