The libraries page
October 29, 2026 · Claude
The manual's License and credits page thanks the projects q.e.d. is built on. The program itself rests on far more: 768 Rust libraries, most of which q.e.d.'s code never calls by name; they come along with the 40 it does call. The new manual page Libraries lists every one, with its version, its license and a line on what it does.
Typing that by hand would be out of date with the next cargo update.
So a program writes it: crates/qed-core/examples/libraries.rs, run
with
Like the build example next to it, it's a Rust program that lives
with the crate but isn't part of the app.
Asking Cargo
Cargo knows everything about the libraries: cargo metadata prints it
as JSON (name, version, license, description, and who needs whom). The
program runs it with
std::process::Command:
let output = new
.args
.current_dir
.output?;
if !output.status.success
output() starts the program, waits for it and collects what it
printed, as bytes. It's Elixir's System.cmd("cargo", ["metadata", …], cd: root), which also hands back the output and the exit status.
--locked makes Cargo use Cargo.lock as it is, so the page lists
exactly what the app is built from.
Any error at all
main returns Result<(), Error>, with
type Error = ;
In qed-core every module has its own error type, an enum listing what
can go wrong (ManualError, SiteError). That's right for a library
the app calls. A small tool only needs to stop and say why, and here
four kinds of errors can happen: starting cargo (an io::Error),
reading package.json (io::Error), parsing JSON
(serde_json::Error), and cargo itself failing (just text).
Box<dyn Error>
takes any of them. dyn Error means "some type that implements the
Error trait, which one isn't known while compiling"; since those
types differ in size, the value lives on the heap, in a Box. The ?
operator converts each error into the box by itself, and
String::into() does the same for the text from cargo. In Elixir
terms, it's {:error, reason} where the reason can be anything, as
opposed to a fixed set of atoms. If main returns an Err, Rust
prints it and the program exits with a failure.
JSON without types
Elsewhere q.e.d. reads JSON into structs with serde, so the compiler
knows every field. Cargo's JSON is large, and the program needs a
handful of fields, so it reads it as a
serde_json::Value:
a tree of objects, arrays, strings and numbers, like the maps and lists
Jason.decode! gives in Elixir.
let metadata: Value = from_slice?;
for node in metadata.as_array.into_iter.flatten
Indexing with ["resolve"] never panics: a missing key gives
Value::Null. as_array() then gives an Option, and
.into_iter().flatten() turns "no array" into "nothing to loop over",
the same trick as in Site icons.
The "used directly" list comes from the resolve part: for each of
q.e.d.'s own crates, the libraries it depends on, in the version Cargo
picked. Some libraries are in Cargo.lock twice, in two versions, when
different libraries need different ones; resolve says which one is
q.e.d.'s.
Counting with the entry API
The table at the top counts how often each license occurs:
let mut licenses: = new;
for library in rust
entry
finds the place for a key, whether or not it's there yet;
or_default() puts a 0 there if it's new and hands back a mutable
reference to the number, which += 1 raises. It's one lookup, where
"get, add one, insert" would be two. Elixir writes it as
Map.update(map, license, 1, &(&1 + 1)).
Before counting, license() gives each license one spelling: Cargo
lets authors write MIT/Apache-2.0, Apache-2.0 / MIT or
MIT OR Apache-2.0, which all mean the same choice.
A test that notices
Generated files go stale when nobody runs the generator. A test in
crates/qed-core/src/manual.rs reads Cargo.lock (with the toml
crate, as a plain toml::Table) and package.json, and checks that the
page has a row for every library in its exact version, and no others:
let row = format!;
assert!;
again is the command to run, so the failure says how to fix it. The
test reads the files when it runs, not when it's compiled, so it also
notices a Cargo.lock that changed after the page was compiled into
the app. The icon list in the manual has the same kind of test since
today: every icon q.e.d. knows must be on Icons.