diff --git a/README.md b/README.md index e0589e6..cb76fb1 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,5 @@ This submodule contains bindings to the Taskchampion # TODO - There is no good way to describe functions that accept interface (e.g. `Replica::new` accepts any of the storage implementations, but Python bindings lack such mechanisms), currently, `Replica::new` just constructs the SqliteStorage from the params passed into the constructor. -- Currently Task class is just a reflection of the rust's `Task` struct, but constructing the standalone `TaskMut` is impossible, as Pyo3 bindings do not allow lifetimes (python has no alternatives to them). Would be nice to expand the `Task` class to include the methods from `TaskMut` and convert into the mutable state and back when they are called. - It is possible to convert `WorkingSet` into a python iterator (you can iterate over it via `for item in :` or `next()`), but that needs a way to store the current state. - +- Possible integration with Github Workflows for deployment. diff --git a/src/replica.rs b/src/replica.rs index 83fbf74..8229e47 100644 --- a/src/replica.rs +++ b/src/replica.rs @@ -38,16 +38,13 @@ impl Replica { /// Create a new task /// The task must not already exist. - pub fn create_task(&mut self, uuid: String) -> anyhow::Result<(Task, Operation)> { + pub fn create_task(&mut self, uuid: String) -> anyhow::Result<(Task, Vec)> { let mut ops = TCOperations::new(); let task = self .0 .create_task(Uuid::parse_str(&uuid)?, &mut ops) .map(|t| Task(t))?; - Ok(( - task, - Operation(ops.get(0).expect("Missing Operation").clone()), - )) + Ok((task, ops.iter().map(|op| Operation(op.clone())).collect())) } /// Get a list of all tasks in the replica. diff --git a/src/task/task.rs b/src/task/task.rs index 7f6c4a6..5ecb83e 100644 --- a/src/task/task.rs +++ b/src/task/task.rs @@ -184,24 +184,24 @@ impl Task { Ok(ops.iter().map(|op| Operation(op.clone())).collect()) } - pub fn set_description(&mut self, description: String) -> anyhow::Result { + pub fn set_description(&mut self, description: String) -> anyhow::Result> { let mut ops: Vec = Vec::new(); self.0.set_description(description, &mut ops).expect(""); - Ok(ops.get(0).map(|op| Operation(op.clone())).unwrap()) + Ok(ops.iter().map(|op| Operation(op.clone())).collect()) } - pub fn set_priority(&mut self, priority: String) -> anyhow::Result { + pub fn set_priority(&mut self, priority: String) -> anyhow::Result> { let mut ops: Vec = Vec::new(); self.0.set_priority(priority, &mut ops).expect(""); - Ok(ops.get(0).map(|op| Operation(op.clone())).unwrap()) + Ok(ops.iter().map(|op| Operation(op.clone())).collect()) } #[pyo3(signature=(entry=None))] - pub fn set_entry(&mut self, entry: Option) -> anyhow::Result { + pub fn set_entry(&mut self, entry: Option) -> anyhow::Result> { let mut ops: Vec = Vec::new(); let timestamp = entry.map(|time| { @@ -212,11 +212,11 @@ impl Task { self.0.set_entry(timestamp, &mut ops).expect(""); - Ok(ops.get(0).map(|op| Operation(op.clone())).unwrap()) + Ok(ops.iter().map(|op| Operation(op.clone())).collect()) } #[pyo3(signature=(wait=None))] - pub fn set_wait(&mut self, wait: Option) -> anyhow::Result { + pub fn set_wait(&mut self, wait: Option) -> anyhow::Result> { let mut ops: Vec = Vec::new(); let timestamp = wait.map(|time| { @@ -227,11 +227,11 @@ impl Task { self.0.set_wait(timestamp, &mut ops).expect(""); - Ok(ops.get(0).map(|op| Operation(op.clone())).unwrap()) + Ok(ops.iter().map(|op| Operation(op.clone())).collect()) } #[pyo3(signature=(modified=None))] - pub fn set_modified(&mut self, modified: Option) -> anyhow::Result { + pub fn set_modified(&mut self, modified: Option) -> anyhow::Result> { let mut ops: Vec = Vec::new(); let timestamp = modified.map(|time| { @@ -242,7 +242,7 @@ impl Task { self.0.set_wait(timestamp, &mut ops).expect(""); - Ok(ops.get(0).map(|op| Operation(op.clone())).unwrap()) + Ok(ops.iter().map(|op| Operation(op.clone())).collect()) } #[pyo3(signature=(property, value=None))] @@ -250,54 +250,54 @@ impl Task { &mut self, property: String, value: Option, - ) -> anyhow::Result { + ) -> anyhow::Result> { let mut ops: Vec = Vec::new(); self.0.set_value(property, value, &mut ops).expect(""); - Ok(ops.get(0).map(|op| Operation(op.clone())).unwrap()) + Ok(ops.iter().map(|op| Operation(op.clone())).collect()) } - pub fn start(&mut self) -> anyhow::Result { + pub fn start(&mut self) -> anyhow::Result> { let mut ops: Vec = Vec::new(); self.0.start(&mut ops).expect(""); - Ok(ops.get(0).map(|op| Operation(op.clone())).unwrap()) + Ok(ops.iter().map(|op| Operation(op.clone())).collect()) } - pub fn stop(&mut self) -> anyhow::Result { + pub fn stop(&mut self) -> anyhow::Result> { let mut ops: Vec = Vec::new(); self.0.stop(&mut ops).expect(""); - Ok(ops.get(0).map(|op| Operation(op.clone())).unwrap()) + Ok(ops.iter().map(|op| Operation(op.clone())).collect()) } - pub fn done(&mut self) -> anyhow::Result { + pub fn done(&mut self) -> anyhow::Result> { let mut ops: Vec = Vec::new(); self.0.done(&mut ops).expect(""); - Ok(ops.get(0).map(|op| Operation(op.clone())).unwrap()) + Ok(ops.iter().map(|op| Operation(op.clone())).collect()) } - pub fn add_tag(&mut self, tag: &Tag) -> anyhow::Result { + pub fn add_tag(&mut self, tag: &Tag) -> anyhow::Result> { let mut ops: Vec = Vec::new(); self.0.add_tag(&tag.0, &mut ops).expect(""); - Ok(ops.get(0).map(|op| Operation(op.clone())).unwrap()) + Ok(ops.iter().map(|op| Operation(op.clone())).collect()) } - pub fn remove_tag(&mut self, tag: &Tag) -> anyhow::Result { + pub fn remove_tag(&mut self, tag: &Tag) -> anyhow::Result> { let mut ops: Vec = Vec::new(); self.0.remove_tag(&tag.0, &mut ops).expect(""); - Ok(ops.get(0).map(|op| Operation(op.clone())).unwrap()) + Ok(ops.iter().map(|op| Operation(op.clone())).collect()) } - pub fn add_annotation(&mut self, ann: &Annotation) -> anyhow::Result { + pub fn add_annotation(&mut self, ann: &Annotation) -> anyhow::Result> { let mut ops: Vec = Vec::new(); let mut annotation = Annotation::new(); @@ -306,24 +306,27 @@ impl Task { self.0.add_annotation(annotation.0, &mut ops).expect(""); - Ok(ops.get(0).map(|op| Operation(op.clone())).unwrap()) + Ok(ops.iter().map(|op| Operation(op.clone())).collect()) } - pub fn remove_annotation(&mut self, timestamp: DateTime) -> anyhow::Result { + pub fn remove_annotation( + &mut self, + timestamp: DateTime, + ) -> anyhow::Result> { let mut ops: Vec = Vec::new(); self.0.remove_annotation(timestamp, &mut ops).expect(""); - Ok(ops.get(0).map(|op| Operation(op.clone())).unwrap()) + Ok(ops.iter().map(|op| Operation(op.clone())).collect()) } #[pyo3(signature=(due=None))] - pub fn set_due(&mut self, due: Option>) -> anyhow::Result { + pub fn set_due(&mut self, due: Option>) -> anyhow::Result> { let mut ops: Vec = Vec::new(); self.0.set_due(due, &mut ops).expect(""); - Ok(ops.get(0).map(|op| Operation(op.clone())).unwrap()) + Ok(ops.iter().map(|op| Operation(op.clone())).collect()) } pub fn set_uda( @@ -331,53 +334,53 @@ impl Task { namespace: String, key: String, value: String, - ) -> anyhow::Result { + ) -> anyhow::Result> { let mut ops: Vec = Vec::new(); self.0.set_uda(namespace, key, value, &mut ops).expect(""); - Ok(ops.get(0).map(|op| Operation(op.clone())).unwrap()) + Ok(ops.iter().map(|op| Operation(op.clone())).collect()) } - pub fn remove_uda(&mut self, namespace: String, key: String) -> anyhow::Result { + pub fn remove_uda(&mut self, namespace: String, key: String) -> anyhow::Result> { let mut ops: Vec = Vec::new(); self.0.remove_uda(namespace, key, &mut ops).expect(""); - Ok(ops.get(0).map(|op| Operation(op.clone())).unwrap()) + Ok(ops.iter().map(|op| Operation(op.clone())).collect()) } - pub fn set_legacy_uda(&mut self, key: String, value: String) -> anyhow::Result { + pub fn set_legacy_uda(&mut self, key: String, value: String) -> anyhow::Result> { let mut ops: Vec = Vec::new(); self.0.set_legacy_uda(key, value, &mut ops).expect(""); - Ok(ops.get(0).map(|op| Operation(op.clone())).unwrap()) + Ok(ops.iter().map(|op| Operation(op.clone())).collect()) } - pub fn remove_legacy_uda(&mut self, key: String) -> anyhow::Result { + pub fn remove_legacy_uda(&mut self, key: String) -> anyhow::Result> { let mut ops: Vec = Vec::new(); self.0.remove_legacy_uda(key, &mut ops).expect(""); - Ok(ops.get(0).map(|op| Operation(op.clone())).unwrap()) + Ok(ops.iter().map(|op| Operation(op.clone())).collect()) } - pub fn add_dependency(&mut self, dep: String) -> anyhow::Result { + pub fn add_dependency(&mut self, dep: String) -> anyhow::Result> { let mut ops: Vec = Vec::new(); let dep_uuid = Uuid::parse_str(&dep).expect("couldn't parse UUID"); self.0.add_dependency(dep_uuid, &mut ops).expect(""); - Ok(ops.get(0).map(|op| Operation(op.clone())).unwrap()) + Ok(ops.iter().map(|op| Operation(op.clone())).collect()) } - pub fn remove_dependency(&mut self, dep: String) -> anyhow::Result { + pub fn remove_dependency(&mut self, dep: String) -> anyhow::Result> { let mut ops: Vec = Vec::new(); let dep_uuid = Uuid::parse_str(&dep).expect("couldn't parse UUID"); self.0.remove_dependency(dep_uuid, &mut ops).expect(""); - Ok(ops.get(0).map(|op| Operation(op.clone())).unwrap()) + Ok(ops.iter().map(|op| Operation(op.clone())).collect()) } } diff --git a/taskchampion.pyi b/taskchampion.pyi index 0d37aa6..8041327 100644 --- a/taskchampion.pyi +++ b/taskchampion.pyi @@ -7,7 +7,7 @@ class Replica: def __init__(self, path: str, create_if_missing: bool): ... @staticmethod def new_inmemory(): ... - def create_task(self, uuid: str) -> tuple["Task", "Operation"]: ... + def create_task(self, uuid: str) -> tuple["Task", list["Operation"]]: ... def all_task_uuids(self) -> list[str]: ... def all_tasks(self) -> dict[str, "Task"]: ... @@ -73,42 +73,35 @@ class Task: def get_dependencies(self) -> list[str]: ... def get_value(self, property: str) -> Optional[str]: ... def set_status(self, status: "Status") -> list["Operation"]: ... - def set_description(self, description: str) -> Optional["Operation"]: ... - def set_priority(self, priority: str) -> Optional["Operation"]: ... - def set_entry(self, entry: Optional[str]) -> Optional["Operation"]: ... - def set_wait(self, wait: Optional[str]) -> Optional["Operation"]: ... - - def set_modified( - self, modified: Optional[str]) -> Optional["Operation"]: ... + def set_description(self, description: str) -> list["Operation"]: ... + def set_priority(self, priority: str) -> list["Operation"]: ... + def set_entry(self, entry: Optional[str]) -> list["Operation"]: ... + def set_wait(self, wait: Optional[str]) -> list["Operation"]: ... + def set_modified(self, modified: Optional[str]) -> list["Operation"]: ... def set_value( self, property: str, value: Optional[str] ) -> Optional["Operation"]: ... - def start(self) -> Optional["Operation"]: ... - def stop(self) -> Optional["Operation"]: ... - def done(self) -> Optional["Operation"]: ... - def add_tag(self, tag: "Tag") -> Optional["Operation"]: ... - def remove_tag(self, tag: "Tag") -> Optional["Operation"]: ... + def start(self) -> list["Operation"]: ... + def stop(self) -> list["Operation"]: ... + def done(self) -> list["Operation"]: ... + def add_tag(self, tag: "Tag") -> list["Operation"]: ... + def remove_tag(self, tag: "Tag") -> list["Operation"]: ... def add_annotation( - self, annotation: "Annotation") -> Optional["Operation"]: ... + self, annotation: "Annotation") -> list["Operation"]: ... def remove_annotation( - self, annotation: "Annotation") -> Optional["Operation"]: ... - - def set_due(self, due: Optional[datetime]) -> Optional["Operation"]: ... - - def set_uda( - self, namespace: str, key: str, value: str - ) -> Optional["Operation"]: ... + self, annotation: "Annotation") -> list["Operation"]: ... - def remove_uda(self, namespace: str, - key: str) -> Optional["Operation"]: ... - def set_legacy_uda( - self, key: str, value: str) -> Optional["Operation"]: ... + def set_due(self, due: Optional[datetime]) -> list["Operation"]: ... + def set_uda(self, namespace: str, key: str, + value: str) -> list["Operation"]: ... - def remove_legacy_uda(self, key: str) -> Optional["Operation"]: ... - def add_dependency(self, uuid: str) -> Optional["Operation"]: ... - def remove_dependency(self, uuid: str) -> Optional["Operation"]: ... + def remove_uda(self, namespace: str, key: str) -> list["Operation"]: ... + def set_legacy_uda(self, key: str, value: str) -> list["Operation"]: ... + def remove_legacy_uda(self, key: str) -> list["Operation"]: ... + def add_dependency(self, uuid: str) -> list["Operation"]: ... + def remove_dependency(self, uuid: str) -> list["Operation"]: ... class WorkingSet: diff --git a/tests/test_replica.py b/tests/test_replica.py index dc41b6d..45c9760 100644 --- a/tests/test_replica.py +++ b/tests/test_replica.py @@ -16,13 +16,13 @@ def empty_replica(tmp_path: Path) -> Replica: def replica_with_tasks(empty_replica: Replica): ops = [] _, op = empty_replica.create_task(str(uuid.uuid4())) - ops.append(op) + ops.extend(op) _, op = empty_replica.create_task(str(uuid.uuid4())) - ops.append(op) + ops.extend(op) _, op = empty_replica.create_task(str(uuid.uuid4())) - ops.append(op) + ops.extend(op) empty_replica.commit_operations(ops) @@ -44,7 +44,7 @@ def test_create_task(empty_replica: Replica): u = uuid.uuid4() _, op = empty_replica.create_task(str(u)) - empty_replica.commit_operations([op]) + empty_replica.commit_operations(op) tasks = empty_replica.all_task_uuids() @@ -54,13 +54,13 @@ def test_create_task(empty_replica: Replica): def test_all_task_uuids(empty_replica: Replica): ops = [] _, op = empty_replica.create_task(str(uuid.uuid4())) - ops.append(op) + ops.extend(op) _, op = empty_replica.create_task(str(uuid.uuid4())) - ops.append(op) + ops.extend(op) _, op = empty_replica.create_task(str(uuid.uuid4())) - ops.append(op) + ops.extend(op) empty_replica.commit_operations(ops) tasks = empty_replica.all_task_uuids() @@ -70,13 +70,13 @@ def test_all_task_uuids(empty_replica: Replica): def test_all_tasks(empty_replica: Replica): ops = [] _, op = empty_replica.create_task(str(uuid.uuid4())) - ops.append(op) + ops.extend(op) _, op = empty_replica.create_task(str(uuid.uuid4())) - ops.append(op) + ops.extend(op) _, op = empty_replica.create_task(str(uuid.uuid4())) - ops.append(op) + ops.extend(op) empty_replica.commit_operations(ops) @@ -122,7 +122,7 @@ def test_num_local_operations(replica_with_tasks: Replica): _, op = replica_with_tasks.create_task(str(uuid.uuid4())) - replica_with_tasks.commit_operations([op]) + replica_with_tasks.commit_operations(op) assert replica_with_tasks.num_local_operations() == 4 @@ -131,7 +131,7 @@ def test_num_undo_points(replica_with_tasks: Replica): _, op = replica_with_tasks.create_task(str(uuid.uuid4())) - replica_with_tasks.commit_operations([op]) + replica_with_tasks.commit_operations(op) assert replica_with_tasks.num_undo_points() == 4 diff --git a/tests/test_working_set.py b/tests/test_working_set.py index 1837357..e8fbd9f 100644 --- a/tests/test_working_set.py +++ b/tests/test_working_set.py @@ -10,14 +10,14 @@ def working_set(tmp_path: Path): ops = [] task, op = r.create_task(str(uuid.uuid4())) - ops.append(op) + ops.extend(op) ops.extend(task.set_status(Status.Pending)) task, op = r.create_task(str(uuid.uuid4())) - ops.append(op) + ops.extend(op) ops.extend(task.set_status(Status.Pending)) - ops.append(task.start()) + ops.extend(task.start()) r.commit_operations(ops) return r.working_set()