summaryrefslogtreecommitdiffstats
path: root/vendor/gix-odb/src/store_impls/loose/write.rs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/gix-odb/src/store_impls/loose/write.rs')
-rw-r--r--vendor/gix-odb/src/store_impls/loose/write.rs135
1 files changed, 135 insertions, 0 deletions
diff --git a/vendor/gix-odb/src/store_impls/loose/write.rs b/vendor/gix-odb/src/store_impls/loose/write.rs
new file mode 100644
index 000000000..e87462e4c
--- /dev/null
+++ b/vendor/gix-odb/src/store_impls/loose/write.rs
@@ -0,0 +1,135 @@
+use std::{convert::TryInto, fs, io, io::Write, path::PathBuf};
+
+use gix_features::{hash, zlib::stream::deflate};
+use gix_object::WriteTo;
+use tempfile::NamedTempFile;
+
+use super::Store;
+use crate::store_impls::loose;
+
+/// Returned by the [`crate::Write`] trait implementation of [`Store`]
+#[derive(thiserror::Error, Debug)]
+#[allow(missing_docs)]
+pub enum Error {
+ #[error("Could not {message} '{path}'")]
+ Io {
+ source: io::Error,
+ message: &'static str,
+ path: PathBuf,
+ },
+ #[error("An IO error occurred while writing an object")]
+ IoRaw(#[from] io::Error),
+ #[error("Could not turn temporary file into persisted file at '{target}'")]
+ Persist {
+ source: tempfile::PersistError,
+ target: PathBuf,
+ },
+}
+
+impl crate::traits::Write for Store {
+ type Error = Error;
+
+ fn write(&self, object: impl WriteTo) -> Result<gix_hash::ObjectId, Self::Error> {
+ let mut to = self.dest()?;
+ to.write_all(&object.loose_header()).map_err(|err| Error::Io {
+ source: err,
+ message: "write header to tempfile in",
+ path: self.path.to_owned(),
+ })?;
+ object.write_to(&mut to).map_err(|err| Error::Io {
+ source: err,
+ message: "stream all data into tempfile in",
+ path: self.path.to_owned(),
+ })?;
+ to.flush()?;
+ self.finalize_object(to)
+ }
+
+ /// Write the given buffer in `from` to disk in one syscall at best.
+ ///
+ /// This will cost at least 4 IO operations.
+ fn write_buf(&self, kind: gix_object::Kind, from: &[u8]) -> Result<gix_hash::ObjectId, Self::Error> {
+ let mut to = self.dest()?;
+ to.write_all(&gix_object::encode::loose_header(kind, from.len()))
+ .map_err(|err| Error::Io {
+ source: err,
+ message: "write header to tempfile in",
+ path: self.path.to_owned(),
+ })?;
+
+ to.write_all(from).map_err(|err| Error::Io {
+ source: err,
+ message: "stream all data into tempfile in",
+ path: self.path.to_owned(),
+ })?;
+ to.flush()?;
+ self.finalize_object(to)
+ }
+
+ /// Write the given stream in `from` to disk with at least one syscall.
+ ///
+ /// This will cost at least 4 IO operations.
+ fn write_stream(
+ &self,
+ kind: gix_object::Kind,
+ size: u64,
+ mut from: impl io::Read,
+ ) -> Result<gix_hash::ObjectId, Self::Error> {
+ let mut to = self.dest()?;
+ to.write_all(&gix_object::encode::loose_header(
+ kind,
+ size.try_into().expect("object size to fit into usize"),
+ ))
+ .map_err(|err| Error::Io {
+ source: err,
+ message: "write header to tempfile in",
+ path: self.path.to_owned(),
+ })?;
+
+ io::copy(&mut from, &mut to).map_err(|err| Error::Io {
+ source: err,
+ message: "stream all data into tempfile in",
+ path: self.path.to_owned(),
+ })?;
+ to.flush()?;
+ self.finalize_object(to)
+ }
+}
+
+type CompressedTempfile = deflate::Write<NamedTempFile>;
+
+impl Store {
+ fn dest(&self) -> Result<hash::Write<CompressedTempfile>, Error> {
+ Ok(hash::Write::new(
+ deflate::Write::new(NamedTempFile::new_in(&self.path).map_err(|err| Error::Io {
+ source: err,
+ message: "create named temp file in",
+ path: self.path.to_owned(),
+ })?),
+ self.object_hash,
+ ))
+ }
+
+ fn finalize_object(
+ &self,
+ hash::Write { hash, inner: file }: hash::Write<CompressedTempfile>,
+ ) -> Result<gix_hash::ObjectId, Error> {
+ let id = gix_hash::ObjectId::from(hash.digest());
+ let object_path = loose::hash_path(&id, 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 {
+ source: err,
+ target: object_path,
+ })?;
+ Ok(id)
+ }
+}