protofetch 0.1.17

A source dependency management tool for Protobuf.
Documentation
use anyhow::bail;
use log::debug;

use crate::model::protofetch::{
    lock::{LockFile, LockedCoordinate},
    Coordinate, ModuleName, RevisionSpecification,
};

use super::{CommitAndDescriptor, ModuleResolver};

pub struct LockFileModuleResolver<R> {
    inner: R,
    lock_file: LockFile,
    locked: bool,
}

impl<R> LockFileModuleResolver<R> {
    pub fn new(inner: R, lock_file: LockFile, locked: bool) -> Self {
        Self {
            inner,
            lock_file,
            locked,
        }
    }
}

impl<R> ModuleResolver for LockFileModuleResolver<R>
where
    R: ModuleResolver,
{
    fn resolve(
        &self,
        coordinate: &Coordinate,
        specification: &RevisionSpecification,
        commit_hash: Option<&str>,
        name: &ModuleName,
    ) -> anyhow::Result<CommitAndDescriptor> {
        let locked_coordinate = LockedCoordinate::from(coordinate);
        let dependency = self.lock_file.dependencies.iter().find(|dependency| {
            dependency.coordinate == locked_coordinate && &dependency.specification == specification
        });
        match dependency {
            Some(dependency) => {
                debug!(
                    "Dependency {} {} found in the lock file with commit {}",
                    coordinate, specification, dependency.commit_hash
                );
                let resolved = self.inner.resolve(
                    coordinate,
                    specification,
                    commit_hash.or(Some(&dependency.commit_hash)),
                    name,
                )?;
                if resolved.commit_hash != dependency.commit_hash {
                    bail!(
                        "Commit hash of {} {} changed: the lock file specifies {}, but the actual commit hash is {}",
                        coordinate,
                        specification,
                        dependency.commit_hash,
                        resolved.commit_hash
                    );
                }
                Ok(resolved)
            }
            None if self.locked => {
                bail!(
                    "No entry for {} {} in the lock file",
                    coordinate,
                    specification
                );
            }
            None => {
                debug!(
                    "Dependency {} {} not found in the lock file",
                    coordinate, specification
                );
                self.inner
                    .resolve(coordinate, specification, commit_hash, name)
            }
        }
    }
}