1#![forbid(unsafe_code)]
2#![doc = include_str!("../README.md")]
3
4use core::{fmt, str::FromStr};
5use std::error::Error;
6
7use use_rating::VoltageRating;
8
9pub mod prelude {
11 pub use crate::{
12 CapacitanceValue, CapacitanceValueError, CapacitorKind, CapacitorKindParseError,
13 CapacitorPolarity, CapacitorPolarityParseError, CapacitorSpec,
14 };
15}
16
17#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
19pub struct CapacitanceValue {
20 farads: f64,
21}
22
23impl CapacitanceValue {
24 pub fn new_farads(value: f64) -> Result<Self, CapacitanceValueError> {
30 if !value.is_finite() {
31 return Err(CapacitanceValueError::NonFinite);
32 }
33
34 if value < 0.0 {
35 return Err(CapacitanceValueError::Negative);
36 }
37
38 Ok(Self { farads: value })
39 }
40
41 #[must_use]
43 pub const fn farads(self) -> f64 {
44 self.farads
45 }
46}
47
48impl fmt::Display for CapacitanceValue {
49 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
50 write!(formatter, "{} F", self.farads)
51 }
52}
53
54#[derive(Clone, Copy, Debug, Eq, PartialEq)]
56pub enum CapacitanceValueError {
57 NonFinite,
59 Negative,
61}
62
63impl fmt::Display for CapacitanceValueError {
64 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
65 match self {
66 Self::NonFinite => formatter.write_str("capacitance must be finite"),
67 Self::Negative => formatter.write_str("capacitance cannot be negative"),
68 }
69 }
70}
71
72impl Error for CapacitanceValueError {}
73
74#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
76pub enum CapacitorKind {
77 Ceramic,
78 Electrolytic,
79 Tantalum,
80 Film,
81 Supercapacitor,
82 Variable,
83 Unknown,
84 Custom(String),
85}
86
87impl fmt::Display for CapacitorKind {
88 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
89 formatter.write_str(match self {
90 Self::Ceramic => "ceramic",
91 Self::Electrolytic => "electrolytic",
92 Self::Tantalum => "tantalum",
93 Self::Film => "film",
94 Self::Supercapacitor => "supercapacitor",
95 Self::Variable => "variable",
96 Self::Unknown => "unknown",
97 Self::Custom(value) => value.as_str(),
98 })
99 }
100}
101
102impl FromStr for CapacitorKind {
103 type Err = CapacitorKindParseError;
104
105 fn from_str(value: &str) -> Result<Self, Self::Err> {
106 let trimmed = value.trim();
107 if trimmed.is_empty() {
108 return Err(CapacitorKindParseError::Empty);
109 }
110
111 match normalized_token(trimmed).as_str() {
112 "ceramic" => Ok(Self::Ceramic),
113 "electrolytic" => Ok(Self::Electrolytic),
114 "tantalum" => Ok(Self::Tantalum),
115 "film" => Ok(Self::Film),
116 "supercapacitor" => Ok(Self::Supercapacitor),
117 "variable" => Ok(Self::Variable),
118 "unknown" => Ok(Self::Unknown),
119 _ => Ok(Self::Custom(trimmed.to_string())),
120 }
121 }
122}
123
124#[derive(Clone, Copy, Debug, Eq, PartialEq)]
126pub enum CapacitorKindParseError {
127 Empty,
129}
130
131impl fmt::Display for CapacitorKindParseError {
132 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
133 match self {
134 Self::Empty => formatter.write_str("capacitor kind cannot be empty"),
135 }
136 }
137}
138
139impl Error for CapacitorKindParseError {}
140
141#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
143pub enum CapacitorPolarity {
144 Polarized,
145 NonPolarized,
146 Unknown,
147}
148
149impl fmt::Display for CapacitorPolarity {
150 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
151 formatter.write_str(match self {
152 Self::Polarized => "polarized",
153 Self::NonPolarized => "non-polarized",
154 Self::Unknown => "unknown",
155 })
156 }
157}
158
159impl FromStr for CapacitorPolarity {
160 type Err = CapacitorPolarityParseError;
161
162 fn from_str(value: &str) -> Result<Self, Self::Err> {
163 let trimmed = value.trim();
164 if trimmed.is_empty() {
165 return Err(CapacitorPolarityParseError::Empty);
166 }
167
168 match normalized_token(trimmed).as_str() {
169 "polarized" => Ok(Self::Polarized),
170 "non-polarized" | "nonpolarized" => Ok(Self::NonPolarized),
171 "unknown" => Ok(Self::Unknown),
172 _ => Err(CapacitorPolarityParseError::Unknown),
173 }
174 }
175}
176
177#[derive(Clone, Copy, Debug, Eq, PartialEq)]
179pub enum CapacitorPolarityParseError {
180 Empty,
182 Unknown,
184}
185
186impl fmt::Display for CapacitorPolarityParseError {
187 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
188 match self {
189 Self::Empty => formatter.write_str("capacitor polarity cannot be empty"),
190 Self::Unknown => formatter.write_str("unknown capacitor polarity"),
191 }
192 }
193}
194
195impl Error for CapacitorPolarityParseError {}
196
197#[derive(Clone, Debug, PartialEq)]
199pub struct CapacitorSpec {
200 capacitance: CapacitanceValue,
201 kind: CapacitorKind,
202 polarity: CapacitorPolarity,
203 voltage_rating: Option<VoltageRating>,
204}
205
206impl CapacitorSpec {
207 #[must_use]
209 pub const fn new(capacitance: CapacitanceValue, kind: CapacitorKind) -> Self {
210 Self {
211 capacitance,
212 kind,
213 polarity: CapacitorPolarity::Unknown,
214 voltage_rating: None,
215 }
216 }
217
218 #[must_use]
220 pub const fn capacitance(&self) -> CapacitanceValue {
221 self.capacitance
222 }
223
224 #[must_use]
226 pub fn kind(&self) -> CapacitorKind {
227 self.kind.clone()
228 }
229
230 #[must_use]
232 pub const fn polarity(&self) -> CapacitorPolarity {
233 self.polarity
234 }
235
236 #[must_use]
238 pub const fn voltage_rating(&self) -> Option<VoltageRating> {
239 self.voltage_rating
240 }
241
242 #[must_use]
244 pub const fn with_polarity(mut self, polarity: CapacitorPolarity) -> Self {
245 self.polarity = polarity;
246 self
247 }
248
249 #[must_use]
251 pub const fn with_voltage_rating(mut self, voltage_rating: VoltageRating) -> Self {
252 self.voltage_rating = Some(voltage_rating);
253 self
254 }
255}
256
257fn normalized_token(value: &str) -> String {
258 value.trim().to_ascii_lowercase().replace(['_', ' '], "-")
259}
260
261#[cfg(test)]
262mod tests {
263 use super::{
264 CapacitanceValue, CapacitanceValueError, CapacitorKind, CapacitorPolarity, CapacitorSpec,
265 };
266 use use_rating::VoltageRating;
267
268 #[test]
269 fn accepts_valid_capacitance() -> Result<(), CapacitanceValueError> {
270 let value = CapacitanceValue::new_farads(0.000_001)?;
271
272 assert!((value.farads() - 0.000_001).abs() < f64::EPSILON);
273 Ok(())
274 }
275
276 #[test]
277 fn rejects_negative_capacitance() {
278 assert_eq!(
279 CapacitanceValue::new_farads(-1.0),
280 Err(CapacitanceValueError::Negative)
281 );
282 }
283
284 #[test]
285 fn displays_and_parses_capacitor_kinds() -> Result<(), Box<dyn std::error::Error>> {
286 assert_eq!("ceramic".parse::<CapacitorKind>()?, CapacitorKind::Ceramic);
287 assert_eq!(CapacitorKind::Supercapacitor.to_string(), "supercapacitor");
288 Ok(())
289 }
290
291 #[test]
292 fn displays_and_parses_polarity() -> Result<(), Box<dyn std::error::Error>> {
293 assert_eq!(
294 "non polarized".parse::<CapacitorPolarity>()?,
295 CapacitorPolarity::NonPolarized
296 );
297 assert_eq!(CapacitorPolarity::Polarized.to_string(), "polarized");
298 Ok(())
299 }
300
301 #[test]
302 fn builds_capacitor_specs_with_voltage_rating() -> Result<(), Box<dyn std::error::Error>> {
303 let spec = CapacitorSpec::new(
304 CapacitanceValue::new_farads(0.000_001)?,
305 CapacitorKind::Ceramic,
306 )
307 .with_voltage_rating(VoltageRating::new_volts(16.0)?);
308
309 assert_eq!(spec.voltage_rating().map(VoltageRating::volts), Some(16.0));
310 Ok(())
311 }
312}