Skip to main content

pg_embedded_setup_unpriv/cache/operations/
populate.rs

1//! Cache population after successful downloads.
2//!
3//! Provides functions to copy freshly downloaded binaries into the cache.
4
5use camino::Utf8Path;
6use color_eyre::eyre::Context;
7use std::fs;
8use tracing::{debug, warn};
9
10use super::copy::copy_dir_recursive;
11use crate::error::BootstrapResult;
12
13/// Marker file name indicating a complete cache entry.
14const COMPLETION_MARKER: &str = ".complete";
15
16/// Observability target for cache operations.
17const LOG_TARGET: &str = "pg_embed::cache";
18
19/// Populates the cache with binaries from the given source directory.
20///
21/// After a successful download, call this function to copy binaries to the
22/// cache and write the completion marker.
23///
24/// # Arguments
25///
26/// * `source` - Directory containing freshly downloaded/extracted binaries
27/// * `cache_dir` - Root directory of the binary cache
28/// * `version` - Version string for the cache entry
29///
30/// # Errors
31///
32/// Returns an error if:
33/// - The cache directory cannot be created
34/// - Copying binaries fails
35/// - Writing the completion marker fails
36///
37/// # Examples
38///
39/// ```no_run
40/// use camino::Utf8Path;
41/// use pg_embedded_setup_unpriv::cache::populate_cache;
42///
43/// let source = Utf8Path::new("/tmp/sandbox/install/17.4.0");
44/// let cache_dir = Utf8Path::new("/home/user/.cache/pg-embedded/binaries");
45/// populate_cache(source, cache_dir, "17.4.0")?;
46/// # Ok::<(), color_eyre::Report>(())
47/// ```
48pub fn populate_cache(
49    source: &Utf8Path,
50    cache_dir: &Utf8Path,
51    version: &str,
52) -> BootstrapResult<()> {
53    let version_dir = cache_dir.join(version);
54
55    log_populate_start(source, cache_dir, version);
56
57    fs::create_dir_all(&version_dir)
58        .with_context(|| format!("failed to create cache directory: {version_dir}"))?;
59
60    copy_dir_recursive(source.as_std_path(), version_dir.as_std_path())
61        .with_context(|| format!("failed to copy binaries to cache: {version_dir}"))?;
62
63    write_completion_marker(&version_dir)?;
64
65    log_populate_complete(version, &version_dir);
66    Ok(())
67}
68
69/// Logs the start of a cache population operation.
70fn log_populate_start(source: &Utf8Path, cache_dir: &Utf8Path, version: &str) {
71    debug!(
72        target: LOG_TARGET,
73        source = %source,
74        cache_dir = %cache_dir,
75        version = %version,
76        "populating cache"
77    );
78}
79
80/// Logs the completion of a cache population operation.
81fn log_populate_complete(version: &str, version_dir: &Utf8Path) {
82    debug!(
83        target: LOG_TARGET,
84        version = %version,
85        path = %version_dir,
86        "cache population completed"
87    );
88}
89
90/// Writes the completion marker to indicate a valid cache entry.
91fn write_completion_marker(cache_path: &Utf8Path) -> BootstrapResult<()> {
92    let marker = cache_path.join(COMPLETION_MARKER);
93    fs::write(&marker, "")
94        .with_context(|| format!("failed to write cache completion marker: {marker}"))?;
95    Ok(())
96}
97
98/// Attempts to populate the cache after a download, logging warnings on failure.
99///
100/// This is a convenience wrapper that does not propagate errors, allowing the
101/// main flow to continue even if caching fails.
102///
103/// # Arguments
104///
105/// * `source` - Directory containing freshly downloaded binaries
106/// * `cache_dir` - Root directory of the binary cache
107/// * `version` - Version string for the cache entry
108pub fn try_populate_cache(source: &Utf8Path, cache_dir: &Utf8Path, version: &str) {
109    if let Err(err) = populate_cache(source, cache_dir, version) {
110        warn!(
111            target: LOG_TARGET,
112            error = %err,
113            version = %version,
114            "failed to populate cache, future runs may re-download"
115        );
116    }
117}