use core::fmt;
use core::str::FromStr;
use std::borrow::Cow;
use schemars::{JsonSchema, Schema, SchemaGenerator, json_schema};
use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Error as _};
const TOOL_TARGET: &str = "tool_target";
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Adjustable {
Feedrate,
Flowrate,
ToolTarget {
tool: i64,
},
BedTarget,
Fan,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AdjustableError {
detail: String,
}
impl AdjustableError {
#[must_use]
pub fn detail(&self) -> &str {
&self.detail
}
}
impl fmt::Display for AdjustableError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(formatter, "not an adjustable: {}", self.detail)
}
}
impl core::error::Error for AdjustableError {}
impl fmt::Display for Adjustable {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Feedrate => formatter.write_str("feedrate"),
Self::Flowrate => formatter.write_str("flowrate"),
Self::ToolTarget { tool } => write!(formatter, "{TOOL_TARGET}:{tool}"),
Self::BedTarget => formatter.write_str("bed_target"),
Self::Fan => formatter.write_str("fan"),
}
}
}
impl FromStr for Adjustable {
type Err = AdjustableError;
fn from_str(text: &str) -> Result<Self, Self::Err> {
match text {
"feedrate" => return Ok(Self::Feedrate),
"flowrate" => return Ok(Self::Flowrate),
"bed_target" => return Ok(Self::BedTarget),
"fan" => return Ok(Self::Fan),
_ => {}
}
let Some(number) = text.strip_prefix(concat!("tool_target", ":")) else {
return Err(AdjustableError {
detail: format!("{text:?} names no adjustable"),
});
};
number
.parse()
.map(|tool| Self::ToolTarget { tool })
.map_err(|error| AdjustableError {
detail: format!("{text:?} carries no tool number: {error}"),
})
}
}
impl Serialize for Adjustable {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.collect_str(self)
}
}
impl<'de> Deserialize<'de> for Adjustable {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
let text = String::deserialize(deserializer)?;
text.parse().map_err(D::Error::custom)
}
}
impl JsonSchema for Adjustable {
fn schema_name() -> Cow<'static, str> {
Cow::Borrowed("Adjustable")
}
fn json_schema(_generator: &mut SchemaGenerator) -> Schema {
json_schema!({
"type": "string",
"title": "Adjustable",
"description": "One thing an adjustment may change.",
"pattern": "^(feedrate|flowrate|bed_target|fan|tool_target:-?[0-9]+)$"
})
}
}