paycheck_utils/
interaction.rs1use crate::utils::check_converted_value;
10use crate::{
11 EmploymentScenario, Expense, Expenses, PostTaxDeduction, PostTaxDeductions, PreTaxDeduction,
12 PreTaxDeductions,
13};
14use std::any::TypeId;
15use std::collections::HashMap;
16use std::io;
17use std::io::prelude::*;
18
19pub fn get_user_input() -> EmploymentScenario {
22 println!(
23 "\n{:^100}",
24 "--- Let's start by gathering some information. ---"
25 );
26
27 println!("\nFirst, let's create an employment scenario.\n");
29 let scenario = create_scenario();
30
31 println!(
33 "\n{:^100}",
34 "--- Great!, now let's create some expenses. ---"
35 );
36 let expenses = get_expenses();
37
38 println!(
40 "\n{:^100}",
41 "--- Finally, let's create some deductions. ---"
42 );
43 let deductions = get_deductions();
44
45 convert_inputs_to_struct(scenario, expenses, deductions)
47}
48
49fn create_scenario() -> HashMap<String, String> {
51 let mut inputs: HashMap<String, String> = HashMap::new();
52 let mut input = String::new();
53 let employed = ["Rate", "Hours"];
54
55 for value in employed {
56 print!("{value}: ");
57 io::stdout().flush().unwrap_or_default();
58 io::stdin().read_line(&mut input).unwrap_or_default();
59 loop {
60 if check_converted_value(&input.trim().parse::<f32>(), TypeId::of::<f32>()) {
61 break;
62 } else {
63 print!(
64 "Please enter a valid number for {value} (examples: 25, 25.5, or 25.00) --> {value}: "
65 );
66 input.clear();
67 io::stdout().flush().unwrap_or_default();
68 io::stdin().read_line(&mut input).unwrap_or_default();
69 }
70 }
71 inputs
72 .entry(value.trim().to_string())
73 .or_insert(input.trim().to_string());
74 input.clear();
75 }
76
77 inputs
78}
79
80fn get_expenses() -> HashMap<String, String> {
82 let mut inputs: HashMap<String, String> = HashMap::new();
83 let mut input = String::new();
84 let expense_categories = [
85 "Housing",
86 "Energy",
87 "Water",
88 "Gas",
89 "Internet",
90 "Phone",
91 "Car Payment",
92 "Car Insurance",
93 "Car Gas",
94 "Groceries",
95 ];
96
97 println!("\nLiving expenses can vary so enter an estimated amount per month or 0.\n");
98
99 for exp in expense_categories {
100 print!("{exp}: ");
101 io::stdout().flush().unwrap_or_default();
102 io::stdin().read_line(&mut input).unwrap_or_default();
103 loop {
104 if check_converted_value(&input.trim().parse::<f32>(), TypeId::of::<f32>()) {
105 break;
106 } else {
107 print!(
108 "Please enter a valid number for {exp} (examples: 25, 25.5, or 25.00) --> {exp}: "
109 );
110 input.clear();
111 io::stdout().flush().unwrap_or_default();
112 io::stdin().read_line(&mut input).unwrap_or_default();
113 }
114 }
115 inputs
116 .entry(exp.trim().to_string())
117 .or_insert(input.trim().to_string());
118 input.clear();
119 }
120
121 inputs
122}
123
124fn get_deductions() -> HashMap<String, String> {
126 let mut inputs: HashMap<String, String> = HashMap::new();
127 let mut input = String::new();
128 let pretax_categories = [
129 "Medical",
130 "Dental",
131 "Vision",
132 "Traditional401K",
133 "HSA",
134 "FSA",
135 ];
136 let posttax_categories = [
137 "Roth401K",
138 "Voluntary Life",
139 "Voluntary ADD",
140 "Voluntary STD",
141 "Voluntary LTD",
142 "Wage Garnishment",
143 ];
144
145 println!(
146 "\nDeductions come in two flavors: pre-tax and post-tax.\nLet's start with the pre-tax deductions.\n"
147 );
148
149 for pre in pretax_categories {
150 print!("{pre}: ");
151 io::stdout().flush().unwrap_or_default();
152 io::stdin().read_line(&mut input).unwrap_or_default();
153 loop {
154 if check_converted_value(&input.trim().parse::<f32>(), TypeId::of::<f32>()) {
155 break;
156 } else {
157 print!(
158 "Please enter a valid number for {pre} (examples: 25, 25.5, or 25.00) --> {pre}: "
159 );
160 input.clear();
161 io::stdout().flush().unwrap_or_default();
162 io::stdin().read_line(&mut input).unwrap_or_default();
163 }
164 }
165 inputs
166 .entry(pre.trim().to_string())
167 .or_insert(input.trim().to_string());
168 input.clear();
169 }
170
171 println!("\nOk, now the post-tax deductions.\n");
172
173 for post in posttax_categories {
174 print!("{post}: ");
175 io::stdout().flush().unwrap_or_default();
176 io::stdin().read_line(&mut input).unwrap_or_default();
177 loop {
178 if check_converted_value(&input.trim().parse::<f32>(), TypeId::of::<f32>()) {
179 break;
180 } else {
181 print!(
182 "Please enter a valid number for {post} (examples: 25, 25.5, or 25.00) --> {post}: "
183 );
184 input.clear();
185 io::stdout().flush().unwrap_or_default();
186 io::stdin().read_line(&mut input).unwrap_or_default();
187 }
188 }
189 inputs
190 .entry(post.trim().to_string())
191 .or_insert(input.trim().to_string());
192 input.clear();
193 }
194
195 inputs
196}
197
198pub fn convert_inputs_to_struct(
200 sc: HashMap<String, String>,
201 ex: HashMap<String, String>,
202 de: HashMap<String, String>,
203) -> EmploymentScenario {
204 let mut scene = EmploymentScenario::default();
205 scene.hourly_rate = sc["Rate"].parse::<f32>().unwrap_or_default();
206 scene.hours_per_week = sc["Hours"].parse::<f32>().unwrap_or_default();
207 scene.expenses = Expenses::new(vec![
208 Expense::Housing(ex["Housing"].parse::<f32>().ok()),
209 Expense::Energy(ex["Energy"].parse::<f32>().ok()),
210 Expense::Water(ex["Water"].parse::<f32>().ok()),
211 Expense::Gas(ex["Gas"].parse::<f32>().ok()),
212 Expense::Internet(ex["Internet"].parse::<f32>().ok()),
213 Expense::Phone(ex["Phone"].parse::<f32>().ok()),
214 Expense::Vehicle(ex["Car Payment"].parse::<f32>().ok()),
215 Expense::VehicleInsurance(ex["Car Insurance"].parse::<f32>().ok()),
216 Expense::VehicleGas(ex["Car Gas"].parse::<f32>().ok()),
217 Expense::Groceries(ex["Groceries"].parse::<f32>().ok()),
218 ]);
219 scene.pretax_deductions = PreTaxDeductions::new(vec![
220 PreTaxDeduction::Medical(de["Medical"].parse::<f32>().ok()),
221 PreTaxDeduction::Dental(de["Dental"].parse::<f32>().ok()),
222 PreTaxDeduction::Vision(de["Vision"].parse::<f32>().ok()),
223 PreTaxDeduction::Traditional401K(de["Traditional401K"].parse::<f32>().ok()),
224 PreTaxDeduction::HSA(de["HSA"].parse::<f32>().ok()),
225 PreTaxDeduction::FSA(de["FSA"].parse::<f32>().ok()),
226 ]);
227 scene.posttax_deductions = PostTaxDeductions::new(vec![
228 PostTaxDeduction::Roth401K(de["Roth401K"].parse::<f32>().ok()),
229 PostTaxDeduction::VoluntaryLife(de["Voluntary Life"].parse::<f32>().ok()),
230 PostTaxDeduction::VoluntaryADD(de["Voluntary ADD"].parse::<f32>().ok()),
231 PostTaxDeduction::VoluntarySTD(de["Voluntary STD"].parse::<f32>().ok()),
232 PostTaxDeduction::VoluntaryLTD(de["Voluntary LTD"].parse::<f32>().ok()),
233 PostTaxDeduction::WageGarnishment(de["Wage Garnishment"].parse::<f32>().ok()),
234 ]);
235
236 scene
237}