factorio_ir/prune/
struct_utils.rs1use std::collections::HashSet;
2
3use crate::{
4 enumeration::Enum, expression::Expression, function::Function, module::Module,
5 prune::module_graph::ModuleGraph, statement::Statement, structure::Struct,
6};
7
8pub fn find_struct<'a>(module: &'a Module, name: &str) -> Option<&'a Struct> {
10 module
11 .body
12 .statements
13 .iter()
14 .chain(module.symbols.iter().map(|symbol| &symbol.statement))
15 .find_map(|statement| match statement {
16 Statement::StructDecl(struct_decl) if struct_decl.name == name => Some(struct_decl),
17 _ => None,
18 })
19}
20
21pub fn find_enum<'a>(module: &'a Module, name: &str) -> Option<&'a Enum> {
23 module
24 .body
25 .statements
26 .iter()
27 .chain(module.symbols.iter().map(|symbol| &symbol.statement))
28 .find_map(|statement| match statement {
29 Statement::EnumDecl(enum_decl) if enum_decl.name == name => Some(enum_decl),
30 _ => None,
31 })
32}
33
34pub fn struct_exists(module: &Module, name: &str) -> bool {
36 find_struct(module, name).is_some() || find_enum(module, name).is_some()
37}
38
39pub fn find_struct_method<'a>(
41 module: &'a Module,
42 struct_name: &str,
43 method_name: &str,
44) -> Option<&'a Function> {
45 find_struct(module, struct_name)
46 .and_then(|struct_decl| {
47 struct_decl
48 .methods
49 .iter()
50 .find(|method| method.name == method_name)
51 })
52 .or_else(|| {
53 find_enum(module, struct_name).and_then(|enum_decl| {
54 enum_decl
55 .methods
56 .iter()
57 .find(|method| method.name == method_name)
58 })
59 })
60}
61
62#[must_use]
64pub fn find_struct_constant<'a>(
65 module: &'a Module,
66 struct_name: &str,
67 constant_name: &str,
68) -> Option<&'a Expression> {
69 find_struct(module, struct_name)
70 .and_then(|struct_decl| {
71 struct_decl
72 .constants
73 .iter()
74 .find_map(|(name, value)| (name == constant_name).then_some(value))
75 })
76 .or_else(|| {
77 find_enum(module, struct_name).and_then(|enum_decl| {
78 enum_decl
79 .constants
80 .iter()
81 .find_map(|(name, value)| (name == constant_name).then_some(value))
82 })
83 })
84}
85
86pub fn struct_has_constant(module: &Module, struct_name: &str, constant_name: &str) -> bool {
88 find_struct_constant(module, struct_name, constant_name).is_some()
89}
90
91pub fn struct_has_method(module: &Module, struct_name: &str, method_name: &str) -> bool {
93 find_struct_method(module, struct_name, method_name).is_some()
94}
95
96#[must_use]
100pub fn struct_owner_module(graph: &ModuleGraph<'_>, module: &Module, struct_name: &str) -> String {
101 if struct_exists(module, struct_name) {
102 return module.name.clone();
103 }
104
105 for import in &module.imports {
106 for item in &import.items {
107 if item.name == struct_name || item.local == struct_name {
108 return import.module.clone();
109 }
110 }
111
112 if module_defines_struct(graph, &import.module, struct_name) {
113 return import.module.clone();
114 }
115 }
116
117 module.name.clone()
118}
119
120pub fn module_defines_struct(
122 graph: &ModuleGraph<'_>,
123 module_name: &str,
124 struct_name: &str,
125) -> bool {
126 let mut stack = vec![module_name.to_string()];
127 let mut seen = HashSet::new();
128
129 while let Some(current) = stack.pop() {
130 if !seen.insert(current.clone()) {
131 continue;
132 }
133
134 if let Some(module) = graph.get(¤t)
135 && struct_exists(module, struct_name)
136 {
137 return true;
138 }
139
140 if let Some(module) = graph.get(¤t) {
141 for child in graph.child_modules(&module.name) {
142 stack.push((*child).to_string());
143 }
144 }
145 }
146
147 false
148}
149
150pub fn find_struct_method_in_module_tree<'a>(
152 graph: &ModuleGraph<'a>,
153 module_name: &str,
154 struct_name: &str,
155 method_name: &str,
156) -> Option<(String, &'a Function)> {
157 let mut stack = vec![module_name.to_string()];
158 let mut seen = HashSet::new();
159
160 while let Some(current) = stack.pop() {
161 if !seen.insert(current.clone()) {
162 continue;
163 }
164
165 let Some(module) = graph.get(¤t) else {
166 continue;
167 };
168
169 if let Some(method) = find_struct_method(module, struct_name, method_name) {
170 return Some((current, method));
171 }
172
173 for child in graph.child_modules(¤t) {
174 stack.push((*child).to_string());
175 }
176 }
177
178 None
179}
180
181#[must_use]
183pub fn find_struct_constant_in_module_tree<'a>(
184 graph: &ModuleGraph<'a>,
185 module_name: &str,
186 struct_name: &str,
187 constant_name: &str,
188) -> Option<(String, &'a Expression)> {
189 let mut stack = vec![module_name.to_string()];
190 let mut seen = HashSet::new();
191
192 while let Some(current) = stack.pop() {
193 if !seen.insert(current.clone()) {
194 continue;
195 }
196
197 let Some(module) = graph.get(¤t) else {
198 continue;
199 };
200
201 if let Some(value) = find_struct_constant(module, struct_name, constant_name) {
202 return Some((current, value));
203 }
204
205 for child in graph.child_modules(¤t) {
206 stack.push((*child).to_string());
207 }
208 }
209
210 None
211}