starknet_devnet_server/
dump_util.rs

1use std::fs::{self, File, OpenOptions};
2use std::io::{Read, Seek, SeekFrom, Write};
3use std::path::Path;
4
5use starknet_core::error::{DevnetResult, Error};
6use starknet_core::starknet::starknet_config::DumpOn;
7
8use crate::rpc_core::request::RpcMethodCall;
9
10pub type DumpEvent = RpcMethodCall;
11
12/// Saves Devnet `events` to the file at `path`. If `events` is empty, does nothing.
13pub fn dump_events(events: &Vec<DumpEvent>, path: &str) -> DevnetResult<()> {
14    if !events.is_empty() {
15        let events_dump = serde_json::to_string(events)
16            .map_err(|e| Error::SerializationError { origin: e.to_string() })?;
17        fs::write(Path::new(&path), events_dump)?;
18    }
19
20    Ok(())
21}
22
23/// Attaches starknet event to the end of the file at `path`. If no file present, creates it.
24pub fn dump_event(event: &DumpEvent, path: &str) -> DevnetResult<()> {
25    let file_path = Path::new(path);
26    if file_path.exists() {
27        // attach to file
28        let event_dump = serde_json::to_string(event)
29            .map_err(|e| Error::SerializationError { origin: e.to_string() })?;
30        let mut file =
31            OpenOptions::new().append(true).read(true).open(file_path).map_err(Error::IoError)?;
32        let mut buffer = [0; 1];
33        file.seek(SeekFrom::End(-1))?;
34        file.read_exact(&mut buffer)?;
35        if String::from_utf8_lossy(&buffer).into_owned() == "]" {
36            // if the last character is "]", remove it and add event at the end
37            let length = file.seek(SeekFrom::End(0)).map_err(Error::IoError)?;
38            file.set_len(length - 1).map_err(Error::IoError)?; // remove last "]" with set_len
39            file.write_all(format!(", {event_dump}]").as_bytes()).map_err(Error::IoError)?;
40        } else {
41            // if the last character is not "]" it means that it's a wrongly formatted
42            // file
43            return Err(Error::FormatError);
44        }
45    } else {
46        // create file
47        let events = vec![event];
48        let events_dump = serde_json::to_string(&events)
49            .map_err(|e| Error::SerializationError { origin: e.to_string() })?;
50        fs::write(Path::new(&path), events_dump)?;
51    }
52
53    Ok(())
54}
55
56/// Returns Devnet events from the provided `path`
57pub fn load_events(dump_on: Option<DumpOn>, path: &str) -> DevnetResult<Vec<DumpEvent>> {
58    let file_path = Path::new(path);
59    if path.is_empty() || !file_path.exists() {
60        return Err(Error::FileNotFound);
61    }
62
63    let file = File::open(file_path).map_err(Error::IoError)?;
64    let events: Vec<DumpEvent> = serde_json::from_reader(file)
65        .map_err(|e| Error::DeserializationError { origin: e.to_string() })?;
66
67    // to avoid doublets in block mode during load, we need to remove the file
68    // because they will be re-executed and saved again
69    if let Some(DumpOn::Block) = dump_on {
70        // TODO refactor: this shouldn't be the responsibility of this method
71        fs::remove_file(file_path).map_err(Error::IoError)?;
72    }
73
74    Ok(events)
75}