use log::debug;
use serde::{Deserialize, Serialize};
use std::{
collections::HashMap,
fs,
path::{Path, PathBuf},
};
use crate::errors::LockError;
use super::Result;
pub const FOUNDRY_LOCK: &str = "foundry.lock";
pub type DepMap = HashMap<PathBuf, DepIdentifier>;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Lockfile {
#[serde(flatten)]
deps: DepMap,
#[serde(skip)]
lockfile_path: PathBuf,
}
impl Lockfile {
pub fn new(project_root: &Path) -> Self {
Self { deps: HashMap::default(), lockfile_path: project_root.join(FOUNDRY_LOCK) }
}
pub fn read(&mut self) -> Result<()> {
if !self.lockfile_path.exists() {
return Err(LockError::FoundryLockMissing);
}
let lockfile_str = fs::read_to_string(&self.lockfile_path)?;
self.deps = serde_json::from_str(&lockfile_str)?;
debug!(lockfile:? = self.deps; "loaded lockfile");
Ok(())
}
pub fn get(&self, path: &Path) -> Option<&DepIdentifier> {
self.deps.get(path)
}
pub fn len(&self) -> usize {
self.deps.len()
}
pub fn is_empty(&self) -> bool {
self.deps.is_empty()
}
pub fn iter(&self) -> impl Iterator<Item = (&PathBuf, &DepIdentifier)> {
self.deps.iter()
}
pub fn exists(&self) -> bool {
self.lockfile_path.exists()
}
}
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub enum DepIdentifier {
#[serde(rename = "branch")]
Branch { name: String, rev: String },
#[serde(rename = "tag")]
Tag { name: String, rev: String },
#[serde(rename = "rev", untagged)]
Rev { rev: String },
}
impl DepIdentifier {
pub fn rev(&self) -> &str {
match self {
Self::Branch { rev, .. } => rev,
Self::Tag { rev, .. } => rev,
Self::Rev { rev, .. } => rev,
}
}
pub fn name(&self) -> &str {
match self {
Self::Branch { name, .. } => name,
Self::Tag { name, .. } => name,
Self::Rev { rev, .. } => rev,
}
}
pub fn checkout_id(&self) -> &str {
match self {
Self::Branch { name, .. } => name,
Self::Tag { name, .. } => name,
Self::Rev { rev, .. } => rev,
}
}
pub fn is_branch(&self) -> bool {
matches!(self, Self::Branch { .. })
}
}
impl std::fmt::Display for DepIdentifier {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Branch { name, rev, .. } => write!(f, "branch={name}@{rev}"),
Self::Tag { name, rev, .. } => write!(f, "tag={name}@{rev}"),
Self::Rev { rev, .. } => write!(f, "rev={rev}"),
}
}
}