#![forbid(unsafe_code)]
#![doc = include_str!("../README.md")]
use core::{fmt, str::FromStr};
use std::error::Error;
use use_rating::{CurrentRating, VoltageRating};
pub mod prelude {
pub use crate::{
DiodeKind, DiodeKindParseError, DiodePolarity, DiodePolarityParseError, DiodeSpec,
};
}
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum DiodeKind {
Signal,
Rectifier,
Zener,
Schottky,
Led,
Tvs,
Photodiode,
Unknown,
Custom(String),
}
impl fmt::Display for DiodeKind {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str(match self {
Self::Signal => "signal",
Self::Rectifier => "rectifier",
Self::Zener => "zener",
Self::Schottky => "schottky",
Self::Led => "led",
Self::Tvs => "tvs",
Self::Photodiode => "photodiode",
Self::Unknown => "unknown",
Self::Custom(value) => value.as_str(),
})
}
}
impl FromStr for DiodeKind {
type Err = DiodeKindParseError;
fn from_str(value: &str) -> Result<Self, Self::Err> {
let trimmed = value.trim();
if trimmed.is_empty() {
return Err(DiodeKindParseError::Empty);
}
match normalized_token(trimmed).as_str() {
"signal" => Ok(Self::Signal),
"rectifier" => Ok(Self::Rectifier),
"zener" => Ok(Self::Zener),
"schottky" => Ok(Self::Schottky),
"led" => Ok(Self::Led),
"tvs" => Ok(Self::Tvs),
"photodiode" => Ok(Self::Photodiode),
"unknown" => Ok(Self::Unknown),
_ => Ok(Self::Custom(trimmed.to_string())),
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum DiodeKindParseError {
Empty,
}
impl fmt::Display for DiodeKindParseError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Empty => formatter.write_str("diode kind cannot be empty"),
}
}
}
impl Error for DiodeKindParseError {}
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum DiodePolarity {
Anode,
Cathode,
}
impl fmt::Display for DiodePolarity {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str(match self {
Self::Anode => "anode",
Self::Cathode => "cathode",
})
}
}
impl FromStr for DiodePolarity {
type Err = DiodePolarityParseError;
fn from_str(value: &str) -> Result<Self, Self::Err> {
let trimmed = value.trim();
if trimmed.is_empty() {
return Err(DiodePolarityParseError::Empty);
}
match normalized_token(trimmed).as_str() {
"anode" => Ok(Self::Anode),
"cathode" => Ok(Self::Cathode),
_ => Err(DiodePolarityParseError::Unknown),
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum DiodePolarityParseError {
Empty,
Unknown,
}
impl fmt::Display for DiodePolarityParseError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Empty => formatter.write_str("diode polarity cannot be empty"),
Self::Unknown => formatter.write_str("unknown diode polarity"),
}
}
}
impl Error for DiodePolarityParseError {}
#[derive(Clone, Debug, PartialEq)]
pub struct DiodeSpec {
kind: DiodeKind,
forward_voltage: Option<VoltageRating>,
reverse_voltage_rating: Option<VoltageRating>,
current_rating: Option<CurrentRating>,
}
impl DiodeSpec {
#[must_use]
pub const fn new(kind: DiodeKind) -> Self {
Self {
kind,
forward_voltage: None,
reverse_voltage_rating: None,
current_rating: None,
}
}
#[must_use]
pub fn kind(&self) -> DiodeKind {
self.kind.clone()
}
#[must_use]
pub const fn forward_voltage(&self) -> Option<VoltageRating> {
self.forward_voltage
}
#[must_use]
pub const fn reverse_voltage_rating(&self) -> Option<VoltageRating> {
self.reverse_voltage_rating
}
#[must_use]
pub const fn current_rating(&self) -> Option<CurrentRating> {
self.current_rating
}
#[must_use]
pub const fn with_forward_voltage(mut self, forward_voltage: VoltageRating) -> Self {
self.forward_voltage = Some(forward_voltage);
self
}
#[must_use]
pub const fn with_reverse_voltage_rating(mut self, reverse_voltage: VoltageRating) -> Self {
self.reverse_voltage_rating = Some(reverse_voltage);
self
}
#[must_use]
pub const fn with_current_rating(mut self, current_rating: CurrentRating) -> Self {
self.current_rating = Some(current_rating);
self
}
}
fn normalized_token(value: &str) -> String {
value.trim().to_ascii_lowercase().replace(['_', ' '], "-")
}
#[cfg(test)]
mod tests {
use super::{DiodeKind, DiodePolarity, DiodeSpec};
use use_rating::{CurrentRating, VoltageRating};
#[test]
fn displays_and_parses_diode_kinds() -> Result<(), Box<dyn std::error::Error>> {
assert_eq!("schottky".parse::<DiodeKind>()?, DiodeKind::Schottky);
assert_eq!(DiodeKind::Photodiode.to_string(), "photodiode");
Ok(())
}
#[test]
fn supports_custom_diode_kinds() -> Result<(), Box<dyn std::error::Error>> {
assert_eq!(
"pin-diode".parse::<DiodeKind>()?,
DiodeKind::Custom("pin-diode".to_string())
);
Ok(())
}
#[test]
fn displays_diode_polarity() {
assert_eq!(DiodePolarity::Anode.to_string(), "anode");
assert_eq!(DiodePolarity::Cathode.to_string(), "cathode");
}
#[test]
fn builds_diode_specs_with_ratings() -> Result<(), Box<dyn std::error::Error>> {
let spec = DiodeSpec::new(DiodeKind::Zener)
.with_forward_voltage(VoltageRating::new_volts(0.7)?)
.with_reverse_voltage_rating(VoltageRating::new_volts(5.1)?)
.with_current_rating(CurrentRating::new_amperes(0.02)?);
assert_eq!(spec.kind(), DiodeKind::Zener);
assert_eq!(
spec.current_rating().map(CurrentRating::amperes),
Some(0.02)
);
Ok(())
}
}