1use serde::{Deserialize, Serialize};
6
7use crate::CalcError;
8
9#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
11pub enum CopperWeight {
12 Oz025,
14 Oz05,
16 Oz1,
18 Oz15,
20 Oz2,
22 Oz25,
24 Oz3,
26 Oz4,
28 Oz5,
30}
31
32impl CopperWeight {
33 pub fn thickness_mils(self) -> f64 {
35 match self {
36 Self::Oz025 => 0.35,
37 Self::Oz05 => 0.70,
38 Self::Oz1 => 1.40,
39 Self::Oz15 => 2.10,
40 Self::Oz2 => 2.80,
41 Self::Oz25 => 3.50,
42 Self::Oz3 => 4.20,
43 Self::Oz4 => 5.60,
44 Self::Oz5 => 7.00,
45 }
46 }
47
48 pub fn thickness_mm(self) -> f64 {
50 match self {
51 Self::Oz025 => 0.009,
52 Self::Oz05 => 0.018,
53 Self::Oz1 => 0.035,
54 Self::Oz15 => 0.053,
55 Self::Oz2 => 0.070,
56 Self::Oz25 => 0.088,
57 Self::Oz3 => 0.106,
58 Self::Oz4 => 0.142,
59 Self::Oz5 => 0.178,
60 }
61 }
62
63 pub fn from_str_oz(s: &str) -> Result<Self, CalcError> {
65 match s.trim().to_lowercase().trim_end_matches("oz").trim() {
66 "0.25" => Ok(Self::Oz025),
67 "0.5" => Ok(Self::Oz05),
68 "1" => Ok(Self::Oz1),
69 "1.5" => Ok(Self::Oz15),
70 "2" => Ok(Self::Oz2),
71 "2.5" => Ok(Self::Oz25),
72 "3" => Ok(Self::Oz3),
73 "4" => Ok(Self::Oz4),
74 "5" => Ok(Self::Oz5),
75 _ => Err(CalcError::UnknownCopperWeight(s.to_string())),
76 }
77 }
78}
79
80#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
82pub enum PlatingThickness {
83 Bare,
84 Oz05,
85 Oz1,
86 Oz15,
87 Oz2,
88 Oz25,
89 Oz3,
90}
91
92impl PlatingThickness {
93 pub fn thickness_mils(self) -> f64 {
95 match self {
96 Self::Bare => 0.0,
97 Self::Oz05 => 0.70,
98 Self::Oz1 => 1.40,
99 Self::Oz15 => 2.10,
100 Self::Oz2 => 2.80,
101 Self::Oz25 => 3.50,
102 Self::Oz3 => 4.20,
103 }
104 }
105}
106
107#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
109pub enum EtchFactor {
110 None,
112 OneToOne,
114 TwoToOne,
116}
117
118impl EtchFactor {
119 pub fn cross_section_sq_mils(self, width: f64, thickness: f64) -> f64 {
121 match self {
122 Self::None => width * thickness,
123 Self::OneToOne => (width + (width - 2.0 * thickness)) * thickness / 2.0,
124 Self::TwoToOne => (width + (width - thickness)) * thickness / 2.0,
125 }
126 }
127}
128
129#[cfg(test)]
130mod tests {
131 use super::*;
132
133 #[test]
134 fn copper_weight_values() {
135 assert!((CopperWeight::Oz1.thickness_mils() - 1.40).abs() < 1e-10);
136 assert!((CopperWeight::Oz1.thickness_mm() - 0.035).abs() < 1e-10);
137 }
138
139 #[test]
140 fn etch_factor_rectangular() {
141 let area = EtchFactor::None.cross_section_sq_mils(10.0, 1.4);
143 assert!((area - 14.0).abs() < 1e-10);
144 }
145
146 #[test]
147 fn parse_copper_weight() {
148 assert_eq!(CopperWeight::from_str_oz("1oz").unwrap(), CopperWeight::Oz1);
149 assert_eq!(CopperWeight::from_str_oz("0.5oz").unwrap(), CopperWeight::Oz05);
150 assert_eq!(CopperWeight::from_str_oz("2.5").unwrap(), CopperWeight::Oz25);
151 }
152}