1 use std
::{convert::TryInto, fs, io, io::Write, path::PathBuf}
;
3 use gix_features
::{hash, zlib::stream::deflate}
;
4 use gix_object
::WriteTo
;
5 use tempfile
::NamedTempFile
;
8 use crate::store_impls
::loose
;
10 /// Returned by the [`crate::Write`] trait implementation of [`Store`]
11 #[derive(thiserror::Error, Debug)]
12 #[allow(missing_docs)]
14 #[error("Could not {message} '{path}'")]
17 message
: &'
static str,
20 #[error("An IO error occurred while writing an object")]
21 IoRaw(#[from] io::Error),
22 #[error("Could not turn temporary file into persisted file at '{target}'")]
24 source
: tempfile
::PersistError
,
29 impl crate::traits
::Write
for Store
{
32 fn write(&self, object
: impl WriteTo
) -> Result
<gix_hash
::ObjectId
, Self::Error
> {
33 let mut to
= self.dest()?
;
34 to
.write_all(&object
.loose_header()).map_err(|err
| Error
::Io
{
36 message
: "write header to tempfile in",
37 path
: self.path
.to_owned(),
39 object
.write_to(&mut to
).map_err(|err
| Error
::Io
{
41 message
: "stream all data into tempfile in",
42 path
: self.path
.to_owned(),
45 self.finalize_object(to
)
48 /// Write the given buffer in `from` to disk in one syscall at best.
50 /// This will cost at least 4 IO operations.
51 fn write_buf(&self, kind
: gix_object
::Kind
, from
: &[u8]) -> Result
<gix_hash
::ObjectId
, Self::Error
> {
52 let mut to
= self.dest()?
;
53 to
.write_all(&gix_object
::encode
::loose_header(kind
, from
.len()))
54 .map_err(|err
| Error
::Io
{
56 message
: "write header to tempfile in",
57 path
: self.path
.to_owned(),
60 to
.write_all(from
).map_err(|err
| Error
::Io
{
62 message
: "stream all data into tempfile in",
63 path
: self.path
.to_owned(),
66 self.finalize_object(to
)
69 /// Write the given stream in `from` to disk with at least one syscall.
71 /// This will cost at least 4 IO operations.
74 kind
: gix_object
::Kind
,
76 mut from
: impl io
::Read
,
77 ) -> Result
<gix_hash
::ObjectId
, Self::Error
> {
78 let mut to
= self.dest()?
;
79 to
.write_all(&gix_object
::encode
::loose_header(
81 size
.try_into().expect("object size to fit into usize"),
83 .map_err(|err
| Error
::Io
{
85 message
: "write header to tempfile in",
86 path
: self.path
.to_owned(),
89 io
::copy(&mut from
, &mut to
).map_err(|err
| Error
::Io
{
91 message
: "stream all data into tempfile in",
92 path
: self.path
.to_owned(),
95 self.finalize_object(to
)
99 type CompressedTempfile
= deflate
::Write
<NamedTempFile
>;
102 fn dest(&self) -> Result
<hash
::Write
<CompressedTempfile
>, Error
> {
104 deflate
::Write
::new(NamedTempFile
::new_in(&self.path
).map_err(|err
| Error
::Io
{
106 message
: "create named temp file in",
107 path
: self.path
.to_owned(),
115 hash
::Write { hash, inner: file }
: hash
::Write
<CompressedTempfile
>,
116 ) -> Result
<gix_hash
::ObjectId
, Error
> {
117 let id
= gix_hash
::ObjectId
::from(hash
.digest());
118 let object_path
= loose
::hash_path(&id
, self.path
.clone());
119 let object_dir
= object_path
121 .expect("each object path has a 1 hex-bytes directory");
122 if let Err(err
) = fs
::create_dir(object_dir
) {
124 io
::ErrorKind
::AlreadyExists
=> {}
125 _
=> return Err(err
.into()),
128 let file
= file
.into_inner();
129 file
.persist(&object_path
).map_err(|err
| Error
::Persist
{