#![deny(missing_docs)]
#![warn(clippy::all, clippy::nursery, clippy::pedantic, clippy::cargo)]
use serde::Deserialize;
use serde_json::{Result as SerdeResult, from_reader};
use std::{
collections::HashMap,
fs::{self, File},
io,
path::{Path, PathBuf},
process,
};
#[derive(Debug, Deserialize)]
struct Entry {
hash: String,
#[allow(dead_code, reason = "we don't need it")]
size: u64,
}
#[derive(Debug, Deserialize)]
struct Data {
objects: HashMap<String, Entry>,
}
pub fn load_index<R: io::Read>(reader: R) -> SerdeResult<HashMap<PathBuf, PathBuf>> {
let data: Data = from_reader(reader)?;
let mut map = HashMap::new();
for (path, entry) in data.objects {
let meaningful_path = PathBuf::from(path);
let hash = entry.hash;
let parts = [str::from_utf8(&hash.as_bytes()[0..2]).unwrap(), &hash];
let hashed_path = PathBuf::from_iter(parts);
map.insert(hashed_path, meaningful_path);
}
Ok(map)
}
pub fn run(index_path: &Path, output_path: &Path) -> io::Result<()> {
if !output_path.exists() {
fs::create_dir_all(output_path)?;
}
let base_path = index_path.parent().and_then(|p| p.parent());
let Some(base_path) = base_path else {
eprintln!("Failed to resolve base path");
process::exit(1);
};
let base_path = base_path.join("objects");
let index_file = File::open(index_path)?;
let index = load_index(index_file)?;
for (hashed_path, meaningful_path) in index.into_iter() {
let src = base_path.join(&hashed_path);
let dest = output_path.join(meaningful_path);
if let Some(parent) = dest.parent() {
fs::create_dir_all(parent)?;
}
fs::copy(src, dest)?;
}
Ok(())
}