summaryrefslogtreecommitdiffstats
path: root/vendor/gix-index/src/file
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/gix-index/src/file')
-rw-r--r--vendor/gix-index/src/file/init.rs81
-rw-r--r--vendor/gix-index/src/file/mod.rs90
-rw-r--r--vendor/gix-index/src/file/verify.rs41
-rw-r--r--vendor/gix-index/src/file/write.rs51
4 files changed, 263 insertions, 0 deletions
diff --git a/vendor/gix-index/src/file/init.rs b/vendor/gix-index/src/file/init.rs
new file mode 100644
index 000000000..534f1f08b
--- /dev/null
+++ b/vendor/gix-index/src/file/init.rs
@@ -0,0 +1,81 @@
+#![allow(unused)]
+
+use std::path::{Path, PathBuf};
+
+use memmap2::Mmap;
+
+use crate::{decode, extension, File, State};
+
+mod error {
+
+ /// The error returned by [File::at()][super::File::at()].
+ #[derive(Debug, thiserror::Error)]
+ #[allow(missing_docs)]
+ pub enum Error {
+ #[error("An IO error occurred while opening the index")]
+ Io(#[from] std::io::Error),
+ #[error(transparent)]
+ Decode(#[from] crate::decode::Error),
+ #[error(transparent)]
+ LinkExtension(#[from] crate::extension::link::decode::Error),
+ }
+}
+
+pub use error::Error;
+
+/// Initialization
+impl File {
+ /// Try to open the index file at `path` with `options`, assuming `object_hash` is used throughout the file, or create a new
+ /// index that merely exists in memory and is empty.
+ ///
+ /// Note that the `path` will not be written if it doesn't exist.
+ pub fn at_or_default(
+ path: impl Into<PathBuf>,
+ object_hash: gix_hash::Kind,
+ options: decode::Options,
+ ) -> Result<Self, Error> {
+ let path = path.into();
+ Ok(match Self::at(&path, object_hash, options) {
+ Ok(f) => f,
+ Err(Error::Io(err)) if err.kind() == std::io::ErrorKind::NotFound => {
+ File::from_state(State::new(object_hash), path)
+ }
+ Err(err) => return Err(err),
+ })
+ }
+
+ /// Open an index file at `path` with `options`, assuming `object_hash` is used throughout the file.
+ pub fn at(path: impl Into<PathBuf>, object_hash: gix_hash::Kind, options: decode::Options) -> Result<Self, Error> {
+ let path = path.into();
+ let (data, mtime) = {
+ // SAFETY: we have to take the risk of somebody changing the file underneath. Git never writes into the same file.
+ let file = std::fs::File::open(&path)?;
+ #[allow(unsafe_code)]
+ let data = unsafe { Mmap::map(&file)? };
+ (data, filetime::FileTime::from_last_modification_time(&file.metadata()?))
+ };
+
+ let (state, checksum) = State::from_bytes(&data, mtime, object_hash, options)?;
+ let mut file = File {
+ state,
+ path,
+ checksum: Some(checksum),
+ };
+ if let Some(mut link) = file.link.take() {
+ link.dissolve_into(&mut file, object_hash, options)?;
+ }
+
+ Ok(file)
+ }
+
+ /// Consume `state` and pretend it was read from `path`, setting our checksum to `null`.
+ ///
+ /// `File` instances created like that should be written to disk to set the correct checksum via `[File::write()]`.
+ pub fn from_state(state: crate::State, path: impl Into<PathBuf>) -> Self {
+ File {
+ state,
+ path: path.into(),
+ checksum: None,
+ }
+ }
+}
diff --git a/vendor/gix-index/src/file/mod.rs b/vendor/gix-index/src/file/mod.rs
new file mode 100644
index 000000000..40332abbd
--- /dev/null
+++ b/vendor/gix-index/src/file/mod.rs
@@ -0,0 +1,90 @@
+mod impls {
+ use std::ops::{Deref, DerefMut};
+
+ use crate::{File, State};
+
+ impl Deref for File {
+ type Target = State;
+
+ fn deref(&self) -> &Self::Target {
+ &self.state
+ }
+ }
+
+ impl DerefMut for File {
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ &mut self.state
+ }
+ }
+}
+
+mod impl_ {
+ use std::fmt::Formatter;
+
+ use crate::{File, State};
+
+ impl std::fmt::Debug for File {
+ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+ f.debug_struct("File")
+ .field("path", &self.path.display())
+ .field("checksum", &self.checksum)
+ .finish_non_exhaustive()
+ }
+ }
+
+ impl From<File> for State {
+ fn from(f: File) -> Self {
+ f.state
+ }
+ }
+}
+
+mod access {
+ use crate::File;
+
+ /// Consumption
+ impl File {
+ /// Take all non-copy parts of the index.
+ pub fn into_parts(self) -> (crate::State, std::path::PathBuf) {
+ (self.state, self.path)
+ }
+ }
+
+ /// Access
+ impl File {
+ /// The path from which the index was read or to which it is supposed to be written when used with [`File::from_state()`].
+ pub fn path(&self) -> &std::path::Path {
+ &self.path
+ }
+
+ /// The checksum over the file that was read or written to disk, or `None` if the state in memory was never serialized.
+ ///
+ /// Note that even if `Some`, it will only represent the state in memory right after reading or [writing][File::write()].
+ pub fn checksum(&self) -> Option<gix_hash::ObjectId> {
+ self.checksum
+ }
+ }
+}
+
+mod mutation {
+ use std::path::PathBuf;
+
+ use crate::File;
+
+ /// Mutating access
+ impl File {
+ /// Set the path at which we think we are located to the given `path`.
+ ///
+ /// This is useful to change the location of the index *once* it is written via [`write()`][File::write()].
+ pub fn set_path(&mut self, path: impl Into<PathBuf>) {
+ self.path = path.into();
+ }
+ }
+}
+
+///
+pub mod init;
+///
+pub mod verify;
+///
+pub mod write;
diff --git a/vendor/gix-index/src/file/verify.rs b/vendor/gix-index/src/file/verify.rs
new file mode 100644
index 000000000..6743b37a7
--- /dev/null
+++ b/vendor/gix-index/src/file/verify.rs
@@ -0,0 +1,41 @@
+use std::sync::atomic::AtomicBool;
+
+use crate::File;
+
+mod error {
+ /// The error returned by [File::verify_integrity()][super::File::verify_integrity()].
+ #[derive(Debug, thiserror::Error)]
+ #[allow(missing_docs)]
+ pub enum Error {
+ #[error("Could not read index file to generate hash")]
+ Io(#[from] std::io::Error),
+ #[error("Index checksum should have been {expected}, but was {actual}")]
+ ChecksumMismatch {
+ actual: gix_hash::ObjectId,
+ expected: gix_hash::ObjectId,
+ },
+ #[error("Checksum of in-memory index wasn't computed yet")]
+ NoChecksum,
+ }
+}
+pub use error::Error;
+
+impl File {
+ /// Verify the integrity of the index to assure its consistency.
+ pub fn verify_integrity(&self) -> Result<(), Error> {
+ let checksum = self.checksum.ok_or(Error::NoChecksum)?;
+ let num_bytes_to_hash = self.path.metadata()?.len() - checksum.as_bytes().len() as u64;
+ let should_interrupt = AtomicBool::new(false);
+ let actual = gix_features::hash::bytes_of_file(
+ &self.path,
+ num_bytes_to_hash as usize,
+ checksum.kind(),
+ &mut gix_features::progress::Discard,
+ &should_interrupt,
+ )?;
+ (actual == checksum).then_some(()).ok_or(Error::ChecksumMismatch {
+ actual,
+ expected: checksum,
+ })
+ }
+}
diff --git a/vendor/gix-index/src/file/write.rs b/vendor/gix-index/src/file/write.rs
new file mode 100644
index 000000000..1e8afc07d
--- /dev/null
+++ b/vendor/gix-index/src/file/write.rs
@@ -0,0 +1,51 @@
+use gix_features::hash;
+
+use crate::{write, File, Version};
+
+/// The error produced by [`File::write()`].
+#[derive(Debug, thiserror::Error)]
+#[allow(missing_docs)]
+pub enum Error {
+ #[error(transparent)]
+ Io(#[from] std::io::Error),
+ #[error("Could not acquire lock for index file")]
+ AcquireLock(#[from] gix_lock::acquire::Error),
+ #[error("Could not commit lock for index file")]
+ CommitLock(#[from] gix_lock::commit::Error<gix_lock::File>),
+}
+
+impl File {
+ /// Write the index to `out` with `options`, to be readable by [`File::at()`], returning the version that was actually written
+ /// to retain all information of this index.
+ pub fn write_to(
+ &self,
+ mut out: impl std::io::Write,
+ options: write::Options,
+ ) -> std::io::Result<(Version, gix_hash::ObjectId)> {
+ let mut hasher = hash::Write::new(&mut out, self.state.object_hash);
+ let version = self.state.write_to(&mut hasher, options)?;
+
+ let hash = hasher.hash.digest();
+ out.write_all(&hash)?;
+ Ok((version, gix_hash::ObjectId::from(hash)))
+ }
+
+ /// Write ourselves to the path we were read from after acquiring a lock, using `options`.
+ ///
+ /// Note that the hash produced will be stored which is why we need to be mutable.
+ pub fn write(&mut self, options: write::Options) -> Result<(), Error> {
+ let mut lock = std::io::BufWriter::new(gix_lock::File::acquire_to_update_resource(
+ &self.path,
+ gix_lock::acquire::Fail::Immediately,
+ None,
+ )?);
+ let (version, digest) = self.write_to(&mut lock, options)?;
+ match lock.into_inner() {
+ Ok(lock) => lock.commit()?,
+ Err(err) => return Err(err.into_error().into()),
+ };
+ self.state.version = version;
+ self.checksum = Some(digest);
+ Ok(())
+ }
+}