wire/answer/doctor.rs
1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at https://mozilla.org/MPL/2.0/.
4// Copyright (c) 2026 2youg1 and the sprawling contributors
5
6//! What this machine has, as the city found it when it started.
7//!
8//! Every state here is an enum rather than a sentence. The terminal
9//! report is one machine's prose and a browser is two languages, so a
10//! wire that carried the wording would make the page's words the
11//! server's to choose. What an item is for travels as its `name`, the
12//! id the page looks its own clause up by. The `said` fields carry what
13//! a platform or a program said, which no reader can derive.
14
15use serde::{Deserialize, Serialize};
16
17/// This machine, item by item, with a verdict for each tier.
18#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
19#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
20pub struct DoctorAnswer {
21 /// Every item this city needs, in the order the requirement table
22 /// states them.
23 pub items: Vec<DoctorItem>,
24 /// One verdict per tier: a person who only wants to run a city is
25 /// not told about what changing this code would need.
26 pub tiers: Vec<DoctorVerdict>,
27 /// The confinement a command an agent asks for runs under, and the
28 /// axes that arm does not hold. An agent has to be able to read
29 /// that it is not in a box with the network closed before it acts.
30 pub sandbox: DoctorSandbox,
31 /// Where credentials rest on this machine, and how long they stay.
32 pub custody: DoctorCustody,
33 /// Where this machine lets the core's threads stand.
34 pub core: DoctorCore,
35}
36
37/// The level this machine gives the core's threads under the person's
38/// setting (`crates/sprawling/Spec.lean` ยง8-93). The dispatched commands are not here:
39/// they always start one level below, and lowering is never refused.
40#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
41#[serde(rename_all = "snake_case")]
42#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
43pub enum DoctorCore {
44 /// One step above normal.
45 Raised,
46 /// Normal, because the person's `[core] priority` asks for it.
47 HeldBySetting,
48 /// Normal, because the platform refused the raise; `said` is the
49 /// platform's own words.
50 Refused { said: String },
51 /// Normal, lowered after keeping a core busy.
52 LoweredByValve,
53 /// The doctor could not ask; `said` is why.
54 Unasked { said: String },
55}
56
57/// Who needs an item, and therefore which verdict it counts towards.
58#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
59#[serde(rename_all = "snake_case")]
60#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
61pub enum DoctorTier {
62 /// Enough to run a city on this machine.
63 Use,
64 /// Enough to change this code and close its checks.
65 Develop,
66}
67
68/// Whether a tier can be reached without the item.
69#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
70#[serde(rename_all = "snake_case")]
71#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
72pub enum DoctorNeed {
73 Required,
74 /// Absent is a fact rather than a fault.
75 Optional,
76}
77
78/// One item, and this machine's answer about it.
79#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
80#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
81pub struct DoctorItem {
82 /// The requirement table's name for the item, which is also its id:
83 /// a page says what the item is for in its own words, looked up by
84 /// this name.
85 pub name: String,
86 pub tier: DoctorTier,
87 pub need: DoctorNeed,
88 /// The item's own site, so a page can link a name to the people who
89 /// publish it. `None` where there is no one site: a platform's
90 /// shell, and this project's own connector.
91 pub homepage: Option<String>,
92 pub state: DoctorState,
93 pub install: DoctorInstall,
94 /// The version this repository pins the item at, read from the file
95 /// that pins it; `None` for an item nothing pins.
96 pub pinned: Option<String>,
97 /// The pack a page draws this item inside, with one install control
98 /// for the pack's missing members; `None` for an item drawn alone.
99 pub pack: Option<DoctorPack>,
100}
101
102/// A set of items a page draws as one row: each member is still its own
103/// item here, because each is detected, judged and installed on its own.
104#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
105#[serde(rename_all = "snake_case")]
106#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
107pub enum DoctorPack {
108 /// The cargo subcommands this repository's recipes and workflows call.
109 RustTools,
110}
111
112/// The newest release of one item upstream, as its publisher states it.
113#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
114#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
115pub struct DoctorUpstream {
116 /// The requirement table's name for the item, as `DoctorItem.name`.
117 pub item: String,
118 pub newest: DoctorNewest,
119}
120
121/// What asking the item's publisher came to.
122#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
123#[serde(rename_all = "snake_case")]
124#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
125pub enum DoctorNewest {
126 /// The city has asked the publisher and has no answer yet; asking
127 /// again later reads what arrived. The question never waits on the
128 /// network, because a session answers its questions one at a time.
129 Asking,
130 /// The newest stable version, as a dotted number.
131 Read { version: String },
132 /// This item has no single upstream version to read, and why.
133 Unread { why: DoctorUnread },
134 /// The source was asked and did not answer; `said` is where the
135 /// call stopped.
136 Refused { said: String },
137}
138
139/// Why an item has no upstream version to compare with.
140#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
141#[serde(rename_all = "snake_case")]
142#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
143pub enum DoctorUnread {
144 /// It ships inside the Rust toolchain, so the toolchain's version is
145 /// the one that moves.
146 WithToolchain,
147 /// A family of browsers, each brand with its own releases.
148 ManyBrands,
149 /// A driver whose version follows the browser beside it.
150 MatchesBrowser,
151 /// A part of this project, released with it.
152 ThisProject,
153 /// Its publisher offers no machine-readable release to ask.
154 NoSource,
155 /// The requirement table carries no item by that name.
156 UnknownItem,
157}
158
159/// Whether the item is here, and in what condition.
160#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
161#[serde(rename_all = "snake_case")]
162#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
163pub enum DoctorState {
164 /// Here, and it started when asked its version.
165 Present {
166 at: String,
167 version: DoctorVersion,
168 },
169 /// Here, and unusable. Counts as missing for a verdict, and as its
170 /// own fault for a person.
171 Broken {
172 at: String,
173 fault: DoctorFault,
174 },
175 Absent {
176 absence: DoctorAbsence,
177 },
178}
179
180/// What a present program said when asked its version. Only the first
181/// arm carries text; the other three are facts about how it did not
182/// answer, and none of them makes the program absent.
183#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
184#[serde(rename_all = "snake_case")]
185#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
186pub enum DoctorVersion {
187 Said {
188 text: String,
189 },
190 /// It started, wrote nothing, and exited.
191 Silent,
192 /// Its first line was not text.
193 Unreadable,
194 /// It had not written a line when the deadline passed.
195 Late,
196}
197
198/// Why a thing that is here cannot be used.
199#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
200#[serde(rename_all = "snake_case")]
201#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
202pub enum DoctorFault {
203 /// The operating system refused to start it.
204 WillNotStart {
205 said: String,
206 },
207 /// The component directory exists and the component file does not:
208 /// an install that stopped half way.
209 HalfWritten,
210 Unreadable {
211 said: String,
212 },
213}
214
215/// Which kind of not being here.
216#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
217#[serde(rename_all = "snake_case")]
218#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
219pub enum DoctorAbsence {
220 NotOnSearchPath,
221 /// A person pointed at a file, and the file is not there.
222 VariableNamesNothing {
223 variable: String,
224 path: String,
225 },
226 NoComponent {
227 dir: String,
228 },
229 NoHome,
230 NotInThisBuild,
231}
232
233/// What getting the item would cost on this machine.
234#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
235#[serde(rename_all = "snake_case")]
236#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
237pub enum DoctorInstall {
238 /// A command this city may run once the person has agreed to it.
239 Command { spelled: String },
240 /// A command printed and never run: a script piped into a shell is
241 /// code nobody read.
242 Print { spelled: String },
243 /// Nothing here can install it; the text says what a person does.
244 Manual { how: String },
245 /// This machine is not one of the three this project names recipes
246 /// for, so nothing can be said about installing anything on it.
247 UnknownPlatform,
248}
249
250/// Whether one tier is reachable on this machine.
251#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
252#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
253pub struct DoctorVerdict {
254 pub tier: DoctorTier,
255 /// The required items of this tier that are absent or broken, in
256 /// table order. Empty means ready.
257 pub missing: Vec<String>,
258}
259
260/// Which backend a host command runs under on this machine, and what
261/// that arm promises.
262#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
263#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
264pub struct DoctorSandbox {
265 pub arm: DoctorSandboxArm,
266 /// One row per axis, in the order the axes are declared. The rows
267 /// are stated rather than left to the page to infer: an arm that
268 /// holds some axes and not others is the whole reason this report
269 /// exists.
270 pub coverage: Vec<DoctorGuarantee>,
271}
272
273/// The backend itself. Closed, so a page has a word for every arm and
274/// a new arm is a compile error at every reader.
275#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
276#[serde(rename_all = "snake_case")]
277#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
278pub enum DoctorSandboxArm {
279 /// Namespaces of its own, made by a wrapper program.
280 LinuxNamespaces,
281 /// A Windows job object: the process tree ends together and the
282 /// limits hold, and the network is not isolated.
283 WindowsJobObject,
284 /// The floor every platform has: the command runs in a copy of the
285 /// working tree.
286 CopiedTree,
287 /// No arm at all, and what this machine is missing.
288 Unavailable { missing: DoctorSandboxMissing },
289}
290
291/// What a machine lacks when it can give no confinement at all.
292#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
293#[serde(rename_all = "snake_case")]
294#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
295pub enum DoctorSandboxMissing {
296 ScratchDirectory,
297}
298
299/// One axis of confinement, and whether this machine's arm holds it.
300#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
301#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
302pub struct DoctorGuarantee {
303 pub axis: DoctorGuaranteeAxis,
304 pub kept: DoctorCoverage,
305}
306
307/// What a confinement can promise.
308#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
309#[serde(rename_all = "snake_case")]
310#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
311pub enum DoctorGuaranteeAxis {
312 Filesystem,
313 Network,
314 ProcessTree,
315 User,
316 Resources,
317}
318
319/// Whether the arm holds the axis. Two words rather than a boolean,
320/// because a page has to word both and a wire that carried `true` would
321/// make every reader choose its own word for `false`.
322#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
323#[serde(rename_all = "snake_case")]
324#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
325pub enum DoctorCoverage {
326 Kept,
327 NotKept,
328}
329
330/// Where this machine's credentials rest, and how long they stay.
331#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
332#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
333pub struct DoctorCustody {
334 pub store: DoctorCustodyStore,
335 pub keeps: DoctorCustodyLifetime,
336 /// The platform service's own refusal, when it did not keep the
337 /// value it was asked to keep. `None` is a service that worked, and
338 /// a store this city chose itself.
339 pub refusal: Option<String>,
340}
341
342/// Which store a city writes secrets to.
343#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
344#[serde(rename_all = "snake_case")]
345#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
346pub enum DoctorCustodyStore {
347 /// The platform's own credential service.
348 PlatformService,
349 /// An encrypted file on this machine, opened once per start with a
350 /// passphrase.
351 EncryptedFile,
352 /// This process only.
353 SessionMemory,
354}
355
356/// How long a value the store keeps stays reachable.
357#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
358#[serde(rename_all = "snake_case")]
359#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
360pub enum DoctorCustodyLifetime {
361 AcrossReboots,
362 WithPassphrase,
363 UntilReboot,
364 ThisProcess,
365}