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}