1use thiserror::Error;
4
5pub type Result<T> = std::result::Result<T, Error>;
7
8pub(crate) fn check_range(name: &str, value: f64, min: f64, max: f64) -> Result<()> {
11 if value >= min && value <= max {
12 Ok(())
13 } else {
14 Err(Error::OutOfRange {
15 parameter: name.to_string(),
16 min: Some(min),
17 max: Some(max),
18 got: value,
19 range: format!("in [{min}, {max}]"),
20 })
21 }
22}
23
24pub(crate) fn check_at_least(name: &str, value: f64, min: f64) -> Result<()> {
27 if value.is_finite() && value >= min {
28 Ok(())
29 } else {
30 Err(Error::OutOfRange {
31 parameter: name.to_string(),
32 min: Some(min),
33 max: None,
34 got: value,
35 range: format!("a finite number >= {min}"),
36 })
37 }
38}
39
40#[derive(Debug, Clone, PartialEq, Error)]
45#[non_exhaustive]
46pub enum Error {
47 #[error("{parameter} must be {range}, got {got}")]
50 OutOfRange {
51 parameter: String,
52 min: Option<f64>,
53 max: Option<f64>,
54 got: f64,
55 range: String,
56 },
57
58 #[error("{parameter}: {message}")]
61 InvalidOption { parameter: String, message: String },
62
63 #[error("unknown {parameter} '{got}'; expected one of {}", expected.join(", "))]
66 UnknownOption {
67 parameter: String,
68 got: String,
69 expected: Vec<String>,
70 },
71
72 #[error("{message}")]
75 MalformedInput {
76 parameter: Option<String>,
77 message: String,
78 },
79
80 #[error("cancelled by the caller")]
82 Cancelled,
83
84 #[error("Invalid geometry: {message}")]
87 InvalidGeometry { id: Option<String>, message: String },
88
89 #[error(
92 "the id '{id}' is given twice, at positions {first} and {second} of geometries \
93 (counting from 0); placements name geometries by id, so every geometry needs \
94 its own"
95 )]
96 DuplicateId {
97 id: String,
98 first: usize,
99 second: usize,
100 },
101
102 #[error("Invalid boundary: {message}")]
105 InvalidBoundary {
106 parameter: Option<String>,
107 message: String,
108 },
109
110 #[error("Internal error: {0}")]
112 Internal(String),
113}
114
115impl Error {
116 pub fn code(&self) -> &'static str {
121 match self {
122 Error::OutOfRange { .. } => "parameter_out_of_range",
123 Error::InvalidOption { .. } => "invalid_option",
124 Error::UnknownOption { .. } => "unknown_option",
125 Error::MalformedInput { .. } => "malformed_input",
126 Error::Cancelled => "cancelled",
127 Error::InvalidGeometry { .. } => "invalid_geometry",
128 Error::DuplicateId { .. } => "duplicate_id",
129 Error::InvalidBoundary { .. } => "invalid_boundary",
130 Error::Internal(_) => "internal",
131 }
132 }
133
134 pub fn invalid_option(parameter: &str, message: String) -> Self {
136 Error::InvalidOption {
137 parameter: parameter.to_string(),
138 message,
139 }
140 }
141
142 pub fn invalid_geometry(id: Option<&str>, message: String) -> Self {
144 Error::InvalidGeometry {
145 id: id.map(str::to_string),
146 message,
147 }
148 }
149
150 pub fn malformed(parameter: Option<&str>, message: String) -> Self {
152 Error::MalformedInput {
153 parameter: parameter.map(str::to_string),
154 message,
155 }
156 }
157
158 pub fn invalid_boundary(parameter: Option<&str>, message: String) -> Self {
160 Error::InvalidBoundary {
161 parameter: parameter.map(str::to_string),
162 message,
163 }
164 }
165}