1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
use super::Db; use crate::{hash, loose, zlib::stream::DeflateWriter}; use git_object::{owned, HashKind}; use quick_error::quick_error; use std::{fs, io, io::Write, path::PathBuf}; use tempfile::NamedTempFile; quick_error! { #[derive(Debug)] pub enum Error { Io(err: io::Error, msg: &'static str, path: PathBuf) { display("Could not {} '{}'", msg, path.display()) source(err) } IoRaw(err: io::Error) { display("An IO error occurred while writing an object") source(err) from() } Persist(err: tempfile::PersistError, target: PathBuf) { display("Could not turn temporary file into persisted file at '{}'", target.display()) source(err) } } } impl crate::Write for Db { type Error = Error; fn write_buf(&self, kind: git_object::Kind, from: &[u8], hash: HashKind) -> Result<owned::Id, Self::Error> { match hash { HashKind::Sha1 => { let mut to = self.write_header(kind, from.len() as u64, hash)?; to.write_all(from) .map_err(|err| Error::Io(err, "stream all data into tempfile in", self.path.to_owned()))?; to.flush()?; self.finalize_object(to) } } } fn write_stream( &self, kind: git_object::Kind, size: u64, mut from: impl io::Read, hash: HashKind, ) -> Result<owned::Id, Self::Error> { match hash { HashKind::Sha1 => { let mut to = self.write_header(kind, size, hash)?; io::copy(&mut from, &mut to) .map_err(|err| Error::Io(err, "stream all data into tempfile in", self.path.to_owned()))?; to.flush()?; self.finalize_object(to) } } } } type HashAndTempFile = DeflateWriter<NamedTempFile>; impl Db { fn write_header( &self, kind: git_object::Kind, size: u64, hash: HashKind, ) -> Result<hash::Write<HashAndTempFile>, Error> { let mut to = hash::Write::new( DeflateWriter::new( NamedTempFile::new_in(&self.path) .map_err(|err| Error::Io(err, "create named temp file in", self.path.to_owned()))?, ), hash, ); loose::object::header::encode(kind, size, &mut to) .map_err(|err| Error::Io(err, "write header to tempfile in", self.path.to_owned()))?; Ok(to) } fn finalize_object( &self, hash::Write { hash, inner: file }: hash::Write<HashAndTempFile>, ) -> Result<owned::Id, Error> { let id = owned::Id::from(hash.digest()); let object_path = loose::db::sha1_path(id.to_borrowed(), self.path.clone()); let object_dir = object_path .parent() .expect("each object path has a 1 hex-bytes directory"); if let Err(err) = fs::create_dir(object_dir) { match err.kind() { io::ErrorKind::AlreadyExists => {} _ => return Err(err.into()), } } let file = file.into_inner(); file.persist(&object_path) .map_err(|err| Error::Persist(err, object_path))?; Ok(id) } }