Skip to main content

Ledger

Struct Ledger 

Source
pub struct Ledger { /* private fields */ }
Expand description

The append-only ledger of one checkout: the canonical record of what happened, written only by Majordomus and in the order the commands ran.

use majordomus_cli::session::Ledger;
use std::path::Path;
assert!(Ledger::of(Path::new("/r")).path().ends_with("ledger.jsonl"));

Implementations§

Source§

impl Ledger

Source

pub fn of(root: &Path) -> Self

The ledger of the repository rooted at root.

use majordomus_cli::session::Ledger;
use std::path::Path;
assert!(Ledger::of(Path::new("/r")).path().ends_with("ledger.jsonl"));
Source

pub fn at(path: impl Into<PathBuf>) -> Self

The ledger at an explicit path, for a test or a store that is not a repository root.

use majordomus_cli::session::Ledger;
let dir = tempfile::tempdir().unwrap();
let ledger = Ledger::at(dir.path().join("elsewhere.jsonl"));
assert!(ledger.read().events.is_empty(), "a ledger nobody has written is empty");
Source

pub fn path(&self) -> &Path

Where it is. A reader that wants to name the file in a diagnostic asks here rather than composing the path a second time.

use majordomus_cli::session::Ledger;
use std::path::Path;
assert!(Ledger::of(Path::new("/r")).path().starts_with("/r"));
Source

pub fn append( &self, event: &LedgerEvent, vocabulary: &Vocabulary, envelope: &Envelope, ) -> Result<(), LedgerError>

Append one rendered line, under an exclusive lock, opened O_APPEND.

The local half is never tracked, so a second worktree of one repository starts without it and the first write creates the directory — the same thing mj_ledger_append does, for the same reason.

use majordomus_cli::session::{Envelope, Ledger, LedgerEvent, Vocabulary};

let dir = tempfile::tempdir().unwrap();
let ledger = Ledger::at(dir.path().join("ledger.jsonl"));
let v = Vocabulary::parse("version: 1\nevents:\n  - id: session.started\n    requires: [owner]\n    public: true\n").unwrap();
let envelope = Envelope {
    ts: "2026-09-11T11:02:35Z".into(), head: "1a651e64".into(),
    branch: "master".into(), by: "majordomus/0.5.0".into(), session: None,
};

ledger.append(&LedgerEvent::new("session.started").with("owner", "korczis"), &v, &envelope).unwrap();
ledger.append(&LedgerEvent::new("session.started").with("owner", "korczis"), &v, &envelope).unwrap();

let read = ledger.read();
assert_eq!(read.events.len(), 2);
assert!(read.corrupt.is_empty());
Source

pub fn read(&self) -> LedgerRead

Read the whole ledger, reporting what did not parse rather than failing on it.

An absent ledger reads as empty: a fresh clone has never run the lifecycle, and that is not a fault.

use majordomus_cli::session::Ledger;

let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("ledger.jsonl");
std::fs::write(
    &path,
    "{\"ts\":\"a\",\"event\":\"session.started\"}\nnot json at all\n{\"ts\":\"b\",\"event\":\"session.closed\"}\n{\"ts\":\"c\",\"event\"\n",
).unwrap();

let read = Ledger::at(&path).read();
assert_eq!(read.events.len(), 2, "everything that parsed");
assert_eq!(read.corrupt.len(), 2, "and both lines that did not, by number");
assert_eq!(read.corrupt[0].line, 2);
assert_eq!(read.corrupt[1].line, 4, "a truncated tail is one bad line, not a failed read");

// absence is an answer
assert!(Ledger::at(dir.path().join("nothing.jsonl")).read().events.is_empty());

Trait Implementations§

Source§

impl Clone for Ledger

Source§

fn clone(&self) -> Ledger

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Ledger

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> DynClone for T
where T: Clone,

Source§

fn __clone_box(&self, _: Private) -> *mut ()

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a [WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a [WithDispatch] wrapper. Read more