gix_discover/
path.rs

1use std::{io::Read, path::PathBuf};
2
3use crate::DOT_GIT_DIR;
4
5///
6pub mod from_gitdir_file {
7    /// The error returned by [`from_gitdir_file()`][crate::path::from_gitdir_file()].
8    #[derive(Debug, thiserror::Error)]
9    #[allow(missing_docs)]
10    pub enum Error {
11        #[error(transparent)]
12        Io(#[from] std::io::Error),
13        #[error(transparent)]
14        Parse(#[from] crate::parse::gitdir::Error),
15    }
16}
17
18fn read_regular_file_content_with_size_limit(path: &std::path::Path) -> std::io::Result<Vec<u8>> {
19    let mut file = std::fs::File::open(path)?;
20    let max_file_size = 1024 * 64; // NOTE: git allows 1MB here
21    let file_size = file.metadata()?.len();
22    if file_size > max_file_size {
23        return Err(std::io::Error::other(format!(
24            "Refusing to open files larger than {} bytes, '{}' was {} bytes large",
25            max_file_size,
26            path.display(),
27            file_size
28        )));
29    }
30    let mut buf = Vec::with_capacity(512);
31    file.read_to_end(&mut buf)?;
32    Ok(buf)
33}
34
35/// Reads a plain path from a file that contains it as its only content, with trailing newlines trimmed.
36pub fn from_plain_file(path: &std::path::Path) -> Option<std::io::Result<PathBuf>> {
37    use bstr::ByteSlice;
38    let mut buf = match read_regular_file_content_with_size_limit(path) {
39        Ok(buf) => buf,
40        Err(err) if err.kind() == std::io::ErrorKind::NotFound => return None,
41        Err(err) => return Some(Err(err)),
42    };
43    let trimmed_len = buf.trim_end().len();
44    buf.truncate(trimmed_len);
45    Some(Ok(gix_path::from_bstring(buf)))
46}
47
48/// Reads typical `gitdir: ` files from disk as used by worktrees and submodules.
49pub fn from_gitdir_file(path: &std::path::Path) -> Result<PathBuf, from_gitdir_file::Error> {
50    let buf = read_regular_file_content_with_size_limit(path)?;
51    let mut gitdir = crate::parse::gitdir(&buf)?;
52    if let Some(parent) = path.parent() {
53        gitdir = parent.join(gitdir);
54    }
55    Ok(gitdir)
56}
57
58/// Conditionally pop a trailing `.git` dir if present.
59pub fn without_dot_git_dir(mut path: PathBuf) -> PathBuf {
60    if path.file_name().and_then(std::ffi::OsStr::to_str) == Some(DOT_GIT_DIR) {
61        path.pop();
62    }
63    path
64}