Skip to main content

use_transistor/
lib.rs

1#![forbid(unsafe_code)]
2#![doc = include_str!("../README.md")]
3
4use core::{fmt, str::FromStr};
5use std::error::Error;
6
7/// Commonly used transistor primitives.
8pub mod prelude {
9    pub use crate::{
10        BjtKind, BjtKindParseError, FetKind, FetKindParseError, TerminalParseError, TransistorKind,
11        TransistorSpec, TransistorTerminal,
12    };
13}
14
15/// Bipolar junction transistor kind.
16#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
17pub enum BjtKind {
18    Npn,
19    Pnp,
20}
21
22impl fmt::Display for BjtKind {
23    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
24        formatter.write_str(match self {
25            Self::Npn => "npn",
26            Self::Pnp => "pnp",
27        })
28    }
29}
30
31impl FromStr for BjtKind {
32    type Err = BjtKindParseError;
33
34    fn from_str(value: &str) -> Result<Self, Self::Err> {
35        let trimmed = value.trim();
36        if trimmed.is_empty() {
37            return Err(BjtKindParseError::Empty);
38        }
39
40        match trimmed.to_ascii_lowercase().as_str() {
41            "npn" => Ok(Self::Npn),
42            "pnp" => Ok(Self::Pnp),
43            _ => Err(BjtKindParseError::Unknown),
44        }
45    }
46}
47
48/// Errors returned while parsing BJT kinds.
49#[derive(Clone, Copy, Debug, Eq, PartialEq)]
50pub enum BjtKindParseError {
51    /// The kind was empty after trimming whitespace.
52    Empty,
53    /// The kind was not part of the fixed vocabulary.
54    Unknown,
55}
56
57impl fmt::Display for BjtKindParseError {
58    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
59        match self {
60            Self::Empty => formatter.write_str("BJT kind cannot be empty"),
61            Self::Unknown => formatter.write_str("unknown BJT kind"),
62        }
63    }
64}
65
66impl Error for BjtKindParseError {}
67
68/// Field-effect transistor kind.
69#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
70pub enum FetKind {
71    Nmos,
72    Pmos,
73    JfetN,
74    JfetP,
75}
76
77impl fmt::Display for FetKind {
78    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
79        formatter.write_str(match self {
80            Self::Nmos => "nmos",
81            Self::Pmos => "pmos",
82            Self::JfetN => "jfet-n",
83            Self::JfetP => "jfet-p",
84        })
85    }
86}
87
88impl FromStr for FetKind {
89    type Err = FetKindParseError;
90
91    fn from_str(value: &str) -> Result<Self, Self::Err> {
92        let trimmed = value.trim();
93        if trimmed.is_empty() {
94            return Err(FetKindParseError::Empty);
95        }
96
97        match normalized_token(trimmed).as_str() {
98            "nmos" => Ok(Self::Nmos),
99            "pmos" => Ok(Self::Pmos),
100            "jfet-n" => Ok(Self::JfetN),
101            "jfet-p" => Ok(Self::JfetP),
102            _ => Err(FetKindParseError::Unknown),
103        }
104    }
105}
106
107/// Errors returned while parsing FET kinds.
108#[derive(Clone, Copy, Debug, Eq, PartialEq)]
109pub enum FetKindParseError {
110    /// The kind was empty after trimming whitespace.
111    Empty,
112    /// The kind was not part of the fixed vocabulary.
113    Unknown,
114}
115
116impl fmt::Display for FetKindParseError {
117    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
118        match self {
119            Self::Empty => formatter.write_str("FET kind cannot be empty"),
120            Self::Unknown => formatter.write_str("unknown FET kind"),
121        }
122    }
123}
124
125impl Error for FetKindParseError {}
126
127/// Descriptive transistor kind vocabulary.
128#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
129pub enum TransistorKind {
130    Bjt(BjtKind),
131    Fet(FetKind),
132    Unknown,
133    Custom(String),
134}
135
136impl fmt::Display for TransistorKind {
137    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
138        match self {
139            Self::Bjt(kind) => write!(formatter, "bjt:{kind}"),
140            Self::Fet(kind) => write!(formatter, "fet:{kind}"),
141            Self::Unknown => formatter.write_str("unknown"),
142            Self::Custom(value) => formatter.write_str(value),
143        }
144    }
145}
146
147/// Transistor terminal vocabulary.
148#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
149pub enum TransistorTerminal {
150    Base,
151    Collector,
152    Emitter,
153    Gate,
154    Drain,
155    Source,
156    Body,
157}
158
159impl fmt::Display for TransistorTerminal {
160    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
161        formatter.write_str(match self {
162            Self::Base => "base",
163            Self::Collector => "collector",
164            Self::Emitter => "emitter",
165            Self::Gate => "gate",
166            Self::Drain => "drain",
167            Self::Source => "source",
168            Self::Body => "body",
169        })
170    }
171}
172
173impl FromStr for TransistorTerminal {
174    type Err = TerminalParseError;
175
176    fn from_str(value: &str) -> Result<Self, Self::Err> {
177        let trimmed = value.trim();
178        if trimmed.is_empty() {
179            return Err(TerminalParseError::Empty);
180        }
181
182        match normalized_token(trimmed).as_str() {
183            "base" => Ok(Self::Base),
184            "collector" => Ok(Self::Collector),
185            "emitter" => Ok(Self::Emitter),
186            "gate" => Ok(Self::Gate),
187            "drain" => Ok(Self::Drain),
188            "source" => Ok(Self::Source),
189            "body" => Ok(Self::Body),
190            _ => Err(TerminalParseError::Unknown),
191        }
192    }
193}
194
195/// Errors returned while parsing transistor terminals.
196#[derive(Clone, Copy, Debug, Eq, PartialEq)]
197pub enum TerminalParseError {
198    /// The terminal was empty after trimming whitespace.
199    Empty,
200    /// The terminal was not part of the fixed vocabulary.
201    Unknown,
202}
203
204impl fmt::Display for TerminalParseError {
205    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
206        match self {
207            Self::Empty => formatter.write_str("transistor terminal cannot be empty"),
208            Self::Unknown => formatter.write_str("unknown transistor terminal"),
209        }
210    }
211}
212
213impl Error for TerminalParseError {}
214
215/// A descriptive transistor specification.
216#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
217pub struct TransistorSpec {
218    kind: TransistorKind,
219}
220
221impl TransistorSpec {
222    /// Creates a transistor spec from a transistor kind.
223    #[must_use]
224    pub const fn new(kind: TransistorKind) -> Self {
225        Self { kind }
226    }
227
228    /// Returns the transistor kind.
229    #[must_use]
230    pub fn kind(&self) -> TransistorKind {
231        self.kind.clone()
232    }
233}
234
235fn normalized_token(value: &str) -> String {
236    value.trim().to_ascii_lowercase().replace(['_', ' '], "-")
237}
238
239#[cfg(test)]
240mod tests {
241    use super::{BjtKind, FetKind, TransistorKind, TransistorSpec, TransistorTerminal};
242
243    #[test]
244    fn displays_and_parses_bjt_kinds() -> Result<(), Box<dyn std::error::Error>> {
245        assert_eq!("npn".parse::<BjtKind>()?, BjtKind::Npn);
246        assert_eq!(BjtKind::Pnp.to_string(), "pnp");
247        Ok(())
248    }
249
250    #[test]
251    fn displays_and_parses_fet_kinds() -> Result<(), Box<dyn std::error::Error>> {
252        assert_eq!("jfet n".parse::<FetKind>()?, FetKind::JfetN);
253        assert_eq!(FetKind::Pmos.to_string(), "pmos");
254        Ok(())
255    }
256
257    #[test]
258    fn displays_and_parses_transistor_terminals() -> Result<(), Box<dyn std::error::Error>> {
259        assert_eq!(
260            "collector".parse::<TransistorTerminal>()?,
261            TransistorTerminal::Collector
262        );
263        assert_eq!(TransistorTerminal::Gate.to_string(), "gate");
264        Ok(())
265    }
266
267    #[test]
268    fn creates_transistor_specs() {
269        let spec = TransistorSpec::new(TransistorKind::Fet(FetKind::Nmos));
270
271        assert_eq!(spec.kind(), TransistorKind::Fet(FetKind::Nmos));
272    }
273}