Skip to content

calternal_notes_core::dayfile

Lossless Daily note parser and builder for the Notes Plugin.

A Daily note owns the first compatible H2 Log section selected by the shared parser. If the body has no Markdown headings, it treats the whole body as an implicit Log until a User adds an entry (#998; DESIGN §17). Reads never add a heading, and all other headed regions stay opaque bytes. New Log entries go into time-order positions without moving existing lines, and batch sends parse and splice once for the durability boundary in DESIGN §§17, 29 and 38 (#427, #468). DELETE removes only the Log line and reparents its child lines while preserving their text bytes (#471; DESIGN §46 A7). The shared source walk retains opaque gaps so edits cannot hide later identities or carry day content in a Log MOVE. Direct editing helpers splice one entry so untouched lines keep their exact spelling and line endings. Saved-place metadata is server-written and stores a stable ID plus a name snapshot, never coordinates (issue #391, DESIGN §47 L4). Log parsing accepts imported 24-hour time spellings, while edits retain each entry’s original time spelling and range separator (#724).

Source: crates/calternal-notes-core/src/dayfile.rs

pub struct DayFile

No doc comment.

Fields

  • pub date: String
  • pub frontmatter_raw: String
  • pub preamble: String
  • pub log_heading: Option<String>
  • pub log_lead: String
  • pub entries: Vec<LogEntry>
  • pub log_trailing: String
  • pub trailing_unknown: String

Implements: Debug, Clone, PartialEq, Eq

Source: crates/calternal-notes-core/src/dayfile.rs:115

pub struct EventAttachment

One local link below a Log entry, including its destination spelling.

Fields

  • pub text: String
  • pub target: String
  • pub embed: bool
  • pub wrapped: bool: Whether Markdown wrote the target inside <...> delimiters.

Implements: Debug, Clone, PartialEq, Eq

Source: crates/calternal-notes-core/src/dayfile.rs:30

pub struct JoinedLogLine

One Log line that looks joined.

Fields

  • pub line_index: usize: Zero-based line index in the complete Daily note.
  • pub raw: String: Line content without its CRLF/LF line ending.
  • pub reason: LogLineJoin

Implements: Debug, Clone, PartialEq, Eq

Source: crates/calternal-notes-core/src/dayfile.rs:225

pub struct LeadingLogTimePrefix

Parsed leading Log time used by the full Composer preview and Daily note readers. The prefix ends before title spacing.

Fields

  • pub start: String: Canonical HH:MM start value.
  • pub end: Option<String>: Canonical HH:MM end value, if the prefix is a range.
  • pub prefix_len: usize: Byte length of the explicit time expression before title spacing.

Implements: Clone, Debug

Source: crates/calternal-notes-core/src/dayfile.rs:2518

pub struct LogEntry

No doc comment.

Fields

  • pub start: String
  • pub end: Option<String>: End time. An end earlier than start ends on the next day (23:00 - 01:00); the entry still belongs to its start day (#99). See LogEntry::ends_next_day.
  • pub zone: Option<String>: IANA zone captured when the Log entry was written. Legacy lines have none.
  • pub title: String
  • pub tags: Vec<String>
  • pub hidden: bool: The reserved hide marker is persisted in the file but stays out of user-facing tags.
  • pub legacy_note_path: Option<String>: Read-compat target from the former linked-title event format.
  • pub block_id: Option<String>
  • pub place: Option<LogPlace>: Saved place captured at creation. The Markdown stores its ID and name, never the coordinates sent by the Installation.
  • pub attachments: Vec<EventAttachment>
  • pub sub_block_raw: String: Exact child lines under this Log entry. This is the write source of truth.

Implements: Debug, Clone, PartialEq, Eq

pub fn ends_next_day(&self) -> bool

True when the range crosses midnight: the end is earlier than the start, so it is on the day after the entry’s day. HH:MM strings compare in time order. An end equal to the start is a zero-length range on the same day, not 24 hours.

pub fn new(
start: impl Into<String>,
end: Option<String>,
title: impl Into<String>,
tags: Vec<String>,
) -> Self

No doc comment.

Source: crates/calternal-notes-core/src/dayfile.rs:53

pub struct LogHeadingDiagnostics

Report a missing Log heading and any duplicate headings after the first.

Fields

  • pub missing: bool
  • pub duplicates: Vec<usize>: Zero-based line indexes in the complete Daily note.

Implements: Debug, Clone, PartialEq, Eq

Source: crates/calternal-notes-core/src/dayfile.rs:149

pub struct LogPlace

A Saved place captured on a Log entry. The name is a snapshot so the entry still explains where it happened after the Saved place is renamed or removed.

Fields

  • pub id: String
  • pub name: String

Implements: Debug, Clone, PartialEq, Eq

Source: crates/calternal-notes-core/src/dayfile.rs:41

pub struct UnparsedLogLine

One malformed top-level bullet in the first Log section.

Fields

  • pub line_index: usize: Zero-based line index in the complete Daily note.
  • pub raw: String: Line content without its CRLF/LF line ending.
  • pub hash: String: BLAKE3 of the zero-based line index and raw. It is stable across CRLF and LF files, and distinguishes identical lines in one file.
  • pub suggestion: Option<String>: Canonical line from the composer parser, when it has an explicit time.

Implements: Debug, Clone, PartialEq, Eq

Source: crates/calternal-notes-core/src/dayfile.rs:135

pub enum LogLineJoin

Why a Log line looks like two Log lines joined into one (#94 damage).

Variants

  • BlockIdFollowedByText: A ^block-id marker is followed by more text on the same line, for example Morning review ^ta- 13:00 Lunch ^tb.
  • SecondTimeInTitle: The title holds a second Log bullet or time range, for example Morning review - 13:00 - 13:45 Lunch.

Implements: Debug, Clone, Copy, PartialEq, Eq

pub fn as_str(self) -> &'static str

No doc comment.

Source: crates/calternal-notes-core/src/dayfile.rs:205

pub fn append_event_attachment(
entry: &LogEntry,
text: &str,
target: &str,
) -> Result<LogEntry, String>

Append one local Note or attachment link below a Log entry.

The returned entry keeps all existing child bytes and adopts their indentation, list marker, and line ending. The caller persists the Daily note through the host filesystem layer.

Source: crates/calternal-notes-core/src/dayfile.rs:1134

pub fn append_log_block_id(text: &str, date: &str, index: usize, id: &str) -> Option<String>

Add a durable block identity to one legacy Log line without normalizing its separator, title, spacing, children, or line ending.

Source: crates/calternal-notes-core/src/dayfile.rs:860

pub fn append_log_entries(text: &str, entries: &[LogEntry]) -> String

Insert several new Log entries in one source splice (#427, #468).

Sort only the new entries by start time, then place each after the last existing equal-time row or before the first existing row with a later time. Existing rows never move, even when the User wrote them out of order. New equal-time rows keep their request order. These invariants implement Glossary and DESIGN §17 while the single span pass serves the batch write contract in DESIGN §§29 and 38.

Source: crates/calternal-notes-core/src/dayfile.rs:1012

pub fn append_log_entry(text: &str, _date: &str, entry: &LogEntry) -> String

Append one Log entry before trailing non-entry text in the Log section. The first write to an implicit headingless Log promotes it with one bare ## Log heading; merely parsing or reading the Daily note writes nothing.

Keep _date in this public helper for existing callers. A lossless splice uses only the byte spans in the Daily note, so the path-derived date does not take part in the rewrite.

Source: crates/calternal-notes-core/src/dayfile.rs:1000

pub fn build_new_day_file(date: &str, now: &str) -> DayFile

Mint a Daily note with the canonical frontmatter, H1, and empty Log section.

Source: crates/calternal-notes-core/src/dayfile.rs:450

pub fn bump_day_file_last_edited(previous: &str, next: &str, now: &str) -> String

Stamp a genuine local Daily note edit without creating timestamp-only rewrites. The host supplies the clock and persists the returned bytes.

Source: crates/calternal-notes-core/src/dayfile.rs:580

pub fn contains_saved_place_marker(value: &str) -> bool

True when plain Log text contains the metadata syntax reserved for server-written Saved place references.

Source: crates/calternal-notes-core/src/dayfile.rs:48

pub fn daily_note_path(date: &str) -> String

Return the canonical Daily note path for a date.

Source: crates/calternal-notes-core/src/dayfile.rs:488

pub fn delete_log_entry(text: &str, date: &str, index: usize) -> Option<String>

Delete one Log line and retain its children (#471; DESIGN §46 A7).

Source records are sibling Log entries in the same day. Removing a later bullet leaves its children under the previous sibling, adjusting only indentation to match that sibling’s level. With no previous sibling, strip the common child indentation to promote the block to the day’s top level. Keep relative nesting, blank lines, trailing spaces and line endings. Return None only for a missing entry. Cross-day moves use remove_log_entry to carry the whole block.

Source: crates/calternal-notes-core/src/dayfile.rs:927

pub fn format_log_entry(entry: &LogEntry) -> String

Render the canonical Log bullet grammar. Normalize Tag values to NFC at this write boundary so Calendar edits and Composer writes store one spelling for canonically equivalent names (#473).

Source: crates/calternal-notes-core/src/dayfile.rs:1607

pub fn insert_log_entry_after(
text: &str,
date: &str,
index: usize,
entry: &LogEntry,
) -> Option<String>

Insert one Log entry immediately after an existing entry in the same Log section. The serializer keeps unchanged source entries byte-for-byte and uses the Daily note’s original line-ending style for the new line.

Source: crates/calternal-notes-core/src/dayfile.rs:840

pub fn is_daily_note_path(path: &str) -> bool

True for a Daily note file name, wherever the User places it in the Home.

Source: crates/calternal-notes-core/src/dayfile.rs:503

pub fn joined_log_lines(text: &str) -> Vec<JoinedLogLine>

Find Log lines in the first Log section or an implicit headingless Log that appear to contain two entries joined into one. This is a read-only damage check for #94. It never proposes a repair because the split point and the lost line ending are unknown. A title can legitimately contain a time, so the check looks only for a second bullet or range, and for a block-id marker that is not the last token.

Source: crates/calternal-notes-core/src/dayfile.rs:239

pub fn log_heading_diagnostics(text: &str) -> LogHeadingDiagnostics

Return heading problems without changing the Daily note.

Source: crates/calternal-notes-core/src/dayfile.rs:344

pub fn migrate_inline_body(entry: &LogEntry, id: &str, now: &str) -> (StandaloneNote, LogEntry)

Move inline text from a Log entry into a standalone Note.

This pure transform returns the Note for the host to write first. The Daily note result keeps attachment lines in order and replaces the first text line with the new Note link, so the host can persist it without losing body text.

Source: crates/calternal-notes-core/src/dayfile.rs:1292

pub fn migrate_path(old: &str) -> String

Map one legacy calternal.js journal/ path into the flat Notes layout. Daily notes and per-entry Notes use the same folder; the file name carries the Daily note identity, so no Journal/ folder is created.

Source: crates/calternal-notes-core/src/dayfile.rs:495

pub fn normalize_event_attachments(entry: &LogEntry) -> LogEntry

Normalize one legacy linked-title entry during a write to that entry.

The old title link becomes the first child attachment when moving it into a bare title would preserve the parsed title, tags, hidden state, and block id. Unsafe titles and external targets keep the old line bytes.

Source: crates/calternal-notes-core/src/dayfile.rs:1255

pub fn parse_day_file(text: &str, date: &str) -> DayFile

Parse a Daily note from the shared lossless source walk (#471; §46 A7). A headingless body uses an implicit Log but keeps its heading absent. Opaque top-level lines stay day content, while later Log identities remain visible to the Index.

Source: crates/calternal-notes-core/src/dayfile.rs:398

pub fn parse_leading_log_time(text: &str) -> Option<LeadingLogTimePrefix>

Parse one explicit leading Log time for the Composer using the same grammar as Daily note reads.

Source: crates/calternal-notes-core/src/dayfile.rs:2529

pub fn parse_log_bullet(line: &str) -> Option<LogEntry>

Parse one time-stamped Log bullet. The caller can use it for composer output as well as lines already in a Daily note.

Source: crates/calternal-notes-core/src/dayfile.rs:1714

pub fn remove_event_attachment(entry: &LogEntry, target: &str) -> Option<LogEntry>

Remove child attachment links for one exact raw target and keep every other child byte intact (#427). A legacy title link is cleared but the title stays.

Source: crates/calternal-notes-core/src/dayfile.rs:1162

pub fn remove_log_entry(text: &str, date: &str, index: usize) -> Option<String>

Remove one Log entry and its child block when a move carries both elsewhere. DELETE callers use delete_log_entry, which re-parents the children because they are not part of the iCalendar VEVENT (#471; §46 A7).

Source: crates/calternal-notes-core/src/dayfile.rs:911

pub fn replace_log_entry(text: &str, date: &str, index: usize, entry: &LogEntry) -> Option<String>

Replace one Log entry line and preserve every other byte.

Source: crates/calternal-notes-core/src/dayfile.rs:781

pub fn replace_log_entry_block(
text: &str,
date: &str,
index: usize,
entry: &LogEntry,
) -> Option<String>

Replace one Log entry and its child block while preserving all other bytes.

This is for operations that add or change child bullets. The edited time keeps its source notation and range separator; child bytes stay in the supplied sub_block_raw value.

Invariant (#94): the replaced span is the whole record, bullet line ending included. The replacement therefore ends with a line ending exactly when the old record did, so the next line (another Log entry, a blank line, a heading) stays a separate line and a file without a final newline keeps that shape.

Source: crates/calternal-notes-core/src/dayfile.rs:808

pub fn replace_unparsed_log_line(
text: &str,
line_index: usize,
expected_hash: &str,
) -> Option<String>

Replace one malformed Log bullet after checking its raw-line hash.

The replacement covers only the line content. Its line ending and every other byte keep their original spelling.

Source: crates/calternal-notes-core/src/dayfile.rs:368

pub fn restore_event_attachment(
entry: &LogEntry,
text: &str,
target: &str,
embed: bool,
legacy: bool,
) -> Result<LogEntry, String>

Restore one removed attachment without replacing edits made since trash. The operation is idempotent, so Trash recovery can safely retry it (#427).

Source: crates/calternal-notes-core/src/dayfile.rs:1188

pub fn restore_event_attachment_with_style(
entry: &LogEntry,
text: &str,
target: &str,
embed: bool,
legacy: bool,
wrapped: bool,
) -> Result<LogEntry, String>

Restore a Trash child with its stored Markdown destination style (#427). Unsafe bare destinations still use <...> so the resulting link stays valid.

Source: crates/calternal-notes-core/src/dayfile.rs:1200

pub fn rewrite_body_links(day_text: &str) -> String

Rewrite legacy Note links inside time-stamped Log bullets while preserving every other byte. The caller writes the returned Daily note through calternal-fs.

Source: crates/calternal-notes-core/src/dayfile.rs:516

pub fn serialize_day_file(day: &DayFile) -> String

Serialize a DayFile using canonical Log bullet lines. Opaque day gaps stay independent of entry slots and survive entry removal (#471; §46 A7). An implicit Log keeps its original entry spelling until a field changes.

Source: crates/calternal-notes-core/src/dayfile.rs:594

pub fn serialize_day_file_preserving(original: &str, day: &DayFile) -> String

Serialize a changed Daily note while retaining source bytes for entries that compare equal. Edited entries keep their source time spelling and line ending; new entries use canonical time spelling. Opaque gaps stay day content and cannot join an unterminated surviving entry (#471, #724; DESIGN §46 A7).

Source: crates/calternal-notes-core/src/dayfile.rs:660

pub fn set_daily_note_navigation(
text: &str,
previous: Option<(&str, &str)>,
next: Option<(&str, &str)>,
) -> String

Set Daily note navigation to existing adjacent notes.

Each target is a CommonMark-relative path from this note, with no leading ./. Missing days have no field, so Link health does not report a future or absent Daily note as broken.

Source: crates/calternal-notes-core/src/dayfile.rs:473

pub fn set_day_file_attachment_text(
text: &str,
date: &str,
targets: &[String],
label: &str,
) -> Result<Option<String>, String>

Relabel matching child attachments in a complete Daily note.

This edits only link labels. It does not serialize Log bullets, so imported separators, whitespace, line endings, and other sections stay unchanged.

Source: crates/calternal-notes-core/src/dayfile.rs:1405

pub fn set_event_attachment_text(entry: &LogEntry, target: &str, text: &str) -> LogEntry

Change the visible text of attachment bullets with one exact target.

Legacy title links stay unchanged. They are normalized only when the host writes the event, so a background relabel cannot rewrite another event line.

Source: crates/calternal-notes-core/src/dayfile.rs:1378

pub fn unparsed_log_lines(text: &str) -> Vec<UnparsedLogLine>

Find malformed top-level list items in the first Log section or an implicit headingless Log. Other headed regions remain opaque Markdown.

Source: crates/calternal-notes-core/src/dayfile.rs:157