diff --git a/src/replica.rs b/src/replica.rs index 59c5fa8..8a2c6b6 100644 --- a/src/replica.rs +++ b/src/replica.rs @@ -4,7 +4,9 @@ use std::rc::Rc; use crate::task::TaskData; use crate::{DependencyMap, Operation, Task, WorkingSet}; use pyo3::prelude::*; -use taskchampion::{Operations as TCOperations, Replica as TCReplica, StorageConfig, Uuid}; +use taskchampion::{ + Operations as TCOperations, Replica as TCReplica, ServerConfig, StorageConfig, Uuid, +}; #[pyclass] /// A replica represents an instance of a user's task data, providing an easy interface @@ -108,9 +110,50 @@ impl Replica { .map(|opt| opt.map(TaskData))?) } - pub fn sync(&self, _avoid_snapshots: bool) { - todo!() + /// Sync with a server crated from `ServerConfig::Local`. + fn sync_to_local(&mut self, server_dir: String, avoid_snapshots: bool) -> anyhow::Result<()> { + let mut server = ServerConfig::Local { + server_dir: server_dir.into(), + } + .into_server()?; + Ok(self.0.sync(&mut server, avoid_snapshots)?) } + + /// Sync with a server created from `ServerConfig::Remote`. + fn sync_to_remote( + &mut self, + url: String, + client_id: String, + encryption_secret: String, + avoid_snapshots: bool, + ) -> anyhow::Result<()> { + let mut server = ServerConfig::Remote { + url, + client_id: Uuid::parse_str(&client_id)?, + encryption_secret: encryption_secret.into(), + } + .into_server()?; + Ok(self.0.sync(&mut server, avoid_snapshots)?) + } + + /// Sync with a server created from `ServerConfig::Gcp`. + #[pyo3(signature=(bucket, credential_path, encryption_secret, avoid_snapshots))] + fn sync_to_gcp( + &mut self, + bucket: String, + credential_path: Option, + encryption_secret: String, + avoid_snapshots: bool, + ) -> anyhow::Result<()> { + let mut server = ServerConfig::Gcp { + bucket, + credential_path, + encryption_secret: encryption_secret.into(), + } + .into_server()?; + Ok(self.0.sync(&mut server, avoid_snapshots)?) + } + pub fn commit_operations(&mut self, operations: Vec) -> anyhow::Result<()> { let ops = operations.iter().map(|op| op.0.clone()).collect(); Ok(self.0.commit_operations(ops)?) diff --git a/taskchampion.pyi b/taskchampion.pyi index 534aa66..844f340 100644 --- a/taskchampion.pyi +++ b/taskchampion.pyi @@ -18,7 +18,17 @@ class Replica: def dependency_map(self, force: bool) -> "DependencyMap": ... def get_task(self, uuid: str) -> Optional["Task"]: ... def import_task_with_uuid(self, uuid: str) -> "Task": ... - def sync(self): ... + def sync_to_local(self, server_dir: str, avoid_snapshots: bool): ... + def sync_to_remote( + self, url: str, client_id: str, encryption_secret: str, avoid_snapshots: bool + ): ... + def sync_to_gcp( + self, + bucket: str, + credential_path: Optional[str], + encryption_secret: str, + avoid_snapshots: bool, + ): ... def rebuild_working_set(self, renumber: bool): ... def add_undo_point(self, force: bool) -> None: ... def num_local_operations(self) -> int: ... diff --git a/tests/test_replica.py b/tests/test_replica.py index f05cfb3..098efcf 100644 --- a/tests/test_replica.py +++ b/tests/test_replica.py @@ -33,6 +33,20 @@ def test_constructor(tmp_path: Path): assert r is not None +def test_sync_to_local(tmp_path: Path): + u = str(uuid.uuid4()) + r = Replica.new_in_memory() + _, op = r.create_task(u) + r.commit_operations(op) + r.sync_to_local(str(tmp_path), False) + + # Verify that task syncs to another replica. + r2 = Replica.new_in_memory() + r2.sync_to_local(str(tmp_path), False) + task = r2.get_task(u) + assert task + + def test_constructor_throws_error_with_missing_database(tmp_path: Path): with pytest.raises(RuntimeError): Replica.new_on_disk(str(tmp_path), False)