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}