pray-core 1.13.0

Core library for Prayfile, the package manager for the language placed before inference
Documentation
use super::{source_map, ResolvedPackage, ResolvedProject};
use crate::config::load_user_config;
use crate::lockfile::Lockfile;
use crate::package_spec_render::{fork_spec_after_refresh, render_package_spec};
use crate::package_upstream::{is_clean_replica, merge_content_files, LockedUpstream};
use crate::paths::find_prayspec_file;
use crate::resolve_context::ResolveOptions;
use crate::resolve_git_sources::prepare_git_sources;
use crate::{PrayError, PrayResult};
use std::collections::BTreeMap;

#[path = "resolve_upstream_io.rs"]
mod input_output;
#[path = "resolve_upstream_lock.rs"]
mod lock;
#[path = "resolve_upstream_context.rs"]
mod resolution_context;

use input_output::{content_file_bytes, write_content_files};
use lock::ensure_locked_upstream_matches;
pub(super) use lock::lock_path_upstream;
pub(super) use resolution_context::UpstreamResolutionContext;

pub fn apply_path_upstream_refreshes(
    project: &ResolvedProject,
    previous: Option<&Lockfile>,
    selected: Option<&str>,
    options: &ResolveOptions,
) -> PrayResult<bool> {
    let user_config = load_user_config()?;
    let git_sources = prepare_git_sources(
        &project.project_root,
        &project.manifest.sources,
        previous,
        options,
    )?;
    let sources = source_map(&project.manifest.sources);
    let mut changed = false;
    for package in &project.packages {
        if selected.is_some_and(|name| name != package.declaration.name) {
            continue;
        }
        if apply_one_path_upstream(
            project,
            package,
            previous,
            &sources,
            &git_sources,
            &user_config,
            options,
        )? {
            changed = true;
        }
    }
    Ok(changed)
}

fn apply_one_path_upstream(
    project: &ResolvedProject,
    package: &ResolvedPackage,
    previous: Option<&Lockfile>,
    sources: &BTreeMap<String, crate::manifest::ManifestSource>,
    git_sources: &BTreeMap<String, crate::resolve_git_sources::GitSourceCheckout>,
    user_config: &crate::config::PrayConfig,
    options: &ResolveOptions,
) -> PrayResult<bool> {
    let Some(new_upstream) = &package.upstream else {
        return Ok(false);
    };
    if package.declaration.path.is_none() {
        return Ok(false);
    }
    let Some(old_upstream) = previous.and_then(|lockfile| {
        lockfile
            .package
            .iter()
            .find(|entry| entry.name == package.declaration.name)
            .and_then(|entry| entry.upstream.clone())
    }) else {
        return Ok(false);
    };
    if old_upstream.version == new_upstream.version
        && old_upstream.tree_hash == new_upstream.tree_hash
    {
        return Ok(false);
    }
    let resolution_context = UpstreamResolutionContext::new(
        &project.project_root,
        sources,
        git_sources,
        user_config,
        previous,
        options,
    );
    let old_package = resolution_context.resolve(
        &old_upstream.name,
        &format!("= {}", old_upstream.version),
        old_upstream.source.as_deref(),
    )?;
    let resolved_old = LockedUpstream {
        name: old_upstream.name.clone(),
        version: old_package.spec.version.clone(),
        source: old_upstream.source.clone(),
        tree_hash: old_package.tree_hash.clone(),
        artifact_hash: old_package.artifact_hash.clone(),
    };
    ensure_locked_upstream_matches(&old_upstream, &resolved_old)?;
    let new_package = resolution_context.resolve(
        &new_upstream.name,
        &format!("= {}", new_upstream.version),
        new_upstream.source.as_deref(),
    )?;
    let old_content = content_file_bytes(&old_package.root, &old_package.spec)?;
    let new_content = content_file_bytes(&new_package.root, &new_package.spec)?;
    let local_content = content_file_bytes(&package.root, &package.spec)?;
    let merged = merge_content_files(&old_content, &new_content, &local_content)?;
    let clean = is_clean_replica(&old_content, &local_content);
    write_content_files(&package.root, &old_content, &merged)?;
    let spec_path = find_prayspec_file(&package.root)?;
    let spec_name = spec_path
        .file_name()
        .and_then(|name| name.to_str())
        .ok_or_else(|| PrayError::Resolution("package prayspec name is not utf-8".to_string()))?
        .to_string();
    let merged_paths = merged.keys().cloned().collect::<Vec<_>>();
    let updated = fork_spec_after_refresh(
        &package.spec,
        &new_package.spec,
        &spec_name,
        clean,
        &merged_paths,
    );
    crate::transaction::write_file(&spec_path, render_package_spec(&updated))?;
    Ok(true)
}

#[cfg(test)]
#[path = "resolve_upstream_tests.rs"]
mod tests;