How Margaret works
Margaret does its work in two stages: while your project builds, and while your application runs. Between them sits plain Rust that you can read.
While your project builds
Your build.rs calls Margaret's generator, which:
reads your crate, starting at
src/lib.rsand following yourmoddeclarations,collects every Margaret attribute and the types around them,
checks that everything fits together,
writes plain Rust into the
margaret/directory.
When step 3 finds a problem, the build stops and names the exact items involved:
Error: Container { source: DependencyCycle { path: "crate::clock::Clock -> crate::lineup::Lineup -> crate::clock::Clock" } }Margaret resolves paths on its own, so it asks for explicit imports: glob imports (use something::*) are build errors everywhere in your crate, test modules included.
The attributes themselves are markers, and your structs stay exactly as you wrote them. The build script is what reads them.
What gets generated
After the first chapter, the box office's margaret/ directory looks like this:
margaret/
├── container/
├── container.rs builds your singletons, in dependency order
├── forwarders/
├── forwarders.rs forwarding between routes
├── http/
├── http.rs one module per server
├── mod.rs
├── routes/
├── routes.rs typed links to your routes
├── run.rs the command line
└── serve.rs starts the servers and background servicesMore modules appear as you use more features: views, middleware, websocket and asset_bag for the web, models, shapes, tables and schema for the database, and sessions, authenticated_users, oauth_clients, oidc_provider and friends for security. They are ordinary Rust, and the whole module forbids unsafe code.
Everything has a purpose
Margaret only builds what something uses. A singleton that no command, route, service, WebSocket session, or middleware ever reaches is a build error, and so is a database nobody queries:
Error: Container { source: UnconsumedSingleton { path: "crate::clock::Clock" } }Dead code doesn't pile up unnoticed.
While your application runs
Running a command, like box_office lineup, builds only the singletons that command needs, runs it, and exits.
Running serve builds every singleton that a server, a background service, or a WebSocket session needs, once, and runs until you stop it with Ctrl+C or a termination signal. Then everything shuts down gracefully.