Skip to content

Commit

Permalink
refactor: lazy initialization of repositories and library
Browse files Browse the repository at this point in the history
  • Loading branch information
koehlma committed Jan 28, 2024
1 parent 8a13cb0 commit 3f4b8f9
Show file tree
Hide file tree
Showing 5 changed files with 53 additions and 14 deletions.
2 changes: 1 addition & 1 deletion crates/rugpi-bakery/src/bake/customize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)?;
Expand Down
2 changes: 1 addition & 1 deletion crates/rugpi-bakery/src/bake/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<PathBuf> {
let library = project.load_library()?;
let library = project.library()?;
let layer = &library.layers[library
.lookup_layer(library.repositories.root_repository, layer_name)
.unwrap()];
Expand Down
3 changes: 1 addition & 2 deletions crates/rugpi-bakery/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down
5 changes: 3 additions & 2 deletions crates/rugpi-bakery/src/project/library.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,17 @@ use crate::{
idx_vec::{new_idx_type, IdxVec},
};

#[derive(Debug)]
pub struct Library {
pub repositories: ProjectRepositories,
pub repositories: Arc<ProjectRepositories>,
pub recipes: IdxVec<RecipeIdx, Arc<Recipe>>,
pub layers: IdxVec<LayerIdx, Layer>,
pub recipe_tables: IdxVec<RepositoryIdx, HashMap<String, RecipeIdx>>,
pub layer_tables: IdxVec<RepositoryIdx, HashMap<String, LayerIdx>>,
}

impl Library {
pub fn load(repositories: ProjectRepositories) -> Anyhow<Self> {
pub fn load(repositories: Arc<ProjectRepositories>) -> Anyhow<Self> {
let mut recipes = IdxVec::new();
let tables = IdxVec::<RepositoryIdx, _>::from_vec(
repositories
Expand Down
55 changes: 47 additions & 8 deletions crates/rugpi-bakery/src/project/mod.rs
Original file line number Diff line number Diff line change
@@ -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;

Expand All @@ -13,6 +17,25 @@ pub mod library;
pub mod recipes;
pub mod repositories;

/// Extension trait for [`OnceCell`].
pub trait OnceCellExt<T> {
/// 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<E>(&self, init: impl FnOnce() -> Result<T, E>) -> Result<&T, E>;
}

impl<T> OnceCellExt<T> for OnceCell<T> {
fn try_get_or_init<E>(&self, init: impl FnOnce() -> Result<T, E>) -> 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]
Expand All @@ -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> {
ProjectRepositories::load(self)
/// The repositories of the project.
pub fn repositories(&self) -> Anyhow<&Arc<ProjectRepositories>> {
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<Library> {
let repositories = self.load_repositories()?;
Library::load(repositories)
/// The library of the project.
pub fn library(&self) -> Anyhow<&Arc<Library>> {
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<Arc<ProjectRepositories>>,
/// The library of the project.
library: OnceCell<Arc<Library>>,
}

/// Project loader.
#[derive(Debug)]
pub struct ProjectLoader {
Expand Down Expand Up @@ -80,6 +118,7 @@ impl ProjectLoader {
Ok(Project {
dir: self.project_dir,
config,
lazy: ProjectLazy::default(),
})
}
}

0 comments on commit 3f4b8f9

Please sign in to comment.