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
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
use super::Db;
use crate::{hash, loose, zlib::stream::deflate};
use std::{fs, io, io::Write, path::PathBuf};
use tempfile::NamedTempFile;
#[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::Write for Db {
type Error = Error;
fn write_buf(
&self,
kind: git_object::Kind,
from: &[u8],
hash: git_hash::Kind,
) -> Result<git_hash::ObjectId, Self::Error> {
match hash {
git_hash::Kind::Sha1 => {
let mut to = self.write_header(kind, from.len() as u64, hash)?;
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)
}
}
}
fn write_stream(
&self,
kind: git_object::Kind,
size: u64,
mut from: impl io::Read,
hash: git_hash::Kind,
) -> Result<git_hash::ObjectId, Self::Error> {
match hash {
git_hash::Kind::Sha1 => {
let mut to = self.write_header(kind, size, hash)?;
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 Db {
fn write_header(
&self,
kind: git_object::Kind,
size: u64,
hash: git_hash::Kind,
) -> Result<hash::Write<CompressedTempfile>, Error> {
let mut to = 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(),
})?),
hash,
);
loose::object::header::encode(kind, size, &mut to).map_err(|err| Error::Io {
source: err,
message: "write header to tempfile in",
path: self.path.to_owned(),
})?;
Ok(to)
}
fn finalize_object(
&self,
hash::Write { hash, inner: file }: hash::Write<CompressedTempfile>,
) -> Result<git_hash::ObjectId, Error> {
let id = git_hash::ObjectId::from(hash.digest());
let object_path = loose::db::sha1_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)
}
}