Imports name this capability’s declared dependencies, which fune builds next to it in your project; each one links to its page.
from typing import List, Sequence
from .todo_item import Todo, TodoDraft, validate_draft ← from todo.item ^1.0.0 · built alongside by fune
from .todo_item_validate_draft import trim_space
from .todo_item_validate_todo import is_utc_timestamp
from .todo_normalise_tags import normalise_tags ← from todo.normalise-tags ^1.0.0 · built alongside by fune
def require_utc_now(now: str) -> None:
"""Refuse a clock reading that would not compare as a string with the stored ones."""
if not is_utc_timestamp(now):
raise ValueError("now must be a UTC timestamp like 2026-09-28T09:30:00Z")
def checked_draft(draft: TodoDraft) -> TodoDraft:
"""The draft as it will be stored (trimmed, blank notes None, tags normalised), checked.
The first field in field order that fails validate_draft is the error.
"""
notes = "" if draft.notes is None else trim_space(draft.notes)
clean = TodoDraft(
title=trim_space(draft.title),
notes=None if notes == "" else notes,
priority=draft.priority,
due=draft.due,
tags=list(normalise_tags(draft.tags)),
recurrence=draft.recurrence,
)
verdict = validate_draft(clean)
for field, message in verdict.errors.items():
raise ValueError(f"invalid todo: {field}: {message}")
return clean
def add_todo(todos: Sequence[Todo], draft: TodoDraft, id: str, now: str) -> List[Todo]:
"""Add a todo from the add form: open, created now, last in manual order, appended.
Caller mistakes (id, clock) are reported before form mistakes (the draft).
"""
if trim_space(id) == "":
raise ValueError("a todo needs an id")
if any(t.id == id for t in todos):
raise ValueError(f'a todo with id "{id}" already exists')
require_utc_now(now)
clean = checked_draft(draft)
order = 0
for t in todos:
if t.order + 1 > order:
order = t.order + 1
todo = Todo(
id=id,
title=clean.title,
notes=clean.notes,
done=False,
priority=clean.priority,
due=clean.due,
tags=clean.tags,
recurrence=clean.recurrence,
created_at=now,
completed_at=None,
order=order,
)
return list(todos) + [todo]