diff --git a/README.md b/README.md index 5ee8cc5..8b4b09e 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ pub struct HelloMessage { #[derive(TypeName, Message, Deserialize, Serialize)] pub struct HelloResponse {} -#[derive(TypeName, FromId, Default)] +#[derive(TypeName, WithId, Default)] pub struct HelloWorldService { pub id: String, } @@ -66,7 +66,7 @@ use rio_rs::object_placement::sql::SqlObjectPlacementProvider; # #[derive(TypeName, Message, Deserialize, Serialize)] # pub struct HelloResponse {} # -# #[derive(TypeName, FromId, Default)] +# #[derive(TypeName, WithId, Default)] # pub struct HelloWorldService { # pub id: String, # } @@ -90,7 +90,7 @@ async fn main() { // Configure types on the server's registry let mut registry = Registry::new(); - registry.add_static_fn::(FromId::from_id); + registry.add_type::(); registry.add_handler::(); // Configure the Cluster Membership provider @@ -148,7 +148,7 @@ use rio_rs::cluster::storage::sql::{SqlMembersStorage}; # #[derive(TypeName, Message, Deserialize, Serialize)] # pub struct HelloResponse {} # -# #[derive(TypeName, FromId, Default)] +# #[derive(TypeName, WithId, Default)] # pub struct HelloWorldService { # pub id: String, # } @@ -205,11 +205,13 @@ There are a few things that must be done before v0.1.0: - [x] Public API renaming - [x] Reduce Boxed objects - [x] Create a Server builder +- [ ] Pub/sub - [ ] Remove the need to pass the StateSaver to `ObjectStateManager::save_state` - [ ] Error and panic handling on life cycle hooks (probably kill the object) - [ ] Handle panics on messages handling - [ ] Include registry configuration in Server builder -- [ ] Remove need to use `add_static_fn(FromId::from_id)` +- [x] Remove need to use `add_static_fn(FromId::from_id)` + - Removed in favour of `Registry::add_type` - [ ] Create a getting started tutorial - [ ] Cargo init - [ ] Add deps (rio-rs, tokio, async_trait, serde, sqlx - optional) @@ -227,8 +229,7 @@ There are a few things that must be done before v0.1.0: - [ ] Reduce static lifetimes - [ ] Increase public API test coverage - [ ] 100% documentation of public API -- [ ] Pub/sub -- [ ] Placement strategies +- [ ] Placement strategies (nodes work with different sets of trait objects) - [ ] Dockerized examples - [ ] Add pgsql jsonb support - [ ] Add all SQL storage behind a feature flag (sqlite, mysql, pgsql, etc) @@ -239,8 +240,9 @@ There are a few things that must be done before v0.1.0: - [ ] Object TTL - [x] Support service background task - [ ] Matrix test with different backends -- [ ] Support 'typed' message/response on client -- [ ] Support ephemeral port +- [?] Support 'typed' message/response on client +- [?] Support ephemeral port +- [ ] Remove the need for an Option value for [managed_state] attributes (as long as it has a 'Default') - [-] Examples covering most use cases - [ ] Background async task on a service - [x] Background blocking task on a service (_see_ [examples/black-jack]()) diff --git a/examples/black-jack/src/rio_server.rs b/examples/black-jack/src/rio_server.rs index 6913b06..e737356 100644 --- a/examples/black-jack/src/rio_server.rs +++ b/examples/black-jack/src/rio_server.rs @@ -23,8 +23,8 @@ pub async fn build_server( // Configure types on the server's registry let mut registry = Registry::new(); - registry.add_static_fn::(FromId::from_id); - registry.add_static_fn::(FromId::from_id); + registry.add_type::(); + registry.add_type::(); registry.add_handler::(); registry.add_handler::(); registry.add_handler::(); diff --git a/examples/black-jack/src/services/cassino.rs b/examples/black-jack/src/services/cassino.rs index 6031dfd..7b455e9 100644 --- a/examples/black-jack/src/services/cassino.rs +++ b/examples/black-jack/src/services/cassino.rs @@ -11,7 +11,7 @@ pub struct CassinoState { pub table_ids: Vec, } -#[derive(Default, Debug, TypeName, FromId, ManagedState)] +#[derive(Default, Debug, TypeName, WithId, ManagedState)] pub struct Cassino { pub id: String, #[managed_state(provider = SqlState)] diff --git a/examples/black-jack/src/services/table.rs b/examples/black-jack/src/services/table.rs index a1f6279..799de4e 100644 --- a/examples/black-jack/src/services/table.rs +++ b/examples/black-jack/src/services/table.rs @@ -22,7 +22,7 @@ pub struct TableState { pub players: HashSet, } -#[derive(Default, Debug, TypeName, FromId, ManagedState)] +#[derive(Default, Debug, TypeName, WithId, ManagedState)] pub struct GameTable { pub id: String, #[managed_state(provider = SqlState)] diff --git a/examples/metric-aggregator/src/bin/server.rs b/examples/metric-aggregator/src/bin/server.rs index 0dc034b..5a2dbd4 100644 --- a/examples/metric-aggregator/src/bin/server.rs +++ b/examples/metric-aggregator/src/bin/server.rs @@ -21,7 +21,7 @@ async fn main() { .unwrap_or("sqlite:///tmp/placement.sqlite3?mode=rwc".to_string()); let mut registry = Registry::new(); - registry.add_static_fn::(FromId::from_id); + registry.add_type::(); registry.add_handler::(); registry.add_handler::(); registry.add_handler::(); diff --git a/examples/metric-aggregator/src/services.rs b/examples/metric-aggregator/src/services.rs index 76e2df5..103d861 100644 --- a/examples/metric-aggregator/src/services.rs +++ b/examples/metric-aggregator/src/services.rs @@ -19,7 +19,7 @@ pub struct MetricStats { pub min: i32, } -#[derive(Debug, Default, TypeName, FromId, ManagedState)] +#[derive(Debug, Default, TypeName, WithId, ManagedState)] pub struct MetricAggregator { pub id: String, #[managed_state(provider = SqlState)] diff --git a/examples/ping-pong/src/bin/server.rs b/examples/ping-pong/src/bin/server.rs index 460db24..d6b13de 100644 --- a/examples/ping-pong/src/bin/server.rs +++ b/examples/ping-pong/src/bin/server.rs @@ -34,7 +34,7 @@ async fn main() { .get_or_insert("sqlite:///tmp/placement.sqlite3?mode=rwc".to_string()); let mut registry = Registry::new(); - registry.add_static_fn::(FromId::from_id); + registry.add_type::(); registry.add_handler::(); registry.add_handler::(); diff --git a/examples/ping-pong/src/services.rs b/examples/ping-pong/src/services.rs index 6101fdd..5f7bbd5 100644 --- a/examples/ping-pong/src/services.rs +++ b/examples/ping-pong/src/services.rs @@ -6,7 +6,7 @@ use std::sync::Arc; use super::*; -#[derive(Debug, Default, TypeName, FromId)] +#[derive(Debug, Default, TypeName, WithId)] pub struct Room { pub id: String, pub request_count: AtomicUsize, diff --git a/justfile b/justfile index 91fed3b..6f4e100 100755 --- a/justfile +++ b/justfile @@ -5,12 +5,17 @@ _ := `ln -sf ${PWD}/pre-commit ${PWD}/.git/hooks/` # Prints this message help: - @just --list + @just --list # Prints the TODOs found throughout the repository todo: @ag --rust -Q 'expect("TODO' +# Prints missing docs +todo-docs: + @ag --rust '/// TODO' + + _fmt WORKDIR: #!/usr/bin/env bash set -euxo pipefail @@ -42,9 +47,9 @@ test-macros: # Runs tests for all the example projects test-examples: - cargo nextest run --manifest-path ./examples/black-jack/Cargo.toml + cargo nextest run --manifest-path ./examples/black-jack/Cargo.toml cargo nextest run --manifest-path ./examples/ping-pong/Cargo.toml - cargo nextest run --manifest-path ./examples/metric-aggregator/Cargo.toml + cargo nextest run --manifest-path ./examples/metric-aggregator/Cargo.toml # Tests all the projects and examples test-all: test test-macros test-examples diff --git a/rio-macros/src/lib.rs b/rio-macros/src/lib.rs index 02d8853..d86db09 100644 --- a/rio-macros/src/lib.rs +++ b/rio-macros/src/lib.rs @@ -7,11 +7,13 @@ use proc_macro2::Ident as Ident2; use proc_macro2::TokenStream as TokenStream2; use quote::{format_ident, quote}; use syn::parse_quote; +use syn::punctuated::Punctuated; use syn::AngleBracketedGenericArguments; use syn::ExprAssign; use syn::ExprPath; use syn::GenericArgument; use syn::PathArguments; +use syn::PathSegment; use syn::Stmt; use syn::{parse2, ItemStruct, Lit, Meta, MetaNameValue}; @@ -82,20 +84,39 @@ pub fn derive_message(tokens: TokenStream) -> TokenStream { TokenStream::from(output) } -#[proc_macro_derive(FromId, attributes(rio_path))] -pub fn derive_from_id(tokens: TokenStream) -> TokenStream { +#[proc_macro_derive(WithId, attributes(rio_path))] +pub fn derive_with_id(tokens: TokenStream) -> TokenStream { let input = TokenStream2::from(tokens); let ast: ItemStruct = parse2(input).unwrap(); let struct_name = format_ident!("{}", ast.ident); let rio_rs = get_crate_path(&ast); - let output = quote! { - impl #rio_rs::service_object::FromId for #struct_name { - fn from_id(id: String) -> Self { - Self { - id, - ..Self::default() + // It has a field `id: String` + // + // This check here is not ideal yet, as I need to refine the type detection + let has_field_id = ast.fields.iter().any(|field| { + field + .ident + .as_ref() + .is_some_and(|field_ident| field_ident.to_string() == "id".to_string()) + && match &field.ty { + syn::Type::Path(path) => { + path.path.segments.last().unwrap().ident.to_string() == "String" } + _ => false, + } + }); + if !has_field_id { + panic!( + "{} doesn't have an `id` attribute of type `String`", + struct_name + ); + } + + let output = quote! { + impl #rio_rs::service_object::WithId for #struct_name { + fn set_id(&mut self, id: String) { + self.id = id; } fn id(&self) -> &str { @@ -217,7 +238,7 @@ impl From for StateDefinition { /// attributed decorated with #[managed_state] /// /// ```ignore -/// #[derive(Default, FromId, TypeName, ManagedState)] +/// #[derive(Default, WithId, TypeName, ManagedState)] /// struct Test2 { /// id: String, /// #[managed_state(provider = Option)] diff --git a/rio-macros/tests/ui_fail/with_id.rs b/rio-macros/tests/ui_fail/with_id.rs new file mode 100644 index 0000000..5e433a4 --- /dev/null +++ b/rio-macros/tests/ui_fail/with_id.rs @@ -0,0 +1,8 @@ +use rio_macros::*; + +#[derive(Default, WithId)] +struct StructWithId { + name: String, +} + +fn main() {} diff --git a/rio-macros/tests/ui_fail/with_id.stderr b/rio-macros/tests/ui_fail/with_id.stderr new file mode 100644 index 0000000..9d86ff5 --- /dev/null +++ b/rio-macros/tests/ui_fail/with_id.stderr @@ -0,0 +1,7 @@ +error: proc-macro derive panicked + --> tests/ui_fail/with_id.rs:3:19 + | +3 | #[derive(Default, WithId)] + | ^^^^^^ + | + = help: message: StructWithId doesn't have an `id` attribute of type `String` diff --git a/rio-macros/tests/ui_pass/managed_state.rs b/rio-macros/tests/ui_pass/managed_state.rs index ee5ebe3..5ba68f1 100644 --- a/rio-macros/tests/ui_pass/managed_state.rs +++ b/rio-macros/tests/ui_pass/managed_state.rs @@ -1,7 +1,7 @@ use rio_macros::*; -use rio_rs::ServiceObject; use rio_rs::state::local::LocalState; use rio_rs::state::ObjectStateManager; +use rio_rs::ServiceObject; #[derive(ManagedState)] struct Test {} @@ -12,7 +12,7 @@ struct TestVec(Vec); #[derive(TypeName, serde::Serialize, serde::Deserialize)] struct NotTestVec(Vec); -#[derive(Default, FromId, TypeName, ManagedState)] +#[derive(Default, WithId, TypeName, ManagedState)] struct Test2 { id: String, #[managed_state] @@ -21,7 +21,7 @@ struct Test2 { // not_tests: Option, } -#[derive(Default, FromId, TypeName, ManagedState)] +#[derive(Default, WithId, TypeName, ManagedState)] struct TestProvider { id: String, #[managed_state(provider = LocalState)] diff --git a/rio-macros/tests/ui_pass/with_id.rs b/rio-macros/tests/ui_pass/with_id.rs new file mode 100644 index 0000000..f424f2c --- /dev/null +++ b/rio-macros/tests/ui_pass/with_id.rs @@ -0,0 +1,14 @@ +use rio_macros::*; +use rio_rs::service_object::WithId; + +#[derive(Default, WithId)] +struct StructWithId { + id: String, + name: String, +} + +fn main() { + let mut a = StructWithId::default(); + a.set_id("1".into()); + assert_eq!(a.id(), "1"); +} diff --git a/src/lib.rs b/src/lib.rs index 55c3c91..807a305 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,10 +19,10 @@ pub mod tap_err; pub use service_object::*; pub mod derive { - pub use rio_macros::FromId; pub use rio_macros::ManagedState; pub use rio_macros::Message; pub use rio_macros::TypeName; + pub use rio_macros::WithId; } pub mod prelude { @@ -33,7 +33,7 @@ pub mod prelude { }; pub use super::cluster::membership_protocol::ClusterProvider; pub use super::cluster::storage::MembersStorage; - pub use super::derive::{FromId, ManagedState, Message, TypeName}; + pub use super::derive::{ManagedState, Message, TypeName, WithId}; pub use super::errors::{ClientBuilderError, HandlerError, ServiceObjectLifeCycleError}; pub use super::protocol::{ClientError, ResponseError}; @@ -42,9 +42,9 @@ pub mod prelude { pub use super::server::Server; pub use super::server::ServerBuilder; pub use super::state::ObjectStateManager; - pub use super::FromId; pub use super::LifecycleMessage; pub use super::ObjectId; pub use super::ServiceObject; pub use super::ServiceObjectStateLoad; + pub use super::WithId; } diff --git a/src/registry.rs b/src/registry.rs index 93c33db..fcdaf1e 100644 --- a/src/registry.rs +++ b/src/registry.rs @@ -2,12 +2,12 @@ //! //! Provides storage for objects and maps their callables to handle registered message types -use crate::{app_data::AppData, errors::HandlerError}; +use crate::{app_data::AppData, errors::HandlerError, WithId}; use async_trait::async_trait; use dashmap::DashMap; use serde::{de::DeserializeOwned, Serialize}; use std::{ - any::{type_name, Any, TypeId}, + any::{type_name, Any}, collections::HashMap, future::Future, pin::Pin, @@ -20,6 +20,8 @@ type BoxFuture = Pin + Send>>; type AsyncRet = BoxFuture, HandlerError>>; type BoxedCallback = Box) -> AsyncRet + Send + Sync>; type BoxedStatic = Box) -> Box + Send + Sync>; +type BoxedDefault = Box Box + Send + Sync>; +type BoxedDefaultWithId = Box Box + Send + Sync>; /// Store objects dynamically, registering handlers for different message types /// @@ -33,8 +35,8 @@ pub struct Registry { // (ObjectTypeName, MessageTypeName) -> Result handler_map: DashMap<(String, String), BoxedCallback>, - // Static Map - static_fn_map: HashMap<(String, TypeId), BoxedStatic>, + /// TODO + type_map: HashMap, } impl Registry { @@ -51,61 +53,23 @@ impl Registry { self.object_map.insert((type_id, k), Box::new(v)); } - /// Registers a static function to build a specific from a parameters - /// - /// The static functions are unique by type (T) and argument type (A) - /// - /// To call these functions one can use `Registry::call_static_fn` or - /// `Registry::call_static_fn_box` - pub fn add_static_fn(&mut self, static_fn: F) + /// TODO + pub fn add_type(&mut self) where - T: IdentifiableType + 'static + Send + Sync, - A: Any, - F: Fn(A) -> T + 'static + Send + Sync, + T: IdentifiableType + 'static + Send + Sync + Default + WithId, { let type_id = T::user_defined_type_id().to_string(); - let argument_type_id = TypeId::of::(); - let boxed_fn = Box::new(move |arg: Box| -> Box { - let cast_arg = arg - .downcast::() - .expect("TODO: Unsupported function called"); - Box::new(static_fn(*cast_arg)) + let boxed_fn = Box::new(move |id: String| -> Box { + let mut value = T::default(); + value.set_id(id); + Box::new(value) }); - - let _ = self - .static_fn_map - .insert((type_id, argument_type_id), boxed_fn); - } - - /// Call function from static registry - /// - /// It looks up the function by its return type (T) + argument type (A) - pub fn call_static_fn(&mut self, argument: A) -> Option - where - T: IdentifiableType + 'static + Send + Sync, - A: Any, - { - let type_id = T::user_defined_type_id().to_string(); - let argument_type_id = TypeId::of::(); - let ret = self.static_fn_map.get(&(type_id, argument_type_id))?(Box::new(argument)); - let ret = ret.downcast::().ok()?; - Some(*ret) + self.type_map.insert(type_id, boxed_fn); } - /// Same as `Registry::call_static_fn` but the caller can refer to the return type by name, - /// instead of using a generic - /// - /// The return will be an Any boxed into an Option, so the caller needs to downcast it - pub fn call_static_fn_box( - &self, - type_id: String, - argument: A, - ) -> Option> - where - A: Any, - { - let argument_type_id = TypeId::of::(); - let ret = self.static_fn_map.get(&(type_id, argument_type_id))?(Box::new(argument)); + /// TODO + pub fn new_from_type(&self, type_id: &str, id: String) -> Option> { + let ret = self.type_map.get(type_id)?(id); Some(ret) } @@ -219,16 +183,23 @@ mod test { use tokio::sync::RwLock; #[derive(Default, Debug, PartialEq)] - struct Human {} + struct Human { + pub id: String, + } + impl IdentifiableType for Human { fn user_defined_type_id() -> &'static str { "Human" } } - impl From for Human { - fn from(_: String) -> Self { - Human {} + impl WithId for Human { + fn set_id(&mut self, id: String) { + self.id = id; + } + + fn id(&self) -> &str { + &self.id } } @@ -338,13 +309,13 @@ mod test { #[tokio::test] async fn sanity_check() { fn is_sync(_t: T) {} - is_sync(Human {}); + is_sync(Human::default()); is_sync(HiMessage {}); is_sync(Registry::new()); is_sync(Box::new(Registry::new())); let mut registry = Registry::new(); - let obj = Human {}; + let obj = Human::default(); registry.add("john".to_string(), obj).await; registry.add_handler::(); registry.add_handler::(); @@ -389,7 +360,7 @@ mod test { #[tokio::test] async fn test_return() { let mut registry = Registry::new(); - let obj = Human {}; + let obj = Human::default(); registry.add("john".to_string(), obj).await; registry.add_handler::(); let ret = registry @@ -408,7 +379,7 @@ mod test { #[tokio::test] async fn test_return_error() { let mut registry = Registry::new(); - let obj = Human {}; + let obj = Human::default(); registry.add("john".to_string(), obj).await; registry.add_handler::(); let ret = registry @@ -429,7 +400,7 @@ mod test { #[tokio::test] async fn test_not_registered_message() { let mut registry = Registry::new(); - let obj = Human {}; + let obj = Human::default(); registry.add("john".to_string(), obj).await; let ret = registry .send( @@ -548,7 +519,7 @@ mod test { let join_handler = tokio::spawn(async move { let mut registry = Registry::new(); registry.add_handler::(); - let obj = Human {}; + let obj = Human::default(); registry.add("john".to_string(), obj).await; registry .send( @@ -569,7 +540,7 @@ mod test { async fn test_send_sync_lock() { let mut registry = Registry::new(); registry.add_handler::(); - let obj = Human {}; + let obj = Human::default(); registry.add("john".to_string(), obj).await; let registry = Arc::new(RwLock::new(registry)); let inner_registry = Arc::clone(®istry); @@ -595,7 +566,7 @@ mod test { #[tokio::test] async fn test_has_object() { let mut registry = Registry::new(); - let obj = Human {}; + let obj = Human::default(); registry.add("john".to_string(), obj).await; assert!(registry.has("Human", "john").await); assert!(!registry.has("Human", "not john").await); @@ -617,18 +588,11 @@ mod test { } #[tokio::test] - async fn test_static_fn() { + async fn test_new_from_type() { let mut registry = Registry::new(); - registry.add_static_fn::(From::::from); - let new_human: Option = registry.call_static_fn("Oi".to_string()); - assert!(new_human.is_some()); - - let boxed_human = registry.call_static_fn_box("Human".to_string(), "Oi".to_string()); - assert!(boxed_human.is_some()); - - assert_eq!( - new_human.unwrap(), - *boxed_human.unwrap().downcast::().unwrap() - ); + registry.add_type::(); + let boxed_human = registry.new_from_type("Human", "1".to_string()).unwrap(); + let human = boxed_human.downcast::().unwrap(); + assert_eq!(human.id(), "1"); } } diff --git a/src/service.rs b/src/service.rs index 864c6c8..a730959 100644 --- a/src/service.rs +++ b/src/service.rs @@ -83,8 +83,8 @@ impl let new_object = registry .read() .await - .call_static_fn_box(req.handler_type.clone(), req.handler_id.clone()) - .unwrap(); + .new_from_type(&req.handler_type, req.handler_id.clone()) + .expect("TODO"); registry .read() diff --git a/src/service_object.rs b/src/service_object.rs index 835ca3c..b6ccfcd 100644 --- a/src/service_object.rs +++ b/src/service_object.rs @@ -13,6 +13,7 @@ use crate::registry::{Handler, IdentifiableType, Message}; use crate::server::{AdminCommands, AdminSender}; use crate::state::ObjectStateManager; +/// TODO docs pub struct ObjectId(pub String, pub String); impl ObjectId { @@ -21,14 +22,27 @@ impl ObjectId { } } -pub trait FromId { - fn from_id(id: String) -> Self; +/// TODO docs +pub trait WithId { + fn set_id(&mut self, id: String); fn id(&self) -> &str; } +/// ServiceObjects are the objects that will respond to various types of messages through +/// the Rio Server +/// +/// The server stores each ServiceObject onto a registry and control their life cycle +/// +/// There are a few requirements in oder to implement a ServiceObject: +/// - Default +/// - WithId +/// - IdentifiableType +/// - ObjectStateManager +/// - ServiceObjectStateLoad +/// TODO docs #[async_trait] pub trait ServiceObject: - FromId + IdentifiableType + ObjectStateManager + ServiceObjectStateLoad + Default + WithId + IdentifiableType + ObjectStateManager + ServiceObjectStateLoad { /// Send a message to Rio cluster using a client tht is stored in AppData async fn send( diff --git a/src/state/mod.rs b/src/state/mod.rs index fd56a1d..e3024ad 100644 --- a/src/state/mod.rs +++ b/src/state/mod.rs @@ -2,7 +2,7 @@ use crate::errors::LoadStateError; use crate::registry::IdentifiableType; -use crate::{FromId, ServiceObject}; +use crate::{ServiceObject, WithId}; use async_trait::async_trait; use serde::de::DeserializeOwned; use serde::Serialize; @@ -51,7 +51,7 @@ pub trait ObjectStateManager { where T: IdentifiableType + Serialize + DeserializeOwned, S: StateLoader, - Self: State + IdentifiableType + FromId + Send + Sync, + Self: State + IdentifiableType + WithId + Send + Sync, { let object_kind = Self::user_defined_type_id(); let object_id = self.id(); @@ -69,7 +69,7 @@ pub trait ObjectStateManager { where T: IdentifiableType + Serialize + DeserializeOwned + Sync, S: StateSaver, - Self: State + IdentifiableType + FromId + Send + Sync, + Self: State + IdentifiableType + WithId + Send + Sync, { let object_kind = Self::user_defined_type_id(); let object_id = self.id(); @@ -114,12 +114,9 @@ where #[cfg(test)] mod test { - use rio_macros::{FromId, ManagedState, TypeName}; - use serde::Deserialize; - - use crate::FromId; - use super::*; + use rio_macros::{ManagedState, TypeName, WithId}; + use serde::Deserialize; type TestResult = Result<(), Box>; @@ -152,7 +149,7 @@ mod test { #[tokio::test] async fn model_call() -> TestResult { - #[derive(Debug, Default, FromId, TypeName, ManagedState)] + #[derive(Debug, Default, WithId, TypeName, ManagedState)] #[rio_path = "crate"] struct Person { id: String, @@ -186,7 +183,7 @@ mod test { let local_state = local::LocalState::new(); { - let mut person = Person::from_id("foo".to_string()); + let mut person = Person::default(); person.person_state = Some(PersonState { name: "Foo".to_string(), age: 22, @@ -198,7 +195,7 @@ mod test { person.save_all_states(&local_state).await?; } { - let mut person = Person::from_id("foo".to_string()); + let mut person = Person::default(); person.load_all_states(&local_state).await?; assert!(person.person_state.is_some()); assert!(person.legal_state.is_some());