1use std::str::FromStr;
2
3use rusistor::{Color, Resistor};
4
5use crate::model::{ColorCodesToSpecsModel, InputFocus, SpecsToColorModel};
6
7pub enum ColorCodesMsg {
8 ThreeBands,
9 FourBands,
10 FiveBands,
11 SixBands,
12 NextBand,
13 PrevBand,
14 NextColor,
15 PrevColor,
16}
17
18pub enum SpecsMsg {
19 Determine,
20 NextSpecInput,
21 PrevSpecInput,
22 PrevHistory,
23 NextHistory,
24 Reset,
25}
26
27pub fn update_on_colorcodemsg(model: &mut ColorCodesToSpecsModel, msg: ColorCodesMsg) {
28 match msg {
29 ColorCodesMsg::ThreeBands => {
30 model.resistor = Resistor::ThreeBand {
31 band1: rusistor::Color::Brown,
32 band2: rusistor::Color::Black,
33 band3: rusistor::Color::Black,
34 };
35 model.selected_band = model.selected_band.min(2)
36 }
37 ColorCodesMsg::FourBands => {
38 model.resistor = Resistor::FourBand {
39 band1: rusistor::Color::Brown,
40 band2: rusistor::Color::Black,
41 band3: rusistor::Color::Black,
42 band4: rusistor::Color::Brown,
43 };
44 model.selected_band = model.selected_band.min(3)
45 }
46 ColorCodesMsg::FiveBands => {
47 model.resistor = Resistor::FiveBand {
48 band1: rusistor::Color::Brown,
49 band2: rusistor::Color::Black,
50 band3: rusistor::Color::Black,
51 band4: rusistor::Color::Black,
52 band5: rusistor::Color::Brown,
53 };
54 model.selected_band = model.selected_band.min(4)
55 }
56 ColorCodesMsg::SixBands => {
57 model.resistor = Resistor::SixBand {
58 band1: rusistor::Color::Brown,
59 band2: rusistor::Color::Black,
60 band3: rusistor::Color::Black,
61 band4: rusistor::Color::Black,
62 band5: rusistor::Color::Brown,
63 band6: rusistor::Color::Black,
64 };
65 model.selected_band = model.selected_band.min(5)
66 }
67 ColorCodesMsg::NextBand => {
68 model.selected_band = (model.selected_band + 1) % model.resistor.bands().len()
69 }
70 ColorCodesMsg::PrevBand => {
71 let bands_count = model.resistor.bands().len();
72 model.selected_band = (model.selected_band + (bands_count - 1)) % bands_count
73 }
74 ColorCodesMsg::NextColor => {
75 let current_idx: usize = *model.resistor.bands()[model.selected_band] as usize;
76 let mut i: usize = 0;
77 let mut resistor = Err("".to_string());
78 while resistor.is_err() {
79 i += 1;
80 let next_color = Color::from((current_idx + i) % 13);
81 resistor = model.resistor.with_color(next_color, model.selected_band);
82 }
83 model.resistor = resistor.unwrap();
84 }
85 ColorCodesMsg::PrevColor => {
86 let current_idx = *model.resistor.bands()[model.selected_band] as usize;
87 let mut i: usize = 13;
88 let mut resistor = Err("".to_string());
89 while resistor.is_err() {
90 i -= 1;
91 let next_color = Color::from((current_idx + i) % 13);
92 resistor = model.resistor.with_color(next_color, model.selected_band);
93 }
94 model.resistor = resistor.unwrap();
95 }
96 }
97}
98pub fn update_on_specsmsg(model: &mut SpecsToColorModel, msg: SpecsMsg) {
99 match msg {
100 SpecsMsg::Determine => {
101 match try_determine_resistor(
102 &model.resistance_input_state.value,
103 &model.tolerance_input_state.value,
104 &model.tcr_input_state.value,
105 ) {
106 Ok(resistor) => {
107 model.resistor = Some(resistor);
108 model.error = None;
109 model.history.add((
110 model.resistance_input_state.value.to_string(),
111 model.tolerance_input_state.value.to_string(),
112 model.tcr_input_state.value.to_string(),
113 ));
114 model.add_specs_to_history();
115 model.history.clear_idx();
116 }
117 Err(e) => {
118 model.resistor = None;
119 model.error = Some(e);
120 }
121 }
122 }
123 SpecsMsg::NextSpecInput | SpecsMsg::PrevSpecInput => {
124 model.error = match model.focus {
125 InputFocus::Resistance => {
126 let value = &model.resistance_input_state.value;
127 if value.trim().is_empty() {
128 None
129 } else {
130 try_parse_resistance(value).err().map(|err| err.to_string())
131 }
132 }
133 InputFocus::Tolerance => {
134 let value = &model.tolerance_input_state.value;
135 if value.trim().is_empty() {
136 None
137 } else {
138 value.parse::<f64>().err().map(|err| err.to_string())
139 }
140 }
141 InputFocus::Tcr => {
142 let value = &model.tcr_input_state.value;
143 if value.trim().is_empty() {
144 None
145 } else {
146 value.parse::<u32>().err().map(|err| err.to_string())
147 }
148 }
149 };
150 if model.error.is_none() {
151 model.focus = match msg {
152 SpecsMsg::NextSpecInput => model.focus.next(),
153 _ => model.focus.prev(),
154 };
155 } else {
156 model.resistor = None;
157 }
158 }
159 SpecsMsg::PrevHistory => {
160 model.history.prev();
161 model.set_specs_from_history();
162 }
163 SpecsMsg::NextHistory => {
164 model.history.next();
165 model.set_specs_from_history();
166 }
167 SpecsMsg::Reset => *model = SpecsToColorModel::default(),
168 }
169}
170
171pub fn try_parse_resistance(input: &str) -> Result<f64, String> {
172 match input.parse::<f64>() {
173 Ok(t) => Ok(t),
174 Err(e) => match engineering_repr::EngineeringQuantity::<i64>::from_str(input) {
175 Ok(t) => {
176 let r: i64 = t.into();
177 let r = r as f64;
178 Ok(r)
179 }
180 Err(_) => Err(format!("invalid input for resistance: {}", e)),
181 },
182 }
183}
184
185pub fn try_determine_resistor(
186 resistance_input: &str,
187 tolerance_input: &str,
188 tcr_input: &str,
189) -> Result<Resistor, String> {
190 let resistance = try_parse_resistance(resistance_input);
191 let tolerance = if tolerance_input.is_empty() {
192 Ok(None)
193 } else {
194 match tolerance_input.parse::<f64>() {
195 Ok(t) => Ok(Some(t)),
196 Err(e) => Err(format!("invalid input for tolerance: {}", e)),
197 }
198 };
199
200 let tcr = if tcr_input.is_empty() {
201 Ok(None)
202 } else {
203 match tcr_input.parse::<u32>() {
204 Ok(t) => Ok(Some(t)),
205 Err(e) => Err(format!("invalid input for tcr: {}", e)),
206 }
207 };
208
209 match (resistance, tolerance, tcr) {
210 (Ok(resistance), Ok(tolerance), Ok(tcr)) => {
211 match Resistor::determine(resistance, tolerance, tcr) {
212 Ok(resistor) => Ok(resistor),
213 Err(e) => Err(format!(
214 "could not determine a resistor for these inputs: {}",
215 e
216 )),
217 }
218 }
219 (res, tol, tcr) => {
220 let mut error_msg: String = String::from("");
221 if let Err(res_error) = res {
222 error_msg.push_str(res_error.to_string().as_str());
223 }
224 if let Err(tol_error) = tol {
225 error_msg.push('\n');
226 error_msg.push_str(tol_error.to_string().as_str());
227 }
228 if let Err(tcr_error) = tcr {
229 error_msg.push('\n');
230 error_msg.push_str(tcr_error.to_string().as_str());
231 }
232 if error_msg.is_empty() {
233 panic!("unknown error");
234 } else {
235 Err(error_msg)
236 }
237 }
238 }
239}
240
241#[cfg(test)]
242mod tests {
243 use super::ColorCodesMsg;
244 use crate::{
245 model::{ColorCodesToSpecsModel, InputState, SpecsToColorModel},
246 update::{SpecsMsg, update_on_colorcodemsg, update_on_specsmsg},
247 };
248
249 #[test]
250 fn test_nbands_msg() {
251 let mut model = ColorCodesToSpecsModel::default();
252 assert_eq!(model.resistor.bands().len(), 6);
253
254 update_on_colorcodemsg(&mut model, ColorCodesMsg::ThreeBands);
255 assert_eq!(model.resistor.bands().len(), 3);
256
257 update_on_colorcodemsg(&mut model, ColorCodesMsg::FourBands);
258 assert_eq!(model.resistor.bands().len(), 4);
259
260 update_on_colorcodemsg(&mut model, ColorCodesMsg::FiveBands);
261 assert_eq!(model.resistor.bands().len(), 5);
262
263 update_on_colorcodemsg(&mut model, ColorCodesMsg::SixBands);
264 assert_eq!(model.resistor.bands().len(), 6);
265 }
266
267 #[test]
268 fn test_reset_msg() {
269 let mut model = SpecsToColorModel {
270 resistance_input_state: InputState {
271 value: String::from("z"),
272 cursor: 1,
273 },
274 tolerance_input_state: InputState {
275 value: String::from("z"),
276 cursor: 1,
277 },
278 tcr_input_state: InputState {
279 value: String::from("z"),
280 cursor: 1,
281 },
282 ..Default::default()
283 };
284 update_on_specsmsg(&mut model, SpecsMsg::Reset);
285 assert_eq!(model.resistance_input_state.value, "");
286 assert_eq!(model.tolerance_input_state.value, "");
287 assert_eq!(model.tcr_input_state.value, "");
288 }
289
290 #[test]
291 fn test_history() {
292 let mut model = SpecsToColorModel {
293 resistance_input_state: InputState {
294 value: String::from("1"),
295 cursor: 1,
296 },
297 tolerance_input_state: InputState {
298 value: String::from("2"),
299 cursor: 1,
300 },
301 tcr_input_state: InputState {
302 value: String::from("5"),
303 cursor: 1,
304 },
305 ..Default::default()
306 };
307 update_on_specsmsg(&mut model, SpecsMsg::Determine);
308
309 model.resistance_input_state = InputState {
310 value: String::from("2"),
311 cursor: 1,
312 };
313 model.tolerance_input_state = InputState {
314 value: String::from("5"),
315 cursor: 1,
316 };
317
318 model.tcr_input_state = InputState {
319 value: String::from("1"),
320 cursor: 1,
321 };
322 update_on_specsmsg(&mut model, SpecsMsg::Determine);
323 update_on_specsmsg(&mut model, SpecsMsg::PrevHistory);
324 update_on_specsmsg(&mut model, SpecsMsg::PrevHistory);
325 assert_eq!(model.resistance_input_state.value, "1");
326 assert_eq!(model.tolerance_input_state.value, "2");
327 assert_eq!(model.tcr_input_state.value, "5");
328 }
329}