use napi::{bindgen_prelude::AsyncTask, Env, Task}; use napi_derive::napi; use std::{ future::Future, sync::{Arc, Mutex}, }; use turso_sync_engine::types::ProtocolCommand; pub const GENERATOR_RESUME_IO: u32 = 0; pub const GENERATOR_RESUME_DONE: u32 = 1; pub trait Generator { fn resume(&mut self, result: Option) -> napi::Result; } impl>> Generator for genawaiter::sync::Gen, F> { fn resume(&mut self, error: Option) -> napi::Result { let result = match error { Some(err) => Err(turso_sync_engine::errors::Error::DatabaseSyncEngineError( format!("JsProtocolIo error: {err}"), )), None => Ok(()), }; match self.resume_with(result) { genawaiter::GeneratorState::Yielded(ProtocolCommand::IO) => Ok(GeneratorResponse::IO), genawaiter::GeneratorState::Complete(Ok(())) => Ok(GeneratorResponse::Done), genawaiter::GeneratorState::Complete(Err(err)) => Err(napi::Error::new( napi::Status::GenericFailure, format!("sync engine operation failed: {err}"), )), } } } #[napi(discriminant = "type")] pub enum GeneratorResponse { IO, Done, SyncEngineStats { operations: i64, main_wal: i64, revert_wal: i64, last_pull_unix_time: i64, last_push_unix_time: Option, revision: Option, }, } #[napi] #[derive(Clone)] pub struct GeneratorHolder { pub(crate) generator: Arc>, pub(crate) response: Arc>>, } pub struct ResumeTask { holder: GeneratorHolder, error: Option, } unsafe impl Send for ResumeTask {} impl Task for ResumeTask { type Output = GeneratorResponse; type JsValue = GeneratorResponse; fn compute(&mut self) -> napi::Result { resume_sync(&self.holder, self.error.take()) } fn resolve(&mut self, _: Env, output: Self::Output) -> napi::Result { Ok(output) } } fn resume_sync(holder: &GeneratorHolder, error: Option) -> napi::Result { let result = holder.generator.lock().unwrap().resume(error)?; if let GeneratorResponse::Done = result { let response = holder.response.lock().unwrap().take(); Ok(response.unwrap_or(GeneratorResponse::Done)) } else { Ok(result) } } #[napi] impl GeneratorHolder { #[napi] pub fn resume_sync(&self, error: Option) -> napi::Result { resume_sync(self, error) } #[napi] pub fn resume_async(&self, error: Option) -> napi::Result> { Ok(AsyncTask::new(ResumeTask { holder: self.clone(), error, })) } }