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