anyfs_backend/
types.rs

1//! # Core Types
2//!
3//! Fundamental types used throughout the AnyFS ecosystem.
4//!
5//! ## Quick Reference
6//!
7//! | Type | Purpose |
8//! |------|---------|
9//! | [`FileType`] | Enum: File, Directory, or Symlink |
10//! | [`Metadata`] | File/directory info: size, type, times, permissions |
11//! | [`DirEntry`] | Single entry from a directory listing |
12//! | [`Permissions`] | Unix-style permission bits (rwxrwxrwx) |
13//! | [`StatFs`] | Filesystem-level statistics (total/used/available space) |
14//! | [`Handle`] | Opaque file handle for POSIX-style operations |
15//! | [`OpenFlags`] | Flags for opening files (read/write/create/truncate) |
16//! | [`LockType`] | Shared or exclusive file lock |
17//! | [`ROOT_INODE`] | Constant: root directory inode (always 1) |
18//!
19//! ## Serde Support
20//!
21//! All types support serialization when the `serde` feature is enabled:
22//!
23//! ```toml
24//! [dependencies]
25//! anyfs-backend = { version = "0.1", features = ["serde"] }
26//! ```
27
28use std::path::PathBuf;
29use std::time::SystemTime;
30
31/// The root directory inode number (FUSE convention).
32///
33/// In FUSE and most Unix filesystems, inode 1 is reserved for the root directory.
34/// This constant ensures consistent behavior across all AnyFS backends.
35///
36/// # Example
37///
38/// ```rust
39/// use anyfs_backend::ROOT_INODE;
40///
41/// assert_eq!(ROOT_INODE, 1);
42/// ```
43pub const ROOT_INODE: u64 = 1;
44
45/// The type of a filesystem entry.
46///
47/// Every path in a filesystem is one of these three types.
48///
49/// # Variants
50///
51/// - [`File`](FileType::File) — Regular file containing data
52/// - [`Directory`](FileType::Directory) — Container for other entries
53/// - [`Symlink`](FileType::Symlink) — Symbolic link pointing to another path
54///
55/// # Example
56///
57/// ```rust
58/// use anyfs_backend::FileType;
59///
60/// let ft = FileType::File;
61/// assert_eq!(ft, FileType::File);
62/// assert_ne!(ft, FileType::Directory);
63/// ```
64///
65/// # Usage with Metadata
66///
67/// ```rust
68/// use anyfs_backend::{Metadata, FileType};
69///
70/// let meta = Metadata::default();
71/// match meta.file_type {
72///     FileType::File => println!("It's a file"),
73///     FileType::Directory => println!("It's a directory"),
74///     FileType::Symlink => println!("It's a symlink"),
75/// }
76/// ```
77#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
78#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
79pub enum FileType {
80    /// Regular file containing data.
81    File,
82    /// Directory containing other entries.
83    Directory,
84    /// Symbolic link pointing to another path.
85    Symlink,
86}
87
88/// Complete metadata for a filesystem entry.
89///
90/// Contains all common metadata fields for files, directories, and symlinks.
91/// Returned by [`FsRead::metadata`](crate::FsRead::metadata) and
92/// [`FsInode::metadata_by_inode`](crate::FsInode::metadata_by_inode).
93///
94/// # Fields
95///
96/// | Field | Type | Description |
97/// |-------|------|-------------|
98/// | `file_type` | [`FileType`] | File, Directory, or Symlink |
99/// | `size` | `u64` | Size in bytes (0 for directories) |
100/// | `permissions` | [`Permissions`] | Unix permission bits |
101/// | `created` | `SystemTime` | Creation timestamp |
102/// | `modified` | `SystemTime` | Last modification timestamp |
103/// | `accessed` | `SystemTime` | Last access timestamp |
104/// | `inode` | `u64` | Unique identifier within filesystem |
105/// | `nlink` | `u64` | Number of hard links |
106///
107/// # Example
108///
109/// ```rust
110/// use anyfs_backend::{Metadata, FileType, Permissions};
111/// use std::time::SystemTime;
112///
113/// let meta = Metadata {
114///     file_type: FileType::File,
115///     size: 1024,
116///     permissions: Permissions::from_mode(0o644),
117///     created: SystemTime::now(),
118///     modified: SystemTime::now(),
119///     accessed: SystemTime::now(),
120///     inode: 42,
121///     nlink: 1,
122/// };
123///
124/// assert!(meta.is_file());
125/// assert_eq!(meta.size, 1024);
126/// ```
127///
128/// # Default Value
129///
130/// The default creates a zero-sized file with standard permissions (0o644):
131///
132/// ```rust
133/// use anyfs_backend::{Metadata, FileType};
134///
135/// let meta = Metadata::default();
136/// assert!(meta.is_file());
137/// assert_eq!(meta.size, 0);
138/// ```
139#[derive(Debug, Clone)]
140#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
141pub struct Metadata {
142    /// Type of the entry (file, directory, symlink).
143    pub file_type: FileType,
144    /// Size in bytes.
145    pub size: u64,
146    /// Permissions.
147    pub permissions: Permissions,
148    /// Creation time.
149    #[cfg_attr(feature = "serde", serde(with = "system_time_serde"))]
150    pub created: SystemTime,
151    /// Last modification time.
152    #[cfg_attr(feature = "serde", serde(with = "system_time_serde"))]
153    pub modified: SystemTime,
154    /// Last access time.
155    #[cfg_attr(feature = "serde", serde(with = "system_time_serde"))]
156    pub accessed: SystemTime,
157    /// Inode number (unique identifier within the filesystem).
158    pub inode: u64,
159    /// Number of hard links.
160    pub nlink: u64,
161}
162
163impl Metadata {
164    /// Returns `true` if this is a regular file.
165    #[inline]
166    pub fn is_file(&self) -> bool {
167        self.file_type == FileType::File
168    }
169
170    /// Returns `true` if this is a directory.
171    #[inline]
172    pub fn is_dir(&self) -> bool {
173        self.file_type == FileType::Directory
174    }
175
176    /// Returns `true` if this is a symbolic link.
177    #[inline]
178    pub fn is_symlink(&self) -> bool {
179        self.file_type == FileType::Symlink
180    }
181}
182
183impl Default for Metadata {
184    fn default() -> Self {
185        Self {
186            file_type: FileType::File,
187            size: 0,
188            permissions: Permissions::default_file(),
189            created: SystemTime::UNIX_EPOCH,
190            modified: SystemTime::UNIX_EPOCH,
191            accessed: SystemTime::UNIX_EPOCH,
192            inode: 0,
193            nlink: 1,
194        }
195    }
196}
197
198/// A single entry from a directory listing.
199///
200/// Returned by [`FsDir::read_dir`](crate::FsDir::read_dir) via [`ReadDirIter`](crate::ReadDirIter).
201/// Contains basic information about each item in a directory.
202///
203/// # Fields
204///
205/// | Field | Type | Description |
206/// |-------|------|-------------|
207/// | `name` | `String` | Filename only (not full path) |
208/// | `path` | `PathBuf` | Full absolute path |
209/// | `file_type` | [`FileType`] | File, Directory, or Symlink |
210/// | `size` | `u64` | Size in bytes |
211/// | `inode` | `u64` | Inode number |
212///
213/// # Example
214///
215/// ```rust
216/// use anyfs_backend::{DirEntry, FileType};
217/// use std::path::PathBuf;
218///
219/// let entry = DirEntry {
220///     name: "readme.md".to_string(),
221///     path: PathBuf::from("/docs/readme.md"),
222///     file_type: FileType::File,
223///     size: 2048,
224///     inode: 123,
225/// };
226///
227/// assert_eq!(entry.name, "readme.md");
228/// assert_eq!(entry.file_type, FileType::File);
229/// ```
230///
231/// # Usage with read_dir
232///
233/// ```rust
234/// use anyfs_backend::Fs;
235/// use std::path::Path;
236///
237/// // Generic function that works with any Fs implementation
238/// fn list_files<B: Fs>(fs: &B) -> Result<(), anyfs_backend::FsError> {
239///     for entry in fs.read_dir(Path::new("/"))? {
240///         let entry = entry?;
241///         println!("{} ({:?}, {} bytes)", entry.name, entry.file_type, entry.size);
242///     }
243///     Ok(())
244/// }
245/// ```
246#[derive(Debug, Clone)]
247#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
248pub struct DirEntry {
249    /// Name of the entry (filename only, not full path).
250    pub name: String,
251    /// Full path to the entry.
252    pub path: PathBuf,
253    /// Type of the entry.
254    pub file_type: FileType,
255    /// Size in bytes.
256    pub size: u64,
257    /// Inode number.
258    pub inode: u64,
259}
260
261/// Unix-style permission bits.
262///
263/// Stores permissions as a standard Unix mode bitmask (rwxrwxrwx format).
264/// The lower 12 bits represent: owner (rwx), group (rwx), other (rwx), plus
265/// setuid/setgid/sticky bits.
266///
267/// # Permission Bits
268///
269/// ```text
270/// Mode: 0o7777 (octal)
271/// ┌─────┬─────┬─────┬────────────────────┐
272/// │ Special │ Owner │ Group │ Other        │
273/// │ (sst)   │ (rwx) │ (rwx) │ (rwx)        │
274/// └─────┴─────┴─────┴────────────────────┘
275/// ```
276///
277/// | Bit | Meaning |
278/// |-----|---------|
279/// | `r` (4) | Read permission |
280/// | `w` (2) | Write permission |
281/// | `x` (1) | Execute/search permission |
282///
283/// # Common Permission Values
284///
285/// | Mode | Meaning |
286/// |------|---------|
287/// | `0o644` | Owner read/write, others read (typical file) |
288/// | `0o755` | Owner all, others read/execute (typical directory) |
289/// | `0o600` | Owner read/write only (private file) |
290/// | `0o444` | Everyone read only |
291///
292/// # Example
293///
294/// ```rust
295/// use anyfs_backend::Permissions;
296///
297/// // Create from octal mode
298/// let perm = Permissions::from_mode(0o755);
299/// assert_eq!(perm.mode(), 0o755);
300/// assert!(!perm.readonly());
301///
302/// // Read-only permissions
303/// let readonly = Permissions::from_mode(0o444);
304/// assert!(readonly.readonly());
305///
306/// // Default permissions
307/// assert_eq!(Permissions::default_file().mode(), 0o644);  // rw-r--r--
308/// assert_eq!(Permissions::default_dir().mode(), 0o755);   // rwxr-xr-x
309/// ```
310#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
311#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
312pub struct Permissions(u32);
313
314impl Permissions {
315    /// Create permissions from a Unix mode (e.g., 0o755).
316    #[inline]
317    pub const fn from_mode(mode: u32) -> Self {
318        Self(mode & 0o7777)
319    }
320
321    /// Get the raw mode value.
322    #[inline]
323    pub const fn mode(&self) -> u32 {
324        self.0
325    }
326
327    /// Returns `true` if these permissions deny writing.
328    #[inline]
329    pub const fn readonly(&self) -> bool {
330        // Check if no write bits are set (user, group, or other)
331        (self.0 & 0o222) == 0
332    }
333
334    /// Default permissions for a new file (0o644 = rw-r--r--).
335    #[inline]
336    pub const fn default_file() -> Self {
337        Self(0o644)
338    }
339
340    /// Default permissions for a new directory (0o755 = rwxr-xr-x).
341    #[inline]
342    pub const fn default_dir() -> Self {
343        Self(0o755)
344    }
345}
346
347impl Default for Permissions {
348    fn default() -> Self {
349        Self::default_file()
350    }
351}
352
353/// Filesystem-level statistics.
354///
355/// Returned by [`FsStats::statfs`](crate::FsStats::statfs). Contains information
356/// about the filesystem's capacity, usage, and limits — similar to the POSIX
357/// `statvfs` system call.
358///
359/// # Fields
360///
361/// | Field | Type | Description |
362/// |-------|------|-------------|
363/// | `total_bytes` | `u64` | Total capacity (0 = unlimited) |
364/// | `used_bytes` | `u64` | Currently used space |
365/// | `available_bytes` | `u64` | Space available for new data |
366/// | `total_inodes` | `u64` | Maximum files/directories (0 = unlimited) |
367/// | `used_inodes` | `u64` | Currently allocated inodes |
368/// | `available_inodes` | `u64` | Inodes available for new entries |
369/// | `block_size` | `u64` | Filesystem block size in bytes |
370/// | `max_name_len` | `u64` | Maximum filename length |
371///
372/// # Example
373///
374/// ```rust
375/// use anyfs_backend::StatFs;
376///
377/// let stats = StatFs {
378///     total_bytes: 1_000_000_000,        // 1 GB
379///     used_bytes: 250_000_000,           // 250 MB used
380///     available_bytes: 750_000_000,      // 750 MB free
381///     total_inodes: 100_000,
382///     used_inodes: 1_234,
383///     available_inodes: 98_766,
384///     block_size: 4096,
385///     max_name_len: 255,
386/// };
387///
388/// let usage_percent = (stats.used_bytes as f64 / stats.total_bytes as f64) * 100.0;
389/// println!("Disk usage: {:.1}%", usage_percent);  // "Disk usage: 25.0%"
390/// ```
391///
392/// # Unlimited Filesystems
393///
394/// For backends without fixed limits (e.g., cloud storage), set capacity fields to 0:
395///
396/// ```rust
397/// use anyfs_backend::StatFs;
398///
399/// let unlimited = StatFs {
400///     total_bytes: 0,      // No limit
401///     total_inodes: 0,     // No limit
402///     ..Default::default()
403/// };
404/// ```
405#[derive(Debug, Clone, Default)]
406#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
407pub struct StatFs {
408    /// Total size in bytes (0 = unlimited).
409    pub total_bytes: u64,
410    /// Currently used bytes.
411    pub used_bytes: u64,
412    /// Available bytes for use.
413    pub available_bytes: u64,
414    /// Total number of inodes (0 = unlimited).
415    pub total_inodes: u64,
416    /// Number of used inodes.
417    pub used_inodes: u64,
418    /// Number of available inodes.
419    pub available_inodes: u64,
420    /// Block size in bytes.
421    pub block_size: u64,
422    /// Maximum filename length.
423    pub max_name_len: u64,
424}
425
426/// Opaque file handle for POSIX-style I/O operations.
427///
428/// Represents an open file descriptor. Used with [`FsHandles`](crate::FsHandles)
429/// for handle-based read/write operations, and with [`FsLock`](crate::FsLock)
430/// for file locking.
431///
432/// # Lifecycle
433///
434/// 1. **Open**: Call [`FsHandles::open`](crate::FsHandles::open) to get a handle
435/// 2. **Use**: Call `read_at`, `write_at`, `lock`, etc.
436/// 3. **Close**: Call [`FsHandles::close`](crate::FsHandles::close) to release
437///
438/// # Example
439///
440/// ```rust
441/// use anyfs_backend::{FsHandles, OpenFlags, Handle};
442/// use std::path::Path;
443///
444/// // Generic function that works with any FsHandles implementation
445/// fn write_with_handle<B: FsHandles>(fs: &B) -> Result<(), anyfs_backend::FsError> {
446///     // Open file for writing
447///     let handle: Handle = fs.open(Path::new("/data.bin"), OpenFlags::WRITE)?;
448///     
449///     // Write at specific offset
450///     fs.write_at(handle, b"Hello", 0)?;
451///     fs.write_at(handle, b"World", 5)?;
452///     
453///     // Always close the handle
454///     fs.close(handle)?;
455///     Ok(())
456/// }
457/// ```
458///
459/// # Internal Value
460///
461/// The `u64` value is backend-defined. It could be an inode number, array index,
462/// or any other unique identifier. Treat it as opaque.
463#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
464#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
465pub struct Handle(pub u64);
466
467/// Flags for opening a file.
468///
469/// Controls how a file is opened: read/write mode, creation behavior, and
470/// truncation. Used with [`FsHandles::open`](crate::FsHandles::open).
471///
472/// # Predefined Constants
473///
474/// | Constant | Behavior |
475/// |----------|----------|
476/// | [`OpenFlags::READ`] | Read-only access |
477/// | [`OpenFlags::WRITE`] | Write with create and truncate |
478/// | [`OpenFlags::READ_WRITE`] | Read and write, file must exist |
479/// | [`OpenFlags::APPEND`] | Append mode (writes go to end) |
480///
481/// # Fields
482///
483/// | Field | Effect |
484/// |-------|--------|
485/// | `read` | Enable reading from file |
486/// | `write` | Enable writing to file |
487/// | `create` | Create file if it doesn't exist |
488/// | `truncate` | Truncate file to zero length on open |
489/// | `append` | Writes always go to end of file |
490///
491/// # Example
492///
493/// ```rust
494/// use anyfs_backend::OpenFlags;
495///
496/// // Use predefined constants
497/// let read_only = OpenFlags::READ;
498/// assert!(read_only.read);
499/// assert!(!read_only.write);
500///
501/// // Custom flags
502/// let custom = OpenFlags {
503///     read: true,
504///     write: true,
505///     create: true,
506///     truncate: false,
507///     append: false,
508/// };
509/// ```
510#[derive(Debug, Clone, Copy, Default)]
511#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
512pub struct OpenFlags {
513    /// Open for reading.
514    pub read: bool,
515    /// Open for writing.
516    pub write: bool,
517    /// Create file if it doesn't exist.
518    pub create: bool,
519    /// Truncate file to zero length.
520    pub truncate: bool,
521    /// Append to end of file.
522    pub append: bool,
523}
524
525impl OpenFlags {
526    /// Read-only access.
527    pub const READ: Self = Self {
528        read: true,
529        write: false,
530        create: false,
531        truncate: false,
532        append: false,
533    };
534
535    /// Write access with create and truncate.
536    pub const WRITE: Self = Self {
537        read: false,
538        write: true,
539        create: true,
540        truncate: true,
541        append: false,
542    };
543
544    /// Read and write access.
545    pub const READ_WRITE: Self = Self {
546        read: true,
547        write: true,
548        create: false,
549        truncate: false,
550        append: false,
551    };
552
553    /// Append mode - writes go to end of file.
554    pub const APPEND: Self = Self {
555        read: false,
556        write: true,
557        create: true,
558        truncate: false,
559        append: true,
560    };
561}
562
563/// Type of file lock.
564///
565/// Used with [`FsLock::lock`](crate::FsLock::lock) to request either shared
566/// or exclusive access to a file.
567///
568/// # Variants
569///
570/// | Variant | Behavior |
571/// |---------|----------|
572/// | [`Shared`](LockType::Shared) | Multiple readers allowed simultaneously |
573/// | [`Exclusive`](LockType::Exclusive) | Single writer, no other access |
574///
575/// # Lock Compatibility
576///
577/// | Held Lock | Shared Request | Exclusive Request |
578/// |-----------|----------------|-------------------|
579/// | None | ✓ Granted | ✓ Granted |
580/// | Shared | ✓ Granted | ✗ Blocked |
581/// | Exclusive | ✗ Blocked | ✗ Blocked |
582///
583/// # Example
584///
585/// ```rust
586/// use anyfs_backend::{FsLock, FsHandles, OpenFlags, LockType};
587/// use std::path::Path;
588///
589/// // Generic function that works with any FsHandles + FsLock implementation
590/// fn safe_read<B: FsHandles + FsLock>(fs: &B) -> Result<(), anyfs_backend::FsError> {
591///     let handle = fs.open(Path::new("/data.txt"), OpenFlags::READ)?;
592///     
593///     // Shared lock allows concurrent readers
594///     fs.lock(handle, LockType::Shared)?;
595///     
596///     // ... read data ...
597///     
598///     fs.unlock(handle)?;
599///     fs.close(handle)?;
600///     Ok(())
601/// }
602/// ```
603#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
604#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
605pub enum LockType {
606    /// Shared lock — multiple readers allowed simultaneously.
607    ///
608    /// Use when reading data that shouldn't change during the read.
609    /// Multiple processes can hold shared locks on the same file.
610    Shared,
611
612    /// Exclusive lock — single writer, blocks all other access.
613    ///
614    /// Use when modifying data. Only one process can hold an exclusive
615    /// lock, and it blocks all shared lock requests.
616    Exclusive,
617}
618
619/// Serde support for SystemTime (when serde feature is enabled).
620#[cfg(feature = "serde")]
621mod system_time_serde {
622    use serde::{Deserialize, Deserializer, Serialize, Serializer};
623    use std::time::{Duration, SystemTime, UNIX_EPOCH};
624
625    pub fn serialize<S>(time: &SystemTime, serializer: S) -> Result<S::Ok, S::Error>
626    where
627        S: Serializer,
628    {
629        let duration = time.duration_since(UNIX_EPOCH).unwrap_or(Duration::ZERO);
630        (duration.as_secs(), duration.subsec_nanos()).serialize(serializer)
631    }
632
633    pub fn deserialize<'de, D>(deserializer: D) -> Result<SystemTime, D::Error>
634    where
635        D: Deserializer<'de>,
636    {
637        let (secs, nanos): (u64, u32) = Deserialize::deserialize(deserializer)?;
638        Ok(UNIX_EPOCH + Duration::new(secs, nanos))
639    }
640}
641
642#[cfg(test)]
643mod tests {
644    use super::*;
645
646    #[test]
647    fn file_type_equality() {
648        assert_eq!(FileType::File, FileType::File);
649        assert_ne!(FileType::File, FileType::Directory);
650    }
651
652    #[test]
653    fn metadata_is_file() {
654        let m = Metadata {
655            file_type: FileType::File,
656            ..Default::default()
657        };
658        assert!(m.is_file());
659        assert!(!m.is_dir());
660        assert!(!m.is_symlink());
661    }
662
663    #[test]
664    fn metadata_is_dir() {
665        let m = Metadata {
666            file_type: FileType::Directory,
667            ..Default::default()
668        };
669        assert!(!m.is_file());
670        assert!(m.is_dir());
671        assert!(!m.is_symlink());
672    }
673
674    #[test]
675    fn metadata_is_symlink() {
676        let m = Metadata {
677            file_type: FileType::Symlink,
678            ..Default::default()
679        };
680        assert!(!m.is_file());
681        assert!(!m.is_dir());
682        assert!(m.is_symlink());
683    }
684
685    #[test]
686    fn permissions_from_mode() {
687        let p = Permissions::from_mode(0o755);
688        assert_eq!(p.mode(), 0o755);
689    }
690
691    #[test]
692    fn permissions_from_mode_masks_extra_bits() {
693        let p = Permissions::from_mode(0o100755);
694        assert_eq!(p.mode(), 0o755);
695    }
696
697    #[test]
698    fn permissions_readonly() {
699        let readonly = Permissions::from_mode(0o444);
700        assert!(readonly.readonly());
701
702        let writable = Permissions::from_mode(0o644);
703        assert!(!writable.readonly());
704    }
705
706    #[test]
707    fn permissions_defaults() {
708        assert_eq!(Permissions::default_file().mode(), 0o644);
709        assert_eq!(Permissions::default_dir().mode(), 0o755);
710    }
711
712    #[test]
713    fn open_flags_constants() {
714        // Bind to variables to avoid constant-expression lints
715        let read = OpenFlags::READ;
716        let write = OpenFlags::WRITE;
717        let read_write = OpenFlags::READ_WRITE;
718        let append = OpenFlags::APPEND;
719
720        // READ: read-only access
721        assert!(read.read);
722        assert!(!read.write);
723        assert!(!read.create);
724
725        // WRITE: write with create and truncate
726        assert!(!write.read);
727        assert!(write.write);
728        assert!(write.create);
729        assert!(write.truncate);
730
731        // READ_WRITE: both read and write
732        assert!(read_write.read);
733        assert!(read_write.write);
734        assert!(!read_write.create);
735
736        // APPEND: write with create, append mode, no truncate
737        assert!(append.write);
738        assert!(append.create);
739        assert!(append.append);
740        assert!(!append.truncate);
741    }
742
743    #[test]
744    fn lock_type_equality() {
745        assert_eq!(LockType::Shared, LockType::Shared);
746        assert_eq!(LockType::Exclusive, LockType::Exclusive);
747        assert_ne!(LockType::Shared, LockType::Exclusive);
748    }
749
750    #[test]
751    fn root_inode_is_one() {
752        assert_eq!(ROOT_INODE, 1);
753    }
754
755    #[test]
756    fn handle_equality() {
757        assert_eq!(Handle(42), Handle(42));
758        assert_ne!(Handle(1), Handle(2));
759    }
760}