1use std::collections::{HashMap, HashSet};
4
5use crate::config::Blocklist;
6use crate::context::CodegenContext;
7use crate::schema::*;
8use crate::type_map;
9
10pub fn apply_filters(ctx: &mut CodegenContext, blocklist: &Blocklist) {
12 let available_types: HashSet<String> = ctx
14 .classes
15 .keys()
16 .chain(ctx.structs.keys())
17 .chain(ctx.enums.keys())
18 .cloned()
19 .collect();
20
21 let blocked_classes: HashSet<&str> = blocklist.classes.iter().map(|s| s.as_str()).collect();
23 let blocked_structs: HashSet<&str> = blocklist.structs.iter().map(|s| s.as_str()).collect();
24 let blocked_functions: Vec<(String, String)> = blocklist.function_tuples();
25
26 for cls in &blocklist.classes {
28 ctx.classes.remove(cls);
29 }
30
31 for classes in ctx.module_classes.values_mut() {
32 classes.retain(|c| !blocked_classes.contains(c.name.as_str()));
34
35 for class in classes.iter_mut() {
36 class
38 .props
39 .retain(|p| is_property_exportable(p, &available_types));
40
41 filter_functions(&class.name, &mut class.funcs, &available_types, &blocked_structs, &blocked_functions);
43 }
44 }
45}
46
47fn is_property_exportable(prop: &PropertyInfo, available: &HashSet<String>) -> bool {
49 if !type_map::is_supported_type(&prop.prop_type) {
51 return false;
52 }
53
54 if prop.array_dim > 1 {
56 match prop.prop_type.as_str() {
57 "StrProperty" | "NameProperty" | "TextProperty" => return false,
58 _ => {} }
60 }
61
62 if prop.prop_flags & CPF_NATIVE_ACCESS_PRIVATE != 0 {
64 return false;
65 }
66 if prop.prop_flags & CPF_NATIVE_ACCESS_PROTECTED != 0 {
67 return false;
68 }
69
70 if is_delegate_type(&prop.prop_type) {
72 return is_delegate_exportable(prop, available);
73 }
74
75 if let Some(ref cls) = prop.class_name {
77 if !available.contains(cls) {
78 return false;
79 }
80 }
81 if let Some(ref sn) = prop.struct_name {
82 if !available.contains(sn) {
83 return false;
84 }
85 }
86 if let Some(ref en) = prop.enum_name {
87 if !available.contains(en) {
88 return false;
89 }
90 }
91 if let Some(ref iface) = prop.interface_name {
92 if !available.contains(iface) {
93 return false;
94 }
95 }
96 if prop.prop_type == "ClassProperty" {
97 if let Some(ref meta_cls) = prop.meta_class_name {
98 if !available.contains(meta_cls) {
99 return false;
100 }
101 }
102 }
103
104 true
105}
106
107fn is_delegate_type(prop_type: &str) -> bool {
108 matches!(
109 prop_type,
110 "DelegateProperty" | "MulticastInlineDelegateProperty" | "MulticastSparseDelegateProperty"
111 )
112}
113
114fn is_delegate_exportable(prop: &PropertyInfo, available: &HashSet<String>) -> bool {
116 let func_info = match &prop.func_info {
117 Some(fi) => fi,
118 None => return false, };
120
121 let params = match func_info.get("params").and_then(|p| p.as_array()) {
123 Some(params) => params,
124 None => return true, };
126
127 for param_value in params {
128 let param_type = match param_value.get("type").and_then(|t| t.as_str()) {
129 Some(t) => t,
130 None => return false,
131 };
132
133 if !type_map::is_supported_type(param_type) {
135 return false;
136 }
137
138 if is_delegate_type(param_type) {
140 return false;
141 }
142 if matches!(param_type, "ArrayProperty" | "MapProperty" | "SetProperty") {
143 return false;
144 }
145
146 if let Some(cls) = param_value.get("class_name").and_then(|v| v.as_str()) {
148 if !available.contains(cls) {
149 return false;
150 }
151 }
152 if let Some(sn) = param_value.get("struct_name").and_then(|v| v.as_str()) {
153 if !available.contains(sn) {
154 return false;
155 }
156 }
157 if let Some(en) = param_value.get("enum_name").and_then(|v| v.as_str()) {
158 if !available.contains(en) {
159 return false;
160 }
161 }
162 }
163
164 true
165}
166
167fn is_container_param_exportable(param: &ParamInfo, available: &HashSet<String>) -> bool {
169 match param.prop_type.as_str() {
170 "ArrayProperty" => {
171 if let Some(ref inner) = param.inner_prop {
172 is_inner_type_exportable(inner, available)
173 } else {
174 false
175 }
176 }
177 "MapProperty" => {
178 let key_ok = param.key_prop.as_ref()
179 .map(|k| is_inner_type_exportable(k, available))
180 .unwrap_or(false);
181 let val_ok = param.value_prop.as_ref()
182 .map(|v| is_inner_type_exportable(v, available))
183 .unwrap_or(false);
184 key_ok && val_ok
185 }
186 "SetProperty" => {
187 if let Some(ref elem) = param.element_prop {
188 is_inner_type_exportable(elem, available)
189 } else {
190 false
191 }
192 }
193 _ => false,
194 }
195}
196
197fn is_inner_type_exportable(inner: &PropertyInfo, available: &HashSet<String>) -> bool {
199 if !type_map::is_supported_type(&inner.prop_type) {
200 return false;
201 }
202 if matches!(inner.prop_type.as_str(), "ArrayProperty" | "MapProperty" | "SetProperty") {
204 return false;
205 }
206 if let Some(ref cls) = inner.class_name {
207 if !available.contains(cls) {
208 return false;
209 }
210 }
211 if let Some(ref sn) = inner.struct_name {
212 if !available.contains(sn) {
213 return false;
214 }
215 }
216 if let Some(ref en) = inner.enum_name {
217 if !available.contains(en) {
218 return false;
219 }
220 }
221 if let Some(ref iface) = inner.interface_name {
222 if !available.contains(iface) {
223 return false;
224 }
225 }
226 true
227}
228
229fn filter_functions(
231 class_name: &str,
232 funcs: &mut Vec<FunctionInfo>,
233 available: &HashSet<String>,
234 blocked_structs: &HashSet<&str>,
235 blocked_functions: &[(String, String)],
236) {
237 let all_names: HashSet<String> = funcs.iter().map(|f| f.name.clone()).collect();
239
240 funcs.retain(|f| {
242 if blocked_functions.iter().any(|(c, func)| c == class_name && func == &f.name) {
244 return false;
245 }
246
247 if f.func_flags & FUNC_NATIVE == 0 {
249 return false;
250 }
251
252 if f.name.starts_with("K2_") {
254 let base_name = &f.name[3..];
255 if all_names.contains(base_name) {
256 return false;
257 }
258 }
259
260 for param in &f.params {
262 if !type_map::is_supported_type(¶m.prop_type) {
263 return false;
264 }
265 if is_delegate_type(¶m.prop_type) {
267 return false;
268 }
269 if matches!(param.prop_type.as_str(), "ArrayProperty" | "MapProperty" | "SetProperty") {
271 if !is_container_param_exportable(param, available) {
272 return false;
273 }
274 }
275 if let Some(ref cls) = param.class_name {
276 if !available.contains(cls) {
277 return false;
278 }
279 }
280 if let Some(ref sn) = param.struct_name {
281 if !available.contains(sn) || blocked_structs.contains(sn.as_str()) {
282 return false;
283 }
284 }
285 if let Some(ref en) = param.enum_name {
286 if !available.contains(en) {
287 return false;
288 }
289 }
290 if let Some(ref iface) = param.interface_name {
291 if !available.contains(iface) {
292 return false;
293 }
294 }
295 if param.prop_type == "ClassProperty" {
296 if let Some(ref meta_cls) = param.meta_class_name {
297 if !available.contains(meta_cls) {
298 return false;
299 }
300 }
301 }
302 }
303
304 true
305 });
306
307 for f in funcs.iter_mut() {
309 f.ue_name = f.name.clone();
310 }
311
312 let mut name_counts: HashMap<String, usize> = HashMap::new();
314 for f in funcs.iter() {
315 *name_counts.entry(f.name.clone()).or_default() += 1;
316 }
317
318 let mut name_indices: HashMap<String, usize> = HashMap::new();
319 for f in funcs.iter_mut() {
320 if let Some(&count) = name_counts.get(&f.name) {
321 if count > 1 {
322 let idx = name_indices.entry(f.name.clone()).or_insert(0);
323 *idx += 1;
324 f.name = format!("{}_{}", f.name, idx);
325 }
326 }
327 }
328}