Skip to main content

Crate todoapp_core

Crate todoapp_core 

Source
Expand description

tda domain core — entities, capability components, the decider, and ports.

Zero I/O dependencies (spec §5): only serde (serialization) and derive_more.

Modules§

testing
Deterministic Clock/IdGenerator fixtures, store- and app-agnostic, so every crate’s tests (app, conformance, adapters) can share one definition.

Structs§

Actor
A human or agent. Not persisted via a port in M1 (the spec lists no ActorRepository); Assignment/Claim only ever reference an actor Id.
Archived
Archived capability: an orthogonal flag, independent of Status (a task can be done and archived, or archived without being done) — presence is the flag, no payload needed. Hidden from default views by callers passing Filter { archived: Some(false), .. } (spec §13 Q4 direction); QueryEngine/Filter itself stay neutral (None = no restriction).
Assignment
One assignee on a task; claimed flips when that actor claims it (§8).
Assignments
Assignment capability: the whole assignee list is one component value (empty ⇒ remove it). Its presence/contents drive Claim (spec §8).
Attachment
One attachment: a Link never has a blob (it’s just a URL); File/ Image may or may not have one — url keeps the original source path/URL either way (e.g. from an import), blob is Some once actual bytes have been stored via crate::BlobStore.
Attachments
Attachments capability: the whole list is one component value (empty ⇒ remove it), like Tags/Assignments.
Collection
A saved tree or saved query (spec §7). spec holds the query for query kind.
Date
A calendar date (Schedule/due dates). Serializes as an ISO-8601 YYYY-MM-DD string (jiff’s own serde impl) — the same shape the field had as a raw String.
DecideCtx
Facts a guard needs beyond the task itself. Just the derived blocked flag for now (spec §8 start-gate); grow as systems need more.
Denied
A refused command, with a human/agent-readable reason (spec §5a).
Due
A due date with an optional time-of-day (spec: a “rendez-vous” due can carry a time; overdue/eta rollups (Aggregate::earliest_due, project_finish_date) stay day-granularity and only ever read .date.time is display-only, never compared.
Duration
An effort/elapsed duration (Estimate, TimeSpent), minute precision. Serializes as an integer number of minutes — the same shape the field had as a raw u32.
Estimate
Estimate capability (effort estimate).
Filter
Id
Stable identity for tasks, actors, collections. Opaque string (a random ULID in real adapters; a sequence in tests). Serializes transparently as that string.
IssueRef
IssueRef capability: a static reference to an external issue tracker’s issue (e.g. imported from another tool). provider is freeform (no closed enum) — no live sync, no computed URL.
Link
A typed, ordered directed edge. child is a single-parent tree; blocks is a DAG (invariants enforced in todoapp-app).
Notes
Notes capability: Markdown body.
Position
Fractional index (spec §7): insert between two neighbours by averaging, so a reorder or subtree move touches one row, never the siblings.
Query
Recurrence
Recurrence capability: a task carrying this resets in place on completion instead of staying done — spec decision: no per-occurrence task spawning, the same task’s Schedule advances and its Status goes back to todo (see the Event::StatusSet(Status::Done) apply arm).
Schedule
Schedule capability: a due date, optionally with a time-of-day.
SortKey
Tags
Tags capability: the whole set is one component value (empty ⇒ remove it).
Time
A time-of-day (minute precision), for a due date’s optional rendez-vous time. Naive/local, like the rest of the codebase — no zone is stored.
TimeLog
TimeLog capability: a per-day breakdown of time spent, keyed by calendar date. TimeSpent stays the fast-path cumulative total — it’s recomputed as this map’s sum whenever it’s set (see the Event::TimeLogSet apply arm), so aggregation (FR-13) keeps reading TimeSpent unchanged. Mixing this with the plain AddTimeSpent command (no date) can leave the two slightly inconsistent — a known, accepted edge case, not guarded against.
TimeSpent
TimeSpent capability (accumulated time).
Timestamp
An instant (task created_at/updated_at). Serializes as epoch milliseconds (a bare integer), matching the pre-jiff Timestamp(i64) shape.
Title
Required Title capability.

Enums§

ActorKind
AttachmentKind
CollectionKind
Command
Intent to mutate one task. (Create is [TaskState::new], not a command.)
Dir
DueFilter
Event
The decided result of a command, folded by apply.
LinkKind
RepeatCycle
A recurrence rule (spec §3): how often a Recurrence-carrying task’s due date advances when it’s completed (see Recurrence::next_due). Covers the common cases (daily interval, weekly weekday set, monthly same-day) — not a full RRULE engine.
ResolvedId
Result of resolving a user-typed id or prefix against the full id set.
SortField
Status
Required Status capability (spec §8). blocked is derived, not stored. Transitions between any two values are unrestricted (no guard) — rank is just for ordering/display, not a legality check.
Weekday
A day of the week, for RepeatCycle::Weekly. A local enum (not jiff’s) so serde stays as simple as Status’s.

Traits§

BlobStore
Blob storage for Attachment file/image content (spec §3): separate from ComponentStore since blobs aren’t a per-task-capability row — a task’s Attachments component only carries a blob id reference.
Clock
Injected time source — deterministic in tests.
CollectionRepository
Component
A capability component (spec §3): a unit of data keyed by task Id in the store. Presence of the value is the capability — there is no monolithic Task struct; a task is the set of components attached to its id, fetched and mutated one capability at a time (store.get::<Status>(id) / store.set(id, Status::Wip)). NAME keys the per-capability map/table (spec §7). Adding a capability = a new Component type; the generic store needs no change.
ComponentStore
Capability-keyed component access (spec §3/§7), ECS/column-store style: read, write, or detach one capability at a time, keyed by task Id. There is no whole-task load/save — a caller (or a guard) touches only the capabilities it needs. Generic methods make this not object-safe, so callers hold a concrete store (Services<St>), not &dyn.
IdGenerator
Injected id source — deterministic in tests.
LinkRepository
QueryEngine
Evaluate a query’s filter — the ids of tasks that match (unsorted). today is the reference date for due:today/overdue. Object-safe (no generic methods), so it rides in Services as &dyn. Sorting + breadcrumbs are the caller’s job (todoapp-app), identical across stores.
TaskEntityStore
The minimal task entity (spec §7): identity + timestamps only. Everything else is a Component. delete cascades every component of the id.

Functions§

apply
Write an event back as components (spec §7 presence-as-capability): a collection that becomes empty is removed, not stored empty.
decide
Run cmd through the ordered guards (first denial wins, spec §13 Q2 default), reading current state from store. On allow, emit the events. Async: guards get only the capabilities they inspect.
descendants
All descendants of id via child links (excludes id). Iterative.
resolve_id_prefix
Resolve a typed id/prefix (e.g. from a CLI arg or TUI lookup) the way git/jj do: an exact full-id match always wins, even if it’s also a prefix of other ids; otherwise the prefix must match exactly one id. Case-insensitive: ids are generated lowercase, but older stores may still hold uppercase ULIDs, and typing case shouldn’t matter either way.
select_matching
Task ids matching filter (unsorted). Loads only the components a given predicate needs, matching the in-app snapshot semantics exactly.
shortest_unique_prefixes
Shortest prefix of each id that’s unique among ids — the length is picked so the prefix differs from both lexical neighbours once sorted.