link-cli 0.2.9

A CLI tool and reusable library for links manipulation backed by a LiNo-notation doublet storage engine.
Documentation

link-cli Rust Package

Rust CI/CD Pipeline Crates.io Docs.rs GitHub Release

This directory contains the Rust link-cli crate, which publishes both a reusable [lib] (link_cli) and the clink [[bin]] from the same package. It mirrors the core query processor, named references, LiNo import/export, and structure formatting used by the production C# tool. The WebAssembly wrapper crate lives in rust/wasm/ and depends on this package.

Install

# Build and install the CLI binary.
cargo install link-cli
# Or pull in the public API to build your own tooling.
cargo add link-cli

API documentation for every published version is hosted on docs.rs/link-cli. A copy is also published to GitHub Pages alongside the C# DocFX site by .github/workflows/docs.yml.

Use

clink '() ((1 1))' --changes --after

Optional Transactions and Version Control

Pass --transactions (or any flag in the family — --transactions-file, --commit-mode, --retention, --log) to record each Create/Update/Delete as a reversible transition in a sidecar links store. Pass --vc (or --vc-file, --branch, --branch-from, --checkout, --tag, --list-branches, --list-tags) to add a version-control layer over the recorded transitions log:

# Record reversible transitions into data.transitions.links
clink --db data.links --transactions --auto-create-missing-references '() ((1 1))'
clink --db data.links --log

# Branch and tag on top of the transitions log
clink --db data.links --vc --tag v1
clink --db data.links --vc --branch feature --branch-from 1
clink --db data.links --vc --list-branches

End-to-end demo scripts live in examples/transactions/ and examples/version-control/.

Use as a library

link_cli is usable as an embedded, doublets-backed transactional store, not only as the code behind clink.

use link_cli::transactions::{
    CommitMode, FileTransitionLog, GenericTransactionsDecorator, LogRetentionPolicy,
};
use link_cli::DoubletsStorage;

fn main() -> Result<(), link_cli::LinkError> {
    // A file-mapped `doublets::unit::Store<usize, _>`, locked for writing.
    let store = DoubletsStorage::<usize, _>::open_exclusive("db.links")?;
    let log = FileTransitionLog::open("db.transitions.log")?;
    let mut tx = GenericTransactionsDecorator::new(
        store,
        log,
        LogRetentionPolicy::default(),
        CommitMode::default(),
        false,
    )?;

    tx.begin_transaction()?;
    let point = tx.create(0, 0)?;
    tx.commit()?;
    println!("created {point}");
    Ok(())
}

A runnable, self-checking version of this is examples/embedded_store.rs (cargo run --example embedded_store).

  • Any address type. GenericTransactionsDecorator<T, S, L> is generic over the doublets address type, the wrapped store, and the transitions log. TransactionsDecorator is the u32 + NamedTypesDecorator specialisation clink uses. The transitions wire format writes addresses in decimal, so it is identical across address types, and an address that does not fit the target type is reported as LinkError::AddressOutOfRange rather than truncated.
  • Any store. storage::LinksStorage<T> is the trait the transactions layer is written against. storage::DoubletsStorage::open creates a file-mapped doublets::unit::Store; DoubletsStorage::wrap adopts a store you already own.
  • In-place mutation. A file-mapped store is written through its mapping, so the inode never changes and other processes that mapped the same file keep observing the same data. Nothing is replaced through a temporary file.
  • Durability. Writes to a file-mapped store survive a process crash without any save() — the mapping is the page cache. Surviving a machine crash needs the fsync that LinksStorage::flush performs (a clean drop also syncs). In-memory stores keep everything in memory until flush/save. Recovery runs when the decorator is created: committed-but-unapplied transitions are replayed, uncommitted ones are rolled back.
  • Multi-process access. storage::lock locks a <database>.lock sidecar, shared for readers and exclusive for writers, through DoubletsStorage::open_shared / open_exclusive / try_open_exclusive. LinksStorage::has_external_changes answers "has anyone else written since I last looked?" from a StorageRevision fingerprint. This requires Rust 1.89, the release that stabilised std::fs::File::lock.
  • Typed errors and paths. Public entry points take AsRef<Path> and return LinkError, not anyhow::Error.

Develop

cargo fmt --manifest-path rust/Cargo.toml --all -- --check
cargo clippy --manifest-path rust/Cargo.toml --all-targets --all-features
cargo test --manifest-path rust/Cargo.toml --all-features

Release automation for this package lives in rust/scripts/ and uses changelog fragments from rust/changelog.d/.