From 3f4b8f990ae38d4451763ae3f08d1eb5630309df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hl?= Date: Sun, 28 Jan 2024 10:49:01 +0100 Subject: [PATCH] refactor: lazy initialization of repositories and library --- crates/rugpi-bakery/src/bake/customize.rs | 2 +- crates/rugpi-bakery/src/bake/mod.rs | 2 +- crates/rugpi-bakery/src/main.rs | 3 +- crates/rugpi-bakery/src/project/library.rs | 5 +- crates/rugpi-bakery/src/project/mod.rs | 55 ++++++++++++++++++---- 5 files changed, 53 insertions(+), 14 deletions(-) diff --git a/crates/rugpi-bakery/src/bake/customize.rs b/crates/rugpi-bakery/src/bake/customize.rs index 183a65be..b53a54a8 100644 --- a/crates/rugpi-bakery/src/bake/customize.rs +++ b/crates/rugpi-bakery/src/bake/customize.rs @@ -43,7 +43,7 @@ pub fn customize( target: &Path, layer_path: &Path, ) -> Anyhow<()> { - let library = project.load_library()?; + let library = project.library()?; // Collect the recipes to apply. let config = layer.config(arch).unwrap(); let jobs = recipe_schedule(layer.repo, config, &library)?; diff --git a/crates/rugpi-bakery/src/bake/mod.rs b/crates/rugpi-bakery/src/bake/mod.rs index 999df070..a1b46dc3 100644 --- a/crates/rugpi-bakery/src/bake/mod.rs +++ b/crates/rugpi-bakery/src/bake/mod.rs @@ -29,7 +29,7 @@ pub fn bake_image(project: &Project, image: &str, output: &Path) -> Anyhow<()> { } pub fn bake_layer(project: &Project, arch: Architecture, layer_name: &str) -> Anyhow { - let library = project.load_library()?; + let library = project.library()?; let layer = &library.layers[library .lookup_layer(library.repositories.root_repository, layer_name) .unwrap()]; diff --git a/crates/rugpi-bakery/src/main.rs b/crates/rugpi-bakery/src/main.rs index 19a828e0..efd55f26 100644 --- a/crates/rugpi-bakery/src/main.rs +++ b/crates/rugpi-bakery/src/main.rs @@ -81,8 +81,7 @@ fn main() -> Anyhow<()> { std::fs::write("run-bakery", interpolate_run_bakery(version))?; } Task::Pull => { - let repositories = project.load_repositories()?; - for (_, repository) in repositories.iter() { + for (_, repository) in project.repositories()?.iter() { println!( "{} {} {}", repository.source.id.as_short_str().blue(), diff --git a/crates/rugpi-bakery/src/project/library.rs b/crates/rugpi-bakery/src/project/library.rs index d331e7aa..05b2c346 100644 --- a/crates/rugpi-bakery/src/project/library.rs +++ b/crates/rugpi-bakery/src/project/library.rs @@ -13,8 +13,9 @@ use crate::{ idx_vec::{new_idx_type, IdxVec}, }; +#[derive(Debug)] pub struct Library { - pub repositories: ProjectRepositories, + pub repositories: Arc, pub recipes: IdxVec>, pub layers: IdxVec, pub recipe_tables: IdxVec>, @@ -22,7 +23,7 @@ pub struct Library { } impl Library { - pub fn load(repositories: ProjectRepositories) -> Anyhow { + pub fn load(repositories: Arc) -> Anyhow { let mut recipes = IdxVec::new(); let tables = IdxVec::::from_vec( repositories diff --git a/crates/rugpi-bakery/src/project/mod.rs b/crates/rugpi-bakery/src/project/mod.rs index deb0fb56..765f2645 100644 --- a/crates/rugpi-bakery/src/project/mod.rs +++ b/crates/rugpi-bakery/src/project/mod.rs @@ -1,6 +1,10 @@ //! In-memory representation of Rugpi Bakery projects. -use std::path::{Path, PathBuf}; +use std::{ + cell::OnceCell, + path::{Path, PathBuf}, + sync::Arc, +}; use rugpi_common::Anyhow; @@ -13,6 +17,25 @@ pub mod library; pub mod recipes; pub mod repositories; +/// Extension trait for [`OnceCell`]. +pub trait OnceCellExt { + /// Gets the contents of the cell or tries to initialize it. + /// + /// We can remove this once `get_or_try_init` lands in the standard library (see + /// [#109737](https://github.com/rust-lang/rust/issues/109737)). + fn try_get_or_init(&self, init: impl FnOnce() -> Result) -> Result<&T, E>; +} + +impl OnceCellExt for OnceCell { + fn try_get_or_init(&self, init: impl FnOnce() -> Result) -> Result<&T, E> { + if let Some(value) = self.get() { + return Ok(value); + } + self.set(init()?).ok(); + Ok(self.get().unwrap()) + } +} + /// A project. #[derive(Debug)] #[non_exhaustive] @@ -21,21 +44,36 @@ pub struct Project { pub config: BakeryConfig, /// The project directory. pub dir: PathBuf, + /// Lazily initialized fields. + lazy: ProjectLazy, } impl Project { - /// Load the repositories of the project. - pub fn load_repositories(&self) -> Anyhow { - ProjectRepositories::load(self) + /// The repositories of the project. + pub fn repositories(&self) -> Anyhow<&Arc> { + self.lazy + .repositories + .try_get_or_init(|| ProjectRepositories::load(self).map(Arc::new)) } - /// Load the library of the project. - pub fn load_library(&self) -> Anyhow { - let repositories = self.load_repositories()?; - Library::load(repositories) + /// The library of the project. + pub fn library(&self) -> Anyhow<&Arc> { + self.lazy.library.try_get_or_init(|| { + let repositories = self.repositories()?.clone(); + Library::load(repositories).map(Arc::new) + }) } } +/// Lazily initialized fields of [`Project`]. +#[derive(Debug, Default)] +struct ProjectLazy { + /// The repositories of the project. + repositories: OnceCell>, + /// The library of the project. + library: OnceCell>, +} + /// Project loader. #[derive(Debug)] pub struct ProjectLoader { @@ -80,6 +118,7 @@ impl ProjectLoader { Ok(Project { dir: self.project_dir, config, + lazy: ProjectLazy::default(), }) } }