mutate

package
v0.1.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 7, 2026 License: MIT Imports: 11 Imported by: 0

Documentation

Overview

Package mutate is the single ULID-keyed mutation engine shared by the CLI and (later) the TUI, implementing the write contract from SPEC §6.1–6.3. Every write to tasks.txt goes through Apply: acquire the lock, re-read the file from disk, run the caller's mutation against the fresh Doc while recording before-images, append the undo transaction, then atomically write. Nothing else writes tasks.txt, so a concurrent `nt add` can never be clobbered by a stale in-memory snapshot.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Complete

func Complete(d *task.Doc, rec *Recorder, t *task.Task, today string)

Complete marks a task done, spawning the next occurrence if it recurs (SPEC §9). Both the completion and the spawned task are recorded in the same undo transaction (rec captures the original's before-image and the spawn as an add), so a single `nt undo` reopens the original and removes the new one (SPEC §6.3). Must run inside an Engine.Apply closure.

func Today

func Today() string

Today returns today's date in todo.txt form.

Types

type DoctorReport

type DoctorReport struct {
	DupIDsRemoved int      // duplicate-ULID lines dropped
	IDsAssigned   int      // task lines that lacked an id and got one
	Actions       []string // human-readable, one per fix
}

DoctorReport summarizes what Doctor found (and, unless dry-run, fixed).

func (DoctorReport) Issues

func (r DoctorReport) Issues() int

Issues is the total number of problems found.

type Engine

type Engine struct {
	S *store.Store
}

Engine owns the store and serializes all task-file writes.

func Open

func Open() (*Engine, error)

Open resolves the store and returns an engine.

func (*Engine) Apply

func (e *Engine) Apply(op string, fn func(d *task.Doc, rec *Recorder) error) error

Apply runs fn under the lock against a freshly-read Doc, journals the resulting transaction, and atomically writes the file. fn mutates the Doc and uses rec to record what it touched.

func (*Engine) Archive

func (e *Engine) Archive() (int, error)

Archive moves completed tasks from tasks.txt to done.txt under the lock. It is an explicit housekeeping action and is intentionally not journaled — the cross-file move is the open question in SPEC §15, so `nt undo` does not revert it (the CLI says so). Returns the number of tasks moved.

func (*Engine) Doctor

func (e *Engine) Doctor(apply bool) (DoctorReport, error)

Doctor reconciles tasks.txt after a git merge (or hand-editing): it drops duplicate-ULID lines that a `merge=union` merge can leave behind, and assigns ids to any task line missing one. Like Archive it runs under the lock and is intentionally NOT journaled — it's a repair, and git is the recovery path for a git-tracked store. With apply=false it reports without writing (dry-run).

Duplicate resolution keeps one line per id, preferring a completed line so a "done on one branch" state is never lost.

func (*Engine) Read

func (e *Engine) Read() (*task.Doc, error)

Read returns the current parsed document (no lock; read-only callers such as list/search tolerate a consistent-at-read-time snapshot).

func (*Engine) RenameNote

func (e *Engine) RenameNote(src *note.Note, all []*note.Note, dest string) (newRel string, updated int, err error)

RenameNote renames or moves a note file and rewrites every [[link]] to it across tasks.txt and notes/ (preserving each link's folder prefix, #fragment, and |alias). dest is a new name or a folder/path relative to notes/ (.md optional); a bare name keeps the note in its current folder. It refuses a destination whose basename collides with another note (which would make bare links ambiguous). A pure move (same basename) needs no link rewrite, since resolution is by path-suffix. Like Archive it is not a single undo transaction.

func (*Engine) Undo

func (e *Engine) Undo() (op string, did bool, err error)

Undo reverts the most recent transaction. It pops the journal, applies each change's inverse (restoring before-images by ULID), writes the file, and appends a swapped transaction so a subsequent `nt undo` redoes the change (SPEC §6.3). The returned op label names what was undone; did is false when there is nothing to undo.

type Recorder

type Recorder struct {
	// contains filtered or unexported fields
}

Recorder accumulates the before/after images of touched tasks so a single undo transaction can reverse the whole mutation.

func (*Recorder) Added

func (r *Recorder) Added(t *task.Task)

Added records a newly appended task (no before-image; undo deletes it).

func (*Recorder) Before

func (r *Recorder) Before(t *task.Task)

Before records a task's current line before the caller mutates it. Call it immediately prior to changing the task.

func (*Recorder) Removed

func (r *Recorder) Removed(id, before string)

Removed records a task removed from the document, capturing its before-image.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL