Skip to main content

paycheck_utils/
interaction.rs

1use 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;
9/// module for user interaction
10/// handles input and output with the user
11/// includes functions for displaying prompts, receiving input, and showing results
12use 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    // get employment scenario input
21    println!("\nFirst, let's create an employment scenario.\n");
22    let scenario = create_scenario();
23
24    // get expenses input
25    println!(
26        "\n{:^100}",
27        "--- Great!, now let's create some expenses. ---"
28    );
29    let expenses = get_expenses();
30
31    // get deductions input
32    println!(
33        "\n{:^100}",
34        "--- Finally, let's create some deductions. ---"
35    );
36    let deductions = get_deductions();
37
38    // // confirm inputs with user
39    // println!("\n{:^100}", "--- Great! Let's make sure everything looks correct. ---");
40    // confirm_inputs(&scenario, &expenses, &deductions);
41
42    // create employment scenario struct
43    convert_inputs_to_struct(scenario, expenses, deductions)
44}
45
46fn create_scenario() -> HashMap<String, String> {
47    // create holding variables
48    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
193// fn confirm_inputs(
194//     scenario: &HashMap<String, String>,
195//     expenses: &HashMap<String, String>,
196//     deductions: &HashMap<String, String>,
197// ) {
198//     let mut input = String::new();
199
200//     println!("\nHere is the scenario we just created:\n");
201
202//     println!("EMPLOYMENT SCENARIO:\n");
203
204//     for (k, v) in scenario.iter() {
205//         println!("{}: {}", k, v);
206//     }
207
208//     println!("\nDoes this look good (y/n)?: {}", input);
209//     io::stdin().read_line(&mut input.trim().to_string()).unwrap_or_default();
210//     input.clear();
211
212//     println!("\nEXPENSES:\n");
213
214//     for (k,v) in expenses.iter() {
215//         println!("{}: {}", k, v);
216//     }
217
218//     println!("\nDoes this look good (y/n)?: {}", input);
219//     io::stdin().read_line(&mut input.trim().to_string()).unwrap_or_default();
220//     input.clear();
221
222//     println!("\nDEDUCTIONS:\n");
223
224//     for (k,v) in deductions.iter() {
225//         println!("{}: {}", k, v);
226//     }
227
228//     println!("\nDoes this look good (y/n)?: {}", input);
229//     io::stdin().read_line(&mut input.trim().to_string()).unwrap_or_default();
230//     input.clear();
231
232//     println!("\n{:^100}", "--- Great! Let's run the numbers! ---");
233
234// }
235
236pub 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}