use anyhow::Result;
use oci_spec::image::{Descriptor, DescriptorBuilder, MediaType};
use sha2::{Digest, Sha256};
use std::time::{SystemTime, UNIX_EPOCH};
use std::{
collections::HashMap,
io,
path::{Path, PathBuf},
};
pub struct BlobWriter {
blobs_dir: PathBuf,
media_type: MediaType,
file_name: PathBuf,
file: std::fs::File,
bytes: usize,
hash: Sha256,
}
impl BlobWriter {
pub fn new<P: AsRef<Path>>(image_dir: P, media_type: MediaType) -> Result<Self> {
use std::fs::{self, File};
let blobs_dir = image_dir.as_ref().join("blobs").join("sha256");
fs::create_dir_all(&blobs_dir)?;
let filename = PathBuf::from(format!("temporary-{}", unique_string()));
let file = File::create(blobs_dir.join(&filename))?;
Ok(Self {
blobs_dir,
media_type,
file_name: filename,
file,
bytes: 0,
hash: Sha256::new(),
})
}
pub fn finish(self, annotations: Option<HashMap<String, String>>) -> Result<Descriptor> {
use std::fs;
let sha256 = format!("{:x}", self.hash.finalize());
fs::rename(
self.blobs_dir.join(self.file_name),
self.blobs_dir.join(&sha256),
)?;
let mut descriptor = DescriptorBuilder::default()
.media_type(self.media_type)
.size(self.bytes as i64)
.digest(format!("sha256:{}", sha256));
if let Some(annotations) = annotations {
descriptor = descriptor.annotations(annotations)
}
let descriptor = descriptor.build()?;
Ok(descriptor)
}
}
impl io::Write for BlobWriter {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.file.write_all(buf)?;
self.bytes += buf.len();
self.hash.update(buf);
Ok(buf.len())
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
pub fn unique_string() -> String {
let nanos = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.subsec_nanos() as usize;
let ptr1 = Box::into_raw(Box::new(0)) as usize;
let ptr2 = Box::into_raw(Box::new(0)) as usize;
format!("{:x}", nanos + ptr1 + ptr2)
}