factorio_codegen/generator/
table.rs1use factorio_ir::{function::Function, module::Module, scope::Scope, structure::Struct};
2
3use crate::{LuaGenerator, LuaGeneratorError, LuaGeneratorResult};
4
5impl LuaGenerator {
6 pub(crate) fn generate_struct(
8 &mut self,
9 struct_decl: &Struct,
10 module: Option<&Module>,
11 scope: Scope,
12 module_name: Option<&str>,
13 ) -> LuaGeneratorResult<()> {
14 if scope == Scope::Private && module_name.is_some() {
15 return Err(LuaGeneratorError::StructLocalAndExported(
16 struct_decl.name.clone(),
17 ));
18 }
19
20 if let Some(module) = module
21 && module.is_imported_type_extension(struct_decl)
22 {
23 let table_path = module
24 .imported_item_local(&struct_decl.name)
25 .ok_or_else(|| {
26 LuaGeneratorError::FailedToGetTablePathForStruct(struct_decl.name.clone())
27 })?
28 .to_string();
29
30 for (name, value) in &struct_decl.constants {
31 let value = self.generate_expression(value);
32 self.write_line(&format!("{table_path}.{name} = {value}"));
33 }
34
35 for method in &struct_decl.methods {
36 self.generate_table_method(method, &struct_decl.name, &table_path)?;
37 }
38
39 return Ok(());
40 }
41
42 let table_path = match (scope, module_name) {
43 (Scope::Public, Some(module_name)) => {
44 format!("{module_name}.{}", struct_decl.name)
45 }
46 (Scope::Private | Scope::Public, None) => struct_decl.name.clone(),
47 (Scope::Private, Some(_)) => unreachable!(),
48 };
49
50 let prefix = if scope == Scope::Private && module_name.is_none() {
51 "local "
52 } else {
53 ""
54 };
55
56 self.write_doc_comments(struct_decl.doc.as_deref());
57
58 if self.debug_level_at_least(0)
59 && let Some(debug) = &struct_decl.debug
60 {
61 self.write_line(&format!("-- {}", debug.header_comment));
62 }
63
64 self.write_line(&format!("{prefix}{table_path} = {{}}"));
65
66 for (name, value) in &struct_decl.constants {
67 let value = self.generate_expression(value);
68 self.write_line(&format!("{table_path}.{name} = {value}"));
69 }
70
71 for method in &struct_decl.methods {
72 self.generate_table_method(method, &struct_decl.name, &table_path)?;
73 }
74
75 Ok(())
76 }
77
78 pub(crate) fn generate_table_method(
80 &mut self,
81 func: &Function,
82 struct_name: &str,
83 table_path: &str,
84 ) -> LuaGeneratorResult<()> {
85 let function_uses_self = func
86 .params
87 .first()
88 .is_some_and(|parameter| parameter.name == "self");
89
90 let params = if function_uses_self {
91 func.params
92 .iter()
93 .skip(1)
94 .map(|parameter| self.format_parameter(parameter))
95 .collect::<Vec<_>>()
96 .join(", ")
97 } else {
98 func.params
99 .iter()
100 .map(|parameter| self.format_parameter(parameter))
101 .collect::<Vec<_>>()
102 .join(", ")
103 };
104
105 let return_comment = self.function_return_comment(
106 func.debug
107 .as_ref()
108 .and_then(|debug| debug.return_type.as_deref()),
109 );
110 let separator = if function_uses_self { ":" } else { "." };
111
112 self.write_doc_comments(func.doc.as_deref());
113
114 if self.debug_level_at_least(0)
115 && let Some(debug) = &func.debug
116 {
117 self.write_line(&format!("-- {}", debug.header_comment));
118 }
119
120 self.write_line(&format!(
121 "function {table_path}{separator}{}({params}){return_comment}",
122 func.name
123 ));
124
125 self.struct_table_context = Some((struct_name.to_string(), table_path.to_string()));
126 self.indent_level += 1;
127 self.generate_block(&func.body, None)?;
128 self.indent_level -= 1;
129 self.struct_table_context = None;
130
131 self.write_line("end");
132
133 Ok(())
134 }
135}