use std::env;
use std::io::{Read, Write};
use std::net::{TcpListener, TcpStream};
use std::thread;
#[derive(Clone, Copy)]
enum ATMState {
Idle,
CardInserted,
Authenticated,
Ready,
Dispensing,
}
struct ATMServerMachine {
state: ATMState,
}
impl ATMServerMachine {
fn new() -> Self {
Self {
state: ATMState::Idle,
}
}
fn process_input(command: &str, current_state: ATMState) -> (ATMState, &'static str) {
match current_state {
ATMState::Idle => match command {
"INSERT_CARD" => (ATMState::CardInserted, "CARD_ACCEPTED"),
_ => (ATMState::Idle, "INVALID_OP"),
},
ATMState::CardInserted => match command {
"ENTER_PIN" => (ATMState::Authenticated, "PIN_VERIFIED"),
"EJECT_CARD" => (ATMState::Idle, "CARD_EJECTED"),
_ => (ATMState::CardInserted, "RETRY"),
},
ATMState::Authenticated => match command {
"REQUEST_WITHDRAW" => (ATMState::Ready, "ENTER_AMOUNT"),
"EJECT_CARD" => (ATMState::Idle, "CARD_EJECTED"),
"TIMEOUT" => (ATMState::Idle, "SESSION_TIMEOUT"),
_ => (ATMState::Authenticated, "INVALID_COMMAND"),
},
ATMState::Ready => match command {
"REQUEST_WITHDRAW" => (ATMState::Dispensing, "DISPENSING"),
"EJECT_CARD" => (ATMState::Idle, "CARD_EJECTED"),
_ => (ATMState::Ready, "WAIT"),
},
ATMState::Dispensing => match command {
"EJECT_CARD" => (ATMState::Idle, "CARD_EJECTED"),
_ => (ATMState::Dispensing, "DISPENSING"),
},
}
}
fn execute_command(&mut self, command: &str) -> &'static str {
let (next_state, response) = Self::process_input(command, self.state);
self.state = next_state;
response
}
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let addr = env::args()
.nth(1)
.unwrap_or_else(|| "127.0.0.1:3001".to_string());
println!("Custom KB ATM server listening on {}", addr);
let listener = TcpListener::bind(&addr)?;
for stream in listener.incoming() {
match stream {
Ok(mut s) => {
let peer = s.peer_addr()?;
println!("Client connected: {}", peer);
thread::spawn(move || {
if let Err(e) = handle_client(&mut s) {
eprintln!("Client {} error: {}", peer, e);
}
println!("Client {} disconnected", peer);
});
}
Err(e) => eprintln!("Accept error: {}", e),
}
}
Ok(())
}
fn handle_client(stream: &mut TcpStream) -> Result<(), Box<dyn std::error::Error>> {
let mut machine = ATMServerMachine::new();
let mut buf = [0u8; 1024];
loop {
let n = stream.read(&mut buf)?;
if n == 0 {
break; }
let req = String::from_utf8_lossy(&buf[..n]);
let trimmed = req.trim();
println!("Received request: '{}'", trimmed);
let response = machine.execute_command(normalize_request(trimmed));
stream.write_all(response.as_bytes())?;
}
Ok(())
}
fn normalize_request(req: &str) -> &str {
req.strip_prefix("Letter('")
.and_then(|s| s.strip_suffix("')"))
.unwrap_or(req)
}