Skip to main content

printobserver_types/
adjustable.rs

1//! The closed set of things an adjustment may change.
2
3use core::fmt;
4use core::str::FromStr;
5use std::borrow::Cow;
6
7use schemars::{JsonSchema, Schema, SchemaGenerator, json_schema};
8use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Error as _};
9
10/// The prefix the tool-target adjustable spells its tool number after.
11const TOOL_TARGET: &str = "tool_target";
12
13/// One thing an adjustment may change.
14///
15/// Serialized as a string rather than an object, because this is the key type
16/// of the manifest's, the envelope's and the effective bounds' maps, and a JSON
17/// object key is a string. The spellings are `feedrate`, `flowrate`,
18/// `bed_target`, `fan`, and `tool_target:<number>`.
19#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
20pub enum Adjustable {
21    /// The feedrate factor.
22    Feedrate,
23    /// The flowrate factor.
24    Flowrate,
25    /// One tool's target temperature, carrying that tool's number.
26    ToolTarget {
27        /// The tool number, in the printer's own numbering.
28        tool: i64,
29    },
30    /// The bed's target temperature.
31    BedTarget,
32    /// The part-cooling fan.
33    Fan,
34}
35
36/// Why a string is no adjustable.
37#[derive(Debug, Clone, PartialEq, Eq)]
38pub struct AdjustableError {
39    /// What is wrong with the string.
40    detail: String,
41}
42
43impl AdjustableError {
44    /// What is wrong with the string.
45    #[must_use]
46    pub fn detail(&self) -> &str {
47        &self.detail
48    }
49}
50
51impl fmt::Display for AdjustableError {
52    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
53        write!(formatter, "not an adjustable: {}", self.detail)
54    }
55}
56
57impl core::error::Error for AdjustableError {}
58
59impl fmt::Display for Adjustable {
60    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
61        match self {
62            Self::Feedrate => formatter.write_str("feedrate"),
63            Self::Flowrate => formatter.write_str("flowrate"),
64            Self::ToolTarget { tool } => write!(formatter, "{TOOL_TARGET}:{tool}"),
65            Self::BedTarget => formatter.write_str("bed_target"),
66            Self::Fan => formatter.write_str("fan"),
67        }
68    }
69}
70
71impl FromStr for Adjustable {
72    type Err = AdjustableError;
73
74    fn from_str(text: &str) -> Result<Self, Self::Err> {
75        match text {
76            "feedrate" => return Ok(Self::Feedrate),
77            "flowrate" => return Ok(Self::Flowrate),
78            "bed_target" => return Ok(Self::BedTarget),
79            "fan" => return Ok(Self::Fan),
80            _ => {}
81        }
82        let Some(number) = text.strip_prefix(concat!("tool_target", ":")) else {
83            return Err(AdjustableError {
84                detail: format!("{text:?} names no adjustable"),
85            });
86        };
87        number
88            .parse()
89            .map(|tool| Self::ToolTarget { tool })
90            .map_err(|error| AdjustableError {
91                detail: format!("{text:?} carries no tool number: {error}"),
92            })
93    }
94}
95
96impl Serialize for Adjustable {
97    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
98        serializer.collect_str(self)
99    }
100}
101
102impl<'de> Deserialize<'de> for Adjustable {
103    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
104        let text = String::deserialize(deserializer)?;
105        text.parse().map_err(D::Error::custom)
106    }
107}
108
109impl JsonSchema for Adjustable {
110    fn schema_name() -> Cow<'static, str> {
111        Cow::Borrowed("Adjustable")
112    }
113
114    fn json_schema(_generator: &mut SchemaGenerator) -> Schema {
115        json_schema!({
116            "type": "string",
117            "title": "Adjustable",
118            "description": "One thing an adjustment may change.",
119            "pattern": "^(feedrate|flowrate|bed_target|fan|tool_target:-?[0-9]+)$"
120        })
121    }
122}