1use std::{
4 fs::File,
5 io::{Result, Write},
6};
7
8use toml::Table;
9
10use super::GDExtension;
11use crate::{args::icons::IconsConfig, NODES_RUST, NODES_RUST_FILENAMES};
12
13#[cfg(feature = "find_icons")]
14use crate::args::icons::DefaultNodeIcon;
15#[cfg(feature = "find_icons")]
16use glob::glob;
17#[cfg(feature = "find_icons")]
18use regex::{Match, Regex};
19#[cfg(feature = "find_icons")]
20use std::{
21 collections::HashMap,
22 io::{BufRead, BufReader},
23};
24
25impl GDExtension {
47 pub fn generate_icons(&mut self, icons_config: IconsConfig) -> Result<&mut Self> {
59 let mut icons = Table::new();
60
61 #[cfg(feature = "find_icons")]
62 if icons_config.default != DefaultNodeIcon::Node {
63 let mut base_class_to_nodes = HashMap::<String, Vec<String>>::new();
64
65 find_children(&mut base_class_to_nodes)?;
66
67 for (icon, nodes) in base_class_to_nodes {
68 for node in nodes {
69 icons.insert(
70 node,
71 match icons_config.default {
72 DefaultNodeIcon::BaseClass => format!(
73 "{}{}.svg",
74 &icons_config
75 .directories
76 .relative_directory
77 .unwrap_or_default()
78 .as_str(),
79 (&icons_config.directories.base_directory)
80 .join(&icons_config.directories.editor_directory)
81 .join(&icon)
82 .to_string_lossy()
83 .replace('\\', "/")
84 )
85 .into(),
86 DefaultNodeIcon::Custom(ref custom_path) => format!(
87 "{}{}",
88 &icons_config
89 .directories
90 .relative_directory
91 .unwrap_or_default()
92 .as_str(),
93 (&icons_config.directories.base_directory)
94 .join(&custom_path)
95 .to_string_lossy()
96 .replace('\\', "/")
97 )
98 .into(),
99 DefaultNodeIcon::NodeRust(node_rust, ref rust_path) => format!(
100 "{}{}/{}",
101 &icons_config
102 .directories
103 .relative_directory
104 .unwrap_or_default()
105 .as_str(),
106 (&icons_config.directories.base_directory)
107 .join(&rust_path)
108 .to_string_lossy()
109 .replace('\\', "/"),
110 NODES_RUST_FILENAMES[node_rust as usize],
111 )
112 .into(),
113 DefaultNodeIcon::Node => "ERROR".into(),
114 },
115 );
116 }
117 }
118 }
119
120 if let Some(custom_icons) = &icons_config.custom_icons {
121 for (node, icon) in custom_icons {
122 icons.insert(
123 node.clone(),
124 format!(
125 "{}{}",
126 &icons_config
127 .directories
128 .relative_directory
129 .unwrap_or_default()
130 .as_str(),
131 (&icons_config.directories.base_directory)
132 .join(&icons_config.directories.custom_directory)
133 .join(icon)
134 .to_string_lossy()
135 .replace('\\', "/")
136 )
137 .into(),
138 );
139 }
140 }
141
142 #[allow(unused_mut)]
143 let mut copy_files = icons_config.copy_strategy.copy_all;
144 #[cfg(feature = "find_icons")]
145 {
146 copy_files |= icons_config.copy_strategy.copy_node_rust;
147 }
148
149 if copy_files {
150 let base_directory_path = icons_config.copy_strategy.path_node_rust;
151 let mut nodes_rust = Vec::new();
152
153 if icons_config.copy_strategy.copy_all {
154 nodes_rust.extend(NODES_RUST_FILENAMES.into_iter().zip(NODES_RUST));
155 } else {
156 #[cfg(feature = "find_icons")]
157 if icons_config.copy_strategy.copy_node_rust {
158 if let DefaultNodeIcon::NodeRust(node_rust, _) = icons_config.default {
159 nodes_rust.push((
160 NODES_RUST_FILENAMES[node_rust as usize],
161 NODES_RUST[node_rust as usize],
162 ));
163 }
164 }
165 }
166
167 for (file_name, node_rust) in nodes_rust {
168 let path_node_rust = (&base_directory_path).join(file_name);
169 if icons_config.copy_strategy.force_copy | !path_node_rust.exists() {
170 File::create(path_node_rust)?.write_all(node_rust.as_bytes())?;
171 }
172 }
173 }
174
175 self.icons = Some(icons);
176
177 Ok(self)
178 }
179}
180
181#[cfg(feature = "find_icons")]
192fn find_children(base_class_to_nodes: &mut HashMap<String, Vec<String>>) -> Result<()> {
193 let base_class_regex = if cfg!(feature = "complex_find") {
195 Regex::new(r"base(\s|(\/\/.*?[\r\n])|(\/\*.*?\*\/))*\=(\s|(\/\/.*?[\r\n])|(\/\*.*?\*\/))*[\w_\d]+(\s|(\/\/.*?[\r\n])|(\/\*.*?\*\/))*[),]")
196 } else {
197 Regex::new(r"base\s*\=\s*[\w_\d]+\s*[),]")
198 }.expect("Invalid regex pattern.");
199 let struct_regex = if cfg!(feature = "complex_find") {
201 Regex::new(r"struct(\s|(\/\/.*?[\r\n])|(\/\*.*?\*\/))*[\w_\d]+(\s|(\/\/.*?[\r\n])|(\/\*.*?\*\/))*[{;<]")
202 } else {
203 Regex::new(r"struct\s*[\w_\d]+\s*[{;<]")
204 }.expect("Invalid regex pattern.");
205
206 let mut base_class = String::new();
207 let mut struct_class;
208 let mut found_base;
209
210 for path_glob in glob("./src/**/*.rs").unwrap() {
211 let path;
212 match path_glob {
213 Ok(pathbuf) => path = pathbuf,
214 Err(_) => continue,
215 }
216 found_base = false;
217 for line in BufReader::new(File::open(path)?).lines() {
218 let line: String = line?;
219 if !line.starts_with("///") & line.contains("base") & line.contains("=") {
220 base_class = if let Some(base_class_match) = base_class_regex.find(&line) {
221 Match::as_str(&base_class_match)
222 .replace("base", "")
223 .replace('=', "")
224 } else {
225 continue;
226 };
227 base_class.pop();
229 base_class = base_class.trim().to_owned();
230 if !base_class_to_nodes.contains_key(&base_class) {
231 base_class_to_nodes.insert(base_class.clone(), Vec::new());
232 }
233 found_base = true;
234 } else if found_base & !line.starts_with("///") & line.contains("struct") {
235 struct_class = if let Some(struct_class_match) = struct_regex.find(&line) {
236 Match::as_str(&struct_class_match).replace("struct", "")
237 } else {
238 continue;
239 };
240 struct_class.pop();
242 struct_class = struct_class.trim().to_owned();
243 base_class_to_nodes
244 .get_mut(&base_class)
245 .expect("The map doesn't contain the key that was just pushed to it.")
246 .push(struct_class);
247 found_base = false;
248 }
249 }
250 }
251
252 Ok(())
253}