Skip to main content

pn_editor_core/editor/input/files/
load.rs

1use super::Editor;
2
3use crate::{net::Net, text_content::TextContent, vector::Vector};
4
5use header_parsing::parse_header;
6use text_editing::TextLine;
7
8use std::{
9    collections::HashMap,
10    fs::File,
11    io::{BufRead, BufReader, Error as IOError},
12    path::{Path, PathBuf},
13};
14
15pub enum Error {
16    IO(IOError),
17    NoDirectorySpecified,
18    SubheaderWithoutHeader(usize),
19}
20
21impl From<IOError> for Error {
22    fn from(err: IOError) -> Self {
23        Self::IO(err)
24    }
25}
26
27fn load_keys(path: &Path) -> Result<Vec<String>, IOError> {
28    let Ok(file) = File::open(path) else {
29        return Ok(Vec::new());
30    };
31
32    let mut result = Vec::new();
33    for line in BufReader::new(file).lines() {
34        result.push(line?);
35    }
36
37    Ok(result)
38}
39
40struct SortedTextMap {
41    names: Vec<Vec<Box<str>>>,
42    contents: HashMap<Vec<Box<str>>, Vec<TextLine>>,
43}
44
45impl SortedTextMap {
46    fn new(names: Vec<Vec<Box<str>>>) -> Self {
47        Self {
48            names,
49            contents: HashMap::new(),
50        }
51    }
52
53    fn add_content(&mut self, key: &Vec<Box<str>>, content: Vec<TextLine>) {
54        if self.names.contains(key) {
55            self.contents.insert(key.clone(), content);
56        }
57    }
58}
59
60impl Editor {
61    fn load_layout(&mut self, file: &mut File) -> Result<(), Error> {
62        let mut removed_tids = Vec::new();
63        for (&tid, node) in &mut self.nodes.transitions.nodes {
64            use std::io::Read;
65            let mut buffer: [u8; 8] = unsafe { std::mem::zeroed() };
66            file.read_exact(&mut buffer)?;
67            if 0x7FFFFFFF == u64::from_le_bytes(buffer) {
68                removed_tids.push(tid);
69            } else {
70                node.position = Vector::new([
71                    f32::from_le_bytes([buffer[0], buffer[1], buffer[2], buffer[3]]),
72                    f32::from_le_bytes([buffer[4], buffer[5], buffer[6], buffer[7]]),
73                ]);
74            }
75        }
76
77        let mut removed_pids = Vec::new();
78        for (&pid, node) in &mut self.nodes.places.nodes {
79            use std::io::Read;
80            node.positions.clear();
81            let mut buffer: [u8; 8] = unsafe { std::mem::zeroed() };
82            file.read_exact(&mut buffer)?;
83            if 0x7FFFFFFF != u64::from_le_bytes(buffer) {
84                let (first, count) = (
85                    f32::from_le_bytes([buffer[0], buffer[1], buffer[2], buffer[3]]),
86                    u32::from_le_bytes([buffer[4], buffer[5], buffer[6], buffer[7]]),
87                );
88
89                if first.is_nan() {
90                    for _i in 0..count {
91                        file.read_exact(&mut buffer)?;
92                        node.positions.push(Vector::new([
93                            f32::from_le_bytes([buffer[0], buffer[1], buffer[2], buffer[3]]),
94                            f32::from_le_bytes([buffer[4], buffer[5], buffer[6], buffer[7]]),
95                        ]));
96                    }
97                } else {
98                    node.positions.push(Vector::new([
99                        f32::from_le_bytes([buffer[0], buffer[1], buffer[2], buffer[3]]),
100                        f32::from_le_bytes([buffer[4], buffer[5], buffer[6], buffer[7]]),
101                    ]));
102                }
103            }
104            if node.positions.is_empty() {
105                removed_pids.push(pid);
106            }
107        }
108
109        for tid in removed_tids {
110            self.nodes.transitions.remove(tid);
111        }
112        for pid in removed_pids {
113            self.nodes.places.remove(pid, self.graph.net());
114        }
115
116        Ok(())
117    }
118
119    fn load_story(file: &File, text_map: &mut SortedTextMap) -> Result<(), Error> {
120        let mut first = false;
121        let mut empty_count = 0;
122        let mut current_key = Vec::new();
123        let mut current_content = Vec::new();
124
125        for (line_number, line) in BufReader::new(file).lines().enumerate() {
126            let line = line?;
127
128            if let Some(success) = parse_header(&mut current_key, &line) {
129                let Ok(changes) = success else {
130                    return Err(Error::SubheaderWithoutHeader(line_number));
131                };
132
133                first = true;
134                if !changes.path.is_empty() {
135                    text_map.add_content(changes.path, current_content);
136                    current_content = Vec::new();
137                    empty_count = 0;
138                }
139
140                changes.apply();
141
142                continue;
143            }
144
145            if line.is_empty() {
146                if first {
147                    first = false;
148                } else {
149                    empty_count += 1;
150                }
151
152                continue;
153            }
154
155            if !current_key.is_empty() {
156                current_content.extend((0..empty_count).map(|_| TextLine::new()));
157                empty_count = 0;
158                current_content.push(TextLine::from_string(line));
159            }
160        }
161
162        if !current_key.is_empty() {
163            text_map.add_content(&current_key, current_content);
164        }
165
166        Ok(())
167    }
168
169    /// Loads the net from a specified path.
170    pub fn load_net(&mut self, folder_path: PathBuf) -> Result<(), Error> {
171        let Some((pns, pnl, pnk, pnkp, story)) = Self::project_paths(&folder_path) else {
172            return Err(Error::NoDirectorySpecified);
173        };
174
175        let net = Net::load(pns.as_ref()).unwrap_or_default();
176
177        let names: Vec<Vec<Box<str>>> = load_keys(&pnk)?
178            .into_iter()
179            .map(|name| name.split('#').map(Into::into).collect())
180            .collect();
181        let place_names: Vec<(Option<Box<str>>, Box<str>)> = load_keys(&pnkp)?
182            .into_iter()
183            .map(|name| {
184                if let Some((aspect, name)) = name.split_once('#') {
185                    (Some(aspect.into()), name.into())
186                } else {
187                    (None, name.into())
188                }
189            })
190            .collect();
191
192        let mut text_map = SortedTextMap::new(names);
193        if let Ok(file) = File::open(story) {
194            Self::load_story(&file, &mut text_map)?;
195        }
196        let SortedTextMap {
197            names,
198            mut contents,
199        } = text_map;
200
201        self.project_path = Some(folder_path);
202        self.reset(net, names, place_names);
203
204        let mut added_contents = HashMap::new();
205        for (&tid, node) in &self.nodes.transitions.nodes {
206            let mut path = node.path.clone();
207            path.push(node.name.as_str().into());
208            let Some(content) = contents.remove(&path) else {
209                continue;
210            };
211
212            if !content.is_empty() {
213                added_contents.insert(tid, content);
214            }
215        }
216        for (tid, content) in added_contents {
217            unsafe {
218                self.nodes
219                    .transitions
220                    .nodes
221                    .get_mut(&tid)
222                    .unwrap_unchecked()
223            }
224            .content = TextContent::new(content);
225        }
226
227        if let Ok(mut file) = File::open(pnl) {
228            self.load_layout(&mut file)?;
229        } else {
230            self.auto_layout();
231        }
232
233        Ok(())
234    }
235
236    /// Loads a new net.
237    pub fn load(&mut self, force_path: bool, ignore_save_warning: bool) {
238        if !ignore_save_warning && !self.ensure_saved(false) {
239            return;
240        }
241        let path = if !force_path && let Some(path) = &self.project_path {
242            path.clone()
243        } else if let Some(path) = self.file_dialog().pick_folder() {
244            path
245        } else {
246            return;
247        };
248
249        let _ = self.load_net(path);
250    }
251}