Skip to main content

printobserver_sdk/
contract.rs

1//! Generated by `contract-codegen` from the checked-in contract schemas.
2//!
3//! This file is the Rust client's request and response types.
4//!
5//! Do not edit by hand: change the contracts, regenerate with `just
6//! generate-clients`, and commit what that writes.
7
8use std::collections::BTreeMap;
9
10use serde::{Deserialize, Serialize};
11
12use crate::{Client, ClientError};
13
14/// The version of the server contract these types were generated from: the
15/// version of the workspace this crate is compiled from. A published crate
16/// reports the release version it was built at, and a crate compiled from a
17/// checkout reports that checkout's workspace version.
18pub const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION");
19
20/// Every operation this client exposes a method for, which is every operation
21/// the server declares and no other.
22pub const OPERATION_NAMES: [&str; 27] = [
23    "prints",
24    "status",
25    "context",
26    "image",
27    "history",
28    "manifest_get",
29    "look",
30    "manifest_set",
31    "spools",
32    "spool",
33    "printer_history",
34    "preflight",
35    "pause",
36    "resume",
37    "cancel",
38    "start_print",
39    "set_feedrate_factor",
40    "set_flowrate_factor",
41    "set_tool_target_c",
42    "set_bed_target_c",
43    "set_fan_percent",
44    "acknowledge_failure",
45    "register_spool",
46    "load_spool",
47    "unload_spool",
48    "adjust_spool",
49    "retire_spool",
50];
51
52/// What an operator's acknowledgement of a failure event asks for next.
53#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
54pub enum AcknowledgementDisposition {
55    /// Carry on printing.
56    #[serde(rename = "continue")]
57    Continue,
58    /// Carry on printing, watched more closely.
59    #[serde(rename = "watch")]
60    Watch,
61    /// The print should stop. Recorded for whoever may cancel it: the
62    /// acknowledgement itself asks nothing of the machine.
63    #[serde(rename = "stop")]
64    Stop,
65}
66
67/// What one mutating request left behind it.
68#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
69pub struct ActionAnswer {
70    /// The bounded intervention it opened, when it opened one.
71    #[serde(default, skip_serializing_if = "Option::is_none")]
72    pub intervention: Option<Intervention>,
73    /// What the printer said, when the request reached it and it refused.
74    #[serde(default, skip_serializing_if = "Option::is_none")]
75    pub printer_refusal: Option<String>,
76    /// The record of the request and the decision taken on it. A rejected
77    /// request's rejection is here, carrying its reason, the value asked for
78    /// and the range allowed, so a caller can ask again inside the range.
79    pub record: ActionRecord,
80}
81
82/// An accepted action reached the printer.
83#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
84pub struct ActionExecutedPayload {
85    /// The action's identifier.
86    pub action_id: ActionId,
87    /// The bounded intervention it opened, when it opened one.
88    #[serde(default, skip_serializing_if = "Option::is_none")]
89    pub intervention_id: Option<InterventionId>,
90}
91
92/// A lowercase hyphenated version 7 UUID identifying one requested action.
93pub type ActionId = String;
94
95/// One action of the closed vocabulary, named without its payload.
96///
97/// This is what a safety envelope grants and what a policy rejection names;
98/// the
99/// payload lives on `PrintAction` itself.
100#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
101pub enum ActionKind {
102    /// Pause the print.
103    #[serde(rename = "pause")]
104    Pause,
105    /// Resume the print.
106    #[serde(rename = "resume")]
107    Resume,
108    /// Cancel the print.
109    #[serde(rename = "cancel")]
110    Cancel,
111    /// Start a print of a named file.
112    #[serde(rename = "start_print")]
113    StartPrint,
114    /// Set the feedrate factor.
115    #[serde(rename = "set_feedrate_factor")]
116    SetFeedrateFactor,
117    /// Set the flowrate factor.
118    #[serde(rename = "set_flowrate_factor")]
119    SetFlowrateFactor,
120    /// Set a tool's target temperature.
121    #[serde(rename = "set_tool_target_c")]
122    SetToolTargetC,
123    /// Set the bed's target temperature.
124    #[serde(rename = "set_bed_target_c")]
125    SetBedTargetC,
126    /// Set the fan percentage.
127    #[serde(rename = "set_fan_percent")]
128    SetFanPercent,
129    /// Acknowledge a failure event.
130    #[serde(rename = "acknowledge_failure")]
131    AcknowledgeFailure,
132    /// Register a spool.
133    #[serde(rename = "register_spool")]
134    RegisterSpool,
135    /// Load a spool onto a tool.
136    #[serde(rename = "load_spool")]
137    LoadSpool,
138    /// Unload a spool from its tool.
139    #[serde(rename = "unload_spool")]
140    UnloadSpool,
141    /// Correct a spool's remaining figure.
142    #[serde(rename = "adjust_spool")]
143    AdjustSpool,
144    /// Retire a spool.
145    #[serde(rename = "retire_spool")]
146    RetireSpool,
147}
148
149/// One request, the decision taken on it, and what became of it.
150///
151/// `executed_at` and `outcome` are both optional and are absent together: a
152/// record is written when the decision is taken, which is before — and, for a
153/// rejected request, instead of — anything reaching the printer.
154#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
155pub struct ActionRecord {
156    /// The decision policy took on it.
157    pub decision: PolicyDecision,
158    /// When it reached the printer, if it did.
159    #[serde(default, skip_serializing_if = "Option::is_none")]
160    pub executed_at: Option<Timestamp>,
161    /// This record's identifier, minted by the store.
162    pub id: ActionId,
163    /// What the printer made of it, if it reached the printer.
164    #[serde(default, skip_serializing_if = "Option::is_none")]
165    pub outcome: Option<ExecutionOutcome>,
166    /// The print this request was made against.
167    pub print_id: PrintId,
168    /// The request itself.
169    pub request: ActionRequest,
170}
171
172/// Policy refused an action.
173#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
174pub struct ActionRejectedPayload {
175    /// The action's identifier.
176    pub action_id: ActionId,
177    /// The whole decision, carrying which rejection it was.
178    pub decision: PolicyDecision,
179}
180
181/// One request an actor made, at the instant it made it.
182#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
183pub struct ActionRequest {
184    /// What was asked for.
185    pub action: PrintAction,
186    /// Who asked.
187    pub actor: Actor,
188    /// When they asked.
189    pub requested_at: Timestamp,
190}
191
192/// An actor asked for an action.
193#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
194pub struct ActionRequestedPayload {
195    /// What was asked for.
196    pub action: PrintAction,
197    /// The action's identifier.
198    pub action_id: ActionId,
199    /// Who asked.
200    pub actor: Actor,
201}
202
203/// Who asked for something.
204#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
205pub enum Actor {
206    /// The supervising agent, naming its session.
207    #[serde(rename = "agent")]
208    Agent {
209        /// The supervision session the agent is acting in.
210        session_name: String,
211    },
212    /// A person.
213    #[serde(rename = "operator")]
214    Operator,
215    /// The supervisor itself.
216    #[serde(rename = "system")]
217    System,
218}
219
220/// An actor class, which is what a safety envelope grants actions to.
221#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
222pub enum ActorClass {
223    /// The supervising agent.
224    #[serde(rename = "agent")]
225    Agent,
226    /// A person.
227    #[serde(rename = "operator")]
228    Operator,
229    /// The supervisor itself.
230    #[serde(rename = "system")]
231    System,
232}
233
234/// One thing an adjustment may change.
235pub type Adjustable = String;
236
237/// The agent's written record of one supervision turn.
238///
239/// This is deliberately not how the agent acts: acting is a
240/// `PrintAction` that policy rules on.
241#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
242pub struct AgentAssessment {
243    /// How sure the agent is.
244    pub confidence: Confidence,
245    /// What the agent did.
246    pub did: String,
247    /// Whether the agent is escalating to a person.
248    pub escalating: bool,
249    /// Whether the print should carry on.
250    pub should_continue: bool,
251    /// One line saying what is happening.
252    pub summary: String,
253    /// Why it did it.
254    pub why: String,
255}
256
257/// The agent wrote down what it made of a turn.
258#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
259pub struct AgentAssessmentPayload {
260    /// What the agent answered with.
261    pub assessment: AgentAssessment,
262    /// The session the turn ran in.
263    pub session_name: String,
264}
265
266/// Somebody took a fresh look at the print.
267///
268/// Written down before the camera is asked for a frame, so that a frame the
269/// camera would not give is recorded against a look that is already in the
270/// history; the frame itself, when there is one, is this event's image.
271#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
272pub struct CameraLookPayload {
273    /// The events for the print that arrived while it waited, and that it
274    /// handed to whoever looked, oldest first.
275    #[serde(default, skip_serializing_if = "Option::is_none")]
276    pub delivered: Option<Vec<EventId>>,
277    /// How long the look waited for something to happen before it was taken,
278    /// in whole seconds.
279    pub waited_s: i64,
280}
281
282/// The durable identity of one debit, so that a replay can never debit twice.
283#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
284#[serde(tag = "key")]
285pub enum ChargeKey {
286    /// One segment of a print this system watched, on one tool.
287    #[serde(rename = "print")]
288    Print {
289        /// The print.
290        print_id: PrintId,
291        /// Which segment: zero from the print's start, and one more each time
292        /// a mid-print swap or a filament-change signal closes the current
293        /// one.
294        segment: i64,
295        /// The tool, in the printer's own numbering.
296        tool: i64,
297    },
298    /// One print this system never saw, on one tool.
299    #[serde(rename = "unobserved")]
300    Unobserved {
301        /// The file that was printed.
302        file_name: String,
303        /// The file's tally generation, which advances each time the
304        /// printer's
305        /// tally for the file is reset.
306        generation: i64,
307        /// Where the file lives, in the printer's own vocabulary.
308        origin: String,
309        /// The print's ordinal in the file's tally — completed and failed
310        /// prints counted up to and including this one — within that
311        /// generation.
312        print_number: i64,
313        /// The tool, in the printer's own numbering.
314        tool: i64,
315    },
316}
317
318/// How the length a charge debits was worked out.
319#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
320pub enum ChargeMethod {
321    /// A completed print: the length the file's analysis gives.
322    #[serde(rename = "slicer_estimate")]
323    SlicerEstimate,
324    /// The extrusion the file's own moves add up to, up to a position in it.
325    #[serde(rename = "gcode_extrusion")]
326    GcodeExtrusion,
327    /// The analysed length scaled by how far into the file the print got; the
328    /// fallback used only when the file itself could not be read.
329    #[serde(rename = "file_fraction")]
330    FileFraction,
331    /// A completed print this system never saw: the file's analysed length.
332    #[serde(rename = "unobserved_estimate")]
333    UnobservedEstimate,
334}
335
336/// Filament a tool fed after an unconfirmed filament change was held for the
337/// next spool loaded onto that tool, or confirmed on it.
338#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
339pub struct ChargePendedPayload {
340    /// What was held.
341    pub pending: PendingCharge,
342}
343
344/// One mark a spool command cleared, and the spool it was cleared from.
345#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
346pub struct ClearedMark {
347    /// The reason that no longer stands.
348    pub reason: UnverifiedReason,
349    /// The spool the mark was cleared from.
350    pub spool_id: SpoolId,
351}
352
353/// How sure the agent is.
354///
355/// A closed vocabulary rather than a number, because a number invites a
356/// precision the agent does not have.
357#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
358pub enum Confidence {
359    /// Not sure.
360    #[serde(rename = "low")]
361    Low,
362    /// Fairly sure.
363    #[serde(rename = "medium")]
364    Medium,
365    /// Sure.
366    #[serde(rename = "high")]
367    High,
368}
369
370/// What a context read answers.
371///
372/// The latest image is **materialized** here rather than left as the handle
373/// `PrintContext` carries: the one thing a supervision turn does with an
374/// image is look at it, and a handle is not something anything can open. The
375/// path is absolute on this server's own filesystem, exactly as
376/// `ImageAnswer`'s is, and is absent when there is no image or when the
377/// record is intact and the file is not.
378#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
379pub struct ContextAnswer {
380    /// Everything a supervision turn is given about the print.
381    pub context: PrintContext,
382    /// Where the latest image's bytes are, absolute on this host.
383    #[serde(default, skip_serializing_if = "Option::is_none")]
384    pub image_path: Option<String>,
385}
386
387/// The envelope intersected with the print's manifest.
388///
389/// This is what context reports, so that the agent can see its own limits
390/// before it asks. Computing one is the supervision core's; this crate
391/// declares
392/// the shape and nothing that produces it.
393#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
394pub struct EffectiveBounds {
395    /// The range each adjustable may be set to, inclusive.
396    pub allowed: BTreeMap<String, Range>,
397}
398
399/// What this server answers when it will not do what it was asked.
400#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
401pub struct ErrorAnswer {
402    /// One line saying why, in the words of whatever refused it.
403    pub error: String,
404}
405
406/// A lowercase hyphenated version 7 UUID identifying one event.
407pub type EventId = String;
408
409/// The name one kind of event is written down under: lowercase `snake_case`,
410/// declared by the domain that owns the event.
411pub type EventKind = String;
412
413/// One event, as the store holds it and the server serves it.
414///
415/// `raw` holds the bytes exactly as received for an externally sourced event
416/// and is absent for an internally raised one — it is what makes the history
417/// auditable when a normalization turns out to be wrong. `print_id` is
418/// optional, because an externally sourced event may name no print this
419/// system
420/// knows.
421#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
422pub struct EventRecord {
423    /// This event's identifier, minted by the store.
424    pub id: EventId,
425    /// The image it arrived with, when it arrived with one.
426    #[serde(default, skip_serializing_if = "Option::is_none")]
427    pub image: Option<ImageRef>,
428    /// Which event this is.
429    pub kind: EventKind,
430    /// What it carries, in the form its kind declares.
431    pub payload: serde_json::Value,
432    /// The print it belongs to, when it belongs to one.
433    #[serde(default, skip_serializing_if = "Option::is_none")]
434    pub print_id: Option<PrintId>,
435    /// The bytes exactly as received, for an externally sourced event.
436    #[serde(default, skip_serializing_if = "Option::is_none")]
437    pub raw: Option<RawBytes>,
438    /// When it was received.
439    pub received_at: Timestamp,
440    /// Where it came from.
441    pub source: EventSource,
442}
443
444/// Where an event came from, as the bare string the domain that raised it
445/// declares for itself.
446pub type EventSource = String;
447
448/// What happened when an accepted action reached the printer.
449#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
450pub enum ExecutionOutcome {
451    /// The printer took it.
452    #[serde(rename = "succeeded")]
453    Succeeded,
454    /// The printer did not, for this reason.
455    #[serde(rename = "failed")]
456    Failed {
457        /// Why it failed.
458        reason: String,
459    },
460}
461
462/// A filament change was signalled during a print.
463#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
464pub struct FilamentChangeSignalledPayload {
465    /// The position the spool was charged up to, when one was known.
466    #[serde(default, skip_serializing_if = "Option::is_none")]
467    pub position: Option<FilePosition>,
468    /// Whether that position was read when the signal was handled, rather
469    /// than
470    /// being the last one recorded; never with no position at all.
471    pub position_read_at_signal: bool,
472    /// The kind of the alert that carried the signal.
473    pub source_kind: EventKind,
474    /// The spool that was on it, when one was tracked.
475    #[serde(default, skip_serializing_if = "Option::is_none")]
476    pub spool_id: Option<SpoolId>,
477    /// The tool the change was signalled for, when it is known.
478    #[serde(default, skip_serializing_if = "Option::is_none")]
479    pub tool: Option<i64>,
480}
481
482/// Filament was charged against a spool, or recorded uncharged.
483#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
484pub struct FilamentConsumedPayload {
485    /// The file's analysed length for the tool, in millimetres, when it had
486    /// one.
487    #[serde(default, skip_serializing_if = "Option::is_none")]
488    pub analysed_length_mm: Option<f64>,
489    /// The durable identity of this debit.
490    pub charge_key: ChargeKey,
491    /// The density the grams were worked out with, in grams per cubic
492    /// centimetre.
493    #[serde(default, skip_serializing_if = "Option::is_none")]
494    pub density_g_cm3: Option<f64>,
495    /// The diameter the grams were worked out with, in millimetres.
496    #[serde(default, skip_serializing_if = "Option::is_none")]
497    pub diameter_mm: Option<f64>,
498    /// What that length weighs, in grams, when a spool gave its diameter and
499    /// density.
500    #[serde(default, skip_serializing_if = "Option::is_none")]
501    pub grams: Option<f64>,
502    /// How much was fed, in millimetres.
503    pub length_mm: f64,
504    /// How the length was worked out.
505    pub method: ChargeMethod,
506    /// Anything a reader needs to make sense of the figure.
507    #[serde(default, skip_serializing_if = "Option::is_none")]
508    pub note: Option<String>,
509    /// How far into the file the charge runs, when that was read.
510    #[serde(default, skip_serializing_if = "Option::is_none")]
511    pub position: Option<FilePosition>,
512    /// The spool's figure after it, in grams.
513    #[serde(default, skip_serializing_if = "Option::is_none")]
514    pub remaining_after_g: Option<f64>,
515    /// The spool's figure before the charge, in grams.
516    #[serde(default, skip_serializing_if = "Option::is_none")]
517    pub remaining_before_g: Option<f64>,
518    /// The spool charged; absent when nothing was tracked on the tool, and
519    /// the
520    /// filament was recorded uncharged.
521    #[serde(default, skip_serializing_if = "Option::is_none")]
522    pub spool_id: Option<SpoolId>,
523    /// The tool that fed it.
524    pub tool: i64,
525}
526
527/// A file name a printer's own file API can be asked for: no path separator,
528/// no NUL byte, no `.` or `..` segment, no drive prefix, and not empty.
529pub type FileName = String;
530
531/// How far into its file a print had got, and when that was read.
532#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
533pub struct FilePosition {
534    /// The byte the printer had read the file up to.
535    pub bytes: i64,
536    /// When the position was read.
537    pub observed_at: Timestamp,
538    /// The file's size in bytes, when it was known.
539    #[serde(default, skip_serializing_if = "Option::is_none")]
540    pub size_bytes: Option<i64>,
541}
542
543/// The printer's tally for a file went down — the file was deleted and
544/// uploaded again — so the file's tally generation advanced, and the new
545/// count
546/// is that generation's growth from zero.
547///
548/// The rebaseline itself marks no spool; only an `unverified` disposition
549/// does.
550#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
551pub struct FileTallyRebaselinedPayload {
552    /// What became of the prints the new generation counts.
553    pub disposition: RebaselineDisposition,
554    /// The file whose tally went down.
555    pub file_name: String,
556    /// The failed prints the new tally counts.
557    pub new_failures: i64,
558    /// The generation it is in now.
559    pub new_generation: i64,
560    /// The completed prints the new tally counts.
561    pub new_successes: i64,
562    /// The failed prints the old tally counted.
563    pub old_failures: i64,
564    /// The generation the tally was in.
565    pub old_generation: i64,
566    /// The completed prints the old tally counted.
567    pub old_successes: i64,
568    /// Where it lives, in the printer's own vocabulary.
569    pub origin: String,
570}
571
572/// One heater, as a source reported it.
573///
574/// Every field is optional, because a source that reports no heater at all
575/// reports none of these; each is a plausibility-ranged reported value in
576/// degrees Celsius.
577#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
578pub struct HeaterSnapshot {
579    /// The temperature the heater is at.
580    #[serde(default, skip_serializing_if = "Option::is_none")]
581    pub actual_c: Option<Reported>,
582    /// The offset applied to this heater's target.
583    #[serde(default, skip_serializing_if = "Option::is_none")]
584    pub offset_c: Option<Reported>,
585    /// The temperature the heater is driving towards.
586    #[serde(default, skip_serializing_if = "Option::is_none")]
587    pub target_c: Option<Reported>,
588}
589
590/// What a history read answers.
591#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
592pub struct HistoryAnswer {
593    /// The print's events, newest first.
594    pub events: Vec<EventRecord>,
595}
596
597/// What an image read answers.
598///
599/// The path is absolute on this server's own filesystem, and is the whole of
600/// the answer about the bytes: nothing here renders them, encodes them or
601/// serves them. It is absent when the record is intact and the file is not,
602/// which is a different answer from there being no such image.
603#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
604pub struct ImageAnswer {
605    /// Where its bytes are, absolute on this host.
606    #[serde(default, skip_serializing_if = "Option::is_none")]
607    pub path: Option<String>,
608    /// The record.
609    pub record: ImageRecord,
610}
611
612/// A lowercase hyphenated version 7 UUID identifying one image.
613pub type ImageId = String;
614
615/// One image, stored beside the event it arrived with.
616///
617/// `relative_path` is relative to the configured state directory, so that the
618/// store stays portable. Materializing an image resolves that to an absolute
619/// path; nothing in this system renders image bytes into JSON.
620#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
621pub struct ImageRecord {
622    /// How many bytes it is.
623    pub byte_len: i64,
624    /// The content type it was served as.
625    pub content_type: String,
626    /// The event it arrived with.
627    pub event_id: EventId,
628    /// When it was fetched.
629    pub fetched_at: Timestamp,
630    /// This image's identifier, minted by the store.
631    pub id: ImageId,
632    /// The print it belongs to.
633    pub print_id: PrintId,
634    /// Where it lives, relative to the configured state directory.
635    pub relative_path: String,
636    /// The SHA-256 of its bytes, lowercase hexadecimal.
637    pub sha256: String,
638    /// Where it was fetched from, when it was fetched from somewhere.
639    #[serde(default, skip_serializing_if = "Option::is_none")]
640    pub source_url: Option<String>,
641}
642
643/// The handle an image travels in context under.
644#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
645pub struct ImageRef {
646    /// The image's identifier.
647    pub id: ImageId,
648    /// The SHA-256 of its bytes, lowercase hexadecimal.
649    pub sha256: String,
650}
651
652/// One adjustment made for a bounded time.
653///
654/// `prior_value` is read from the printer snapshot taken before the change,
655/// and
656/// is absent when the printer reported none — in which case expiry restores
657/// nothing and the outcome says so rather than guessing a default.
658#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
659pub struct Intervention {
660    /// The action that asked for it.
661    pub action_id: ActionId,
662    /// What it changed.
663    pub adjustable: Adjustable,
664    /// When it was applied.
665    pub applied_at: Timestamp,
666    /// What it was changed to.
667    pub applied_value: f64,
668    /// When it stops standing.
669    pub expires_at: Timestamp,
670    /// This intervention's identifier, minted by the store.
671    pub id: InterventionId,
672    /// What became of it.
673    pub outcome: InterventionOutcome,
674    /// The print it was made against.
675    pub print_id: PrintId,
676    /// What the printer reported before the change, if it reported anything.
677    #[serde(default, skip_serializing_if = "Option::is_none")]
678    pub prior_value: Option<f64>,
679    /// When the prior value was put back, if it was.
680    #[serde(default, skip_serializing_if = "Option::is_none")]
681    pub restored_at: Option<Timestamp>,
682}
683
684/// A bounded intervention expired.
685#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
686pub struct InterventionExpiredPayload {
687    /// What it had changed.
688    pub adjustable: Adjustable,
689    /// The intervention's identifier.
690    pub intervention_id: InterventionId,
691    /// What became of it.
692    pub outcome: InterventionOutcome,
693}
694
695/// A lowercase hyphenated version 7 UUID identifying one bounded
696/// intervention.
697pub type InterventionId = String;
698
699/// What became of a bounded change.
700#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
701pub enum InterventionOutcome {
702    /// It is still in force.
703    #[serde(rename = "still_active")]
704    StillActive,
705    /// The prior value was put back.
706    #[serde(rename = "restored")]
707    Restored,
708    /// There was no prior value to put back.
709    #[serde(rename = "restore_unavailable")]
710    RestoreUnavailable,
711    /// Putting the prior value back failed.
712    #[serde(rename = "restore_failed")]
713    RestoreFailed {
714        /// Why it failed.
715        reason: String,
716    },
717    /// Another intervention replaced it before it expired.
718    #[serde(rename = "superseded")]
719    Superseded {
720        /// The intervention that replaced it.
721        by: InterventionId,
722    },
723}
724
725/// What a sliced job declares about itself and about what may be adjusted.
726///
727/// An adjustable the manifest does not name takes the envelope's own range; a
728/// manifest range wider than the envelope's is narrowed to the envelope's and
729/// the narrowing is recorded on the print. A manifest may only narrow.
730#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
731pub struct JobManifest {
732    /// The range each named adjustable may take, inclusive.
733    pub allowed: BTreeMap<String, Range>,
734    /// The file this manifest is about, as the slicer named it.
735    pub file_name: String,
736    /// The material the job is sliced for.
737    pub material: String,
738    /// Whatever else the slicer recorded.
739    pub metadata: BTreeMap<String, String>,
740    /// The nozzle the job is sliced for, in millimetres.
741    pub nozzle_diameter_mm: f64,
742    /// The slicer profile the job was sliced with.
743    pub slicer_profile: String,
744}
745
746/// The job a printer reports it is running.
747///
748/// Every field but `state` is optional: an absent one means the source did
749/// not
750/// report it.
751#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
752pub struct JobSnapshot {
753    /// How far through the print is, as a fraction from zero to one.
754    ///
755    /// Normalized to a fraction here regardless of how the source expresses
756    /// it.
757    #[serde(default, skip_serializing_if = "Option::is_none")]
758    pub completion: Option<Reported>,
759    /// The error the source reports, when it reports one.
760    #[serde(default, skip_serializing_if = "Option::is_none")]
761    pub error: Option<String>,
762    /// The whole print's estimated duration, in whole seconds.
763    #[serde(default, skip_serializing_if = "Option::is_none")]
764    pub estimated_print_time_s: Option<i64>,
765    /// The filament the source's analysis of the file says each tool uses;
766    /// empty when the source reported none.
767    #[serde(default, skip_serializing_if = "Option::is_none")]
768    pub filament: Option<Vec<ToolFilament>>,
769    /// The name of the file being printed, as the source reported it.
770    #[serde(default, skip_serializing_if = "Option::is_none")]
771    pub file_name: Option<String>,
772    /// Where the file lives, in the source's own vocabulary.
773    #[serde(default, skip_serializing_if = "Option::is_none")]
774    pub file_origin: Option<String>,
775    /// How far into the file the printer has read, in bytes, when the source
776    /// reported it.
777    #[serde(default, skip_serializing_if = "Option::is_none")]
778    pub file_position_bytes: Option<i64>,
779    /// How long the print has left, in whole seconds.
780    #[serde(default, skip_serializing_if = "Option::is_none")]
781    pub print_time_left_s: Option<i64>,
782    /// How long the print has been running, in whole seconds.
783    #[serde(default, skip_serializing_if = "Option::is_none")]
784    pub print_time_s: Option<i64>,
785    /// The file's size in bytes.
786    #[serde(default, skip_serializing_if = "Option::is_none")]
787    pub size_bytes: Option<i64>,
788    /// The state the source reports the job to be in.
789    pub state: PrinterState,
790}
791
792/// One spool loaded on one tool, as a print started.
793#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
794pub struct LoadedSpool {
795    /// The spool loaded on it.
796    pub spool_id: SpoolId,
797    /// The tool, in the printer's own numbering.
798    pub tool: i64,
799}
800
801/// One fresh look at a print.
802///
803/// An answer rather than a record, so it admits fields beside its own the way
804/// every answer of the server does: a client that could not open the frame's
805/// path says so in a field of its own beside the rest of the answer.
806#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
807pub struct Look {
808    /// The events for this print that arrived while the look waited, oldest
809    /// first; the look returned early when there were any.
810    #[serde(default, skip_serializing_if = "Option::is_none")]
811    pub arrived: Option<Vec<EventRecord>>,
812    /// Whether the print is paused and the pause is the detector's, which is
813    /// what an adjustment asked for now would be applied under.
814    pub detector_paused: bool,
815    /// The look itself, as it was written into the print's history, carrying
816    /// the frame as its image when there is one.
817    pub event: EventRecord,
818    /// The frame the camera gave, absent when no camera is configured or it
819    /// gave none — which the history records as a port failure.
820    #[serde(default, skip_serializing_if = "Option::is_none")]
821    pub frame: Option<ImageRef>,
822    /// The absolute path the frame's bytes are at, on the supervisor's host.
823    #[serde(default, skip_serializing_if = "Option::is_none")]
824    pub image_path: Option<String>,
825    /// The job the printer reports now, absent when it could not be read.
826    #[serde(default, skip_serializing_if = "Option::is_none")]
827    pub job: Option<JobSnapshot>,
828    /// The printer's state now, absent when it could not be read.
829    #[serde(default, skip_serializing_if = "Option::is_none")]
830    pub printer: Option<PrinterSnapshot>,
831}
832
833/// An external body arrived that could not be read.
834///
835/// This kind always carries its `raw` bytes, and it exists so that an alert
836/// this system cannot read is written down rather than dropped.
837#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
838pub struct MalformedExternalEventPayload {
839    /// One line saying why the body could not be read.
840    pub detail: String,
841}
842
843/// What a manifest read or write answers.
844#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
845pub struct ManifestAnswer {
846    /// The manifest, when the print has one.
847    #[serde(default, skip_serializing_if = "Option::is_none")]
848    pub manifest: Option<JobManifest>,
849    /// Every range this manifest asked wider than the envelope allows,
850    /// narrowed
851    /// to the envelope's — recorded on the print, so nobody has to wonder
852    /// later
853    /// which bound applied.
854    pub narrowings: Vec<ManifestNarrowing>,
855}
856
857/// One adjustable whose manifest range was wider than the envelope's.
858#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
859pub struct ManifestNarrowing {
860    /// The adjustable that was narrowed.
861    pub adjustable: Adjustable,
862    /// The range that stands.
863    pub applied: Range,
864    /// The range the manifest asked for.
865    pub requested: Range,
866}
867
868/// Obico reported a print failure.
869///
870/// The two instants are optional because Obico's own field for each is a Unix
871/// timestamp number, an empty string, or absent, and the last two both mean
872/// the
873/// producer reported no instant. An absent field here is that, never an epoch
874/// date standing in for it.
875#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
876pub struct ObicoFailureAlertPayload {
877    /// When the print ended, when Obico reported an instant for it.
878    #[serde(default, skip_serializing_if = "Option::is_none")]
879    pub ended_at: Option<Timestamp>,
880    /// The file being printed, when Obico named one.
881    #[serde(default, skip_serializing_if = "Option::is_none")]
882    pub file_name: Option<String>,
883    /// Whether Obico called it a warning rather than a failure.
884    pub is_warning: bool,
885    /// Obico's own identifier for the print, when it named one.
886    #[serde(default, skip_serializing_if = "Option::is_none")]
887    pub obico_print_id: Option<i64>,
888    /// Whether Obico paused the print itself.
889    pub print_paused: bool,
890    /// When the print started, when Obico reported an instant for it.
891    #[serde(default, skip_serializing_if = "Option::is_none")]
892    pub started_at: Option<Timestamp>,
893}
894
895/// The kind of printer notification Obico sent, normalized.
896#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
897pub enum ObicoNotificationType {
898    /// A print started.
899    #[serde(rename = "started")]
900    Started,
901    /// A print finished.
902    #[serde(rename = "done")]
903    Done,
904    /// A print was cancelled.
905    #[serde(rename = "cancelled")]
906    Cancelled,
907    /// A print was paused.
908    #[serde(rename = "paused")]
909    Paused,
910    /// A print was resumed.
911    #[serde(rename = "resumed")]
912    Resumed,
913    /// The printer is waiting for a filament change.
914    #[serde(rename = "filament_change")]
915    FilamentChange,
916    /// A heater cooled down.
917    #[serde(rename = "heater_cooled")]
918    HeaterCooled,
919    /// A heater reached its target.
920    #[serde(rename = "heater_target")]
921    HeaterTarget,
922}
923
924/// Obico sent a printer notification.
925///
926/// The two instants are optional for the same reason
927/// `ObicoFailureAlertPayload`'s are, and are absent along with the rest of
928/// the print's fields when the notification is about no print at all.
929#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
930pub struct ObicoPrinterNotificationPayload {
931    /// When the print ended, when Obico reported an instant for it.
932    #[serde(default, skip_serializing_if = "Option::is_none")]
933    pub ended_at: Option<Timestamp>,
934    /// The file being printed, when the notification is about one.
935    #[serde(default, skip_serializing_if = "Option::is_none")]
936    pub file_name: Option<String>,
937    /// Which notification it is.
938    pub notification_type: ObicoNotificationType,
939    /// Obico's own identifier for the print, when the notification is about
940    /// one.
941    #[serde(default, skip_serializing_if = "Option::is_none")]
942    pub obico_print_id: Option<i64>,
943    /// When the print started, when Obico reported an instant for it.
944    #[serde(default, skip_serializing_if = "Option::is_none")]
945    pub started_at: Option<Timestamp>,
946}
947
948/// An operator acknowledged an event.
949#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
950pub struct OperatorAcknowledgementPayload {
951    /// The event being acknowledged.
952    pub acknowledged_event_id: EventId,
953    /// What the operator asked for next.
954    pub disposition: AcknowledgementDisposition,
955}
956
957/// Filament a tool fed after a filament change nobody has confirmed, held
958/// until
959/// somebody says which spool fed it.
960///
961/// A length rather than grams, because the grams follow the spool that takes
962/// it.
963#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
964pub struct PendingCharge {
965    /// The file's analysed length for the tool, in millimetres, when it had
966    /// one.
967    #[serde(default, skip_serializing_if = "Option::is_none")]
968    pub analysed_length_mm: Option<f64>,
969    /// The key of the print segment the filament was fed in.
970    pub charge_key: ChargeKey,
971    /// How much was fed, in millimetres.
972    pub length_mm: f64,
973    /// How that length was worked out.
974    pub method: ChargeMethod,
975    /// Anything a reader needs to make sense of the figure.
976    #[serde(default, skip_serializing_if = "Option::is_none")]
977    pub note: Option<String>,
978    /// When it was held.
979    pub pended_at: Timestamp,
980    /// How far into the file the segment ran, when that was read.
981    #[serde(default, skip_serializing_if = "Option::is_none")]
982    pub position: Option<FilePosition>,
983    /// The print it was fed in.
984    pub print_id: PrintId,
985    /// The tool that fed it.
986    pub tool: i64,
987}
988
989/// One pending charge a spool command took.
990#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
991pub struct PendingTaken {
992    /// The key it is charged under.
993    pub charge_key: ChargeKey,
994    /// The print it was fed in.
995    pub print_id: PrintId,
996}
997
998/// The supervisor looked in on an active print on its own schedule, with no
999/// detector having said anything about it.
1000///
1001/// What a failure detector never alerts on — a progress that stopped moving,
1002/// a
1003/// heater whose target fell to nothing mid-print, a fan cut at a bridge, a
1004/// wall
1005/// going thin — is still on the machine and in the camera's frame, so every
1006/// active print is given one of these each interval and a turn on it. It
1007/// carries what the printer reported as it was taken; the frame the camera
1008/// gave
1009/// at the same moment, untagged by any detector, is this event's image.
1010#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1011pub struct PeriodicObservationPayload {
1012    /// How often these are taken, in whole seconds.
1013    pub interval_s: i64,
1014    /// The job the printer reported as the observation was taken, absent when
1015    /// it could not be read.
1016    #[serde(default, skip_serializing_if = "Option::is_none")]
1017    pub job: Option<JobSnapshot>,
1018    /// The printer's state as the observation was taken, absent when it could
1019    /// not be read — which the history records as a port failure.
1020    #[serde(default, skip_serializing_if = "Option::is_none")]
1021    pub printer: Option<PrinterSnapshot>,
1022}
1023
1024/// The decision policy took on one request.
1025#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1026pub enum PolicyDecision {
1027    /// The request may proceed.
1028    #[serde(rename = "accepted")]
1029    Accepted,
1030    /// The request may not, for this reason.
1031    #[serde(rename = "rejected")]
1032    Rejected(RejectionReason),
1033}
1034
1035/// A port failed while one event was being handled.
1036///
1037/// The event is named rather than implied, so that a reader holding an
1038/// event's
1039/// identifier reaches every failure recorded while that event was being
1040/// handled. A failure recorded here is one the handling survived: the event
1041/// is
1042/// already in the history by the time any of these sites is reached, and the
1043/// loop goes on to handle the next event.
1044#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1045pub struct PortFailurePayload {
1046    /// What the port said about it, in the port's own words.
1047    pub detail: String,
1048    /// The event whose handling reached the failing call.
1049    pub event_id: EventId,
1050    /// Where it failed.
1051    pub site: PortFailureSite,
1052}
1053
1054/// Where a port failed while an event was being handled.
1055///
1056/// A closed set of exactly the sites at which a failure has nowhere else to
1057/// be
1058/// recorded. The printer's action methods record theirs on the
1059/// `ActionRecord` the request minted, and a restoring
1060/// call records its own on the `Intervention` it was
1061/// expiring; those are not sites here, because a second record of them would
1062/// be
1063/// a second version of one fact.
1064#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1065pub enum PortFailureSite {
1066    /// Reading the printer's own state.
1067    #[serde(rename = "printer_snapshot")]
1068    PrinterSnapshot,
1069    /// Reading the job the printer reports it is running.
1070    #[serde(rename = "printer_job")]
1071    PrinterJob,
1072    /// Writing the image the event arrived with.
1073    #[serde(rename = "image_write")]
1074    ImageWrite,
1075    /// Running the supervision turn the event prompted.
1076    #[serde(rename = "supervision_turn")]
1077    SupervisionTurn,
1078    /// Taking a fresh frame from the camera, for a look at the print or for a
1079    /// periodic observation of it.
1080    #[serde(rename = "camera_look")]
1081    CameraLook,
1082    /// Telling the detector that paused the print its detection was handled.
1083    #[serde(rename = "detector_acknowledgement")]
1084    DetectorAcknowledgement,
1085}
1086
1087/// What a filament check found for one print.
1088#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1089pub struct Preflight {
1090    /// When the check was made.
1091    pub checked_at: Timestamp,
1092    /// The file the print is of.
1093    pub file_name: String,
1094    /// What it found for each tool.
1095    pub tools: Vec<PreflightTool>,
1096    /// The worst of the tools' verdicts, in the order insufficient,
1097    /// unverified spool, no analysis, warned, overridden, unchecked, passed.
1098    pub verdict: PreflightVerdict,
1099}
1100
1101/// What a filament check answers.
1102#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1103pub struct PreflightAnswer {
1104    /// What the check found, read now.
1105    pub preflight: Preflight,
1106}
1107
1108/// The filament check before a start was made.
1109#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1110pub struct PreflightCheckedPayload {
1111    /// The start it was made for, when it was made for one.
1112    #[serde(default, skip_serializing_if = "Option::is_none")]
1113    pub action_id: Option<ActionId>,
1114    /// What the check found.
1115    pub preflight: Preflight,
1116}
1117
1118/// What a filament check found for one tool.
1119#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1120pub struct PreflightTool {
1121    /// The margin asked for beyond that, in grams.
1122    #[serde(default, skip_serializing_if = "Option::is_none")]
1123    pub buffer_g: Option<f64>,
1124    /// The loaded spool's marks at the check, oldest first, whatever the
1125    /// verdict, so a spool both short and unverified says both.
1126    #[serde(default, skip_serializing_if = "Option::is_none")]
1127    pub marks: Option<Vec<UnverifiedMark>>,
1128    /// What the spool holds, in grams.
1129    #[serde(default, skip_serializing_if = "Option::is_none")]
1130    pub remaining_g: Option<f64>,
1131    /// What the print needs from it, in grams, when the file's analysis says.
1132    #[serde(default, skip_serializing_if = "Option::is_none")]
1133    pub required_g: Option<f64>,
1134    /// The spool loaded on it, when one is.
1135    #[serde(default, skip_serializing_if = "Option::is_none")]
1136    pub spool_id: Option<SpoolId>,
1137    /// That spool's name, beside its identifier, so a reader need not look it
1138    /// up.
1139    #[serde(default, skip_serializing_if = "Option::is_none")]
1140    pub spool_name: Option<String>,
1141    /// The tool, in the printer's own numbering.
1142    pub tool: i64,
1143    /// What the check found for this tool.
1144    pub verdict: PreflightVerdict,
1145}
1146
1147/// What a filament check found, for one tool or for a whole print.
1148#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1149pub enum PreflightVerdict {
1150    /// The spool holds what the print needs and its buffer.
1151    #[serde(rename = "passed")]
1152    Passed,
1153    /// No tracked spool is loaded on the tool, so nothing was checked.
1154    #[serde(rename = "unchecked")]
1155    Unchecked,
1156    /// The spool holds less than the print needs and its buffer.
1157    #[serde(rename = "insufficient")]
1158    Insufficient,
1159    /// The spool's figure is in doubt.
1160    #[serde(rename = "unverified_spool")]
1161    UnverifiedSpool,
1162    /// `OctoPrint`'s analysis of the file had still not appeared when the
1163    /// start's wait for it elapsed, or has not appeared yet when the check is
1164    /// only read.
1165    #[serde(rename = "no_analysis")]
1166    NoAnalysis,
1167    /// The start went ahead despite the check, because the actor said so.
1168    #[serde(rename = "overridden")]
1169    Overridden,
1170    /// The check is set to warn, and let an insufficient, unverified or
1171    /// unanalysed start go ahead.
1172    #[serde(rename = "warned")]
1173    Warned,
1174}
1175
1176/// The whole vocabulary an actor may ask for, and there is no other.
1177#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1178#[serde(tag = "action")]
1179pub enum PrintAction {
1180    /// Pause the print.
1181    #[serde(rename = "pause")]
1182    Pause {
1183        /// Who is asking.
1184        actor: Actor,
1185        /// Why the actor is asking for this.
1186        reason: String,
1187    },
1188    /// Resume the print.
1189    #[serde(rename = "resume")]
1190    Resume {
1191        /// Who is asking.
1192        actor: Actor,
1193        /// Why the actor is asking for this.
1194        reason: String,
1195    },
1196    /// Cancel the print.
1197    #[serde(rename = "cancel")]
1198    Cancel {
1199        /// Who is asking.
1200        actor: Actor,
1201        /// Why the actor is asking for this.
1202        reason: String,
1203    },
1204    /// Start a print of a named file, with a manifest.
1205    #[serde(rename = "start_print")]
1206    StartPrint {
1207        /// Who is asking.
1208        actor: Actor,
1209        /// The file to print, validated as a name a file API can be asked
1210        /// for.
1211        file_name: FileName,
1212        /// The manifest this print is bounded by.
1213        manifest: JobManifest,
1214        /// Start despite an insufficient or unverified spool, or a file
1215        /// `OctoPrint` has not analysed; the start's own reason is the
1216        /// override's reason.
1217        #[serde(default, skip_serializing_if = "Option::is_none")]
1218        override_filament_check: Option<bool>,
1219        /// Why the actor is asking for this.
1220        reason: String,
1221    },
1222    /// Set the feedrate factor, where one means one hundred percent.
1223    #[serde(rename = "set_feedrate_factor")]
1224    SetFeedrateFactor {
1225        /// Who is asking.
1226        actor: Actor,
1227        /// How long the change stands for, in whole seconds.
1228        #[serde(default, skip_serializing_if = "Option::is_none")]
1229        duration_s: Option<i64>,
1230        /// The multiplier asked for.
1231        factor: f64,
1232        /// Why the actor is asking for this.
1233        reason: String,
1234    },
1235    /// Set the flowrate factor, where one means one hundred percent.
1236    #[serde(rename = "set_flowrate_factor")]
1237    SetFlowrateFactor {
1238        /// Who is asking.
1239        actor: Actor,
1240        /// How long the change stands for, in whole seconds.
1241        #[serde(default, skip_serializing_if = "Option::is_none")]
1242        duration_s: Option<i64>,
1243        /// The multiplier asked for.
1244        factor: f64,
1245        /// Why the actor is asking for this.
1246        reason: String,
1247    },
1248    /// Set one tool's target temperature.
1249    #[serde(rename = "set_tool_target_c")]
1250    SetToolTargetC {
1251        /// Who is asking.
1252        actor: Actor,
1253        /// How long the change stands for, in whole seconds.
1254        #[serde(default, skip_serializing_if = "Option::is_none")]
1255        duration_s: Option<i64>,
1256        /// Why the actor is asking for this.
1257        reason: String,
1258        /// The target temperature asked for, in degrees Celsius.
1259        target_c: f64,
1260        /// The tool, in the printer's own numbering.
1261        tool: i64,
1262    },
1263    /// Set the bed's target temperature.
1264    #[serde(rename = "set_bed_target_c")]
1265    SetBedTargetC {
1266        /// Who is asking.
1267        actor: Actor,
1268        /// How long the change stands for, in whole seconds.
1269        #[serde(default, skip_serializing_if = "Option::is_none")]
1270        duration_s: Option<i64>,
1271        /// Why the actor is asking for this.
1272        reason: String,
1273        /// The target temperature asked for, in degrees Celsius.
1274        target_c: f64,
1275    },
1276    /// Set the part-cooling fan percentage.
1277    #[serde(rename = "set_fan_percent")]
1278    SetFanPercent {
1279        /// Who is asking.
1280        actor: Actor,
1281        /// How long the change stands for, in whole seconds.
1282        #[serde(default, skip_serializing_if = "Option::is_none")]
1283        duration_s: Option<i64>,
1284        /// The percentage asked for.
1285        percent: f64,
1286        /// Why the actor is asking for this.
1287        reason: String,
1288    },
1289    /// Acknowledge a failure event, with a disposition.
1290    #[serde(rename = "acknowledge_failure")]
1291    AcknowledgeFailure {
1292        /// Who is asking.
1293        actor: Actor,
1294        /// What to do next.
1295        disposition: AcknowledgementDisposition,
1296        /// The event being acknowledged.
1297        event_id: EventId,
1298        /// Why the actor is asking for this.
1299        reason: String,
1300    },
1301}
1302
1303/// Everything a supervision turn is given about one print.
1304#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1305pub struct PrintContext {
1306    /// The limits in force, so that the agent can see them before it asks.
1307    pub bounds: EffectiveBounds,
1308    /// The interventions still active.
1309    pub interventions: Vec<Intervention>,
1310    /// The job, when a snapshot could be taken.
1311    #[serde(default, skip_serializing_if = "Option::is_none")]
1312    pub job: Option<JobSnapshot>,
1313    /// The most recent image, when there is one.
1314    #[serde(default, skip_serializing_if = "Option::is_none")]
1315    pub latest_image: Option<ImageRef>,
1316    /// The manifest, when the print has one.
1317    #[serde(default, skip_serializing_if = "Option::is_none")]
1318    pub manifest: Option<JobManifest>,
1319    /// The print itself.
1320    pub print: PrintRecord,
1321    /// The printer, when a snapshot could be taken.
1322    #[serde(default, skip_serializing_if = "Option::is_none")]
1323    pub printer: Option<PrinterSnapshot>,
1324    /// A bounded list of this print's events, newest first.
1325    pub recent_events: Vec<EventRecord>,
1326}
1327
1328/// A print ended.
1329#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1330pub struct PrintEndedPayload {
1331    /// How far into its file it last read, when that was read.
1332    #[serde(default, skip_serializing_if = "Option::is_none")]
1333    pub last_position: Option<FilePosition>,
1334    /// How it ended.
1335    pub outcome: PrintOutcome,
1336    /// Why it ended, in the words the end was recorded with.
1337    pub reason: String,
1338    /// The state the printer was in as it ended.
1339    pub state: PrinterState,
1340}
1341
1342/// A lowercase hyphenated version 7 UUID identifying one print.
1343pub type PrintId = String;
1344
1345/// How a print ended.
1346#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1347pub enum PrintOutcome {
1348    /// It ran to its end.
1349    #[serde(rename = "completed")]
1350    Completed,
1351    /// Somebody cancelled it.
1352    #[serde(rename = "cancelled")]
1353    Cancelled,
1354    /// It stopped on an error.
1355    #[serde(rename = "failed")]
1356    Failed,
1357}
1358
1359/// One print, and the record supervision keys from.
1360///
1361/// The provider's own print id is carried beside this record's identifier
1362/// rather than as it, because a print may be observed before the provider
1363/// that reports it has one.
1364#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1365pub struct PrintRecord {
1366    /// Why it ended, if it has.
1367    #[serde(default, skip_serializing_if = "Option::is_none")]
1368    pub end_reason: Option<String>,
1369    /// When it ended, if it has.
1370    #[serde(default, skip_serializing_if = "Option::is_none")]
1371    pub ended_at: Option<Timestamp>,
1372    /// The file being printed, as the source reported it.
1373    #[serde(default, skip_serializing_if = "Option::is_none")]
1374    pub file_name: Option<String>,
1375    /// This print's identifier, minted by the store.
1376    pub id: PrintId,
1377    /// The longest running time, in whole seconds, the printer reported for
1378    /// this print's job at a read that found it printing rather than paused.
1379    ///
1380    /// The time a job has spent printing only grows, pause or no pause, so a
1381    /// job of this print's file reporting less — by more than
1382    /// `listing::JOB_IDENTITY_TOLERANCE_S` — is a later job. Absent until a
1383    /// read finds the job printing.
1384    #[serde(default, skip_serializing_if = "Option::is_none")]
1385    pub job_print_time_s: Option<i64>,
1386    /// When the printer's job this print records began, as the printer's own
1387    /// running time put it at the latest read that found the job running or
1388    /// paused: the instant of that read, less the running time it reported.
1389    ///
1390    /// `OctoPrint` counts a pause into the running time until the job resumes
1391    /// and then takes the pause back out, so a pause moves this later: a read
1392    /// putting it further than `listing::JOB_IDENTITY_TOLERANCE_S` from the
1393    /// one
1394    /// recorded resets it, rather than it being fixed once. Absent when no
1395    /// read has
1396    /// found this print's job reporting a running time — a print opened by an
1397    /// alert or a start before any read, and every print recorded before this
1398    /// was.
1399    #[serde(default, skip_serializing_if = "Option::is_none")]
1400    pub job_started_at: Option<Timestamp>,
1401    /// Every manifest range this print narrowed to the envelope's.
1402    pub narrowings: Vec<ManifestNarrowing>,
1403    /// When the print was opened.
1404    pub opened_at: Timestamp,
1405    /// The provider's own identifier for the print, whichever provider
1406    /// reported it, when one has.
1407    #[serde(default, skip_serializing_if = "Option::is_none")]
1408    pub provider_print_id: Option<i64>,
1409    /// The state the print is in.
1410    pub state: PrinterState,
1411}
1412
1413/// A print started.
1414#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1415pub struct PrintStartedPayload {
1416    /// The file being printed, when it is known.
1417    #[serde(default, skip_serializing_if = "Option::is_none")]
1418    pub file_name: Option<String>,
1419    /// The spool loaded on each tool as it started.
1420    pub loaded: Vec<LoadedSpool>,
1421    /// The filament check made before it started, when one was.
1422    #[serde(default, skip_serializing_if = "Option::is_none")]
1423    pub preflight: Option<Preflight>,
1424    /// The action that started it; absent when the print was observed rather
1425    /// than started through this system.
1426    #[serde(default, skip_serializing_if = "Option::is_none")]
1427    pub started_by: Option<ActionId>,
1428}
1429
1430/// A print that was already open when spool tracking was installed: it is
1431/// never charged, and this says why it carries no charge.
1432#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1433pub struct PrintUntrackedPayload {
1434    /// Why the print carries no charge.
1435    pub reason: String,
1436}
1437
1438/// A printer, as a source reported it at one instant.
1439#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1440pub struct PrinterSnapshot {
1441    /// The bed heater, when the printer reports one.
1442    #[serde(default, skip_serializing_if = "Option::is_none")]
1443    pub bed: Option<HeaterSnapshot>,
1444    /// The chamber heater, when the printer reports one.
1445    #[serde(default, skip_serializing_if = "Option::is_none")]
1446    pub chamber: Option<HeaterSnapshot>,
1447    /// The state the printer is in.
1448    pub connection: PrinterState,
1449    /// The part-cooling fan, in percent.
1450    #[serde(default, skip_serializing_if = "Option::is_none")]
1451    pub fan_percent: Option<Reported>,
1452    /// The feedrate multiplier, where one means one hundred percent.
1453    #[serde(default, skip_serializing_if = "Option::is_none")]
1454    pub feedrate_factor: Option<Reported>,
1455    /// The flowrate multiplier, where one means one hundred percent.
1456    #[serde(default, skip_serializing_if = "Option::is_none")]
1457    pub flowrate_factor: Option<Reported>,
1458    /// The instant this observation was taken.
1459    pub observed_at: Timestamp,
1460    /// The tool heaters, indexed by tool number.
1461    pub tools: Vec<HeaterSnapshot>,
1462}
1463
1464/// The state a source reports a printer or a print to be in.
1465///
1466/// The `unknown` arm exists so that a state nobody anticipated is recorded
1467/// carrying the source's own word for it rather than lost.
1468#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1469pub enum PrinterState {
1470    /// Connected and idle.
1471    #[serde(rename = "operational")]
1472    Operational,
1473    /// Printing, but paused.
1474    #[serde(rename = "paused")]
1475    Paused,
1476    /// Printing.
1477    #[serde(rename = "printing")]
1478    Printing,
1479    /// Cancelling a print.
1480    #[serde(rename = "cancelling")]
1481    Cancelling,
1482    /// In an error state.
1483    #[serde(rename = "error")]
1484    Error,
1485    /// Not reachable.
1486    #[serde(rename = "offline")]
1487    Offline,
1488    /// A state this vocabulary does not name, in the source's own word for
1489    /// it.
1490    #[serde(rename = "unknown")]
1491    Unknown(String),
1492}
1493
1494/// What the prints read answers: every print, and the one the printer is
1495/// running.
1496///
1497/// An open print's recorded `state` is the state it was opened in, which is
1498/// `printing` whether or not the machine is paused now; what the machine is
1499/// doing is the status read's answer.
1500#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1501pub struct PrintsAnswer {
1502    /// The print the printer's current job belongs to, present exactly when
1503    /// the
1504    /// printer reports a job it is printing or has paused. Absent when the
1505    /// printer could not be read.
1506    #[serde(default, skip_serializing_if = "Option::is_none")]
1507    pub active: Option<PrintId>,
1508    /// Every print this server holds, ended or not, most recently opened
1509    /// first.
1510    pub prints: Vec<PrintRecord>,
1511}
1512
1513/// An inclusive pair of 64-bit floats.
1514#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1515pub struct Range {
1516    /// The highest value the range admits, inclusive.
1517    pub max: f64,
1518    /// The lowest value the range admits, inclusive.
1519    pub min: f64,
1520}
1521
1522/// Bytes exactly as received, base64-encoded.
1523pub type RawBytes = String;
1524
1525/// What became of the prints a file's new tally generation counts.
1526#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1527pub enum RebaselineDisposition {
1528    /// They were accounted as prints this system never saw.
1529    #[serde(rename = "accounted")]
1530    Accounted,
1531    /// It could not be told which of them this system already settled, so the
1532    /// loaded spool was marked unverified rather than charged on a guess.
1533    #[serde(rename = "unverified")]
1534    Unverified,
1535}
1536
1537/// Why a request was refused.
1538///
1539/// Each rejection is a distinct variant, so a consumer distinguishes them by
1540/// matching rather than by reading a message.
1541#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1542pub enum RejectionReason {
1543    /// The value asked for is outside the range allowed for that adjustable.
1544    #[serde(rename = "out_of_bounds")]
1545    OutOfBounds {
1546        /// The adjustable that was asked for.
1547        adjustable: Adjustable,
1548        /// The range that was allowed.
1549        allowed: Range,
1550        /// The value that was asked for.
1551        requested: f64,
1552    },
1553    /// This actor class may not request this action at all.
1554    #[serde(rename = "actor_may_not_request")]
1555    ActorMayNotRequest {
1556        /// The action they asked for.
1557        action: ActionKind,
1558        /// The class of the actor that asked.
1559        actor_class: ActorClass,
1560    },
1561    /// There is no active print to act on.
1562    #[serde(rename = "no_active_print")]
1563    NoActivePrint,
1564    /// The printer is not in a state this action is valid from.
1565    #[serde(rename = "invalid_from_state")]
1566    InvalidFromState {
1567        /// The state the printer is in.
1568        state: PrinterState,
1569    },
1570    /// The agent's minimum interval has not elapsed.
1571    #[serde(rename = "min_interval_not_elapsed")]
1572    MinIntervalNotElapsed {
1573        /// The minimum interval, in whole seconds.
1574        interval_s: i64,
1575        /// How long it has been since the last agent action, in whole
1576        /// seconds.
1577        since_last_s: i64,
1578    },
1579    /// The adjustable is not one this printer has.
1580    #[serde(rename = "unsupported_adjustable")]
1581    UnsupportedAdjustable {
1582        /// The adjustable this printer cannot express.
1583        adjustable: Adjustable,
1584    },
1585    /// A start was refused because the spool on one tool holds less than the
1586    /// print needs and its buffer.
1587    #[serde(rename = "insufficient_filament")]
1588    InsufficientFilament {
1589        /// The margin asked for beyond that, in grams.
1590        buffer_g: f64,
1591        /// What the spool holds, in grams.
1592        remaining_g: f64,
1593        /// What the print needs from it, in grams.
1594        required_g: f64,
1595        /// The spool loaded on it.
1596        spool_id: SpoolId,
1597        /// That spool's name.
1598        spool_name: String,
1599        /// The tool whose spool is short.
1600        tool: i64,
1601        /// The spool's oldest mark, when the short spool is also unverified.
1602        #[serde(default, skip_serializing_if = "Option::is_none")]
1603        unverified: Option<UnverifiedReason>,
1604    },
1605    /// A start was refused because the spool on one tool is unverified.
1606    #[serde(rename = "unverified_spool")]
1607    UnverifiedSpool {
1608        /// The spool's oldest mark.
1609        reason: UnverifiedReason,
1610        /// The spool loaded on it.
1611        spool_id: SpoolId,
1612        /// That spool's name.
1613        spool_name: String,
1614        /// The tool whose spool is unverified.
1615        tool: i64,
1616    },
1617    /// A start was refused because `OctoPrint`'s analysis of the file had not
1618    /// appeared when the start's wait for it elapsed.
1619    #[serde(rename = "analysis_unavailable")]
1620    AnalysisUnavailable {
1621        /// The file that has no analysis.
1622        file_name: String,
1623        /// How long the start waited for one, in whole seconds.
1624        waited_s: i64,
1625    },
1626    /// A spool command could not be carried out.
1627    #[serde(rename = "spool_refused")]
1628    SpoolRefused {
1629        /// Why, in a sentence naming what to do instead.
1630        detail: String,
1631    },
1632}
1633
1634/// A value as a source reported it, flagged when it is outside the
1635/// plausibility range this crate declares for its field.
1636#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1637pub struct Reported {
1638    /// One shape of the contracts, which say nothing more about it.
1639    pub out_of_range: bool,
1640    /// One shape of the contracts, which say nothing more about it.
1641    pub value: f64,
1642}
1643
1644/// A spool's remaining figure was corrected.
1645#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1646pub struct SpoolAdjustedPayload {
1647    /// Who corrected it.
1648    pub actor: Actor,
1649    /// Every mark the correction cleared.
1650    #[serde(default, skip_serializing_if = "Option::is_none")]
1651    pub cleared_marks: Option<Vec<ClearedMark>>,
1652    /// The scale reading the figure was worked out from, when it was given as
1653    /// one.
1654    #[serde(default, skip_serializing_if = "Option::is_none")]
1655    pub gross_g: Option<f64>,
1656    /// Why.
1657    pub reason: String,
1658    /// Its figure after, in grams.
1659    pub remaining_after_g: f64,
1660    /// Its figure before, in grams, after any pending charge it took.
1661    pub remaining_before_g: f64,
1662    /// The spool corrected.
1663    pub spool_id: SpoolId,
1664    /// The pending charges the correction took first, each also its own
1665    /// `filament_consumed` event under its key.
1666    #[serde(default, skip_serializing_if = "Option::is_none")]
1667    pub took_pending: Option<Vec<PendingTaken>>,
1668}
1669
1670/// What one spool's read answers: the spool, and the newest of the printer's
1671/// own events that name it.
1672#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1673pub struct SpoolAnswer {
1674    /// The newest timeline events naming it, newest first.
1675    pub recent: Vec<EventRecord>,
1676    /// The spool, retired or not.
1677    pub spool: SpoolRecord,
1678}
1679
1680/// The whole vocabulary of the spool ledger, and there is no other.
1681#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1682#[serde(tag = "command")]
1683pub enum SpoolCommand {
1684    /// Register a spool.
1685    #[serde(rename = "register_spool")]
1686    RegisterSpool {
1687        /// Who is asking.
1688        actor: Actor,
1689        /// The filament's colour.
1690        #[serde(default, skip_serializing_if = "Option::is_none")]
1691        color: Option<String>,
1692        /// The material's density, in grams per cubic centimetre; the known
1693        /// density of the material when absent, and a material with none
1694        /// known
1695        /// is refused.
1696        #[serde(default, skip_serializing_if = "Option::is_none")]
1697        density_g_cm3: Option<f64>,
1698        /// The filament's diameter, in millimetres; 1.75 when absent.
1699        #[serde(default, skip_serializing_if = "Option::is_none")]
1700        diameter_mm: Option<f64>,
1701        /// The net filament on the spool when full, in grams.
1702        initial_g: f64,
1703        /// The material, such as `PLA`.
1704        material: String,
1705        /// The operator's own label for it.
1706        name: String,
1707        /// Why the actor is asking for this.
1708        reason: String,
1709        /// The filament on it now, in grams; the full figure when absent.
1710        #[serde(default, skip_serializing_if = "Option::is_none")]
1711        remaining_g: Option<f64>,
1712        /// The empty spool's own weight, in grams.
1713        #[serde(default, skip_serializing_if = "Option::is_none")]
1714        tare_g: Option<f64>,
1715    },
1716    /// Load a spool onto a tool.
1717    ///
1718    /// Onto an occupied tool it is a swap, and the spool there is unloaded in
1719    /// the same write. Naming the spool already on the tool confirms it was
1720    /// never swapped, which is accepted only while that tool holds a pending
1721    /// charge or that spool holds a filament-change mark.
1722    #[serde(rename = "load_spool")]
1723    LoadSpool {
1724        /// Who is asking.
1725        actor: Actor,
1726        /// Why the actor is asking for this.
1727        reason: String,
1728        /// The spool to load.
1729        spool_id: SpoolId,
1730        /// The tool, in the printer's own numbering.
1731        tool: i64,
1732    },
1733    /// Unload a spool from its tool.
1734    #[serde(rename = "unload_spool")]
1735    UnloadSpool {
1736        /// Who is asking.
1737        actor: Actor,
1738        /// Why the actor is asking for this.
1739        reason: String,
1740        /// The spool to unload.
1741        spool_id: SpoolId,
1742    },
1743    /// Correct a spool's remaining figure, from exactly one of the two; a
1744    /// command naming both or neither is refused as `spool_refused`.
1745    #[serde(rename = "adjust_spool")]
1746    AdjustSpool {
1747        /// Who is asking.
1748        actor: Actor,
1749        /// A scale reading of the spool, in grams, which needs its tare.
1750        #[serde(default, skip_serializing_if = "Option::is_none")]
1751        gross_g: Option<f64>,
1752        /// Why the actor is asking for this.
1753        reason: String,
1754        /// The filament left, in grams.
1755        #[serde(default, skip_serializing_if = "Option::is_none")]
1756        remaining_g: Option<f64>,
1757        /// The spool to correct.
1758        spool_id: SpoolId,
1759    },
1760    /// Retire a spool, after which it refuses every command.
1761    #[serde(rename = "retire_spool")]
1762    RetireSpool {
1763        /// Who is asking.
1764        actor: Actor,
1765        /// Why the actor is asking for this.
1766        reason: String,
1767        /// The spool to retire.
1768        spool_id: SpoolId,
1769    },
1770}
1771
1772/// What a spool command answers, accepted or refused.
1773///
1774/// A refusal is the same shape and deliberately so: the decision carries the
1775/// grant or the ledger's own reason, and the events are the record the
1776/// refusal
1777/// wrote.
1778#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1779pub struct SpoolCommandAnswer {
1780    /// The decision taken on the command.
1781    pub decision: PolicyDecision,
1782    /// Every event it wrote, in the order they were written.
1783    pub events: Vec<EventRecord>,
1784    /// The spool it was about, as the command left it, when there is one.
1785    #[serde(default, skip_serializing_if = "Option::is_none")]
1786    pub spool: Option<SpoolRecord>,
1787}
1788
1789/// A spool command was refused.
1790#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1791pub struct SpoolCommandRejectedPayload {
1792    /// The command, as it was asked for.
1793    pub command: SpoolCommand,
1794    /// The whole decision, carrying which refusal it was.
1795    pub decision: PolicyDecision,
1796}
1797
1798/// A lowercase hyphenated version 7 UUID identifying one registered spool.
1799pub type SpoolId = String;
1800
1801/// A spool was loaded onto a tool, or confirmed as never having left it.
1802#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1803pub struct SpoolLoadedPayload {
1804    /// Who loaded it.
1805    pub actor: Actor,
1806    /// Every mark the load cleared, on either spool.
1807    #[serde(default, skip_serializing_if = "Option::is_none")]
1808    pub cleared_marks: Option<Vec<ClearedMark>>,
1809    /// Why.
1810    pub reason: String,
1811    /// The spool the load swapped off the tool, when it was occupied.
1812    #[serde(default, skip_serializing_if = "Option::is_none")]
1813    pub replaced: Option<SpoolId>,
1814    /// The spool loaded.
1815    pub spool_id: SpoolId,
1816    /// The pending charges the load took, each also its own
1817    /// `filament_consumed` event under its key.
1818    #[serde(default, skip_serializing_if = "Option::is_none")]
1819    pub took_pending: Option<Vec<PendingTaken>>,
1820    /// The tool, in the printer's own numbering.
1821    pub tool: i64,
1822}
1823
1824/// One spool the operator registered, and what is known of it now.
1825#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1826pub struct SpoolRecord {
1827    /// The filament's colour, when the operator gave one.
1828    #[serde(default, skip_serializing_if = "Option::is_none")]
1829    pub color: Option<String>,
1830    /// The material's density, in grams per cubic centimetre; above zero.
1831    pub density_g_cm3: f64,
1832    /// The filament's diameter, in millimetres; above zero.
1833    pub diameter_mm: f64,
1834    /// This spool's identifier, minted when it was registered.
1835    pub id: SpoolId,
1836    /// The net filament on the spool when it was full, in grams; above zero.
1837    pub initial_g: f64,
1838    /// The tool the spool is loaded on, when it is loaded.
1839    #[serde(default, skip_serializing_if = "Option::is_none")]
1840    pub loaded_on_tool: Option<i64>,
1841    /// The material, as given and trimmed, such as `PLA`; never empty.
1842    pub material: String,
1843    /// The operator's own label for the spool; never empty.
1844    pub name: String,
1845    /// When the spool was registered.
1846    pub registered_at: Timestamp,
1847    /// The filament left, in grams.
1848    ///
1849    /// Always the initial figure less every charge plus every correction. A
1850    /// charge is never refused or clamped, so this may go below zero.
1851    pub remaining_g: f64,
1852    /// When the spool was retired, if it has been.
1853    #[serde(default, skip_serializing_if = "Option::is_none")]
1854    pub retired_at: Option<Timestamp>,
1855    /// The empty spool's own weight, in grams, when the operator gave it.
1856    #[serde(default, skip_serializing_if = "Option::is_none")]
1857    pub tare_g: Option<f64>,
1858    /// How far the remaining figure can be trusted.
1859    pub trust: SpoolTrust,
1860    /// When anything about the spool last changed.
1861    pub updated_at: Timestamp,
1862}
1863
1864/// A spool was registered.
1865#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1866pub struct SpoolRegisteredPayload {
1867    /// Who registered it.
1868    pub actor: Actor,
1869    /// Why.
1870    pub reason: String,
1871    /// The spool, as it was registered.
1872    pub spool: SpoolRecord,
1873}
1874
1875/// A spool was retired.
1876#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1877pub struct SpoolRetiredPayload {
1878    /// Who retired it.
1879    pub actor: Actor,
1880    /// Why.
1881    pub reason: String,
1882    /// Its figure as it was retired, in grams.
1883    pub remaining_g: f64,
1884    /// The spool retired.
1885    pub spool_id: SpoolId,
1886}
1887
1888/// How far a spool's remaining figure can be trusted.
1889#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1890#[serde(tag = "state")]
1891pub enum SpoolTrust {
1892    /// Every charge against the spool was taken exactly.
1893    #[serde(rename = "verified")]
1894    Verified,
1895    /// At least one thing happened that could not be charged exactly.
1896    #[serde(rename = "unverified")]
1897    Unverified {
1898        /// Every reason the figure is in doubt, oldest first, at most one per
1899        /// distinct reason; never empty.
1900        marks: Vec<UnverifiedMark>,
1901    },
1902}
1903
1904/// A spool was unloaded from its tool.
1905#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1906pub struct SpoolUnloadedPayload {
1907    /// Who unloaded it.
1908    pub actor: Actor,
1909    /// Why.
1910    pub reason: String,
1911    /// The spool unloaded.
1912    pub spool_id: SpoolId,
1913    /// The tool it left.
1914    pub tool: i64,
1915}
1916
1917/// A spool was marked as one whose figure is in doubt.
1918#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1919pub struct SpoolUnverifiedPayload {
1920    /// Why its figure is in doubt.
1921    pub reason: UnverifiedReason,
1922    /// The spool marked.
1923    pub spool_id: SpoolId,
1924}
1925
1926/// What the spools read answers.
1927///
1928/// `at_a_glance` is one line per spool, in the order `spools` lists them,
1929/// composed here so that what is left on each spool is the first thing a
1930/// reader of either rendering meets rather than a figure inside a whole
1931/// record. Its name is what puts it first: both renderings walk an answer's
1932/// fields in name order, ahead of `pending` and `spools`.
1933#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1934pub struct SpoolsAnswer {
1935    /// One line per spool in `spools`, in the same order.
1936    pub at_a_glance: Vec<String>,
1937    /// Every charge held after a filament change nobody has confirmed, oldest
1938    /// first: what the next spool loaded on that tool is charged.
1939    pub pending: Vec<PendingCharge>,
1940    /// Every spool, retired ones only when asked for, most recently
1941    /// registered
1942    /// first.
1943    pub spools: Vec<SpoolRecord>,
1944}
1945
1946/// What one restart put back the way it found it.
1947///
1948/// A supervisor that has been restarted adopts whatever the store holds
1949/// rather
1950/// than starting empty, and each of these is one of those adoptions. They are
1951/// recorded rather than merely done, because a print that carried on across a
1952/// restart and one that was started again look identical afterwards unless
1953/// the
1954/// history says which happened.
1955#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1956pub enum StartupOutcome {
1957    /// A print left open was adopted as the print this supervisor is
1958    /// watching.
1959    #[serde(rename = "print_adopted")]
1960    PrintAdopted,
1961    /// A session left open was resumed rather than replaced.
1962    #[serde(rename = "session_resumed")]
1963    SessionResumed {
1964        /// The session's own name in the harness.
1965        session_name: String,
1966    },
1967    /// An intervention already past its expiry was expired on start.
1968    #[serde(rename = "intervention_expired")]
1969    InterventionExpired {
1970        /// What it had changed.
1971        adjustable: Adjustable,
1972        /// The intervention that had outlived its bound.
1973        intervention_id: InterventionId,
1974        /// What became of putting the prior value back.
1975        outcome: InterventionOutcome,
1976    },
1977}
1978
1979/// A supervisor reconciled one thing the store held when it started.
1980#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1981pub struct StartupReconciliationPayload {
1982    /// What was reconciled.
1983    pub outcome: StartupOutcome,
1984    /// The print it is about.
1985    pub print_id: PrintId,
1986}
1987
1988/// What a status read answers.
1989#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1990pub struct StatusAnswer {
1991    /// The bounded interventions still in force.
1992    pub interventions: Vec<Intervention>,
1993    /// The job, when a snapshot could be taken.
1994    #[serde(default, skip_serializing_if = "Option::is_none")]
1995    pub job: Option<JobSnapshot>,
1996    /// The print.
1997    pub print: PrintRecord,
1998    /// The printer, when a snapshot could be taken.
1999    #[serde(default, skip_serializing_if = "Option::is_none")]
2000    pub printer: Option<PrinterSnapshot>,
2001    /// The supervision session watching it, when one is.
2002    #[serde(default, skip_serializing_if = "Option::is_none")]
2003    pub session: Option<SupervisionSession>,
2004}
2005
2006/// The supervision session keyed to one print.
2007#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2008pub struct SupervisionSession {
2009    /// Why it was closed, if it was.
2010    #[serde(default, skip_serializing_if = "Option::is_none")]
2011    pub close_reason: Option<String>,
2012    /// When the session was closed, if it was.
2013    #[serde(default, skip_serializing_if = "Option::is_none")]
2014    pub closed_at: Option<Timestamp>,
2015    /// When the session was opened.
2016    pub created_at: Timestamp,
2017    /// The identity the harness ran the session under.
2018    pub harness_identity: String,
2019    /// When the last turn ran.
2020    pub last_turn_at: Timestamp,
2021    /// The print this session watches.
2022    pub print_id: PrintId,
2023    /// The session's own name in the harness.
2024    pub session_name: String,
2025}
2026
2027/// A supervision session was closed.
2028#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2029pub struct SupervisionSessionClosedPayload {
2030    /// Why it was closed.
2031    pub close_reason: String,
2032    /// The session's own name in the harness.
2033    pub session_name: String,
2034}
2035
2036/// A supervision session was opened.
2037#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2038pub struct SupervisionSessionOpenedPayload {
2039    /// The identity the harness ran it under.
2040    pub harness_identity: String,
2041    /// The session's own name in the harness.
2042    pub session_name: String,
2043}
2044
2045/// What a printer history read answers: one page of the printer's own events.
2046#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2047pub struct TimelineAnswer {
2048    /// The events, newest first.
2049    pub events: Vec<EventRecord>,
2050    /// The cursor for the following, older page, given as `before`; absent
2051    /// when there is none.
2052    #[serde(default, skip_serializing_if = "Option::is_none")]
2053    pub next: Option<EventId>,
2054}
2055
2056/// An instant in UTC, as an RFC 3339 string with a zero offset.
2057pub type Timestamp = String;
2058
2059/// The filament one tool feeds, as a length and, when it is known, a volume.
2060#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2061pub struct ToolFilament {
2062    /// The length of filament fed, in millimetres.
2063    pub length_mm: f64,
2064    /// The tool, in the printer's own numbering.
2065    pub tool: i64,
2066    /// The volume of filament fed, in cubic centimetres, when the source
2067    /// reported one.
2068    #[serde(default, skip_serializing_if = "Option::is_none")]
2069    pub volume_cm3: Option<f64>,
2070}
2071
2072/// A print this system never saw was found in the printer's own tally.
2073#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2074pub struct UnaccountedPrintPayload {
2075    /// Whether it was charged against a spool.
2076    pub charged: bool,
2077    /// The file that was printed.
2078    pub file_name: String,
2079    /// When it was printed, when the printer says.
2080    #[serde(default, skip_serializing_if = "Option::is_none")]
2081    pub printed_at: Option<Timestamp>,
2082    /// Whether it completed.
2083    pub success: bool,
2084}
2085
2086/// One reason a spool's figure is in doubt, and since when.
2087#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2088pub struct UnverifiedMark {
2089    /// Why the figure is in doubt.
2090    pub reason: UnverifiedReason,
2091    /// When the spool was marked for it.
2092    pub since: Timestamp,
2093}
2094
2095/// Why a spool's figure is in doubt.
2096#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2097#[serde(tag = "reason")]
2098pub enum UnverifiedReason {
2099    /// A print ran on the spool's tool and nothing could be charged, because
2100    /// the file had no analysis — an SD-card file, for one.
2101    #[serde(rename = "uncharged_print")]
2102    UnchargedPrint {
2103        /// The print nothing could be charged for.
2104        print_id: PrintId,
2105    },
2106    /// A print this system never saw ran and did not complete.
2107    #[serde(rename = "unaccounted_print")]
2108    UnaccountedPrint {
2109        /// The file that was printed.
2110        file_name: String,
2111        /// When it was printed.
2112        printed_at: Timestamp,
2113    },
2114    /// A filament change was signalled and nobody has said which spool is now
2115    /// loaded. The spool was charged up to a position read when the signal
2116    /// was
2117    /// handled.
2118    #[serde(rename = "filament_change_unconfirmed")]
2119    FilamentChangeUnconfirmed {
2120        /// The print the change was signalled during.
2121        print_id: PrintId,
2122    },
2123    /// A filament change was signalled, nobody has said which spool is now
2124    /// loaded, and no fresh position could be read at the signal: the spool
2125    /// was
2126    /// charged only up to the last recorded position, so its figure may be
2127    /// overstated.
2128    #[serde(rename = "filament_change_unconfirmed_position")]
2129    FilamentChangeUnconfirmedPosition {
2130        /// The print the change was signalled during.
2131        print_id: PrintId,
2132    },
2133    /// A file's print tally was reset, and it cannot be told which of the
2134    /// prints since the re-upload were already settled.
2135    #[serde(rename = "tally_reset")]
2136    TallyReset {
2137        /// The file whose tally was reset.
2138        file_name: String,
2139        /// Where the file lives, in the printer's own vocabulary.
2140        origin: String,
2141    },
2142}
2143
2144/// One payload type declared under a kind name, by the domain that owns it.
2145///
2146/// The table from kind name to payload type, as the server declares it: every
2147/// kind below has exactly one implementation here, and a kind this client
2148/// does not know flows through `EventRecord` untouched.
2149pub trait EventPayloadKind: serde::de::DeserializeOwned {
2150    /// The name this payload's events are written down under.
2151    const KIND: &'static str;
2152}
2153
2154impl EventPayloadKind for ActionExecutedPayload {
2155    const KIND: &'static str = "action_executed";
2156}
2157
2158impl EventPayloadKind for ActionRejectedPayload {
2159    const KIND: &'static str = "action_rejected";
2160}
2161
2162impl EventPayloadKind for ActionRequestedPayload {
2163    const KIND: &'static str = "action_requested";
2164}
2165
2166impl EventPayloadKind for AgentAssessmentPayload {
2167    const KIND: &'static str = "agent_assessment";
2168}
2169
2170impl EventPayloadKind for CameraLookPayload {
2171    const KIND: &'static str = "camera_look";
2172}
2173
2174impl EventPayloadKind for ChargePendedPayload {
2175    const KIND: &'static str = "charge_pended";
2176}
2177
2178impl EventPayloadKind for FilamentChangeSignalledPayload {
2179    const KIND: &'static str = "filament_change_signalled";
2180}
2181
2182impl EventPayloadKind for FilamentConsumedPayload {
2183    const KIND: &'static str = "filament_consumed";
2184}
2185
2186impl EventPayloadKind for FileTallyRebaselinedPayload {
2187    const KIND: &'static str = "file_tally_rebaselined";
2188}
2189
2190impl EventPayloadKind for InterventionExpiredPayload {
2191    const KIND: &'static str = "intervention_expired";
2192}
2193
2194impl EventPayloadKind for MalformedExternalEventPayload {
2195    const KIND: &'static str = "malformed_external_event";
2196}
2197
2198impl EventPayloadKind for ObicoFailureAlertPayload {
2199    const KIND: &'static str = "obico_failure_alert";
2200}
2201
2202impl EventPayloadKind for ObicoPrinterNotificationPayload {
2203    const KIND: &'static str = "obico_printer_notification";
2204}
2205
2206impl EventPayloadKind for OperatorAcknowledgementPayload {
2207    const KIND: &'static str = "operator_acknowledgement";
2208}
2209
2210impl EventPayloadKind for PeriodicObservationPayload {
2211    const KIND: &'static str = "periodic_observation";
2212}
2213
2214impl EventPayloadKind for PortFailurePayload {
2215    const KIND: &'static str = "port_failure";
2216}
2217
2218impl EventPayloadKind for PreflightCheckedPayload {
2219    const KIND: &'static str = "preflight_checked";
2220}
2221
2222impl EventPayloadKind for PrintEndedPayload {
2223    const KIND: &'static str = "print_ended";
2224}
2225
2226impl EventPayloadKind for PrintStartedPayload {
2227    const KIND: &'static str = "print_started";
2228}
2229
2230impl EventPayloadKind for PrintUntrackedPayload {
2231    const KIND: &'static str = "print_untracked";
2232}
2233
2234impl EventPayloadKind for SpoolAdjustedPayload {
2235    const KIND: &'static str = "spool_adjusted";
2236}
2237
2238impl EventPayloadKind for SpoolCommandRejectedPayload {
2239    const KIND: &'static str = "spool_command_rejected";
2240}
2241
2242impl EventPayloadKind for SpoolLoadedPayload {
2243    const KIND: &'static str = "spool_loaded";
2244}
2245
2246impl EventPayloadKind for SpoolRegisteredPayload {
2247    const KIND: &'static str = "spool_registered";
2248}
2249
2250impl EventPayloadKind for SpoolRetiredPayload {
2251    const KIND: &'static str = "spool_retired";
2252}
2253
2254impl EventPayloadKind for SpoolUnloadedPayload {
2255    const KIND: &'static str = "spool_unloaded";
2256}
2257
2258impl EventPayloadKind for SpoolUnverifiedPayload {
2259    const KIND: &'static str = "spool_unverified";
2260}
2261
2262impl EventPayloadKind for StartupReconciliationPayload {
2263    const KIND: &'static str = "startup_reconciliation";
2264}
2265
2266impl EventPayloadKind for SupervisionSessionClosedPayload {
2267    const KIND: &'static str = "supervision_session_closed";
2268}
2269
2270impl EventPayloadKind for SupervisionSessionOpenedPayload {
2271    const KIND: &'static str = "supervision_session_opened";
2272}
2273
2274impl EventPayloadKind for UnaccountedPrintPayload {
2275    const KIND: &'static str = "unaccounted_print";
2276}
2277
2278/// Every kind the server declares a payload type for, in name order.
2279pub const EVENT_KINDS: [&str; 31] = [
2280    "action_executed",
2281    "action_rejected",
2282    "action_requested",
2283    "agent_assessment",
2284    "camera_look",
2285    "charge_pended",
2286    "filament_change_signalled",
2287    "filament_consumed",
2288    "file_tally_rebaselined",
2289    "intervention_expired",
2290    "malformed_external_event",
2291    "obico_failure_alert",
2292    "obico_printer_notification",
2293    "operator_acknowledgement",
2294    "periodic_observation",
2295    "port_failure",
2296    "preflight_checked",
2297    "print_ended",
2298    "print_started",
2299    "print_untracked",
2300    "spool_adjusted",
2301    "spool_command_rejected",
2302    "spool_loaded",
2303    "spool_registered",
2304    "spool_retired",
2305    "spool_unloaded",
2306    "spool_unverified",
2307    "startup_reconciliation",
2308    "supervision_session_closed",
2309    "supervision_session_opened",
2310    "unaccounted_print",
2311];
2312
2313impl EventRecord {
2314    /// The payload as `P`, when this event is under the kind `P` declares.
2315    ///
2316    /// Answers `None` for an event of any other kind — one this client knows
2317    /// or one it does not — and the parse failure for an event under the
2318    /// right kind whose payload is not of the type.
2319    #[must_use]
2320    pub fn payload_as<P: EventPayloadKind>(&self) -> Option<Result<P, serde_json::Error>> {
2321        (self.kind == P::KIND).then(|| serde_json::from_value(self.payload.clone()))
2322    }
2323}
2324
2325impl Client {
2326    /// Call `prints` on the configured supervisor.
2327    ///
2328    /// # Errors
2329    ///
2330    /// Returns `ClientError` when the supervisor could not be reached, when
2331    /// it answered something this client cannot read.
2332    // llmlint: ignore[names_match_behavior] See suppressions.toml.
2333    pub fn prints(&self) -> Result<PrintsAnswer, ClientError> {
2334        let target = String::from("/v1/prints");
2335        let asked: Vec<(String, String)> = Vec::new();
2336        let sending: Option<serde_json::Value> = None;
2337        self.call("GET", &target, &asked, sending.as_ref())
2338    }
2339
2340    /// Call `status` on the configured supervisor.
2341    ///
2342    /// # Errors
2343    ///
2344    /// Returns `ClientError` when the supervisor could not be reached, when
2345    /// it answered something this client cannot read.
2346    pub fn status(&self, print_id: &str) -> Result<StatusAnswer, ClientError> {
2347        let target = format!("/v1/prints/{print_id}/status");
2348        let asked: Vec<(String, String)> = Vec::new();
2349        let sending: Option<serde_json::Value> = None;
2350        self.call("GET", &target, &asked, sending.as_ref())
2351    }
2352
2353    /// Call `context` on the configured supervisor.
2354    ///
2355    /// # Errors
2356    ///
2357    /// Returns `ClientError` when the supervisor could not be reached, when
2358    /// it answered something this client cannot read.
2359    pub fn context(&self, print_id: &str) -> Result<ContextAnswer, ClientError> {
2360        let target = format!("/v1/prints/{print_id}/context");
2361        let asked: Vec<(String, String)> = Vec::new();
2362        let sending: Option<serde_json::Value> = None;
2363        self.call("GET", &target, &asked, sending.as_ref())
2364    }
2365
2366    /// Call `image` on the configured supervisor.
2367    ///
2368    /// # Errors
2369    ///
2370    /// Returns `ClientError` when the supervisor could not be reached, when
2371    /// it answered something this client cannot read.
2372    pub fn image(&self, image_id: &str) -> Result<ImageAnswer, ClientError> {
2373        let target = format!("/v1/images/{image_id}");
2374        let asked: Vec<(String, String)> = Vec::new();
2375        let sending: Option<serde_json::Value> = None;
2376        self.call("GET", &target, &asked, sending.as_ref())
2377    }
2378
2379    /// Call `history` on the configured supervisor.
2380    ///
2381    /// # Errors
2382    ///
2383    /// Returns `ClientError` when the supervisor could not be reached, when
2384    /// it answered something this client cannot read.
2385    pub fn history(
2386        &self,
2387        print_id: &str,
2388        limit: Option<i64>,
2389    ) -> Result<HistoryAnswer, ClientError> {
2390        let target = format!("/v1/prints/{print_id}/history");
2391        let mut asked: Vec<(String, String)> = Vec::new();
2392        if let Some(value) = limit {
2393            asked.push(("limit".to_owned(), value.to_string()));
2394        }
2395        let sending: Option<serde_json::Value> = None;
2396        self.call("GET", &target, &asked, sending.as_ref())
2397    }
2398
2399    /// Call `manifest_get` on the configured supervisor.
2400    ///
2401    /// # Errors
2402    ///
2403    /// Returns `ClientError` when the supervisor could not be reached, when
2404    /// it answered something this client cannot read.
2405    pub fn manifest_get(&self, print_id: &str) -> Result<ManifestAnswer, ClientError> {
2406        let target = format!("/v1/prints/{print_id}/manifest");
2407        let asked: Vec<(String, String)> = Vec::new();
2408        let sending: Option<serde_json::Value> = None;
2409        self.call("GET", &target, &asked, sending.as_ref())
2410    }
2411
2412    /// Call `look` on the configured supervisor.
2413    ///
2414    /// # Errors
2415    ///
2416    /// Returns `ClientError` when the supervisor could not be reached, when
2417    /// it answered something this client cannot read.
2418    pub fn look(&self, print_id: &str, wait_s: Option<i64>) -> Result<Look, ClientError> {
2419        let target = format!("/v1/prints/{print_id}/look");
2420        let mut asked: Vec<(String, String)> = Vec::new();
2421        if let Some(value) = wait_s {
2422            asked.push(("wait_s".to_owned(), value.to_string()));
2423        }
2424        let sending: Option<serde_json::Value> = None;
2425        self.call("GET", &target, &asked, sending.as_ref())
2426    }
2427
2428    /// Call `manifest_set` on the configured supervisor.
2429    ///
2430    /// # Errors
2431    ///
2432    /// Returns `ClientError` when the supervisor could not be reached, when
2433    /// it answered something this client cannot read, when this client
2434    /// refuses the call before making it because the reason is empty, and
2435    /// when the policy refused the action — which arrives as
2436    /// `ClientError::Rejected`, carrying the reason, the value asked for and
2437    /// the range allowed.
2438    pub fn manifest_set(
2439        &self,
2440        print_id: &str,
2441        reason: &str,
2442        manifest: &JobManifest,
2443    ) -> Result<ManifestAnswer, ClientError> {
2444        crate::reason_given(reason)?;
2445        let target = format!("/v1/prints/{print_id}/manifest");
2446        let asked: Vec<(String, String)> = Vec::new();
2447        let mut sending = serde_json::Map::new();
2448        sending.insert("reason".to_owned(), crate::as_value(&reason)?);
2449        sending.insert("manifest".to_owned(), crate::as_value(&manifest)?);
2450        let sending = Some(serde_json::Value::Object(sending));
2451        self.call("PUT", &target, &asked, sending.as_ref())
2452    }
2453
2454    /// Call `spools` on the configured supervisor.
2455    ///
2456    /// # Errors
2457    ///
2458    /// Returns `ClientError` when the supervisor could not be reached, when
2459    /// it answered something this client cannot read.
2460    pub fn spools(&self, include_retired: Option<bool>) -> Result<SpoolsAnswer, ClientError> {
2461        let target = String::from("/v1/spools");
2462        let mut asked: Vec<(String, String)> = Vec::new();
2463        if let Some(value) = include_retired {
2464            asked.push(("include_retired".to_owned(), value.to_string()));
2465        }
2466        let sending: Option<serde_json::Value> = None;
2467        self.call("GET", &target, &asked, sending.as_ref())
2468    }
2469
2470    /// Call `spool` on the configured supervisor.
2471    ///
2472    /// # Errors
2473    ///
2474    /// Returns `ClientError` when the supervisor could not be reached, when
2475    /// it answered something this client cannot read.
2476    pub fn spool(&self, spool_id: &str) -> Result<SpoolAnswer, ClientError> {
2477        let target = format!("/v1/spools/{spool_id}");
2478        let asked: Vec<(String, String)> = Vec::new();
2479        let sending: Option<serde_json::Value> = None;
2480        self.call("GET", &target, &asked, sending.as_ref())
2481    }
2482
2483    /// Call `printer_history` on the configured supervisor.
2484    ///
2485    /// # Errors
2486    ///
2487    /// Returns `ClientError` when the supervisor could not be reached, when
2488    /// it answered something this client cannot read.
2489    pub fn printer_history(
2490        &self,
2491        kinds: Option<Vec<String>>,
2492        since: Option<String>,
2493        until: Option<String>,
2494        spool_id: Option<String>,
2495        before: Option<String>,
2496        limit: Option<i64>,
2497    ) -> Result<TimelineAnswer, ClientError> {
2498        let target = String::from("/v1/history");
2499        let mut asked: Vec<(String, String)> = Vec::new();
2500        for value in kinds.into_iter().flatten() {
2501            asked.push(("kinds".to_owned(), value));
2502        }
2503        if let Some(value) = since {
2504            asked.push(("since".to_owned(), value));
2505        }
2506        if let Some(value) = until {
2507            asked.push(("until".to_owned(), value));
2508        }
2509        if let Some(value) = spool_id {
2510            asked.push(("spool_id".to_owned(), value));
2511        }
2512        if let Some(value) = before {
2513            asked.push(("before".to_owned(), value));
2514        }
2515        if let Some(value) = limit {
2516            asked.push(("limit".to_owned(), value.to_string()));
2517        }
2518        let sending: Option<serde_json::Value> = None;
2519        self.call("GET", &target, &asked, sending.as_ref())
2520    }
2521
2522    /// Call `preflight` on the configured supervisor.
2523    ///
2524    /// # Errors
2525    ///
2526    /// Returns `ClientError` when the supervisor could not be reached, when
2527    /// it answered something this client cannot read.
2528    pub fn preflight(&self, file_name: &str) -> Result<PreflightAnswer, ClientError> {
2529        let target = String::from("/v1/preflight");
2530        let asked: Vec<(String, String)> = vec![("file_name".to_owned(), file_name.to_owned())];
2531        let sending: Option<serde_json::Value> = None;
2532        self.call("GET", &target, &asked, sending.as_ref())
2533    }
2534
2535    /// Call `pause` on the configured supervisor.
2536    ///
2537    /// # Errors
2538    ///
2539    /// Returns `ClientError` when the supervisor could not be reached, when
2540    /// it answered something this client cannot read, when this client
2541    /// refuses the call before making it because the reason is empty, and
2542    /// when the policy refused the action — which arrives as
2543    /// `ClientError::Rejected`, carrying the reason, the value asked for and
2544    /// the range allowed.
2545    pub fn pause(&self, print_id: &str, reason: &str) -> Result<ActionAnswer, ClientError> {
2546        crate::reason_given(reason)?;
2547        let target = format!("/v1/prints/{print_id}/actions/pause");
2548        let asked: Vec<(String, String)> = Vec::new();
2549        let mut sending = serde_json::Map::new();
2550        sending.insert("actor".to_owned(), crate::as_value(&self.actor())?);
2551        sending.insert("reason".to_owned(), crate::as_value(&reason)?);
2552        let sending = Some(serde_json::Value::Object(sending));
2553        self.call("POST", &target, &asked, sending.as_ref())
2554    }
2555
2556    /// Call `resume` on the configured supervisor.
2557    ///
2558    /// # Errors
2559    ///
2560    /// Returns `ClientError` when the supervisor could not be reached, when
2561    /// it answered something this client cannot read, when this client
2562    /// refuses the call before making it because the reason is empty, and
2563    /// when the policy refused the action — which arrives as
2564    /// `ClientError::Rejected`, carrying the reason, the value asked for and
2565    /// the range allowed.
2566    pub fn resume(&self, print_id: &str, reason: &str) -> Result<ActionAnswer, ClientError> {
2567        crate::reason_given(reason)?;
2568        let target = format!("/v1/prints/{print_id}/actions/resume");
2569        let asked: Vec<(String, String)> = Vec::new();
2570        let mut sending = serde_json::Map::new();
2571        sending.insert("actor".to_owned(), crate::as_value(&self.actor())?);
2572        sending.insert("reason".to_owned(), crate::as_value(&reason)?);
2573        let sending = Some(serde_json::Value::Object(sending));
2574        self.call("POST", &target, &asked, sending.as_ref())
2575    }
2576
2577    /// Call `cancel` on the configured supervisor.
2578    ///
2579    /// # Errors
2580    ///
2581    /// Returns `ClientError` when the supervisor could not be reached, when
2582    /// it answered something this client cannot read, when this client
2583    /// refuses the call before making it because the reason is empty, and
2584    /// when the policy refused the action — which arrives as
2585    /// `ClientError::Rejected`, carrying the reason, the value asked for and
2586    /// the range allowed.
2587    pub fn cancel(&self, print_id: &str, reason: &str) -> Result<ActionAnswer, ClientError> {
2588        crate::reason_given(reason)?;
2589        let target = format!("/v1/prints/{print_id}/actions/cancel");
2590        let asked: Vec<(String, String)> = Vec::new();
2591        let mut sending = serde_json::Map::new();
2592        sending.insert("actor".to_owned(), crate::as_value(&self.actor())?);
2593        sending.insert("reason".to_owned(), crate::as_value(&reason)?);
2594        let sending = Some(serde_json::Value::Object(sending));
2595        self.call("POST", &target, &asked, sending.as_ref())
2596    }
2597
2598    /// Call `start_print` on the configured supervisor.
2599    ///
2600    /// # Errors
2601    ///
2602    /// Returns `ClientError` when the supervisor could not be reached, when
2603    /// it answered something this client cannot read, when this client
2604    /// refuses the call before making it because the reason is empty, and
2605    /// when the policy refused the action — which arrives as
2606    /// `ClientError::Rejected`, carrying the reason, the value asked for and
2607    /// the range allowed.
2608    pub fn start_print(
2609        &self,
2610        print_id: &str,
2611        file_name: &FileName,
2612        manifest: &JobManifest,
2613        reason: &str,
2614        override_filament_check: Option<bool>,
2615    ) -> Result<ActionAnswer, ClientError> {
2616        crate::reason_given(reason)?;
2617        let target = format!("/v1/prints/{print_id}/actions/start_print");
2618        let asked: Vec<(String, String)> = Vec::new();
2619        let mut sending = serde_json::Map::new();
2620        sending.insert("actor".to_owned(), crate::as_value(&self.actor())?);
2621        sending.insert("file_name".to_owned(), crate::as_value(&file_name)?);
2622        sending.insert("manifest".to_owned(), crate::as_value(&manifest)?);
2623        if let Some(value) = override_filament_check {
2624            sending.insert(
2625                "override_filament_check".to_owned(),
2626                crate::as_value(&value)?,
2627            );
2628        }
2629        sending.insert("reason".to_owned(), crate::as_value(&reason)?);
2630        let sending = Some(serde_json::Value::Object(sending));
2631        self.call("POST", &target, &asked, sending.as_ref())
2632    }
2633
2634    /// Call `set_feedrate_factor` on the configured supervisor.
2635    ///
2636    /// # Errors
2637    ///
2638    /// Returns `ClientError` when the supervisor could not be reached, when
2639    /// it answered something this client cannot read, when this client
2640    /// refuses the call before making it because the reason is empty, and
2641    /// when the policy refused the action — which arrives as
2642    /// `ClientError::Rejected`, carrying the reason, the value asked for and
2643    /// the range allowed.
2644    pub fn set_feedrate_factor(
2645        &self,
2646        print_id: &str,
2647        factor: f64,
2648        reason: &str,
2649        duration_s: Option<i64>,
2650    ) -> Result<ActionAnswer, ClientError> {
2651        crate::reason_given(reason)?;
2652        let target = format!("/v1/prints/{print_id}/actions/set_feedrate_factor");
2653        let asked: Vec<(String, String)> = Vec::new();
2654        let mut sending = serde_json::Map::new();
2655        sending.insert("actor".to_owned(), crate::as_value(&self.actor())?);
2656        if let Some(value) = duration_s {
2657            sending.insert("duration_s".to_owned(), crate::as_value(&value)?);
2658        }
2659        sending.insert("factor".to_owned(), crate::as_value(&factor)?);
2660        sending.insert("reason".to_owned(), crate::as_value(&reason)?);
2661        let sending = Some(serde_json::Value::Object(sending));
2662        self.call("POST", &target, &asked, sending.as_ref())
2663    }
2664
2665    /// Call `set_flowrate_factor` on the configured supervisor.
2666    ///
2667    /// # Errors
2668    ///
2669    /// Returns `ClientError` when the supervisor could not be reached, when
2670    /// it answered something this client cannot read, when this client
2671    /// refuses the call before making it because the reason is empty, and
2672    /// when the policy refused the action — which arrives as
2673    /// `ClientError::Rejected`, carrying the reason, the value asked for and
2674    /// the range allowed.
2675    pub fn set_flowrate_factor(
2676        &self,
2677        print_id: &str,
2678        factor: f64,
2679        reason: &str,
2680        duration_s: Option<i64>,
2681    ) -> Result<ActionAnswer, ClientError> {
2682        crate::reason_given(reason)?;
2683        let target = format!("/v1/prints/{print_id}/actions/set_flowrate_factor");
2684        let asked: Vec<(String, String)> = Vec::new();
2685        let mut sending = serde_json::Map::new();
2686        sending.insert("actor".to_owned(), crate::as_value(&self.actor())?);
2687        if let Some(value) = duration_s {
2688            sending.insert("duration_s".to_owned(), crate::as_value(&value)?);
2689        }
2690        sending.insert("factor".to_owned(), crate::as_value(&factor)?);
2691        sending.insert("reason".to_owned(), crate::as_value(&reason)?);
2692        let sending = Some(serde_json::Value::Object(sending));
2693        self.call("POST", &target, &asked, sending.as_ref())
2694    }
2695
2696    /// Call `set_tool_target_c` on the configured supervisor.
2697    ///
2698    /// # Errors
2699    ///
2700    /// Returns `ClientError` when the supervisor could not be reached, when
2701    /// it answered something this client cannot read, when this client
2702    /// refuses the call before making it because the reason is empty, and
2703    /// when the policy refused the action — which arrives as
2704    /// `ClientError::Rejected`, carrying the reason, the value asked for and
2705    /// the range allowed.
2706    pub fn set_tool_target_c(
2707        &self,
2708        print_id: &str,
2709        reason: &str,
2710        target_c: f64,
2711        tool: i64,
2712        duration_s: Option<i64>,
2713    ) -> Result<ActionAnswer, ClientError> {
2714        crate::reason_given(reason)?;
2715        let target = format!("/v1/prints/{print_id}/actions/set_tool_target_c");
2716        let asked: Vec<(String, String)> = Vec::new();
2717        let mut sending = serde_json::Map::new();
2718        sending.insert("actor".to_owned(), crate::as_value(&self.actor())?);
2719        if let Some(value) = duration_s {
2720            sending.insert("duration_s".to_owned(), crate::as_value(&value)?);
2721        }
2722        sending.insert("reason".to_owned(), crate::as_value(&reason)?);
2723        sending.insert("target_c".to_owned(), crate::as_value(&target_c)?);
2724        sending.insert("tool".to_owned(), crate::as_value(&tool)?);
2725        let sending = Some(serde_json::Value::Object(sending));
2726        self.call("POST", &target, &asked, sending.as_ref())
2727    }
2728
2729    /// Call `set_bed_target_c` on the configured supervisor.
2730    ///
2731    /// # Errors
2732    ///
2733    /// Returns `ClientError` when the supervisor could not be reached, when
2734    /// it answered something this client cannot read, when this client
2735    /// refuses the call before making it because the reason is empty, and
2736    /// when the policy refused the action — which arrives as
2737    /// `ClientError::Rejected`, carrying the reason, the value asked for and
2738    /// the range allowed.
2739    pub fn set_bed_target_c(
2740        &self,
2741        print_id: &str,
2742        reason: &str,
2743        target_c: f64,
2744        duration_s: Option<i64>,
2745    ) -> Result<ActionAnswer, ClientError> {
2746        crate::reason_given(reason)?;
2747        let target = format!("/v1/prints/{print_id}/actions/set_bed_target_c");
2748        let asked: Vec<(String, String)> = Vec::new();
2749        let mut sending = serde_json::Map::new();
2750        sending.insert("actor".to_owned(), crate::as_value(&self.actor())?);
2751        if let Some(value) = duration_s {
2752            sending.insert("duration_s".to_owned(), crate::as_value(&value)?);
2753        }
2754        sending.insert("reason".to_owned(), crate::as_value(&reason)?);
2755        sending.insert("target_c".to_owned(), crate::as_value(&target_c)?);
2756        let sending = Some(serde_json::Value::Object(sending));
2757        self.call("POST", &target, &asked, sending.as_ref())
2758    }
2759
2760    /// Call `set_fan_percent` on the configured supervisor.
2761    ///
2762    /// # Errors
2763    ///
2764    /// Returns `ClientError` when the supervisor could not be reached, when
2765    /// it answered something this client cannot read, when this client
2766    /// refuses the call before making it because the reason is empty, and
2767    /// when the policy refused the action — which arrives as
2768    /// `ClientError::Rejected`, carrying the reason, the value asked for and
2769    /// the range allowed.
2770    pub fn set_fan_percent(
2771        &self,
2772        print_id: &str,
2773        percent: f64,
2774        reason: &str,
2775        duration_s: Option<i64>,
2776    ) -> Result<ActionAnswer, ClientError> {
2777        crate::reason_given(reason)?;
2778        let target = format!("/v1/prints/{print_id}/actions/set_fan_percent");
2779        let asked: Vec<(String, String)> = Vec::new();
2780        let mut sending = serde_json::Map::new();
2781        sending.insert("actor".to_owned(), crate::as_value(&self.actor())?);
2782        if let Some(value) = duration_s {
2783            sending.insert("duration_s".to_owned(), crate::as_value(&value)?);
2784        }
2785        sending.insert("percent".to_owned(), crate::as_value(&percent)?);
2786        sending.insert("reason".to_owned(), crate::as_value(&reason)?);
2787        let sending = Some(serde_json::Value::Object(sending));
2788        self.call("POST", &target, &asked, sending.as_ref())
2789    }
2790
2791    /// Call `acknowledge_failure` on the configured supervisor.
2792    ///
2793    /// # Errors
2794    ///
2795    /// Returns `ClientError` when the supervisor could not be reached, when
2796    /// it answered something this client cannot read, when this client
2797    /// refuses the call before making it because the reason is empty, and
2798    /// when the policy refused the action — which arrives as
2799    /// `ClientError::Rejected`, carrying the reason, the value asked for and
2800    /// the range allowed.
2801    pub fn acknowledge_failure(
2802        &self,
2803        print_id: &str,
2804        disposition: &AcknowledgementDisposition,
2805        event_id: &EventId,
2806        reason: &str,
2807    ) -> Result<ActionAnswer, ClientError> {
2808        crate::reason_given(reason)?;
2809        let target = format!("/v1/prints/{print_id}/actions/acknowledge_failure");
2810        let asked: Vec<(String, String)> = Vec::new();
2811        let mut sending = serde_json::Map::new();
2812        sending.insert("actor".to_owned(), crate::as_value(&self.actor())?);
2813        sending.insert("disposition".to_owned(), crate::as_value(&disposition)?);
2814        sending.insert("event_id".to_owned(), crate::as_value(&event_id)?);
2815        sending.insert("reason".to_owned(), crate::as_value(&reason)?);
2816        let sending = Some(serde_json::Value::Object(sending));
2817        self.call("POST", &target, &asked, sending.as_ref())
2818    }
2819
2820    /// Call `register_spool` on the configured supervisor.
2821    ///
2822    /// # Errors
2823    ///
2824    /// Returns `ClientError` when the supervisor could not be reached, when
2825    /// it answered something this client cannot read, when this client
2826    /// refuses the call before making it because the reason is empty, and
2827    /// when the policy refused the action — which arrives as
2828    /// `ClientError::Rejected`, carrying the reason, the value asked for and
2829    /// the range allowed.
2830    // One argument per value the operation declares, as every client takes them; suppressions.toml has the reason.
2831    #[expect(clippy::too_many_arguments)]
2832    pub fn register_spool(
2833        &self,
2834        initial_g: f64,
2835        material: &str,
2836        name: &str,
2837        reason: &str,
2838        color: Option<String>,
2839        density_g_cm3: Option<f64>,
2840        diameter_mm: Option<f64>,
2841        remaining_g: Option<f64>,
2842        tare_g: Option<f64>,
2843    ) -> Result<SpoolCommandAnswer, ClientError> {
2844        crate::reason_given(reason)?;
2845        let target = String::from("/v1/spools/actions/register_spool");
2846        let asked: Vec<(String, String)> = Vec::new();
2847        let mut sending = serde_json::Map::new();
2848        sending.insert("actor".to_owned(), crate::as_value(&self.actor())?);
2849        if let Some(value) = color {
2850            sending.insert("color".to_owned(), crate::as_value(&value)?);
2851        }
2852        if let Some(value) = density_g_cm3 {
2853            sending.insert("density_g_cm3".to_owned(), crate::as_value(&value)?);
2854        }
2855        if let Some(value) = diameter_mm {
2856            sending.insert("diameter_mm".to_owned(), crate::as_value(&value)?);
2857        }
2858        sending.insert("initial_g".to_owned(), crate::as_value(&initial_g)?);
2859        sending.insert("material".to_owned(), crate::as_value(&material)?);
2860        sending.insert("name".to_owned(), crate::as_value(&name)?);
2861        sending.insert("reason".to_owned(), crate::as_value(&reason)?);
2862        if let Some(value) = remaining_g {
2863            sending.insert("remaining_g".to_owned(), crate::as_value(&value)?);
2864        }
2865        if let Some(value) = tare_g {
2866            sending.insert("tare_g".to_owned(), crate::as_value(&value)?);
2867        }
2868        let sending = Some(serde_json::Value::Object(sending));
2869        self.call("POST", &target, &asked, sending.as_ref())
2870    }
2871
2872    /// Call `load_spool` on the configured supervisor.
2873    ///
2874    /// # Errors
2875    ///
2876    /// Returns `ClientError` when the supervisor could not be reached, when
2877    /// it answered something this client cannot read, when this client
2878    /// refuses the call before making it because the reason is empty, and
2879    /// when the policy refused the action — which arrives as
2880    /// `ClientError::Rejected`, carrying the reason, the value asked for and
2881    /// the range allowed.
2882    pub fn load_spool(
2883        &self,
2884        spool_id: &str,
2885        reason: &str,
2886        tool: i64,
2887    ) -> Result<SpoolCommandAnswer, ClientError> {
2888        crate::reason_given(reason)?;
2889        let target = format!("/v1/spools/{spool_id}/actions/load_spool");
2890        let asked: Vec<(String, String)> = Vec::new();
2891        let mut sending = serde_json::Map::new();
2892        sending.insert("actor".to_owned(), crate::as_value(&self.actor())?);
2893        sending.insert("reason".to_owned(), crate::as_value(&reason)?);
2894        sending.insert("tool".to_owned(), crate::as_value(&tool)?);
2895        let sending = Some(serde_json::Value::Object(sending));
2896        self.call("POST", &target, &asked, sending.as_ref())
2897    }
2898
2899    /// Call `unload_spool` on the configured supervisor.
2900    ///
2901    /// # Errors
2902    ///
2903    /// Returns `ClientError` when the supervisor could not be reached, when
2904    /// it answered something this client cannot read, when this client
2905    /// refuses the call before making it because the reason is empty, and
2906    /// when the policy refused the action — which arrives as
2907    /// `ClientError::Rejected`, carrying the reason, the value asked for and
2908    /// the range allowed.
2909    pub fn unload_spool(
2910        &self,
2911        spool_id: &str,
2912        reason: &str,
2913    ) -> Result<SpoolCommandAnswer, ClientError> {
2914        crate::reason_given(reason)?;
2915        let target = format!("/v1/spools/{spool_id}/actions/unload_spool");
2916        let asked: Vec<(String, String)> = Vec::new();
2917        let mut sending = serde_json::Map::new();
2918        sending.insert("actor".to_owned(), crate::as_value(&self.actor())?);
2919        sending.insert("reason".to_owned(), crate::as_value(&reason)?);
2920        let sending = Some(serde_json::Value::Object(sending));
2921        self.call("POST", &target, &asked, sending.as_ref())
2922    }
2923
2924    /// Call `adjust_spool` on the configured supervisor.
2925    ///
2926    /// # Errors
2927    ///
2928    /// Returns `ClientError` when the supervisor could not be reached, when
2929    /// it answered something this client cannot read, when this client
2930    /// refuses the call before making it because the reason is empty, and
2931    /// when the policy refused the action — which arrives as
2932    /// `ClientError::Rejected`, carrying the reason, the value asked for and
2933    /// the range allowed.
2934    pub fn adjust_spool(
2935        &self,
2936        spool_id: &str,
2937        reason: &str,
2938        gross_g: Option<f64>,
2939        remaining_g: Option<f64>,
2940    ) -> Result<SpoolCommandAnswer, ClientError> {
2941        crate::reason_given(reason)?;
2942        let target = format!("/v1/spools/{spool_id}/actions/adjust_spool");
2943        let asked: Vec<(String, String)> = Vec::new();
2944        let mut sending = serde_json::Map::new();
2945        sending.insert("actor".to_owned(), crate::as_value(&self.actor())?);
2946        if let Some(value) = gross_g {
2947            sending.insert("gross_g".to_owned(), crate::as_value(&value)?);
2948        }
2949        sending.insert("reason".to_owned(), crate::as_value(&reason)?);
2950        if let Some(value) = remaining_g {
2951            sending.insert("remaining_g".to_owned(), crate::as_value(&value)?);
2952        }
2953        let sending = Some(serde_json::Value::Object(sending));
2954        self.call("POST", &target, &asked, sending.as_ref())
2955    }
2956
2957    /// Call `retire_spool` on the configured supervisor.
2958    ///
2959    /// # Errors
2960    ///
2961    /// Returns `ClientError` when the supervisor could not be reached, when
2962    /// it answered something this client cannot read, when this client
2963    /// refuses the call before making it because the reason is empty, and
2964    /// when the policy refused the action — which arrives as
2965    /// `ClientError::Rejected`, carrying the reason, the value asked for and
2966    /// the range allowed.
2967    pub fn retire_spool(
2968        &self,
2969        spool_id: &str,
2970        reason: &str,
2971    ) -> Result<SpoolCommandAnswer, ClientError> {
2972        crate::reason_given(reason)?;
2973        let target = format!("/v1/spools/{spool_id}/actions/retire_spool");
2974        let asked: Vec<(String, String)> = Vec::new();
2975        let mut sending = serde_json::Map::new();
2976        sending.insert("actor".to_owned(), crate::as_value(&self.actor())?);
2977        sending.insert("reason".to_owned(), crate::as_value(&reason)?);
2978        let sending = Some(serde_json::Value::Object(sending));
2979        self.call("POST", &target, &asked, sending.as_ref())
2980    }
2981}