use std::{ collections::HashMap, fs::File, io::{self, BufRead, BufReader, Read, Write}, path::Path, process::ExitCode, str::FromStr, }; use petri_nets::{FireDirection, FireState, SimulatedNet, Simulation, SimulationStateMut}; use pns::{Net, Tid}; fn main() -> ExitCode { let mut save = false; let mut load = false; let mut args = std::env::args(); args.next(); for arg in args { match arg.as_ref() { "save" => save = true, "load" => load = true, _ => eprintln!("Ignore invalid argument '{arg}'"), } } let Ok(net) = Net::load("examples/example.pn".as_ref()) else { eprintln!("Failed to load example net"); return ExitCode::FAILURE; }; let mut net = SimulatedNet::new(net); let mut names = HashMap::new(); let Ok(file) = File::open("examples/example.pnk") else { eprintln!("Failed to load example keys"); return ExitCode::FAILURE; }; for (tid, line) in net.transition_ids().zip(BufReader::new(file).lines()) { let Ok(line) = line else { eprintln!("Failed to read key"); return ExitCode::FAILURE; }; names.insert(tid, line); } if save && net.save("examples/example_copy.pns".as_ref()).is_err() { eprintln!("Failed to save example net"); } if load { fn read_values(filename: &Path) -> std::io::Result> { let size = (std::fs::metadata(filename)?.len() + 3) / 4; let mut file = File::open(filename)?; (0..size) .map(|_| { let mut value = [0; 4]; file.read_exact(&mut value)?; Ok(u32::from_le_bytes(value)) }) .collect() } let Ok(data) = read_values("examples/example.pns".as_ref()) else { eprintln!("Reading state data failed"); return ExitCode::FAILURE; }; if net.add_simulation_from_data(data).is_err() { eprintln!("State initialization failed"); return ExitCode::FAILURE; } } else { net.add_simulation(); } enum StepAction { Continue, Reverse, Save, Quit, } let mut forward = true; for mut simulation in &mut net { fn step( fire: FireState, names: &HashMap, ) -> StepAction { if fire.callables.is_empty() { return StepAction::Reverse; } for (i, tid) in fire.callables.iter().enumerate() { println!("{}: {}", i + 1, names[tid]); } let stdin = io::stdin(); let mut string = String::new(); let Ok(size) = stdin.read_line(&mut string) else { eprintln!("Input error"); return StepAction::Continue; }; if size == 0 { return StepAction::Continue; } match unsafe { string.chars().next().unwrap_unchecked() } { 'r' => return StepAction::Reverse, 'q' => return StepAction::Quit, 's' => return StepAction::Save, _ => (), } match usize::from_str(&string[..(string.len() - 1)]) { Ok(num) if num != 0 && num <= fire.callables.len() => { fire.call(num - 1); } _ => { println!( "You have to input a valid number from 1 to {}", fire.callables.len() ); println!("You can also quit by writing \"q\", save the current state by writing \"s\" or reverse by writing \"r\""); return StepAction::Continue; } } StepAction::Continue } loop { let step_action = if forward { step(simulation.prepare_call(), &names) } else { step(simulation.prepare_revert(), &names) }; use StepAction::*; match step_action { Continue => (), Reverse => { println!("Reverse play direction!"); forward = !forward; } Save => { fn save(data: &[usize], filename: &Path) -> std::io::Result<()> { let mut file = File::create(filename)?; for &count in data { file.write_all(&(count as u32).to_le_bytes())?; } Ok(()) } let data = simulation.data(); println!( "{}", if save(data, "examples/example.pns".as_ref()).is_ok() { "Saving successful" } else { "Saving failed" } ); } Quit => break, } } } ExitCode::SUCCESS }