Functional Weave
Code in Rust

todo.list@1.1.0

impl/python/toggle_todo.py

1,387 bytes · the Python implementation · view raw

Imports name this capability’s declared dependencies, which fune builds next to it in your project; each one links to its page.

from dataclasses import replace
from typing import List, Sequence

from .todo_item import Todo  ← from todo.item ^1.0.0 · built alongside by fune
from .todo_item_validate_draft import is_iso_date
from .todo_list_add_todo import require_utc_now  ← addTodo, another function of this group · built into the same file, even by a slim install
from .todo_list_update_todo import index_of_todo  ← updateTodo, another function of this group · built into the same file, even by a slim install
from .todo_next_occurrence import next_occurrence  ← from todo.next-occurrence ^1.0.0 · built alongside by fune


def require_today(today: str) -> None:
    """Refuse a today that is not a real YYYY-MM-DD date, whether or not a repeat needs it."""
    if not is_iso_date(today):
        raise ValueError("today must be a date like 2026-09-28")


def complete_todo(todo: Todo, now: str, today: str) -> Todo:
    """Complete one open todo; a repeating one stays open and rolls forward to its next occurrence."""
    if todo.recurrence is None:
        return replace(todo, done=True, completed_at=now)
    if todo.due is None:
        raise ValueError(f'repeating todo "{todo.id}" has no due date')
    return replace(todo, done=False, completed_at=None, due=next_occurrence(todo.recurrence, todo.due, today))


def toggle_todo(todos: Sequence[Todo], id: str, now: str, today: str) -> List[Todo]:
    """Tick or untick one todo. Reopening clears the completion time."""
    at = index_of_todo(todos, id)
    require_utc_now(now)
    require_today(today)
    out = list(todos)
    t = todos[at]
    out[at] = replace(t, done=False, completed_at=None) if t.done else complete_todo(t, now, today)
    return out