Add lazy loading and caching of project registry.
Change-Id: If5d20a1037fa9738f6f08a8ec5a9b603e1a878a3
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/qg/+/125131
Commit-Queue: Erik Gilling <konkers@google.com>
Reviewed-by: Alexei Frolov <frolv@google.com>
diff --git a/qg-cli/src/target.rs b/qg-cli/src/target.rs
index 497da63..07f5ccf 100644
--- a/qg-cli/src/target.rs
+++ b/qg-cli/src/target.rs
@@ -24,7 +24,7 @@
#[allow(clippy::unused_async)]
pub async fn run(_args: &Command) -> Result<()> {
let project = Project::locate()?;
- let registry = project.parse_manifests()?;
+ let registry = project.registry()?;
println!(
"Project {}, {} total targets",
diff --git a/qg/src/project/mod.rs b/qg/src/project/mod.rs
index 68f32ec..06ecfd1 100644
--- a/qg/src/project/mod.rs
+++ b/qg/src/project/mod.rs
@@ -13,6 +13,7 @@
// the License.
use std::{
+ cell::{Ref, RefCell},
ffi::OsStr,
path::{Path, PathBuf},
};
@@ -33,6 +34,9 @@
root: PathBuf,
cache_dir: PathBuf,
name: String,
+ // Use a RefCell to provide interior mutability on the registry so that it can
+ // be lazily loaded without taking a mutable reference on the Project.
+ registry: RefCell<Option<Registry>>,
}
impl Project {
@@ -78,6 +82,7 @@
root: project_root.to_owned(),
cache_dir: project_root.join(Self::CACHE_DIRECTORY),
name: manifest.project.name,
+ registry: RefCell::new(None),
})
}
@@ -108,6 +113,7 @@
root: root.to_owned(),
cache_dir: root.join(Self::CACHE_DIRECTORY),
name: name.into(),
+ registry: RefCell::new(None),
};
let manifest = Manifest::new(name);
@@ -146,13 +152,36 @@
Self::relative_file(&self.cache_dir, Path::new(path.as_ref()))
}
+ /// Returns the target registry for this project. Will locate and parse
+ /// manifests if they have not already been read.
+ ///
+ /// # Errors
+ /// Returns an error if manifest files could not be read or are improperly
+ /// structured.
+ pub fn registry(&self) -> Result<Ref<Registry>> {
+ {
+ // Scope the mutable borrow to just the existence check and
+ // manifest parsing.
+ let mut registry = self.registry.borrow_mut();
+ if registry.is_none() {
+ *registry = Some(self.parse_manifests()?);
+ }
+ }
+
+ Ok(Ref::map(self.registry.borrow(), |registry| {
+ registry
+ .as_ref()
+ .expect("registry should be valid due to being created above")
+ }))
+ }
+
/// Reads `qg` manifest files starting from the project root into a registry
/// of available packages.
///
/// # Errors
/// Returns an error if manifest files could not be read or are improperly
/// structured.
- pub fn parse_manifests(&self) -> Result<Registry> {
+ fn parse_manifests(&self) -> Result<Registry> {
let root_manifest_file = self.root_manifest();
let root_manifest = root_manifest_file.deserialize_toml::<Manifest>()?;
let mut registry = Registry::new();
@@ -244,6 +273,7 @@
cache_dir: root.join("qg-cache"),
root,
name: "qg2".into(),
+ registry: RefCell::new(None),
};
assert_eq!(
@@ -267,6 +297,7 @@
cache_dir: root.join("qg-cache"),
root,
name: "qg2".into(),
+ registry: RefCell::new(None),
};
assert!(matches!(