storage

package
v0.166.1 Latest Latest
Warning

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

Go to latest
Published: Oct 8, 2026 License: BSD-2-Clause Imports: 19 Imported by: 1

Documentation

Index

Constants

View Source
const AnswerDelimiter = " → **"

AnswerDelimiter separates an item's question from its recorded answer. It is exported so a writer can refuse an answer containing it: an answer that carried the delimiter would make the line ambiguous, and re-answering would then split at the wrong place and destroy the previous answer.

Variables

View Source
var (

	// CheckboxRegex matches a Markdown checkbox line with either `-` or `*` as
	// the list marker. Capture groups: 1=leading whitespace, 2=state (` `, `x`,
	// or `/`), 3=task text. Shared across storage and ops packages to keep the
	// parser shape in one place.
	CheckboxRegex = regexp.MustCompile(`^(\s*)[-*] \[([ x/])\] (.+)$`)

	// CheckboxCompleteRegex matches an unchecked or in-progress checkbox marker
	// (` ` or `/`) and is used by rewriters that force a line to checked.
	// Capture groups: 1=list marker (`-` or `*`), 2=state (` ` or `/`).
	CheckboxCompleteRegex = regexp.MustCompile(`([-*]) \[([ /])\]`)

	// CheckboxUncompleteRegex matches a checked checkbox marker and is used by
	// rewriters that force a line to unchecked. Capture group 1=list marker.
	CheckboxUncompleteRegex = regexp.MustCompile(`([-*]) \[x\]`)
)
View Source
var ErrNotFound = stderrors.New("file not found")

ErrNotFound indicates a requested markdown file could not be resolved by name within a vault. Callers (notably the multi-vault dispatcher) test for it with errors.Is to distinguish a genuine not-found from other failures (e.g. a precondition failure such as incomplete subtasks).

Functions

func FormatOpenQuestionItem added in v0.163.0

func FormatOpenQuestionItem(item OpenQuestionItem, answer string, ending string) string

FormatOpenQuestionItem renders an item with the given answer, preserving the item's own list marker, indentation and line ending. answer must contain neither AnswerDelimiter nor `**` — the caller enforces both — which is what lets ParseOpenQuestions split the result back into the same question and answer. An answer carrying `**` would close the emphasis early and read back with extra asterisks: `**critical**` becomes `***critical**`.

func ParseFrontmatterMap added in v0.149.0

func ParseFrontmatterMap(ctx context.Context, content []byte) (map[string]any, error)

ParseFrontmatterMap parses the YAML frontmatter block from content into a map[string]any, preserving all fields including unknown ones. Returns an error if no frontmatter block is found or YAML is invalid. A bare Obsidian wikilink value is quoted before unmarshal — see quoteBareWikilinks — so it is read as a string rather than a nested list.

Types

type Config

type Config struct {
	TasksDir      string
	GoalsDir      string
	ThemesDir     string
	ObjectivesDir string
	VisionDir     string
	DailyDir      string
	TopicsDir     string
	Excludes      []string
}

Config holds the configuration for storage paths.

func DefaultConfig

func DefaultConfig() *Config

DefaultConfig returns the default storage configuration.

func NewConfigFromVault

func NewConfigFromVault(vault *config.Vault) *Config

NewConfigFromVault creates a Config from a Vault.

type DailyNoteStorage added in v0.25.3

type DailyNoteStorage interface {
	ReadDailyNote(ctx context.Context, vaultPath string, date string) (string, error)
	WriteDailyNote(ctx context.Context, vaultPath string, date string, content string) error
}

func NewDailyNoteStorage added in v0.25.3

func NewDailyNoteStorage(storageConfig *Config) DailyNoteStorage

NewDailyNoteStorage creates a storage for daily note operations only.

type DecisionStorage added in v0.25.3

type DecisionStorage interface {
	ListDecisions(ctx context.Context, vaultPath string) ([]*domain.Decision, error)
	FindDecisionByName(ctx context.Context, vaultPath string, name string) (*domain.Decision, error)
	WriteDecision(ctx context.Context, decision *domain.Decision) error
}

func NewDecisionStorage added in v0.25.3

func NewDecisionStorage(storageConfig *Config) DecisionStorage

NewDecisionStorage creates a storage for decision operations only.

type GoalStorage added in v0.25.3

type GoalStorage interface {
	WriteGoal(ctx context.Context, goal *domain.Goal) error
	FindGoalByName(ctx context.Context, vaultPath string, name string) (*domain.Goal, error)
}

func NewGoalStorage added in v0.25.3

func NewGoalStorage(storageConfig *Config) GoalStorage

NewGoalStorage creates a storage for goal operations only.

type ObjectiveStorage added in v0.29.0

type ObjectiveStorage interface {
	WriteObjective(ctx context.Context, objective *domain.Objective) error
	FindObjectiveByName(
		ctx context.Context,
		vaultPath string,
		name string,
	) (*domain.Objective, error)
}

func NewObjectiveStorage added in v0.29.0

func NewObjectiveStorage(storageConfig *Config) ObjectiveStorage

NewObjectiveStorage creates a storage for objective operations only.

type OpenQuestionItem added in v0.163.0

type OpenQuestionItem struct {
	Index    int
	Question string
	Answer   string
	Marker   string
	Line     int
}

OpenQuestionItem is one item of a task's `Open Questions` section.

Index is POSITIONAL — the item's ordinal among the section's items, counted from 1 — and not the numeric list marker the file happens to render. A section written as `1.` / `3.` reports indices 1 and 2. Callers address items by this index; the marker is preserved on write but never interpreted.

Question and Answer are the item's text split at its trailing ` → **<answer>**` marker, which is the form this package's writer produces. Splitting here, once, is what keeps the reader and any rewriter agreeing on what "the question" is — a rewriter that re-derived the split could disagree and edit the wrong text. Answer is empty for an item that carries none.

Line is the item's index into strings.Split(content, "\n") and Marker is its list prefix (indentation, list marker and the whitespace after it), so a rewriter can address the same physical line the reader counted and preserve the file's own list style.

func ParseOpenQuestions added in v0.163.0

func ParseOpenQuestions(ctx context.Context, content string) ([]OpenQuestionItem, error)

ParseOpenQuestions returns the top-level list items of the first `Open Questions` section in content, in section order, each carrying its 1-based index and the line it occupies. A content without such a section yields an empty (non-nil) slice, so a caller can serialize it as `[]` rather than `null`. A blank list item is skipped rather than yielding an empty question, and the index counts only the items returned.

The heading is matched EXACTLY and case-sensitively against `Open Questions` at any level. A variant such as `Open questions` or `Open Questions:` is not recognised, and the section then reads as absent — an empty list with no error, which is the documented contract in docs/task-writing.md.

This is the single parse both the reader and any rewriter use, so the index a caller names always addresses the line the rewriter edits and both agree on where the question ends and the answer begins.

type PageStorage added in v0.25.3

type PageStorage interface {
	ListPages(ctx context.Context, vaultPath string, pagesDir string) ([]*domain.Page, error)
	// ReadPage returns a single page from a specific directory in the vault.
	// Unlike ListPages it reads only the named file, and it fails when that file
	// is missing or unparseable rather than skipping it.
	//
	// name must be a bare page base name without the .md extension — the same
	// value ListPages reports in Page.Name.
	ReadPage(
		ctx context.Context,
		vaultPath string,
		pagesDir string,
		name string,
	) (*domain.Page, error)
}

func NewPageStorage added in v0.25.3

func NewPageStorage(storageConfig *Config) PageStorage

NewPageStorage creates a storage for page operations only.

type Storage

type Storage interface {
	TaskStorage
	GoalStorage
	ThemeStorage
	ObjectiveStorage
	VisionStorage
	TopicStorage
	DailyNoteStorage
	PageStorage
	DecisionStorage
	// Legacy methods — used by storage tests, not by ops.
	// Keep on composed interface for backward compat; not on narrow interfaces.
	ReadTask(ctx context.Context, vaultPath string, taskID domain.TaskID) (*domain.Task, error)
	ListTasks(ctx context.Context, vaultPath string) ([]*domain.Task, error)
	ReadGoal(ctx context.Context, vaultPath string, goalID domain.GoalID) (*domain.Goal, error)
	ReadTheme(ctx context.Context, vaultPath string, themeID domain.ThemeID) (*domain.Theme, error)
	ReadObjective(
		ctx context.Context,
		vaultPath string,
		objectiveID domain.ObjectiveID,
	) (*domain.Objective, error)
	ReadVision(
		ctx context.Context,
		vaultPath string,
		visionID domain.VisionID,
	) (*domain.Vision, error)
}

func NewStorage

func NewStorage(storageConfig *Config) Storage

NewStorage creates a new markdown storage instance with custom configuration.

type TaskStorage added in v0.25.3

type TaskStorage interface {
	WriteTask(ctx context.Context, task *domain.Task) error
	FindTaskByName(ctx context.Context, vaultPath string, name string) (*domain.Task, error)
	// ListTasks returns every task in the vault's configured tasks_dir. An
	// unreadable task file is skipped (logged at debug level) so a listing still
	// succeeds — see ListTasksStrict for the caller that must not lose a file.
	ListTasks(ctx context.Context, vaultPath string) ([]*domain.Task, error)
	// ListTasksStrict returns every task in the vault's configured tasks_dir and
	// fails on an unreadable task file instead of skipping it. A caller whose
	// result must not silently describe a smaller set (the weekly rollup) uses
	// this; the error names the offending file.
	ListTasksStrict(ctx context.Context, vaultPath string) ([]*domain.Task, error)
}

func NewTaskStorage added in v0.25.3

func NewTaskStorage(storageConfig *Config) TaskStorage

NewTaskStorage creates a storage for task operations only.

type ThemeStorage added in v0.29.0

type ThemeStorage interface {
	WriteTheme(ctx context.Context, theme *domain.Theme) error
	FindThemeByName(ctx context.Context, vaultPath string, name string) (*domain.Theme, error)
}

func NewThemeStorage added in v0.29.0

func NewThemeStorage(storageConfig *Config) ThemeStorage

NewThemeStorage creates a storage for theme operations only.

type TopicStorage added in v0.142.0

type TopicStorage interface {
	WriteTopic(ctx context.Context, topic *domain.Topic) error
	FindTopicByName(ctx context.Context, vaultPath string, name string) (*domain.Topic, error)
}

func NewTopicStorage added in v0.142.0

func NewTopicStorage(storageConfig *Config) TopicStorage

NewTopicStorage creates a storage for topic operations only.

type VisionStorage added in v0.29.0

type VisionStorage interface {
	WriteVision(ctx context.Context, vision *domain.Vision) error
	FindVisionByName(ctx context.Context, vaultPath string, name string) (*domain.Vision, error)
}

func NewVisionStorage added in v0.29.0

func NewVisionStorage(storageConfig *Config) VisionStorage

NewVisionStorage creates a storage for vision operations only.

Jump to

Keyboard shortcuts

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