mago_project/module.rs
1use serde::Deserialize;
2use serde::Serialize;
3
4use mago_ast::Program;
5use mago_interner::ThreadedInterner;
6use mago_names::Names;
7use mago_parser::error::ParseError;
8use mago_php_version::PHPVersion;
9use mago_reflection::CodebaseReflection;
10use mago_reporting::IssueCollection;
11use mago_source::Source;
12use mago_source::SourceCategory;
13
14use crate::internal;
15
16/// `Module` represents a processed PHP source code module.
17///
18/// It encapsulates the original source, resolved names, any parsing errors,
19/// optional reflection data (if enabled), and a collection of semantic issues.
20#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
21pub struct Module {
22 pub source: Source,
23 pub names: Names,
24 pub parse_error: Option<ParseError>,
25 pub reflection: Option<CodebaseReflection>,
26 pub issues: IssueCollection,
27}
28
29/// `ModuleBuildOptions` configures the behavior of the module building process.
30///
31/// The options determine whether the module should perform reflection, validation, or both:
32/// - `reflect`: When true, reflection is performed.
33/// - `validate`: When true, semantic validation is performed.
34#[derive(Debug, Clone, Copy, Eq, PartialEq, Serialize, Deserialize)]
35pub struct ModuleBuildOptions {
36 pub reflect: bool,
37 pub validate: bool,
38}
39
40impl Module {
41 /// Builds a `Module` from a given PHP source.
42 ///
43 /// This convenience function parses the PHP source code to generate an abstract syntax tree (AST),
44 /// resolves names, optionally performs reflection and/or semantic validation based on the provided
45 /// build options, and collects any issues encountered during analysis.
46 ///
47 /// Internally, it delegates to [`build_with_ast`] and discards the AST.
48 ///
49 /// # Arguments
50 ///
51 /// * `interner` - A reference to a `ThreadedInterner` used for efficient string interning.
52 /// * `version` - The PHP version guiding the parsing and analysis.
53 /// * `source` - The PHP source code encapsulated in a `Source` struct.
54 /// * `options` - The build options controlling reflection and validation.
55 ///
56 /// # Returns
57 ///
58 /// A new `Module` instance containing the resolved names, any parse errors, optional reflection data,
59 /// and a collection of issues.
60 #[inline(always)]
61 pub fn build(
62 interner: &ThreadedInterner,
63 version: PHPVersion,
64 source: Source,
65 options: ModuleBuildOptions,
66 ) -> Self {
67 let (module, _) = Self::build_with_ast(interner, version, source, options);
68
69 module
70 }
71
72 /// Builds a `Module` from a given PHP source and returns the associated AST.
73 ///
74 /// This function performs the complete module processing workflow:
75 /// - Parses the PHP source to generate an AST (`Program`).
76 /// - Resolves symbol names.
77 /// - Optionally performs reflection and/or semantic validation based on the provided build options.
78 /// - Collects any issues encountered during analysis.
79 ///
80 /// # Arguments
81 ///
82 /// * `interner` - A reference to a `ThreadedInterner` for efficient string interning.
83 /// * `version` - The PHP version used to guide parsing and analysis.
84 /// * `source` - The PHP source code encapsulated in a `Source` struct.
85 /// * `options` - The build options that control reflection and validation.
86 ///
87 /// # Returns
88 ///
89 /// A tuple containing:
90 /// - A `Module` instance with the processed data (names, reflection, issues, etc.).
91 /// - The `Program` representing the parsed abstract syntax tree (AST) of the source.
92 ///
93 /// This is useful when the AST is required immediately before moving the module
94 /// to another context (e.g., across threads) so that the cost of re-parsing can be avoided.
95 #[inline(always)]
96 pub fn build_with_ast(
97 interner: &ThreadedInterner,
98 version: PHPVersion,
99 source: Source,
100 options: ModuleBuildOptions,
101 ) -> (Self, Program) {
102 let (program, parse_error) = mago_parser::parse_source(interner, &source);
103 let names = Names::resolve(interner, &program);
104 let (reflection, issues) = internal::build(interner, version, &source, &program, &names, options);
105 let module = Self { source, parse_error, names, reflection, issues };
106
107 (module, program)
108 }
109
110 /// Parses the module's source code to generate an abstract syntax tree (AST).
111 ///
112 /// For performance reasons, the AST is not stored within the module. If an AST is needed later,
113 /// this method re-parses the source code on demand.
114 ///
115 /// # Arguments
116 ///
117 /// * `interner` - A reference to a `ThreadedInterner` used for efficient string handling during parsing.
118 ///
119 /// # Returns
120 ///
121 /// A `Program` representing the abstract syntax tree (AST) of the module.
122 pub fn parse(&self, interner: &ThreadedInterner) -> Program {
123 mago_parser::parse_source(interner, &self.source).0
124 }
125
126 /// Retrieves the category of the module's source.
127 ///
128 /// This method extracts the source category (e.g., user-defined, built-in, external)
129 /// from the module's source identifier.
130 #[inline]
131 pub const fn category(&self) -> SourceCategory {
132 self.source.identifier.category()
133 }
134}
135
136impl ModuleBuildOptions {
137 /// Creates a new `ModuleBuildOptions` with the specified settings.
138 #[inline(always)]
139 pub const fn new(reflect: bool, validate: bool) -> Self {
140 Self { reflect, validate }
141 }
142
143 /// Returns build options configured for reflection only (without validation).
144 #[inline(always)]
145 pub const fn reflection() -> Self {
146 Self { reflect: true, validate: false }
147 }
148
149 /// Returns build options configured for validation only (without reflection).
150 #[inline(always)]
151 pub const fn validation() -> Self {
152 Self { reflect: false, validate: true }
153 }
154}
155
156impl Default for ModuleBuildOptions {
157 /// Returns the default build options with both reflection and validation enabled.
158 fn default() -> Self {
159 Self::new(true, true)
160 }
161}