X-Git-Url: https://git.giacomolli.uk/onechat/blobdiff_plain/d9fddc0f506eb4ead7049f5638674b2b8773fcaa..f5c601f1801aaa276f8c335fcb51c373edc0b50c:/log.go diff --git a/log.go b/log.go index 75080f9..2b9f32f 100644 --- a/log.go +++ b/log.go @@ -21,6 +21,38 @@ var logKinds = map[string]bool{ "note": true, } +// Log is the append-only message log of one chat. It writes one JSON line for +// each message to main/YYYY-MM-DD.jsonl, named for the local day of the +// message. It never edits or deletes a line. Each line goes to the operating +// system before Append returns, so a crash of the process loses no message. An +// exclusive lock on the lock file makes sure that only one process writes the +// log. After a failed write, the end of the file is not known, so the log +// refuses all writes until it is opened again. The caller serializes all calls. +// +// The log keeps no message in memory. The index index/main.idx holds the +// position of message i in record i, so a read of a message is one read of the +// index and one of its day file. At open the log indexes the lines after the +// last indexed one, so a missing index is built again from the day files. +type Log struct { + clock func() time.Time + lock *os.File + journal Journal + index *Records + failure error +} + +// Message is one entry of the chat log. ID is its position in the log, from 0. +// A rebuild assigns positions in the merged log. Size is the UTF-8 byte length +// of Text. Date is the message time in RFC 3339 format, supplied at append or +// taken from the clock. +type Message struct { + ID int `json:"i"` + Kind string `json:"kind"` + Text string `json:"text"` + Size int `json:"size"` + Date string `json:"date"` +} + // openLog locks the chat in root and indexes its new lines. The clock gives the // date of each new message. func openLog(ctx context.Context, root string, clock func() time.Time) (*Log, error) { @@ -57,26 +89,6 @@ func openLog(ctx context.Context, root string, clock func() time.Time) (*Log, er return l, nil } -// Log is the append-only message log of one chat. It writes one JSON line for -// each message to main/YYYY-MM-DD.jsonl, named for the local day of the -// message. It never edits or deletes a line. Each line goes to the operating -// system before Append returns, so a crash of the process loses no message. An -// exclusive lock on the lock file makes sure that only one process writes the -// log. After a failed write, the end of the file is not known, so the log -// refuses all writes until it is opened again. The caller serializes all calls. -// -// The log keeps no message in memory. The index index/main.idx holds the -// position of message i in record i, so a read of a message is one read of the -// index and one of its day file. At open the log indexes the lines after the -// last indexed one, so a missing index is built again from the day files. -type Log struct { - clock func() time.Time - lock *os.File - journal Journal - index *Records - failure error -} - // Close releases the lock. The log refuses all calls after Close. func (l *Log) Close() error { err := l.journal.Close() @@ -111,6 +123,7 @@ func (l *Log) AppendDated(kind, text, date string) (Message, error) { } var err error + now := l.clock() if date == "" { date = now.Format(time.RFC3339Nano) @@ -176,6 +189,7 @@ func (l *Log) Message(id int) (Message, error) { } var message Message + err = json.Unmarshal(line, &message) if err != nil { return Message{}, fmt.Errorf("decode message %d: %w", id, err) @@ -206,6 +220,7 @@ func (l *Log) catchUp(ctx context.Context) error { return l.journal.Scan(ctx, start, func(line []byte, place Position) error { var message Message + err := json.Unmarshal(line, &message) if err != nil { return fmt.Errorf("decode log message: %w", err) @@ -231,18 +246,6 @@ func (l *Log) addIndex(place Position) error { return l.index.Append(record) } -// Message is one entry of the chat log. ID is its position in the log, from 0. -// A rebuild assigns positions in the merged log. Size is the UTF-8 byte length -// of Text. Date is the message time in RFC 3339 format, supplied at append or -// taken from the clock. -type Message struct { - ID int `json:"i"` - Kind string `json:"kind"` - Text string `json:"text"` - Size int `json:"size"` - Date string `json:"date"` -} - // logValidateMessage makes sure that a message has a known kind, valid UTF-8 // text, the size of its text, and a valid date. func logValidateMessage(message Message) error {