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
diagnosticsmodule and worse. - Cost to replace: contained. Only
diagnostics::rendertouches it; the publicDiagnostictype does not expose it. Swapping tocodespan-reportingor a custom renderer is a one-file change. (ADR 0008)
thiserror — error type boilerplate
- Does: derives
std::error::Error/Displayfor internal error enums. - Why not std: hand-written
DisplayandErrorimpls for every internal error enum are pure noise and drift out of sync with their variants. - Cost to replace: mechanical.
thiserrorgenerates code you could write by hand; removing it is find-and-replace with manual impls.
stacker — on-demand native stack growth
- Does:
stacker::maybe_growchecks 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.
stackerturns that into graceful growth (and theRECURSION_LIMITbackstop turns truly unbounded recursion into a cleanE0313). - 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
framesinstead.stackerstays 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 notSend, so the interpreter cannot be moved to a worker thread with a large stack.stackeris 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_continueper Unicode UAX #31, the standard for programming-language identifiers. - Why not std:
char::is_alphanumericis 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--helpgeneration forkorrin 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;rustylineis the standard pure-Rust choice.korrin-pkg(path dependency, workspace-internal) — everything behindkorrin pkg ...and the manifest/lockfile discoverykorrin rundoes before setting up either engine. Kept as a separate crate specifically sokorrin-cli's ownCargo.tomlnever 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 placekorrin.toml(korrin pkg add/removemust not disturb formatting or comments elsewhere in the file) and serializeskorrin.lock. Chosen over the plainertomlcrate for exactly that round-trip-preserving editing, the same reason Cargo itself usestoml_editforcargo add/cargo removerather than its owntomldependency.thiserror— same justification as incrates/korrin: derivesDisplay/ErrorforPkgErrorinstead of hand-written boilerplate.- Git access has no dependency at all. Every git operation
(
clone/fetch/checkout/rev-parse) runs the installedgitbinary as a subprocess (std::process::Command), not a linked library. Considered and rejected:git2(a C library behindunsafeFFI, at odds with a workspace whose own crates never writeunsafe) andgix(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. semverwas 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 installedcargobinary as a subprocess, exactly the same shelling-out stance already taken forgitabove, 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 anative = truedependency gains a new baseline assumption on top of "gitis on PATH": a working Rust toolchain is too — surfaced asPkgError::CargoNotFoundif it isn't. A plain, all-.korproject needs neithercargonor a Rust toolchain to install its packages, only to buildkorrinitself 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 overtower-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-serverandlsp-typesboth build on these;korrin-lspuses 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 thekorrinbinary.