envelope_cli/setup/steps/
account.rs

1//! Account setup step
2//!
3//! Creates the user's first account with an optional starting balance.
4
5use std::io::{self, Write};
6
7use crate::error::{EnvelopeError, EnvelopeResult};
8use crate::models::account::{Account, AccountType};
9use crate::models::Money;
10
11/// Account setup step result
12pub struct AccountSetupResult {
13    /// The created account
14    pub account: Account,
15    /// Starting balance as income (to be added to Available to Budget)
16    pub starting_balance: Money,
17}
18
19/// Account setup step
20pub struct AccountSetupStep;
21
22impl AccountSetupStep {
23    /// Run the account setup step
24    pub fn run() -> EnvelopeResult<AccountSetupResult> {
25        println!();
26        println!("Step 1: Create Your First Account");
27        println!("==================================");
28        println!();
29        println!("Let's create your first account. This is typically your main checking");
30        println!("account that you use for everyday spending.");
31        println!();
32
33        // Get account name
34        let name = prompt_string("Account name (e.g., 'Checking', 'Main Account'): ")?;
35        if name.is_empty() {
36            return Err(EnvelopeError::Validation(
37                "Account name cannot be empty".into(),
38            ));
39        }
40
41        // Get account type
42        println!();
43        println!("Account type:");
44        println!("  1. Checking (default)");
45        println!("  2. Savings");
46        println!("  3. Credit Card");
47        println!("  4. Cash");
48        println!("  5. Other");
49        let type_choice = prompt_string("Select account type [1]: ")?;
50        let account_type = match type_choice.trim() {
51            "" | "1" => AccountType::Checking,
52            "2" => AccountType::Savings,
53            "3" => AccountType::Credit,
54            "4" => AccountType::Cash,
55            "5" => AccountType::Other,
56            _ => AccountType::Checking,
57        };
58
59        // Get starting balance
60        println!();
61        println!("What is the current balance of this account?");
62        println!("(This will be added to your 'Available to Budget')");
63        let balance_str = prompt_string("Starting balance (e.g., 1000.00): ")?;
64        let starting_balance = if balance_str.is_empty() {
65            Money::zero()
66        } else {
67            Money::parse(&balance_str)
68                .map_err(|e| EnvelopeError::Validation(format!("Invalid amount: {}", e)))?
69        };
70
71        // Create the account
72        let account = Account::new(name, account_type);
73
74        println!();
75        println!(
76            "Account '{}' will be created with balance {}",
77            account.name, starting_balance
78        );
79
80        Ok(AccountSetupResult {
81            account,
82            starting_balance,
83        })
84    }
85}
86
87/// Prompt for a string input
88fn prompt_string(prompt: &str) -> EnvelopeResult<String> {
89    print!("{}", prompt);
90    io::stdout().flush()?;
91
92    let mut input = String::new();
93    io::stdin().read_line(&mut input)?;
94
95    Ok(input.trim().to_string())
96}