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