Help us shape the future of Go! Take the 2026 Go Developer Survey

study

package
v0.8.1 Latest Latest
Warning

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

Go to latest
Published: Sep 28, 2026 License: MIT Imports: 15 Imported by: 0

Documentation

Overview

Package study keeps a player's own notes and bookmarks on saved games.

A study lives beside its game rather than inside it. The game's record is canonical and carries digests that say what the game was, so nothing written here ever touches it; a study is this machine's annotation of one record, kept in a directory of its own with one file per saved game, named by the game's identifier. Exporting a game therefore exports the record alone, and deleting a saved game leaves its study file where it was, unattached but recoverable.

A study is tied to the record it was written against, not only to the game's identifier: it carries that record's digest. A note on entry 14 is about the position after the fourteenth entry of one particular record, so a study whose digest no longer matches the game's record is refused rather than moved onto a different position without anyone noticing.

Every successful save increments the study's revision, and a save names the revision it was made against. Two windows editing the same study cannot overwrite each other: the second to save finds that the revision has moved on and is refused, which leaves the first window's save on disk and the second window's text in its editor, both still there to be reconciled.

Index

Constants

View Source
const (
	// Version is the study document format this build reads and writes.
	Version = 1
	// MaxNoteBytes bounds one note, counted in UTF-8 bytes.
	MaxNoteBytes = 8 << 10
	// MaxFileBytes bounds one study document on disk. Nothing this build
	// writes exceeds it, so anything that does was not written by twixtui.
	MaxFileBytes = 1 << 20
)

Variables

View Source
var (
	// ErrRecordChanged reports a stored study written against a different
	// record of the game. The study file is left as it is.
	ErrRecordChanged = errors.New("study belongs to a different record of this game")
	// ErrStale reports a save made against a revision the stored study has
	// since moved past.
	ErrStale = errors.New("study has been saved elsewhere since it was loaded")
	// ErrNewerVersion reports a study document written in a format newer than
	// this build knows.
	ErrNewerVersion = errors.New("study was written by a newer version of twixtui")
	// ErrCorrupt reports a study document that cannot be read as one, or, from
	// Set, one that can be read but can take no further save.
	ErrCorrupt = errors.New("study file is damaged")
	// ErrUnfinished reports a save for a game that has no result yet.
	ErrUnfinished = errors.New("game is not finished")
	// ErrInvalid reports a mark that cannot be saved.
	ErrInvalid = errors.New("invalid study mark")
)

Functions

This section is empty.

Types

type Mark

type Mark struct {
	Entry    int    `json:"entry"`
	Note     string `json:"note,omitempty"`
	Bookmark bool   `json:"bookmark,omitempty"`
}

Mark is what a player keeps about one entry: a note, a bookmark, or both.

type Store

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

Store is the collection of studies in one configuration directory.

func Open

func Open(configDir string) *Store

Open prepares the store of studies kept in configDir. The directory is created on the first save rather than here or on a load, so looking at a game nobody has taken notes on leaves nothing behind.

func (*Store) Dir

func (st *Store) Dir() string

Dir returns the directory studies are kept in.

func (*Store) Load

func (st *Store) Load(t Target) (Study, error)

Load reads the study of the game t names. A game with no study yet has an empty one at revision 0.

A stored study written against a different record is refused with ErrRecordChanged, one from a newer format with ErrNewerVersion, and one that cannot be read as a study of this record with ErrCorrupt; each of them comes back with no study at all, so nothing from the refused file can be mistaken for this record's notes. Loading never writes, not even a lock file.

func (*Store) Set

func (st *Store) Set(t Target, baseRevision int64, m Mark) (Study, error)

Set saves m in place of whatever mark its entry held, or removes that mark when m has neither a note nor a bookmark. baseRevision is the revision the caller loaded and edited.

The target and the mark are checked before anything is created, so a refused save of an unfinished game or of a bad mark leaves no trace. The stored study is then read again under the study's exclusive lock, which is held until the write is done: comparing revisions is only worth anything while nobody else can move them. A stored revision other than baseRevision means somebody else saved first; the save is refused with ErrStale and the current stored study is returned, so the caller can show what is there beside its own unsaved text. Otherwise the revision advances by one and the study is replaced atomically. A save that would make the study larger than MaxFileBytes is refused with ErrInvalid, since the file could not be loaded again.

A stored study already at revision math.MaxInt64 is refused with ErrCorrupt and left as it is. No run of saves gets a study that far, so something else wrote the number, and there is no next revision to give it: one more would wrap around to a negative revision that Load refuses, and a save reporting success would have made every note in the file unavailable. Refused, the file still loads with its notes. The check comes before the revisions are compared, because a stale refusal would invite the caller to try again against a study that can take no save at all.

type Study

type Study struct {
	Version      int    `json:"version"`
	GameID       string `json:"game_id"`
	RecordDigest string `json:"record_digest"`
	Revision     int64  `json:"revision"`
	// Marks is sorted by Entry, holds each entry at most once, and never holds
	// a mark with neither a note nor a bookmark.
	Marks []Mark `json:"entries"`
}

Study is the stored document for one saved game.

func (Study) At

func (s Study) At(entry int) Mark

At returns the mark on entry, or an empty mark for that entry when there is none.

type Target

type Target struct {
	// GameID is the saved game's identifier. It becomes a file name, so it is
	// checked with gamestore.ValidateID before it is used.
	GameID string
	// RecordDigest is the digest of the record the study is about.
	RecordDigest string
	// Entries is the number of transcript entries in the record. Marks may be
	// placed on entries 0 through Entries, where 0 is the initial position.
	Entries int
	// Finished reports whether the record has a result. Only a finished game's
	// study may be saved.
	Finished bool
}

Target identifies the saved game a study belongs to. The caller derives it from the saved game's decoded, digest-checked record.

Jump to

Keyboard shortcuts

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