The container
The line-up from the first chapter is hard-coded, and it never notices that last week's concerts are over. Let's give the staff a file they can edit, and hide the concerts that already happened. Both the lineup command and the concert page need that logic, and this is exactly what the container is for: it creates your objects once and hands them to whoever asks.
Singletons
A #[singleton] is created once and shared. A struct without fields needs nothing else. The box office keeps asking what time it is, so the clock becomes a singleton:
use DateTime;
use Utc;
use singleton;
;
Asking a singleton for the time, instead of calling Utc::now() everywhere, keeps the time in one place. Every chapter from here on asks the Clock.
Constructors
A struct with fields gets a #[constructor]. The Lineup reads the staff's lineup.json when it's created. Its constructor is async, because reading a file takes a moment, and it returns a Result, because the file might be missing:
use Arc;
use constructor;
use singleton;
use crateClock;
use crateShow;
Each show is a plain struct, read with serde:
use DateTime;
use Utc;
use Deserialize;
Add chrono (with serde), serde (with derive), and serde_json to your dependencies, and the fs feature to tokio.
Injecting dependencies
Singletons ask for each other as Arc constructor parameters. The Lineup asked for the Clock, so Margaret builds the Clock first. The command asks for the Lineup, and only prints what's still ahead:
use Arc;
use CommandOutcome;
use console_command;
use constructor;
use process;
use singleton;
use crateLineup;
$ box_office lineup
Nov 14 Paper Lanterns
Nov 21 The Midnight Owls
Dec 5 Velvet StaticThe /concerts page asks for the same Lineup in its constructor, and gets the very same instance.
When construction fails
If a constructor returns an error, the command stops before doing anything:
$ box_office lineup
failed to construct 'crate::lineup::Lineup': No such file or directory (os error 2)When singletons are built
A command builds only what it needs, so
lineupbuilds theClockand theLineup, and nothing else.servebuilds every singleton that a server or a background service needs, once, before it starts.
What the build checks
Asking for something that isn't a singleton is a build error, and so are dependency cycles. A cycle names every step:
Error: Container { source: DependencyCycle { path: "crate::clock::Clock -> crate::lineup::Lineup -> crate::clock::Clock" } }A singleton that nothing uses is an error too. Add the Clock before anything asks for it, and the build refuses:
Error: Container { source: UnconsumedSingleton { path: "crate::clock::Clock" } }Margaret matches dependencies by their full path, so two structs with the same name in different modules are two different singletons.