]> git.proxmox.com Git - rustc.git/blob - vendor/gix-odb/src/store_impls/loose/write.rs
New upstream version 1.70.0+dfsg2
[rustc.git] / vendor / gix-odb / src / store_impls / loose / write.rs
1 use std::{convert::TryInto, fs, io, io::Write, path::PathBuf};
2
3 use gix_features::{hash, zlib::stream::deflate};
4 use gix_object::WriteTo;
5 use tempfile::NamedTempFile;
6
7 use super::Store;
8 use crate::store_impls::loose;
9
10 /// Returned by the [`crate::Write`] trait implementation of [`Store`]
11 #[derive(thiserror::Error, Debug)]
12 #[allow(missing_docs)]
13 pub enum Error {
14 #[error("Could not {message} '{path}'")]
15 Io {
16 source: io::Error,
17 message: &'static str,
18 path: PathBuf,
19 },
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}'")]
23 Persist {
24 source: tempfile::PersistError,
25 target: PathBuf,
26 },
27 }
28
29 impl crate::traits::Write for Store {
30 type Error = Error;
31
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 {
35 source: err,
36 message: "write header to tempfile in",
37 path: self.path.to_owned(),
38 })?;
39 object.write_to(&mut to).map_err(|err| Error::Io {
40 source: err,
41 message: "stream all data into tempfile in",
42 path: self.path.to_owned(),
43 })?;
44 to.flush()?;
45 self.finalize_object(to)
46 }
47
48 /// Write the given buffer in `from` to disk in one syscall at best.
49 ///
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 {
55 source: err,
56 message: "write header to tempfile in",
57 path: self.path.to_owned(),
58 })?;
59
60 to.write_all(from).map_err(|err| Error::Io {
61 source: err,
62 message: "stream all data into tempfile in",
63 path: self.path.to_owned(),
64 })?;
65 to.flush()?;
66 self.finalize_object(to)
67 }
68
69 /// Write the given stream in `from` to disk with at least one syscall.
70 ///
71 /// This will cost at least 4 IO operations.
72 fn write_stream(
73 &self,
74 kind: gix_object::Kind,
75 size: u64,
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(
80 kind,
81 size.try_into().expect("object size to fit into usize"),
82 ))
83 .map_err(|err| Error::Io {
84 source: err,
85 message: "write header to tempfile in",
86 path: self.path.to_owned(),
87 })?;
88
89 io::copy(&mut from, &mut to).map_err(|err| Error::Io {
90 source: err,
91 message: "stream all data into tempfile in",
92 path: self.path.to_owned(),
93 })?;
94 to.flush()?;
95 self.finalize_object(to)
96 }
97 }
98
99 type CompressedTempfile = deflate::Write<NamedTempFile>;
100
101 impl Store {
102 fn dest(&self) -> Result<hash::Write<CompressedTempfile>, Error> {
103 Ok(hash::Write::new(
104 deflate::Write::new(NamedTempFile::new_in(&self.path).map_err(|err| Error::Io {
105 source: err,
106 message: "create named temp file in",
107 path: self.path.to_owned(),
108 })?),
109 self.object_hash,
110 ))
111 }
112
113 fn finalize_object(
114 &self,
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
120 .parent()
121 .expect("each object path has a 1 hex-bytes directory");
122 if let Err(err) = fs::create_dir(object_dir) {
123 match err.kind() {
124 io::ErrorKind::AlreadyExists => {}
125 _ => return Err(err.into()),
126 }
127 }
128 let file = file.into_inner();
129 file.persist(&object_path).map_err(|err| Error::Persist {
130 source: err,
131 target: object_path,
132 })?;
133 Ok(id)
134 }
135 }