dfwasm_template/
splitter.rs1use std::collections::HashSet;
2
3use crate::{Args, Block, BracketDirection, Item, Template};
4
5const CODE_BLOCK_SIZE: usize = 2;
7const SAFE_SPACE: usize = 4;
8
9pub fn split_templates(templates: Vec<Template>, max_size: usize) -> Vec<Template> {
16 let mut function_names = HashSet::new();
17
18 for template in &templates {
19 if let Some(Block::Function { name, .. }) = template.blocks.first() {
20 function_names.insert(name.clone());
21 }
22 }
23
24 let mut stack = templates;
25 let mut result = Vec::new();
26
27 while let Some(mut template) = stack.pop() {
28 if template.blocks.len() * CODE_BLOCK_SIZE + SAFE_SPACE <= max_size {
30 result.push(template);
31 continue;
32 }
33
34 let new_function_name = match template.blocks.first() {
36 Some(Block::Function { args, name }) => {
37 let mut non_tag_args = args
38 .0
39 .iter()
40 .filter(|(_, item)| !matches!(item, Item::Tag { .. }));
41
42 if non_tag_args.next().is_none() {
43 get_next_function_name(name, &mut function_names)
44 } else {
45 panic!("Functions with parameters are not supported for splitting");
46 }
47 }
48 _ => panic!("Non-functions are not supported for splitting"),
49 };
50
51 let last_global_scope = find_last_global_scope(&template.blocks, max_size);
53
54 let mut right_template = Template::start_function_hidden(new_function_name.clone());
56
57 split_into_existing(
59 &mut template.blocks,
60 last_global_scope,
61 &mut right_template.blocks,
62 );
63
64 template.blocks.push(Block::CallFunction {
66 args: Args::default(),
67 func: new_function_name,
68 });
69
70 result.push(template);
72
73 stack.push(right_template);
75 }
76
77 result
78}
79
80fn find_last_global_scope(blocks: &[Block], max_size: usize) -> usize {
82 let mut bracket_depth = 0;
83 let mut last_global_scope = 0;
84
85 for (i, block) in blocks.iter().enumerate() {
86 match block {
87 Block::IfEntity { .. }
88 | Block::IfGame { .. }
89 | Block::IfPlayer { .. }
90 | Block::IfVariable { .. }
91 | Block::Else => {
92 }
94 Block::Bracket { direction, .. } => match direction {
95 BracketDirection::Open => bracket_depth += 1,
96 BracketDirection::Close => bracket_depth -= 1,
97 },
98 _ => {
99 if (i + 1) * CODE_BLOCK_SIZE + SAFE_SPACE > max_size {
100 break;
102 } else if bracket_depth == 0 {
103 last_global_scope = i;
105 }
106 }
107 }
108 }
109
110 last_global_scope
111}
112
113fn get_next_function_name(s: &str, function_names: &mut HashSet<String>) -> String {
115 let (prefix, mut n) = if let Some(pos) = s.rfind("--") {
116 let prefix = &s[..pos];
117 let suffix = &s[pos + 2..];
118
119 if let Ok(n) = suffix.parse::<usize>() {
120 (prefix.to_string(), n)
121 } else {
122 (s.to_string(), 0)
123 }
124 } else {
125 (s.to_string(), 0)
126 };
127
128 loop {
129 n += 1;
130 let new_name = format!("{}--{}", prefix, n);
131 if !function_names.contains(&new_name) {
132 function_names.insert(new_name.clone());
133 return new_name;
134 }
135 }
136}
137
138fn split_into_existing<T>(vec: &mut Vec<T>, at: usize, target: &mut Vec<T>) {
140 let drained = vec.drain(at..);
141 target.extend(drained);
142}