tauri_plugin_powersync/
lib.rs1use powersync::error::PowerSyncError;
2use powersync::schema::SchemaOrCustom;
3use powersync::{env::PowerSyncEnvironment, ConnectionPool, PowerSyncDatabase};
4use rusqlite::Connection;
5use std::collections::hash_map::Entry;
6use std::marker::PhantomData;
7use std::sync::{Arc, Weak};
8use std::{collections::HashMap, sync::Mutex};
9use tauri::{
10 plugin::{Builder, TauriPlugin},
11 AppHandle, Manager, Runtime,
12};
13
14mod commands;
15mod database;
16mod error;
17mod handle;
18
19use crate::database::TauriDatabaseState;
20use crate::handle::JavaScriptHandles;
21pub use error::Result;
22
23pub trait PowerSyncExt<R: Runtime> {
26 fn powersync(&self) -> &PowerSync<R>;
27}
28
29impl<R: Runtime, T: Manager<R>> PowerSyncExt<R> for T {
30 fn powersync(&self) -> &PowerSync<R> {
31 self.state::<PowerSync<R>>().inner()
32 }
33}
34
35pub struct PowerSync<R: Runtime> {
36 app: PhantomData<AppHandle<R>>,
37 databases: Mutex<HashMap<String, Weak<TauriDatabaseState>>>,
38 pub(crate) handles: JavaScriptHandles,
39}
40
41impl<R: Runtime> PowerSync<R> {
42 pub(crate) fn open_database(
43 &self,
44 app: AppHandle<R>,
45 name: &str,
46 schema: SchemaOrCustom,
47 ) -> Result<Arc<TauriDatabaseState>> {
48 let mut map = self.databases.lock().unwrap();
49 let mut entry = map.entry(name.to_owned());
50
51 if let Entry::Occupied(entry) = &mut entry {
52 if let Some(existing) = entry.get().upgrade() {
53 return Ok(existing);
54 }
55 };
56
57 PowerSyncEnvironment::powersync_auto_extension()?;
58 let pool = if name == ":memory:" {
59 ConnectionPool::single_connection(
60 Connection::open_in_memory().map_err(PowerSyncError::from)?,
61 )
62 } else {
63 ConnectionPool::open(name)?
64 };
65
66 let env = PowerSyncEnvironment::custom(
67 reqwest::Client::new(),
68 pool,
69 PowerSyncEnvironment::tokio_timer(),
70 );
71
72 let database = PowerSyncDatabase::new(env, schema);
73 database.async_tasks().spawn_with_tokio();
74
75 let db = Arc::new(TauriDatabaseState::new(app, name, database));
76 entry.insert_entry(Arc::downgrade(&db));
77
78 Ok(db)
79 }
80
81 pub fn database_from_javascript_handle(&self, handle: usize) -> Result<PowerSyncDatabase> {
84 let handle = self.handles.lookup(handle)?;
85 Ok(handle.as_database()?.clone())
86 }
87}
88
89pub fn init<R: Runtime>() -> TauriPlugin<R> {
91 Builder::new("powersync")
92 .invoke_handler(tauri::generate_handler![commands::powersync])
93 .setup(|app, _api| {
94 let powersync = PowerSync {
95 app: PhantomData::<AppHandle<R>>,
96 databases: Default::default(),
97 handles: Default::default(),
98 };
99 app.manage(powersync);
100 Ok(())
101 })
102 .build()
103}