forked from GitoxideLabs/gitoxide
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat!: Add git-style metadata support.
As opposed to the Rust standard library, this one will get the ctime from the file itself, instead of from the inode. That way, the index file written by `gix` will not continuously be expensively rewritten by `git`, and vice versa.
- Loading branch information
Showing
6 changed files
with
194 additions
and
23 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,166 @@ | ||
//! This module contains a `Metadata` implementation that must be used instead of `std::fs::Metadata` to assure | ||
//! that the `ctime` information is populated exactly like the one in `git`, which wouldn't be the case on unix. | ||
#![allow(clippy::useless_conversion)] // on some MacOOS conversions are required, but on linux usually not. | ||
#![allow(clippy::unnecessary_cast)] | ||
|
||
// it's allowed for good measure, in case there are systems that use different types for that. | ||
use std::path::Path; | ||
use std::time::{Duration, SystemTime}; | ||
|
||
/// A structure to partially mirror [`std::fs::Metadata`]. | ||
#[cfg(not(windows))] | ||
pub struct Metadata(rustix::fs::Stat); | ||
|
||
#[cfg(windows)] | ||
/// A structure to partially mirror [`std::fs::Metadata`]. | ||
pub struct Metadata(std::fs::Metadata); | ||
|
||
/// Lifecycle | ||
impl Metadata { | ||
/// Obtain the metadata at `path` without following symlinks. | ||
pub fn from_path_no_follow(path: &Path) -> Result<Self, std::io::Error> { | ||
#[cfg(not(windows))] | ||
{ | ||
rustix::fs::lstat(path).map(Metadata).map_err(Into::into) | ||
} | ||
#[cfg(windows)] | ||
path.symlink_metadata().map(Metadata) | ||
} | ||
|
||
/// Obtain the metadata at `path` without following symlinks. | ||
pub fn from_file(file: &std::fs::File) -> Result<Self, std::io::Error> { | ||
#[cfg(not(windows))] | ||
{ | ||
rustix::fs::fstat(file).map(Metadata).map_err(Into::into) | ||
} | ||
#[cfg(windows)] | ||
file.metadata().map(Metadata) | ||
} | ||
} | ||
|
||
/// Access | ||
#[allow(clippy::len_without_is_empty)] | ||
impl Metadata { | ||
/// Return true if the metadata belongs to a directory | ||
pub fn is_dir(&self) -> bool { | ||
#[cfg(not(windows))] | ||
{ | ||
(self.0.st_mode & libc::S_IFMT) == libc::S_IFDIR | ||
} | ||
#[cfg(windows)] | ||
self.0.is_dir() | ||
} | ||
|
||
/// Return the time at which the underlying file was modified. | ||
pub fn modified(&self) -> Option<SystemTime> { | ||
#[cfg(not(windows))] | ||
{ | ||
Some(system_time_from_secs_nanos( | ||
self.0.st_mtime.try_into().ok()?, | ||
self.0.st_mtime_nsec.try_into().ok()?, | ||
)) | ||
} | ||
#[cfg(windows)] | ||
self.0.modified().ok() | ||
} | ||
|
||
/// Return the time at which the underlying file was created. | ||
/// | ||
/// Note that this differes from [`std::fs::Metadata::created()`] which would return | ||
/// the inode birth time, which is notably different to what `git` does. | ||
pub fn created(&self) -> Option<SystemTime> { | ||
#[cfg(not(windows))] | ||
{ | ||
Some(system_time_from_secs_nanos( | ||
self.0.st_ctime.try_into().ok()?, | ||
self.0.st_ctime_nsec.try_into().ok()?, | ||
)) | ||
} | ||
#[cfg(windows)] | ||
self.0.created().ok() | ||
} | ||
|
||
/// Return the size of the file in bytes. | ||
pub fn len(&self) -> u64 { | ||
#[cfg(not(windows))] | ||
{ | ||
self.0.st_size as u64 | ||
} | ||
#[cfg(windows)] | ||
self.0.len() | ||
} | ||
|
||
/// Return the device id on which the file is located, or 0 on windows. | ||
pub fn dev(&self) -> u64 { | ||
#[cfg(not(windows))] | ||
{ | ||
self.0.st_dev as u64 | ||
} | ||
#[cfg(windows)] | ||
0 | ||
} | ||
|
||
/// Return the inode id tracking the file, or 0 on windows. | ||
pub fn ino(&self) -> u64 { | ||
#[cfg(not(windows))] | ||
{ | ||
self.0.st_ino as u64 | ||
} | ||
#[cfg(windows)] | ||
0 | ||
} | ||
|
||
/// Return the user-id of the file or 0 on windows. | ||
pub fn uid(&self) -> u32 { | ||
#[cfg(not(windows))] | ||
{ | ||
self.0.st_uid as u32 | ||
} | ||
#[cfg(windows)] | ||
0 | ||
} | ||
|
||
/// Return the group-id of the file or 0 on windows. | ||
pub fn gid(&self) -> u32 { | ||
#[cfg(not(windows))] | ||
{ | ||
self.0.st_gid as u32 | ||
} | ||
#[cfg(windows)] | ||
0 | ||
} | ||
|
||
/// Return `true` if the file's executable bit is set, or `false` on windows. | ||
pub fn is_executable(&self) -> bool { | ||
#[cfg(not(windows))] | ||
{ | ||
(self.0.st_mode & libc::S_IFMT) == libc::S_IFREG && self.0.st_mode & libc::S_IXUSR == libc::S_IXUSR | ||
} | ||
#[cfg(windows)] | ||
gix_fs::is_executable(&self.0) | ||
} | ||
|
||
/// Return `true` if the file's is a symbolic link. | ||
pub fn is_symlink(&self) -> bool { | ||
#[cfg(not(windows))] | ||
{ | ||
(self.0.st_mode & libc::S_IFMT) == libc::S_IFLNK | ||
} | ||
#[cfg(windows)] | ||
self.0.is_symlink() | ||
} | ||
|
||
/// Return `true` if this is a regular file, executable or not. | ||
pub fn is_file(&self) -> bool { | ||
#[cfg(not(windows))] | ||
{ | ||
(self.0.st_mode & libc::S_IFMT) == libc::S_IFREG | ||
} | ||
#[cfg(windows)] | ||
self.0.is_file() | ||
} | ||
} | ||
|
||
fn system_time_from_secs_nanos(secs: u64, nanos: u32) -> SystemTime { | ||
std::time::UNIX_EPOCH + Duration::new(secs, nanos) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters