From f1100a271e9dc26e78817b71779fa0d7a6093e54 Mon Sep 17 00:00:00 2001 From: dr-frmr Date: Mon, 22 Jul 2024 18:02:24 +0300 Subject: [PATCH] refactor: remove anyhow from everywhere but kv, sqlite, http (those next) --- src/lib.rs | 62 +++++++++++++++++--------- src/net.rs | 30 +++++++------ src/timer.rs | 46 +++++-------------- src/types/address.rs | 7 +-- src/types/capability.rs | 40 ++++++++++++++--- src/types/message.rs | 17 ++++++++ src/types/on_exit.rs | 36 ++++++++------- src/types/request.rs | 97 ++++++++++++++++++++++------------------- src/types/response.rs | 16 ++++--- 9 files changed, 202 insertions(+), 149 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 1cd6004..ba7c074 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,7 +14,6 @@ //! For blobs, we recommend bincode to serialize and deserialize to bytes. //! pub use crate::kinode::process::standard::*; -use serde::{Deserialize, Serialize}; use serde_json::Value; wit_bindgen::generate!({ @@ -27,28 +26,45 @@ wit_bindgen::generate!({ pub mod eth; /// Interact with the system homepage. /// -/// Note that your process must have the capability to message +/// Your process must have the capability to message /// `homepage:homepage:sys` to use this module. pub mod homepage; /// Interact with the HTTP server and client modules. /// Contains types from the `http` crate to use as well. +/// +/// Your process must have the capability to message and receive messages from +/// `http_server:distro:sys` and/or `http_client:distro:sys` to use this module. pub mod http; /// The types that the kernel itself uses -- warning -- these will /// be incompatible with WIT types in some cases, leading to annoying errors. /// Use only to interact with the kernel or runtime in certain ways. pub mod kernel_types; -/// Interact with the kimap module +/// Interact with kimap, the onchain namespace pub mod kimap; /// Interact with the key_value module +/// +/// Your process must have the capability to message and receive messages from +/// `kv:distro:sys` to use this module. pub mod kv; /// Interact with the networking module /// For configuration, debugging, and creating signatures with networking key. +/// +/// Your process must have the capability to message and receive messages from +/// `net:distro:sys` to use this module. pub mod net; /// Interact with the sqlite module +/// +/// Your process must have the capability to message and receive messages from +/// `sqlite:distro:sys` to use this module. pub mod sqlite; /// Interact with the timer runtime module. +/// +/// The `timer:distro:sys` module is public, so no special capabilities needed. pub mod timer; /// Interact with the virtual filesystem +/// +/// Your process must have the capability to message and receive messages from +/// `vfs:distro:sys` to use this module. pub mod vfs; mod types; @@ -103,7 +119,7 @@ macro_rules! println { /// /// Example: /// ```no_run -/// use kinode_process_lib::await_message; +/// use kinode_process_lib::{await_message, println}; /// /// loop { /// match await_message() { @@ -124,7 +140,8 @@ pub fn await_message() -> Result { } } -/// Simple wrapper over spawn() in WIT to make use of our good types +/// Spawn a new process. This function is a wrapper around the standard `spawn()` function +/// provided in `kinode::process::standard` (which is generated by the WIT file). pub fn spawn( name: Option<&str>, wasm_path: &str, @@ -165,9 +182,10 @@ pub fn spawn( /// /// make_blob(&my_type, |t| Ok(bincode::serialize(t)?)); /// ``` -pub fn make_blob(blob: &T, serializer: F) -> anyhow::Result +pub fn make_blob(blob: &T, serializer: F) -> Result where - F: Fn(&T) -> anyhow::Result>, + F: Fn(&T) -> Result, E>, + E: std::error::Error, { Ok(LazyLoadBlob { mime: None, @@ -197,9 +215,10 @@ where /// field_two: HashSet::new(), /// }); /// ``` -pub fn get_typed_blob(deserializer: F) -> Option +pub fn get_typed_blob(deserializer: F) -> Option where - F: Fn(&[u8]) -> anyhow::Result, + F: Fn(&[u8]) -> Result, + E: std::error::Error, { match crate::get_blob() { Some(blob) => match deserializer(&blob.bytes) { @@ -232,9 +251,10 @@ where /// field_two: HashSet::new(), /// }); /// ``` -pub fn get_typed_state(deserializer: F) -> Option +pub fn get_typed_state(deserializer: F) -> Option where - F: Fn(&[u8]) -> anyhow::Result, + F: Fn(&[u8]) -> Result, + E: std::error::Error, { match crate::get_state() { Some(bytes) => match deserializer(&bytes) { @@ -254,21 +274,21 @@ pub fn can_message(address: &Address) -> bool { } /// Get a capability in our store -pub fn get_capability(our: &Address, params: &str) -> Option { +pub fn get_capability(issuer: &Address, params: &str) -> Option { let params = serde_json::from_str::(params).unwrap_or_default(); - crate::our_capabilities() - .iter() - .find(|cap| { - let cap_params = serde_json::from_str::(&cap.params).unwrap_or_default(); - cap.issuer == *our && params == cap_params - }) - .cloned() + crate::our_capabilities().into_iter().find(|cap| { + let cap_params = serde_json::from_str::(&cap.params).unwrap_or_default(); + cap.issuer == *issuer && params == cap_params + }) } /// Get the next message body from the message queue, or propagate the error pub fn await_next_message_body() -> Result, SendError> { match await_message() { - Ok(msg) => Ok(msg.body().to_vec()), - Err(e) => Err(e.into()), + Ok(msg) => Ok(match msg { + Message::Request { body, .. } => body, + Message::Response { body, .. } => body, + }), + Err(e) => Err(e), } } diff --git a/src/net.rs b/src/net.rs index bc1cfef..efb5db3 100644 --- a/src/net.rs +++ b/src/net.rs @@ -175,25 +175,27 @@ where } /// get a kimap name from namehash -pub fn get_name( - namehash: &str, - block: Option, - timeout: Option, -) -> anyhow::Result { +pub fn get_name(namehash: T, block: Option, timeout: Option) -> Option +where + T: Into, +{ let res = Request::to(("our", "kns_indexer", "kns_indexer", "sys")) .body( serde_json::to_vec(&IndexerRequests::NamehashToName(NamehashToNameRequest { - hash: namehash.to_string(), - block: block, + hash: namehash.into(), + block, })) .unwrap(), ) - .send_and_await_response(timeout.unwrap_or(5))??; + .send_and_await_response(timeout.unwrap_or(30)) + .unwrap() + .ok()?; - let response = serde_json::from_slice::(res.body()); - match response { - Ok(IndexerResponses::Name(Some(name))) => Ok(name), - Ok(IndexerResponses::Name(None)) => Err(anyhow::anyhow!("name not found")), - _ => Err(anyhow::anyhow!("unexpected response: {:?}", response)), - } + let Ok(IndexerResponses::Name(maybe_name)) = + serde_json::from_slice::(res.body()) + else { + return None; + }; + + maybe_name } diff --git a/src/timer.rs b/src/timer.rs index 9670799..269e117 100644 --- a/src/timer.rs +++ b/src/timer.rs @@ -1,5 +1,5 @@ -use crate::*; -use anyhow::Result; +use crate::{Context, Message, Request, SendError}; +use serde::{Deserialize, Serialize}; #[derive(Debug, Clone, Serialize, Deserialize)] pub enum TimerAction { @@ -10,45 +10,23 @@ pub enum TimerAction { /// Set a timer using the runtime that will return a Response after the specified duration. /// The duration should be a number of milliseconds. pub fn set_timer(duration: u64, context: Option) { - match context { - None => { - Request::new() - .target(Address::new( - "our", - ProcessId::new(Some("timer"), "distro", "sys"), - )) - .body(serde_json::to_vec(&TimerAction::SetTimer(duration)).unwrap()) - .expects_response((duration / 1000) + 1) - // safe to unwrap this call when we know we've set both target and body - .send() - .unwrap(); - } - Some(context) => { - Request::new() - .target(Address::new( - "our", - ProcessId::new(Some("timer"), "distro", "sys"), - )) - .body(serde_json::to_vec(&TimerAction::SetTimer(duration)).unwrap()) - .expects_response((duration / 1000) + 1) - .context(context) - // safe to unwrap this call when we know we've set both target and body - .send() - .unwrap(); - } + let mut request = Request::to(("our", "timer", "distro", "sys")) + .body(serde_json::to_vec(&TimerAction::SetTimer(duration)).unwrap()) + .expects_response((duration / 1000) + 1); + + if let Some(context) = context { + request = request.context(context); } + // safe to unwrap this call when we know we've set both target and body + request.send().unwrap(); } /// Set a timer using the runtime that will return a Response after the specified duration, /// then wait for that timer to resolve. The duration should be a number of milliseconds. pub fn set_and_await_timer(duration: u64) -> Result { - Request::new() - .target(Address::new( - "our", - ProcessId::new(Some("timer"), "distro", "sys"), - )) + Request::to(("our", "timer", "distro", "sys")) .body(serde_json::to_vec(&TimerAction::SetTimer(duration)).unwrap()) - // safe to unwrap this call when we know we've set both target and body .send_and_await_response((duration / 1000) + 1) + // safe to unwrap this call when we know we've set both target and body .unwrap() } diff --git a/src/types/address.rs b/src/types/address.rs index 7d5d7e3..c6d1660 100644 --- a/src/types/address.rs +++ b/src/types/address.rs @@ -151,11 +151,12 @@ where } } -impl From<(&str, T)> for Address +impl From<(T, U)> for Address where - T: Into, + T: Into, + U: Into, { - fn from(input: (&str, T)) -> Self { + fn from(input: (T, U)) -> Self { Address::new(input.0, input.1) } } diff --git a/src/types/capability.rs b/src/types/capability.rs index db0949c..356051a 100644 --- a/src/types/capability.rs +++ b/src/types/capability.rs @@ -5,9 +5,9 @@ use std::hash::{Hash, Hasher}; /// Capability is defined in the wit bindings, but constructors and methods here. /// A `Capability` is a combination of an Address and a set of Params (a serialized -/// JSON string). Capabilities are attached to messages to either share that capability -/// with the receiving process, or to prove that a process has authority to perform a -/// certain action. +/// JSON string by convention). Capabilities are attached to messages to either share +/// that capability with the receiving process, or to prove that a process has +/// authority to perform a certain action. impl Capability { /// Create a new `Capability`. Takes a node ID and a process ID. pub fn new(address: T, params: U) -> Capability @@ -172,15 +172,16 @@ impl PartialEq for Capability { impl From<&Capability> for Capability { fn from(input: &Capability) -> Self { - input.clone() + input.to_owned() } } -impl From<(T, &str)> for Capability +impl From<(T, U)> for Capability where T: Into
, + U: Into, { - fn from(input: (T, &str)) -> Self { + fn from(input: (T, U)) -> Self { Capability::new(input.0, input.1) } } @@ -190,3 +191,30 @@ impl std::fmt::Display for Capability { write!(f, "{}({})", self.issuer, self.params) } } + +#[cfg(test)] +mod tests { + use super::*; + use crate::ProcessId; + + #[test] + fn test_capability() { + let cap = Capability::new( + Address::new("test", ProcessId::new(None, "test", "test")), + r#"{"test": "params"}"#, + ); + let serialized = serde_json::to_string(&cap).unwrap(); + let deserialized: Capability = serde_json::from_str(&serialized).unwrap(); + assert_eq!(cap, deserialized); + } + + #[test] + fn test_capability_json() { + let cap = Capability::new( + Address::new("test", ProcessId::new(None, "test", "test")), + r#"{"test": "params"}"#, + ); + let json = cap.params_json().unwrap(); + assert_eq!(json, serde_json::json!({"test": "params"})); + } +} diff --git a/src/types/message.rs b/src/types/message.rs index d167c97..8e7ccb0 100644 --- a/src/types/message.rs +++ b/src/types/message.rs @@ -25,6 +25,23 @@ pub enum Message { }, } +#[derive(Debug, Serialize, Deserialize)] +pub enum BuildError { + NoBody, + NoTarget, +} + +impl std::fmt::Display for BuildError { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + BuildError::NoBody => write!(f, "no body set for message"), + BuildError::NoTarget => write!(f, "no target set for message"), + } + } +} + +impl std::error::Error for BuildError {} + impl Message { /// Get the source of a message. pub fn source(&self) -> &Address { diff --git a/src/types/on_exit.rs b/src/types/on_exit.rs index 5647f22..81d02c7 100644 --- a/src/types/on_exit.rs +++ b/src/types/on_exit.rs @@ -1,4 +1,4 @@ -use crate::{Address, LazyLoadBlob, Request}; +use crate::{types::message::BuildError, Address, LazyLoadBlob, Request}; #[derive(Clone, Debug)] pub enum OnExit { @@ -63,24 +63,25 @@ impl OnExit { OnExit::Requests(reqs) => Some(reqs), } } - /// Add a request to this OnExit if it is Requests, fail otherwise - pub fn add_request(&mut self, new: Request) -> anyhow::Result<()> { - match self { - OnExit::None => Err(anyhow::anyhow!("cannot add request to None")), - OnExit::Restart => Err(anyhow::anyhow!("cannot add request to Restart")), - OnExit::Requests(ref mut reqs) => { - reqs.push(new); - Ok(()) - } + /// Add a request to this OnExit if it is of variant `Requests` + pub fn add_request(&mut self, new: Request) { + if let OnExit::Requests(ref mut reqs) = self { + reqs.push(new); } } - /// Set the OnExit behavior for this process - pub fn set(self) -> anyhow::Result<()> { + /// Set the OnExit behavior for this process. + /// + /// Will return a [`BuildError`] if any requests within the `Requests` behavior are + /// not valid (by not having a `body` and/or `target` set). + pub fn set(self) -> Result<(), BuildError> { crate::kinode::process::standard::set_on_exit(&self._to_standard()?); Ok(()) } - /// Convert this OnExit to the kernel's OnExit type - pub fn _to_standard(self) -> anyhow::Result { + /// Convert this OnExit to the kernel's OnExit type. + /// + /// Will return a [`BuildError`] if any requests within the `Requests` behavior are + /// not valid (by not having a `body` and/or `target` set). + pub fn _to_standard(self) -> Result { match self { OnExit::None => Ok(crate::kinode::process::standard::OnExit::None), OnExit::Restart => Ok(crate::kinode::process::standard::OnExit::Restart), @@ -92,14 +93,11 @@ impl OnExit { )> = Vec::with_capacity(reqs.len()); for req in reqs { kernel_reqs.push(( - req.target - .ok_or(anyhow::anyhow!("request without target given"))?, + req.target.ok_or(BuildError::NoTarget)?, crate::kinode::process::standard::Request { inherit: req.inherit, expects_response: None, - body: req - .body - .ok_or(anyhow::anyhow!("request without body given"))?, + body: req.body.ok_or(BuildError::NoBody)?, metadata: req.metadata, capabilities: req.capabilities, }, diff --git a/src/types/request.rs b/src/types/request.rs index f5fb687..c70a90f 100644 --- a/src/types/request.rs +++ b/src/types/request.rs @@ -1,10 +1,10 @@ use crate::{ Address, Capability, LazyLoadBlob, Message, SendError, _wit_message_to_message, - _wit_send_error_to_send_error, + _wit_send_error_to_send_error, types::message::BuildError, }; -/// Request builder. Use [`Request::new()`] to start a request, then build it, -/// then call [`Request::send()`] on it to fire. +/// Request builder. Use [`Request::new()`] or [`Request::to()`] to start a request, +/// then build it, then call [`Request::send()`] on it to fire. #[derive(Clone, Debug)] pub struct Request { pub target: Option
, @@ -110,9 +110,10 @@ impl Request { /// type that's got an implementation of [`TryInto`] for `Vec`. It's best /// to define an IPC body type within your app, then implement TryFrom/TryInto for /// all IPC body serialization/deserialization. - pub fn try_body(mut self, body: T) -> anyhow::Result + pub fn try_body(mut self, body: T) -> Result where - T: TryInto, Error = anyhow::Error>, + T: TryInto, Error = E>, + E: std::error::Error, { self.body = Some(body.try_into()?); Ok(self) @@ -183,9 +184,10 @@ impl Request { } /// Set the blob's bytes with a type that implements `TryInto>` /// and may or may not successfully be set. - pub fn try_blob_bytes(mut self, bytes: T) -> anyhow::Result + pub fn try_blob_bytes(mut self, bytes: T) -> Result where - T: TryInto, Error = anyhow::Error>, + T: TryInto, Error = E>, + E: std::error::Error, { if self.blob.is_none() { self.blob = Some(LazyLoadBlob { @@ -219,9 +221,10 @@ impl Request { /// Attempt to set the context field of the request with a type that implements /// `TryInto>`. It's best to define a context type within your app, /// then implement TryFrom/TryInto for all context serialization/deserialization. - pub fn try_context(mut self, context: T) -> anyhow::Result + pub fn try_context(mut self, context: T) -> Result where - T: TryInto, Error = anyhow::Error>, + T: TryInto, Error = E>, + E: std::error::Error, { self.context = Some(context.try_into()?); Ok(self) @@ -240,48 +243,52 @@ impl Request { } /// Attempt to send the request. This will only fail if the `target` or `body` /// fields have not been set. - pub fn send(self) -> anyhow::Result<()> { - if let (Some(target), Some(body)) = (self.target, self.body) { - crate::send_request( - &target, - &crate::kinode::process::standard::Request { - inherit: self.inherit, - expects_response: self.timeout, - body, - metadata: self.metadata, - capabilities: self.capabilities, - }, - self.context.as_ref(), - self.blob.as_ref(), - ); - Ok(()) - } else { - Err(anyhow::anyhow!("missing fields")) - } + pub fn send(self) -> Result<(), BuildError> { + let Some(target) = self.target else { + return Err(BuildError::NoTarget); + }; + let Some(body) = self.body else { + return Err(BuildError::NoBody); + }; + crate::send_request( + &target, + &crate::kinode::process::standard::Request { + inherit: self.inherit, + expects_response: self.timeout, + body, + metadata: self.metadata, + capabilities: self.capabilities, + }, + self.context.as_ref(), + self.blob.as_ref(), + ); + Ok(()) } /// Attempt to send the request, then await its response or error (timeout, offline node). /// This will only fail if the `target` or `body` fields have not been set. pub fn send_and_await_response( self, timeout: u64, - ) -> anyhow::Result> { - if let (Some(target), Some(body)) = (self.target, self.body) { - match crate::send_and_await_response( - &target, - &crate::kinode::process::standard::Request { - inherit: self.inherit, - expects_response: Some(timeout), - body, - metadata: self.metadata, - capabilities: self.capabilities, - }, - self.blob.as_ref(), - ) { - Ok((source, message)) => Ok(Ok(_wit_message_to_message(source, message))), - Err(send_err) => Ok(Err(_wit_send_error_to_send_error(send_err, self.context))), - } - } else { - Err(anyhow::anyhow!("missing fields")) + ) -> Result, BuildError> { + let Some(target) = self.target else { + return Err(BuildError::NoTarget); + }; + let Some(body) = self.body else { + return Err(BuildError::NoBody); + }; + match crate::send_and_await_response( + &target, + &crate::kinode::process::standard::Request { + inherit: self.inherit, + expects_response: Some(timeout), + body, + metadata: self.metadata, + capabilities: self.capabilities, + }, + self.blob.as_ref(), + ) { + Ok((source, message)) => Ok(Ok(_wit_message_to_message(source, message))), + Err(send_err) => Ok(Err(_wit_send_error_to_send_error(send_err, self.context))), } } } diff --git a/src/types/response.rs b/src/types/response.rs index b0ee16c..d173482 100644 --- a/src/types/response.rs +++ b/src/types/response.rs @@ -1,4 +1,4 @@ -use crate::*; +use crate::{types::message::BuildError, Capability, LazyLoadBlob}; /// Response builder. Use [`Response::new()`] to start a response, then build it, /// then call [`Response::send()`] on it to fire. @@ -52,9 +52,10 @@ impl Response { /// type that's got an implementation of [`TryInto`] for `Vec`. It's best /// to define an IPC body type within your app, then implement TryFrom/TryInto for /// all IPC body serialization/deserialization. - pub fn try_body(mut self, body: T) -> anyhow::Result + pub fn try_body(mut self, body: T) -> Result where - T: TryInto, Error = anyhow::Error>, + T: TryInto, Error = E>, + E: std::error::Error, { self.body = Some(body.try_into()?); Ok(self) @@ -125,9 +126,10 @@ impl Response { } /// Set the blob's bytes with a type that implements `TryInto>` /// and may or may not successfully be set. - pub fn try_blob_bytes(mut self, bytes: T) -> anyhow::Result + pub fn try_blob_bytes(mut self, bytes: T) -> Result where - T: TryInto, Error = anyhow::Error>, + T: TryInto, Error = E>, + E: std::error::Error, { if self.blob.is_none() { self.blob = Some(LazyLoadBlob { @@ -150,7 +152,7 @@ impl Response { } /// Attempt to send the response. This will only fail if the IPC body field of /// the response has not yet been set using `body()` or `try_body()`. - pub fn send(self) -> anyhow::Result<()> { + pub fn send(self) -> Result<(), BuildError> { if let Some(body) = self.body { crate::send_response( &crate::kinode::process::standard::Response { @@ -163,7 +165,7 @@ impl Response { ); Ok(()) } else { - Err(anyhow::anyhow!("missing IPC body")) + Err(BuildError::NoBody) } } }