Remove lazy loading of registry
This removes the need for interior mutability and makes the Project
Send + Sync.
Change-Id: I4fe3004f33e0d3502e4ea99a974603605183acdf
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/qg/+/126423
Reviewed-by: Alexei Frolov <frolv@google.com>
Commit-Queue: Erik Gilling <konkers@google.com>
diff --git a/qg-cli/src/graph.rs b/qg-cli/src/graph.rs
index d311063..13cd3cf 100644
--- a/qg-cli/src/graph.rs
+++ b/qg-cli/src/graph.rs
@@ -23,7 +23,7 @@
pub async fn run(_args: &Command) -> Result<()> {
let project = Project::locate()?;
- let graph = project.registry()?.dot_dependency_graph();
+ let graph = project.registry().dot_dependency_graph();
io::stdout().write_all(graph.as_bytes()).await?;
Ok(())
diff --git a/qg-cli/src/target.rs b/qg-cli/src/target.rs
index 07f5ccf..3b3b6a1 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.registry()?;
+ let registry = project.registry();
println!(
"Project {}, {} total targets",
diff --git a/qg/src/project/mod.rs b/qg/src/project/mod.rs
index c4ff0d4..35efbbf 100644
--- a/qg/src/project/mod.rs
+++ b/qg/src/project/mod.rs
@@ -13,12 +13,12 @@
// the License.
use std::{
- cell::{Ref, RefCell},
ffi::OsStr,
path::{Path, PathBuf},
+ sync::Arc,
};
-use crate::{registry::Registry, target::Provider};
+use crate::registry::Registry;
use crate::{Error, Result};
use manifest::Manifest;
@@ -34,9 +34,7 @@
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>>,
+ registry: Arc<Registry>,
}
impl Project {
@@ -93,18 +91,21 @@
/// - [`Error::ProjectNotFound`]: No project manifest found in `project_root`.
pub fn load<P: AsRef<Path>>(project_root: P) -> Result<Self> {
let project_root = project_root.as_ref();
- if !project_root.join(Self::MANIFEST_FILE).exists() {
+ let manifest_path = project_root.join(Self::MANIFEST_FILE);
+ if !manifest_path.exists() {
return Err(Error::ProjectNotFound);
}
- let manifest = self::File::new(project_root.join(Self::MANIFEST_FILE))
- .deserialize_toml::<Manifest>()?;
+ let manifest_file = self::File::new(manifest_path);
+ let manifest = manifest_file.deserialize_toml::<Manifest>()?;
+
+ let registry = Registry::parse_manifests(&manifest_file)?;
Ok(Self {
root: project_root.to_owned(),
cache_dir: project_root.join(Self::CACHE_DIRECTORY),
name: manifest.project.name,
- registry: RefCell::new(None),
+ registry: Arc::new(registry),
})
}
@@ -131,17 +132,10 @@
return Err(Error::InvalidName(name.into()));
}
- let project = Self {
- root: root.to_owned(),
- cache_dir: root.join(Self::CACHE_DIRECTORY),
- name: name.into(),
- registry: RefCell::new(None),
- };
-
let manifest = Manifest::new(name);
- project.root_manifest().serialize_toml(&manifest)?;
+ Self::relative_file(root, Path::new(Self::MANIFEST_FILE))?.serialize_toml(&manifest)?;
- Ok(project)
+ Self::load(root)
}
/// Returns the root directory of the project.
@@ -174,90 +168,10 @@
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.
- 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();
-
- let project_provider = Provider::new(
- &root_manifest.project.name,
- root_manifest_file.path(),
- false,
- );
- registry.add_provider(project_provider)?;
-
- for (name, target) in root_manifest.targets {
- registry.add_target(crate::Target::from_manifest(
- &name,
- &root_manifest.project.name,
- target.namespace.is_global(),
- target,
- )?)?;
- }
-
- for (provider, desc) in &root_manifest.providers {
- if let Some(manifest) = &desc.manifest {
- let provider_file = root_manifest_file.relative_file(manifest);
-
- let provider_data = provider_file.deserialize_toml::<manifest::ProviderFile>()?;
- let global_provider = if let Some(desc) = &provider_data.provider {
- desc.namespace.is_global()
- } else {
- false
- };
-
- registry.add_provider(Provider::new(
- provider,
- provider_file.path(),
- global_provider,
- ))?;
-
- for (name, target) in provider_data.targets {
- registry.add_target(crate::Target::from_manifest(
- &name,
- provider,
- global_provider || target.namespace.is_global(),
- target,
- )?)?;
- }
- }
- }
-
- Ok(registry)
- }
-
- fn root_manifest(&self) -> self::File {
- self.file(Self::MANIFEST_FILE)
- .expect("manifest path is not absolute")
+ /// Returns the target registry for this project.
+ #[must_use]
+ pub fn registry(&self) -> Arc<Registry> {
+ self.registry.clone()
}
fn relative_file(root: &Path, path: &Path) -> Result<self::File> {
@@ -299,7 +213,7 @@
cache_dir: root.join("qg-cache"),
root,
name: "qg2".into(),
- registry: RefCell::new(None),
+ registry: Arc::new(Registry::new()),
};
assert_eq!(
@@ -323,7 +237,7 @@
cache_dir: root.join("qg-cache"),
root,
name: "qg2".into(),
- registry: RefCell::new(None),
+ registry: Arc::new(Registry::new()),
};
assert!(matches!(
@@ -364,7 +278,7 @@
let project = Project::load("./src/test_projects/dependency_test").unwrap();
assert_eq!(project.name, "dep-test");
- let registry = project.registry().unwrap();
+ let registry = project.registry();
let a_id = registry.get_node_id("dep-test:a").unwrap();
let b_id = registry.get_node_id("dep-test:b").unwrap();
let c_id = registry.get_node_id("dep-test:c").unwrap();
diff --git a/qg/src/registry.rs b/qg/src/registry.rs
index 6876f0f..b52f10e 100644
--- a/qg/src/registry.rs
+++ b/qg/src/registry.rs
@@ -24,8 +24,14 @@
Directed, Direction, Graph,
};
-use crate::target::{Provider, Target};
-use crate::{Error, Result};
+use crate::{
+ project::{
+ manifest::{self, Manifest},
+ File,
+ },
+ target::{Provider, Target},
+ Error, Result,
+};
/// A node in the dependency graph.
#[derive(Debug)]
@@ -97,6 +103,63 @@
}
}
+ /// 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(root_manifest_file: &File) -> Result<Self> {
+ let root_manifest = root_manifest_file.deserialize_toml::<Manifest>()?;
+ let mut registry = Registry::new();
+
+ let project_provider = Provider::new(
+ &root_manifest.project.name,
+ root_manifest_file.path(),
+ false,
+ );
+ registry.add_provider(project_provider)?;
+
+ for (name, target) in root_manifest.targets {
+ registry.add_target(crate::Target::from_manifest(
+ &name,
+ &root_manifest.project.name,
+ target.namespace.is_global(),
+ target,
+ )?)?;
+ }
+
+ for (provider, desc) in &root_manifest.providers {
+ if let Some(manifest) = &desc.manifest {
+ let provider_file = root_manifest_file.relative_file(manifest);
+
+ let provider_data = provider_file.deserialize_toml::<manifest::ProviderFile>()?;
+ let global_provider = if let Some(desc) = &provider_data.provider {
+ desc.namespace.is_global()
+ } else {
+ false
+ };
+
+ registry.add_provider(Provider::new(
+ provider,
+ provider_file.path(),
+ global_provider,
+ ))?;
+
+ for (name, target) in provider_data.targets {
+ registry.add_target(crate::Target::from_manifest(
+ &name,
+ provider,
+ global_provider || target.namespace.is_global(),
+ target,
+ )?)?;
+ }
+ }
+ }
+
+ Ok(registry)
+ }
+
/// Returns the number of registered targets.
#[must_use]
pub fn target_count(&self) -> usize {