paycheck_utils/
interaction.rs1use crate::utils::check_converted_value;
2use crate::{
3 EmploymentScenario, Expense, Expenses, PostTaxDeduction, PostTaxDeductions, PreTaxDeduction,
4 PreTaxDeductions,
5};
6use std::any::TypeId;
7use std::collections::HashMap;
8use std::io;
9use std::io::prelude::*;
13
14pub fn get_user_input() -> EmploymentScenario {
15 println!(
16 "\n{:^100}",
17 "--- Let's start by gathering some information. ---"
18 );
19
20 println!("\nFirst, let's create an employment scenario.\n");
22 let scenario = create_scenario();
23
24 println!(
26 "\n{:^100}",
27 "--- Great!, now let's create some expenses. ---"
28 );
29 let expenses = get_expenses();
30
31 println!(
33 "\n{:^100}",
34 "--- Finally, let's create some deductions. ---"
35 );
36 let deductions = get_deductions();
37
38 convert_inputs_to_struct(scenario, expenses, deductions)
44}
45
46fn create_scenario() -> HashMap<String, String> {
47 let mut inputs: HashMap<String, String> = HashMap::new();
49 let mut input = String::new();
50 let employed = ["Rate", "Hours"];
51
52 for value in employed {
53 print!("{value}: ");
54 io::stdout().flush().unwrap_or_default();
55 io::stdin().read_line(&mut input).unwrap_or_default();
56 loop {
57 if check_converted_value(&input.trim().parse::<f32>(), TypeId::of::<f32>()) {
58 break;
59 } else {
60 print!(
61 "Please enter a valid number for {value} (examples: 25, 25.5, or 25.00) --> {value}: "
62 );
63 input.clear();
64 io::stdout().flush().unwrap_or_default();
65 io::stdin().read_line(&mut input).unwrap_or_default();
66 }
67 }
68 inputs
69 .entry(value.trim().to_string())
70 .or_insert(input.trim().to_string());
71 input.clear();
72 }
73
74 inputs
75}
76
77fn get_expenses() -> HashMap<String, String> {
78 let mut inputs: HashMap<String, String> = HashMap::new();
79 let mut input = String::new();
80 let expense_categories = [
81 "Housing",
82 "Energy",
83 "Water",
84 "Gas",
85 "Internet",
86 "Phone",
87 "Car Payment",
88 "Car Insurance",
89 "Car Gas",
90 "Groceries",
91 ];
92
93 println!("\nLiving expenses can vary so enter an estimated amount per month or 0.\n");
94
95 for exp in expense_categories {
96 print!("{exp}: ");
97 io::stdout().flush().unwrap_or_default();
98 io::stdin().read_line(&mut input).unwrap_or_default();
99 loop {
100 if check_converted_value(&input.trim().parse::<f32>(), TypeId::of::<f32>()) {
101 break;
102 } else {
103 print!(
104 "Please enter a valid number for {exp} (examples: 25, 25.5, or 25.00) --> {exp}: "
105 );
106 input.clear();
107 io::stdout().flush().unwrap_or_default();
108 io::stdin().read_line(&mut input).unwrap_or_default();
109 }
110 }
111 inputs
112 .entry(exp.trim().to_string())
113 .or_insert(input.trim().to_string());
114 input.clear();
115 }
116
117 inputs
118}
119
120fn get_deductions() -> HashMap<String, String> {
121 let mut inputs: HashMap<String, String> = HashMap::new();
122 let mut input = String::new();
123 let pretax_categories = [
124 "Medical",
125 "Dental",
126 "Vision",
127 "Traditional401K",
128 "HSA",
129 "FSA",
130 ];
131 let posttax_categories = [
132 "Roth401K",
133 "Voluntary Life",
134 "Voluntary ADD",
135 "Voluntary STD",
136 "Voluntary LTD",
137 "Wage Garnishment",
138 ];
139
140 println!(
141 "\nDeductions come in two flavors: pre-tax and post-tax.\nLet's start with the pre-tax deductions.\n"
142 );
143
144 for pre in pretax_categories {
145 print!("{pre}: ");
146 io::stdout().flush().unwrap_or_default();
147 io::stdin().read_line(&mut input).unwrap_or_default();
148 loop {
149 if check_converted_value(&input.trim().parse::<f32>(), TypeId::of::<f32>()) {
150 break;
151 } else {
152 print!(
153 "Please enter a valid number for {pre} (examples: 25, 25.5, or 25.00) --> {pre}: "
154 );
155 input.clear();
156 io::stdout().flush().unwrap_or_default();
157 io::stdin().read_line(&mut input).unwrap_or_default();
158 }
159 }
160 inputs
161 .entry(pre.trim().to_string())
162 .or_insert(input.trim().to_string());
163 input.clear();
164 }
165
166 println!("\nOk, now the post-tax deductions.\n");
167
168 for post in posttax_categories {
169 print!("{post}: ");
170 io::stdout().flush().unwrap_or_default();
171 io::stdin().read_line(&mut input).unwrap_or_default();
172 loop {
173 if check_converted_value(&input.trim().parse::<f32>(), TypeId::of::<f32>()) {
174 break;
175 } else {
176 print!(
177 "Please enter a valid number for {post} (examples: 25, 25.5, or 25.00) --> {post}: "
178 );
179 input.clear();
180 io::stdout().flush().unwrap_or_default();
181 io::stdin().read_line(&mut input).unwrap_or_default();
182 }
183 }
184 inputs
185 .entry(post.trim().to_string())
186 .or_insert(input.trim().to_string());
187 input.clear();
188 }
189
190 inputs
191}
192
193pub fn convert_inputs_to_struct(
237 sc: HashMap<String, String>,
238 ex: HashMap<String, String>,
239 de: HashMap<String, String>,
240) -> EmploymentScenario {
241 let mut scene = EmploymentScenario::default();
242 scene.hourly_rate = sc["Rate"].parse::<f32>().unwrap_or_default();
243 scene.hours_per_week = sc["Hours"].parse::<f32>().unwrap_or_default();
244 scene.expenses = Expenses::new(vec![
245 Expense::Housing(ex["Housing"].parse::<f32>().ok()),
246 Expense::Energy(ex["Energy"].parse::<f32>().ok()),
247 Expense::Water(ex["Water"].parse::<f32>().ok()),
248 Expense::Gas(ex["Gas"].parse::<f32>().ok()),
249 Expense::Internet(ex["Internet"].parse::<f32>().ok()),
250 Expense::Phone(ex["Phone"].parse::<f32>().ok()),
251 Expense::Vehicle(ex["Car Payment"].parse::<f32>().ok()),
252 Expense::VehicleInsurance(ex["Car Insurance"].parse::<f32>().ok()),
253 Expense::VehicleGas(ex["Car Gas"].parse::<f32>().ok()),
254 Expense::Groceries(ex["Groceries"].parse::<f32>().ok()),
255 ]);
256 scene.pretax_deductions = PreTaxDeductions::new(vec![
257 PreTaxDeduction::Medical(de["Medical"].parse::<f32>().ok()),
258 PreTaxDeduction::Dental(de["Dental"].parse::<f32>().ok()),
259 PreTaxDeduction::Vision(de["Vision"].parse::<f32>().ok()),
260 PreTaxDeduction::Traditional401K(de["Traditional401K"].parse::<f32>().ok()),
261 PreTaxDeduction::HSA(de["HSA"].parse::<f32>().ok()),
262 PreTaxDeduction::FSA(de["FSA"].parse::<f32>().ok()),
263 ]);
264 scene.posttax_deductions = PostTaxDeductions::new(vec![
265 PostTaxDeduction::Roth401K(de["Roth401K"].parse::<f32>().ok()),
266 PostTaxDeduction::VoluntaryLife(de["Voluntary Life"].parse::<f32>().ok()),
267 PostTaxDeduction::VoluntaryADD(de["Voluntary ADD"].parse::<f32>().ok()),
268 PostTaxDeduction::VoluntarySTD(de["Voluntary STD"].parse::<f32>().ok()),
269 PostTaxDeduction::VoluntaryLTD(de["Voluntary LTD"].parse::<f32>().ok()),
270 PostTaxDeduction::WageGarnishment(de["Wage Garnishment"].parse::<f32>().ok()),
271 ]);
272
273 scene
274}