Skip to content

Commit

Permalink
Support sync (#17)
Browse files Browse the repository at this point in the history
This replicates how Taskwarrior has wrapped the sync method and its use
of `dyn Trait` -- creating a distinct method for each known
implementation of the trait.
  • Loading branch information
djmitche authored Jan 1, 2025
1 parent 24410a0 commit a044e26
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 4 deletions.
49 changes: 46 additions & 3 deletions src/replica.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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<String>,
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<Operation>) -> anyhow::Result<()> {
let ops = operations.iter().map(|op| op.0.clone()).collect();
Ok(self.0.commit_operations(ops)?)
Expand Down
12 changes: 11 additions & 1 deletion taskchampion.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -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: ...
Expand Down
14 changes: 14 additions & 0 deletions tests/test_replica.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down

0 comments on commit a044e26

Please sign in to comment.