Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Dependencies

Policy: ADR 0013. Every runtime dependency of the published korrin crate is listed here with its justification.

crates/korrin (published library)

ariadne — diagnostic rendering

  • Does: turns a Diagnostic (code, message, labelled spans, notes) into an annotated terminal snippet with carets, line numbers, and colour.
  • Why not std: aligning carets under multi-byte characters, rendering multi-line spans, and handling tab expansion are genuinely intricate. A hand-rolled version would be larger than the rest of the diagnostics module and worse.
  • Cost to replace: contained. Only diagnostics::render touches it; the public Diagnostic type does not expose it. Swapping to codespan-reporting or a custom renderer is a one-file change. (ADR 0008)

thiserror — error type boilerplate

  • Does: derives std::error::Error / Display for internal error enums.
  • Why not std: hand-written Display and Error impls for every internal error enum are pure noise and drift out of sync with their variants.
  • Cost to replace: mechanical. thiserror generates code you could write by hand; removing it is find-and-replace with manual impls.

stacker — on-demand native stack growth

  • Does: stacker::maybe_grow checks the remaining Rust stack and allocates a fresh segment before it runs out.
  • Why not std: the tree-walking interpreter recurses in Rust for every nested Korrin expression, so a moderately recursive Korrin program can exhaust a fixed-size stack and abort the process. stacker turns that into graceful growth (and the RECURSION_LIMIT backstop turns truly unbounded recursion into a clean E0313).
  • Still needed? Yes, but only for one engine. The bytecode VM runs Korrin calls in a loop over its own frame stack, so it needs no native-stack growth and caps frames instead. stacker stays as long as the interpreter does, which is for good (ADR 0024).
  • Why not a big fixed stack thread: Korrin values are Rc-based and not Send, so the interpreter cannot be moved to a worker thread with a large stack. stacker is the approach rustc itself uses for the same reason.
  • Cost to replace: contained — one helper (interpreter::grow_stack) wraps it. Removing it means picking a fixed stack strategy and living with its limits.

unicode-ident — identifier character classification

  • Does: is_xid_start / is_xid_continue per Unicode UAX #31, the standard for programming-language identifiers.
  • Why not std: char::is_alphanumeric is not the right set and would let Korrin's notion of "identifier" drift from the Unicode standard the spec cites.
  • Cost to replace: low, but only downward — restricting identifiers to ASCII would remove it at the cost of a spec change.

crates/korrin-cli (binary only — not published, not depended upon)

Per policy, the CLI may use mature crates for CLI concerns.

  • clap — argument parsing and --help generation for korrin run / the REPL. Reimplementing arg parsing well is not a good use of effort.
  • rustyline — line editing, history, and multi-line input for the REPL. A usable REPL needs readline-style editing; rustyline is the standard pure-Rust choice.
  • korrin-pkg (path dependency, workspace-internal) — everything behind korrin pkg ... and the manifest/lockfile discovery korrin run does before setting up either engine. Kept as a separate crate specifically so korrin-cli's own Cargo.toml never needs a TOML parser or a git-access strategy directly (ADR 0033).

crates/korrin-pkg (manifest/lockfile/git — not published, not depended upon by korrin)

Per policy, and per ADR 0033, korrin-pkg sits in the same freer tier as korrin-cli/korrin-lsp: not published, a tool crate, free to use mature crates for its genuine concern (reading/writing korrin.toml/korrin.lock, fetching git repositories).

  • toml_edit — parses and, critically, edits in place korrin.toml (korrin pkg add/remove must not disturb formatting or comments elsewhere in the file) and serializes korrin.lock. Chosen over the plainer toml crate for exactly that round-trip-preserving editing, the same reason Cargo itself uses toml_edit for cargo add/cargo remove rather than its own toml dependency.
  • thiserror — same justification as in crates/korrin: derives Display/Error for PkgError instead of hand-written boilerplate.
  • Git access has no dependency at all. Every git operation (clone/fetch/checkout/rev-parse) runs the installed git binary as a subprocess (std::process::Command), not a linked library. Considered and rejected: git2 (a C library behind unsafe FFI, at odds with a workspace whose own crates never write unsafe) and gix (pure Rust, but a much larger and less mature dependency than shelling out). Shelling out also inherits whatever git auth (SSH agent, credential helpers) is already configured on the machine, which either library would otherwise need to reimplement or delegate back to system git for anyway. See ADR 0033 for the full comparison.
  • semver was considered and not added. Dependency versions are exact git refs (a tag, a branch's current tip, or a commit), never a version range — nothing here compares or constrains version numbers, so a semver crate would have no load-bearing job (ADR 0031).
  • Building a native module's generated project has no dependency at all either. korrin pkg build (Milestone 6) runs the installed cargo binary as a subprocess, exactly the same shelling-out stance already taken for git above, for the same reasons: no build-orchestration library to vet, and it inherits whatever toolchain (stable/nightly, target) is already configured on the machine. A project with a native = true dependency gains a new baseline assumption on top of "git is on PATH": a working Rust toolchain is too — surfaced as PkgError::CargoNotFound if it isn't. A plain, all-.kor project needs neither cargo nor a Rust toolchain to install its packages, only to build korrin itself once.

crates/korrin-lsp (language server — not published, not depended upon)

Per policy, and per ADR 0026, korrin-lsp sits in the same freer tier as korrin-cli: not published, a tool crate, free to use mature crates for its genuine concern (speaking the Language Server Protocol).

  • lsp-server — the JSON-RPC-over-stdio connection and message types (Connection, Message, Request/Notification/Response). Synchronous, no async runtime — this is the reason it was chosen over tower-lsp, see ADR 0026. What rust-analyzer itself is built on.
  • lsp-types — the protocol's own vocabulary as serde types (Position, Diagnostic, Hover, Location, capability structs). Needed regardless of transport; reimplementing the LSP's JSON schema by hand would be pure busywork with no payoff.
  • serde / serde_json — lsp-server and lsp-types both build on these; korrin-lsp uses them directly to (de)serialize request/response payloads at the edges of the dispatch loop.

Dev-dependencies (do not ship)

  • insta — snapshot testing for token streams and AST dumps.
  • proptest — property tests (lexer/parser never panic; round-trips).
  • assert_cmd / predicates — black-box tests of the korrin binary.