Skip to main content

factorio_codegen/generator/
table.rs

1use factorio_ir::{function::Function, module::Module, scope::Scope, structure::Struct};
2
3use crate::{LuaGenerator, LuaGeneratorError, LuaGeneratorResult};
4
5impl LuaGenerator {
6    /// Generate a `struct`.
7    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    /// Generate a method found on a `struct`
79    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}