arena_core/engine/
inspector.rs1use std::{fs::OpenOptions, io::Seek};
2
3use serde::{Deserialize, Serialize};
4
5pub trait Inspector<V> {
7 fn log(&mut self, value: V);
9
10 fn inspect(&self, step: usize) -> Option<V>;
12
13 fn save(&self);
15}
16
17#[derive(Serialize, Deserialize, Debug, Clone)]
19pub struct LogMessage {
20 pub id: usize,
22
23 pub name: String,
25
26 pub data: String,
28}
29
30impl LogMessage {
31 pub fn new(name: String, data: String) -> Self {
33 Self { id: 0, name, data }
34 }
35}
36
37#[derive(Debug)]
38pub struct Logger {
40 values: Vec<LogMessage>,
41 counter: usize,
42 file_path: String,
43 format: LogFormat,
44}
45
46#[derive(Debug)]
47enum LogFormat {
49 Csv,
50 Json,
51}
52
53impl Logger {
54 pub fn new_csv(file_path: String) -> Self {
56 Self {
57 values: Vec::new(),
58 counter: 0,
59 file_path,
60 format: LogFormat::Csv,
61 }
62 }
63
64 pub fn new_json(file_path: String) -> Self {
66 Self {
67 values: Vec::new(),
68 counter: 0,
69 file_path,
70 format: LogFormat::Json,
71 }
72 }
73
74 fn append_to_file(&self, record: &LogMessage) -> Result<(), Box<dyn std::error::Error>> {
76 let mut file = OpenOptions::new()
77 .append(true)
78 .create(true)
79 .open(&self.file_path)?;
80
81 match self.format {
82 LogFormat::Csv => {
83 let mut writer = csv::Writer::from_writer(file);
84 writer.serialize((record.id, &record.name, &record.data))?;
85 writer.flush()?;
86 }
87 LogFormat::Json => {
88 let mut records: Vec<LogMessage> = if file.metadata()?.len() > 0 {
89 serde_json::from_reader(&file)?
90 } else {
91 Vec::new()
92 };
93 records.push(record.clone());
94 file.set_len(0)?;
95 file.seek(std::io::SeekFrom::Start(0))?;
96 serde_json::to_writer_pretty(file, &records)?;
97 }
98 }
99 Ok(())
100 }
101}
102
103impl Inspector<LogMessage> for Logger {
104 fn log(&mut self, mut value: LogMessage) {
105 value.id = self.counter;
106 self.counter += 1;
107 self.values.push(value.clone());
108
109 if let Err(e) = self.append_to_file(&value) {
110 eprintln!("Failed to append to file: {}", e);
111 }
112 }
113
114 fn inspect(&self, step: usize) -> Option<LogMessage> {
115 self.values.get(step).cloned()
116 }
117
118 fn save(&self) {}
119}
120
121pub struct EmptyInspector;
123
124impl Inspector<f64> for EmptyInspector {
125 fn inspect(&self, _step: usize) -> Option<f64> {
126 None
127 }
128 fn log(&mut self, _value: f64) {}
129 fn save(&self) {}
130}