Skip to main content

rpstate/
lib.rs

1#![allow(clippy::complexity)]
2pub mod codec;
3pub mod error;
4pub mod migration;
5pub mod reactive;
6pub mod store;
7
8use serde::Serialize;
9use serde::de::DeserializeOwned;
10use std::sync::Arc;
11
12pub use error::Result;
13pub use inventory;
14pub use reactive::{
15    AccessMode, Change, Field, InterceptDisposer, IntoPipeline, MapChange, MapSubscription,
16    Pipeline, Reactive, ReactiveMap, ReactiveScope, ReadOnly, ReadOnlyMode, RpState, RpStateNode,
17    Signal, SignalSubscription, StoreSubscription, Writable, WritableMode,
18};
19pub use serde;
20pub use serde_json;
21
22pub use store::{
23    StateScope, Store, StoreEvent, StoreOp, SubscriptionKind, builder::StoreBuilder,
24    config::StoreConfig, reactive_map, reactive_map_with_path, scoped_path,
25};
26
27pub use migration::{MigrationContext, MigrationError, MigrationReport, Migrator};
28pub use rpstate_macros::{RpType, rpstate};
29
30#[cfg(feature = "json")]
31pub use store::backend::json::JsonStore;
32
33#[cfg(feature = "redb")]
34pub use store::backend::redb::RedbStore;
35
36#[cfg(feature = "redb")]
37pub type DefaultStore = RedbStore;
38
39#[cfg(all(feature = "json", not(feature = "redb")))]
40pub type DefaultStore = JsonStore;
41
42#[cfg(not(any(feature = "json", feature = "redb")))]
43compile_error!(
44    "rpstate requires at least one backend feature to be enabled. \
45     Please enable either 'redb' (recommended) or 'json' in your Cargo.toml."
46);
47
48pub fn field<TScope, TValue>(
49    store: &Arc<DefaultStore>,
50    key: &str,
51    default: TValue,
52) -> Result<Field<TValue, DefaultStore, WritableMode>>
53where
54    TScope: StateScope,
55    TValue: Serialize + Default + DeserializeOwned + Clone + Send + Sync + 'static,
56{
57    store::field::<TScope, TValue, DefaultStore>(store, key, default)
58}
59
60#[macro_export]
61macro_rules! register_migrations {
62    ($T:ty) => {
63        #[cfg(not(target_arch = "wasm32"))]
64        ::inventory::submit! {
65            $crate::migration::registry::MigrationStepEntry {
66                prefix:       <$T as $crate::StateScope>::PREFIX,
67                target_version: 0,
68                description: "",
69                dependencies: <$T as $crate::migration::registry::HasMigrations>::MIGRATION_DEPS,
70                run: |_| Ok(()),
71            }
72        }
73    };
74}
75
76#[macro_export]
77macro_rules! migrate {
78    (
79        $old:path => $new:path,
80        rename: $rename:tt,
81        convert: $convert:tt,
82        |$($args:ident),* $(,)?| $logic_block:block
83    ) => {
84        $crate::migrate!(@route $old => $new, rename: $rename, convert: $convert, |$($args),*| $logic_block);
85    };
86
87    (
88        $old:path => $new:path,
89        rename: $rename:tt,
90        |$($args:ident),* $(,)?| $logic_block:block
91    ) => {
92        $crate::migrate!(@route $old => $new, rename: $rename, |$($args),*| $logic_block);
93    };
94
95    (
96        $old:path => $new:path,
97        convert: $convert:tt,
98        |$($args:ident),* $(,)?| $logic_block:block
99    ) => {
100        $crate::migrate!(@route $old => $new, rename: [], convert: $convert, |$($args),*| $logic_block);
101    };
102
103    (
104        $old:path => $new:path,
105        |$($args:ident),* $(,)?| $logic_block:block
106    ) => {
107        $crate::migrate!(@route $old => $new, rename: [], |$($args),*| $logic_block);
108    };
109
110    (@route $old:path => $new:path, rename: $rename:tt $(, convert: $convert:tt)? , |$old_val:ident| $logic_block:block) => {
111        $crate::migrate!(@impl $old => $new, rename: $rename $(, convert: $convert)? , |$old_val, _unused_ctx| $logic_block);
112    };
113
114    (@route $old:path => $new:path, rename: $rename:tt $(, convert: $convert:tt)? , |$old_val:ident, $ctx_val:ident| $logic_block:block) => {
115        $crate::migrate!(@impl $old => $new, rename: $rename $(, convert: $convert)? , |$old_val, $ctx_val| $logic_block);
116    };
117
118    (
119        @impl $old:path => $new:path,
120        rename: [$($old_f:ident => $new_f:ident),* $(,)?]
121        $(, convert: [$($conv_f:ident : $conv_old:ty => $conv_new:ty),* $(,)?])?
122        , |$old_val:ident, $ctx_val:ident| $logic_block:block
123    ) => {
124        const _: () = {
125            #[allow(dead_code, clippy::no_effect, unused_variables)]
126            fn _check_fields(old: &$old, new: &$new) {
127                $(
128                    let _ = &old.$old_f;
129                    let _ = &new.$new_f;
130                )*
131            }
132        };
133
134        impl $crate::migration::migrate_from::MigrateFrom<$old> for $new {
135            const RENAMES: &'static [(&'static str, &'static str)] = &[
136                $((stringify!($old_f), stringify!($new_f))),*
137            ];
138
139            const CONVERTS: &'static [(&'static str, u64, u64)] = &[
140                $($( (
141                    stringify!($conv_f),
142                    <$conv_old as $crate::migration::types::RpType>::TYPE_HASH,
143                    <$conv_new as $crate::migration::types::RpType>::TYPE_HASH,
144                ) ),*)?
145            ];
146
147            fn migrate($old_val: $old, $ctx_val: &mut $crate::migration::MigrationContext) -> $crate::Result<Self> {
148                $logic_block
149            }
150        }
151
152        $crate::inventory::submit! {
153            $crate::migration::registry::MigrationStepEntry {
154                prefix: <$new as $crate::migration::fields::RpStateFields>::PARENT_PREFIX,
155                target_version: <$new as $crate::migration::fields::RpStateFields>::VERSION,
156                dependencies: <$new as $crate::migration::fields::RpStateFields>::MIGRATION_DEPS,
157                description: "migrate!",
158                schema_hash: <$new as $crate::migration::fields::RpStateFields>::SCHEMA_HASH,
159                fields: <$new as $crate::migration::fields::RpStateFields>::FIELDS,
160                run: |ctx| {
161                    use $crate::migration::fields::RpStateFields;
162                    use $crate::migration::migrate_from::MigrateFrom;
163
164                    let old_data = <$old as RpStateFields>::load_struct(ctx)?;
165                    let new_data = <$new as MigrateFrom<$old>>::migrate(old_data, ctx)?;
166
167                    for field in <$old as RpStateFields>::FIELDS {
168                        let is_renamed = <$new as MigrateFrom<$old>>::RENAMES
169                            .iter()
170                            .any(|(old_k, _)| *old_k == field.name);
171                        let is_kept = <$new as RpStateFields>::FIELDS
172                            .iter()
173                            .any(|f| f.name == field.name);
174
175                        if is_renamed || !is_kept {
176                            ctx.delete(field.name)?;
177                        }
178                    }
179
180                    new_data.save_struct(ctx)?;
181                    Ok(())
182                }
183            }
184        }
185    };
186}
187
188#[macro_export]
189macro_rules! migrate_field {
190    ($ctx:ident, $old_obj:ident . $field:ident) => {
191        $ctx.nested::<_, _>(stringify!($field), $old_obj.$field)?
192    };
193
194    ($ctx:ident, $key:expr, $old_val:expr) => {
195        $ctx.nested::<_, _>($key, $old_val)?
196    };
197}
198
199pub mod tauri_codegen;