git-index 0.3.0

A work-in-progress crate of the gitoxide project dedicated implementing the git index file
Documentation
use bitflags::bitflags;

bitflags! {
    pub struct Mode: u32 {
        /// directory (only used for sparse checkouts), equivalent to a tree
        const DIR = 0o040000;
        /// regular file
        const FILE = 0o100644;
        /// regular file, executable
        const FILE_EXECUTABLE = 0o100755;
        /// Symbolic link
        const SYMLINK = 0o120000;
        /// A git commit for submodules
        const COMMIT = 0o160000;
    }
}

pub(crate) mod mode {
    impl super::Mode {
        pub fn is_sparse(&self) -> bool {
            *self == Self::DIR
        }
    }
}

pub(crate) mod at_rest {
    use bitflags::bitflags;

    bitflags! {
        /// Flags how they are serialized to a storage location
        pub struct Flags: u16 {
            /// A portion of a the flags that encodes the length of the path that follows.
            const PATH_LEN = 0x0fff;
            const STAGE_MASK = 0x3000;
            /// If set, there is more extended flags past this one
            const EXTENDED = 0x4000;
            /// If set, the entry be assumed to match with the version on the working tree, as a way to avoid `lstat()`  checks.
            const ASSUME_VALID = 0x8000;
        }
    }

    bitflags! {
        /// Extended flags - add flags for serialization here and offset them down to u16.
        pub struct FlagsExtended: u16 {
            const INTENT_TO_ADD = 1 << (29 - 16);
            const SKIP_WORKTREE = 1 << (30 - 16);
        }
    }

    impl FlagsExtended {
        pub fn to_flags(self) -> Option<super::Flags> {
            super::Flags::from_bits((self.bits as u32) << 16)
        }
    }

    impl Flags {
        pub fn to_memory(self) -> super::Flags {
            super::Flags::from_bits((self & (Flags::PATH_LEN | Flags::STAGE_MASK | Flags::ASSUME_VALID)).bits as u32)
                .expect("PATHLEN is part of memory representation")
        }
    }

    #[cfg(test)]
    mod tests {
        use super::*;

        #[test]
        fn flags_from_bits_with_conflict() {
            let input = 0b1110_0010_1000_1011;
            assert_eq!(Flags::from_bits(input).unwrap().bits, input);
        }
    }
}

bitflags! {
    /// In-memory flags
    pub struct Flags: u32 {
        const STAGE_MASK = 0x3000;
        // TODO: could we use the pathlen ourselves to save 8 bytes? And how to handle longer paths than that? 0 as sentinel maybe?
        const PATH_LEN = 0x0fff;
        /// If set, the entry be assumed to match with the version on the working tree, as a way to avoid `lstat()`  checks.
        const ASSUME_VALID = 1 << 15;
        const UPDATE = 1 << 16;
        const REMOVE = 1 << 17;
        const UPTODATE = 1 << 18;
        const ADDED = 1 << 19;

        const HASHED = 1 << 20;
        const FSMONITOR_VALID = 1 << 21;
        /// Remove in work directory
        const WORKTREE_REMOVE = 1 << 22;
        const CONFLICTED = 1 << 23;

        const UNPACKED = 1 << 24;
        const NEW_SKIP_WORKTREE = 1 << 25;

        /// temporarily mark paths matched by a path spec
        const PATHSPEC_MATCHED = 1 << 26;

        const UPDATE_IN_BASE = 1 << 27;
        const STRIP_NAME = 1 << 28;

        const INTENT_TO_ADD = 1 << 29; // stored at rest, see at_rest::FlagsExtended
        const SKIP_WORKTREE = 1 << 30; // stored at rest
    }
}

impl Flags {
    pub fn stage(&self) -> u32 {
        (*self & Flags::STAGE_MASK).bits >> 12
    }
}

#[derive(PartialEq, Eq, Hash, Ord, PartialOrd, Clone, Copy)]
#[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))]
pub struct Time {
    /// The amount of seconds elapsed since EPOCH
    pub secs: u32,
    /// The amount of nanoseconds elapsed in the current second, ranging from 0 to 999.999.999 .
    pub nsecs: u32,
}

#[derive(PartialEq, Eq, Hash, Ord, PartialOrd, Clone, Copy)]
#[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))]
pub struct Stat {
    pub mtime: Time,
    pub ctime: Time,
    pub dev: u32,
    pub ino: u32,
    pub uid: u32,
    pub gid: u32,
    /// The size of bytes on disk. Capped to u32 so files bigger than that will need thorough checking (and hopefully never make it)
    pub size: u32,
}

mod access {
    use bstr::{BStr, ByteSlice};

    use crate::{Entry, State};

    impl Entry {
        pub fn path<'a>(&self, state: &'a State) -> &'a BStr {
            (&state.path_backing[self.path.clone()]).as_bstr()
        }

        pub fn path_in<'backing>(&self, backing: &'backing crate::PathStorageRef) -> &'backing BStr {
            (backing[self.path.clone()]).as_bstr()
        }

        pub fn stage(&self) -> u32 {
            self.flags.stage()
        }
    }
}

mod _impls {
    use std::cmp::Ordering;

    use crate::{Entry, State};

    impl Entry {
        pub fn cmp(&self, other: &Self, state: &State) -> Ordering {
            let lhs = self.path(state);
            let rhs = other.path(state);
            let common_len = lhs.len().min(rhs.len());
            lhs[..common_len]
                .cmp(&rhs[..common_len])
                .then_with(|| lhs.len().cmp(&rhs.len()))
                .then_with(|| self.stage().cmp(&other.stage()))
        }
    }
}