mago_project/lib.rs
1use serde::Deserialize;
2use serde::Serialize;
3
4use mago_interner::ThreadedInterner;
5use mago_reflection::CodebaseReflection;
6
7use crate::module::Module;
8
9mod internal;
10
11pub mod module;
12
13/// A builder for incrementally constructing a [`Project`].
14///
15/// `ProjectBuilder` allows you to start from an existing codebase reflection
16/// or from an empty state, and then add [`Module`]s one by one. When finished,
17/// calling [`build`] returns a fully constructed [`Project`] with all module reflections
18/// merged into a unified reflection.
19#[derive(Debug, Clone)]
20pub struct ProjectBuilder {
21 interner: ThreadedInterner,
22 reflection: CodebaseReflection,
23 modules: Vec<Module>,
24}
25
26impl ProjectBuilder {
27 /// Creates a new empty `ProjectBuilder` using the provided interner.
28 ///
29 /// This builder starts with an empty set of modules and an empty reflection.
30 ///
31 /// # Arguments
32 ///
33 /// * `interner` - A reference to a [`ThreadedInterner`] used for string interning and name resolution.
34 pub fn new(interner: ThreadedInterner) -> Self {
35 Self { interner, reflection: CodebaseReflection::new(), modules: Vec::new() }
36 }
37
38 pub fn from_reflection(interner: ThreadedInterner, reflection: CodebaseReflection) -> Self {
39 Self { interner, reflection, modules: Vec::new() }
40 }
41
42 /// Adds a module to the builder.
43 ///
44 /// If the provided module has reflection data, it is merged into the builder's
45 /// accumulated reflection, which is used to populate additional reflection data.
46 ///
47 /// # Arguments
48 ///
49 /// * `module` - A [`Module`] to be added to the builder.
50 pub fn add_module(&mut self, mut module: Module) {
51 if let Some(reflection) = module.reflection.take() {
52 self.reflection.merge(&self.interner, reflection);
53 }
54
55 self.modules.push(module);
56 }
57
58 /// Consumes the builder and constructs a [`Project`].
59 ///
60 /// This method merges all the reflection data collected from the added modules,
61 /// optionally populating additional reflection for non-user-defined elements, and
62 /// returns a fully built [`Project`].
63 ///
64 /// # Arguments
65 ///
66 /// * `populate_non_user_defined` - A boolean flag indicating whether to populate reflection
67 /// data for non-user-defined elements.
68 pub fn build(mut self, populate_non_user_defined: bool) -> Project {
69 internal::populator::populate(&self.interner, &mut self.reflection, populate_non_user_defined);
70
71 Project { modules: self.modules, reflection: self.reflection }
72 }
73}
74
75/// Represents a complete PHP code project.
76///
77/// A `Project` is composed of multiple modules along with a unified reflection of the codebase.
78/// The reflection provides a merged view of code insights (such as classes and functions) extracted from each module.
79#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
80pub struct Project {
81 /// A list of modules that comprise the project.
82 pub modules: Vec<Module>,
83 /// The merged reflection of the project containing information aggregated from each module.
84 pub reflection: CodebaseReflection,
85}
86
87impl Project {
88 /// Creates a new `ProjectBuilder` to start building a project.
89 pub fn builder(interner: ThreadedInterner) -> ProjectBuilder {
90 ProjectBuilder::new(interner)
91 }
92}