file_system/fundamentals/
entry.rs

1//! Directory entry representation for file system operations.
2
3//!
4//! This module provides the [`Entry_type`] structure which represents individual
5//! entries within directories, such as files, subdirectories, and other file system objects.
6
7use alloc::string::String;
8
9use crate::Kind;
10
11use super::{Inode, Size};
12
13/// Represents a single entry within a directory.
14///
15/// A directory entry contains metadata about a file system object including its
16/// inode number, name, type, and size. This structure is used when reading directory
17/// contents to provide information about each item within the directory.
18///
19/// # Examples
20///
21/// ```rust
22/// # extern crate alloc;
23/// use file_system::*;
24/// use alloc::string::String;
25///
26/// // Create a directory entry for a regular file
27/// let file_entry = Entry_type::new(
28///     Inode_type::new(42),
29///     String::from("document.txt"),
30///     Type_type::File,
31///     Size::new(1024)
32/// );
33///
34/// assert_eq!(file_entry.get_name(), "document.txt");
35/// assert_eq!(file_entry.get_type(), Type_type::File);
36/// assert_eq!(file_entry.get_size().As_u64(), 1024);
37/// ```
38#[derive(Clone, Debug, PartialEq, Eq)]
39pub struct Entry {
40    /// The inode number identifying this file system object
41    inode: Inode,
42    /// The name of this directory entry
43    name: String,
44    /// The type of file system object (file, directory, etc.)
45    r#type: Kind,
46    /// The size of the file system object in bytes
47    size: Size,
48}
49
50impl Entry {
51    /// Create a new directory entry with the specified metadata.
52    ///
53    /// # Arguments
54    ///
55    /// * `Inode` - Unique inode number for this file system object
56    /// * `Name` - Name of the file or directory
57    /// * `Type` - Type of the file system object
58    /// * `Size` - Size in bytes (for files) or entry count (for directories)
59    ///
60    /// # Examples
61    ///
62    /// ```rust
63    /// # extern crate alloc;
64    /// use file_system::*;
65    /// use alloc::string::String;
66    ///
67    /// let entry = Entry_type::new(
68    ///     Inode_type::new(123),
69    ///     String::from("example.txt"),
70    ///     Type_type::File,
71    ///     Size::new(2048)
72    /// );
73    /// ```
74    pub fn new(inode: Inode, name: String, r#type: Kind, size: Size) -> Self {
75        Self {
76            inode,
77            name,
78            r#type,
79            size,
80        }
81    }
82
83    /// Get the inode number of this directory entry.
84    ///
85    /// # Returns
86    ///
87    /// The unique inode number identifying this file system object.
88    pub fn get_inode(&self) -> Inode {
89        self.inode
90    }
91
92    /// Get the name of this directory entry.
93    ///
94    /// # Returns
95    ///
96    /// A reference to the string containing the file or directory name.
97    pub fn get_name(&self) -> &String {
98        &self.name
99    }
100
101    /// Get the type of this directory entry.
102    ///
103    /// # Returns
104    ///
105    /// The type of file system object (file, directory, symbolic link, etc.).
106    pub fn get_type(&self) -> Kind {
107        self.r#type
108    }
109
110    /// Get the size of this directory entry.
111    ///
112    /// # Returns
113    ///
114    /// For files, this is the size in bytes. For directories, this may represent
115    /// the number of entries or be implementation-defined.
116    pub fn get_size(&self) -> Size {
117        self.size
118    }
119
120    /// Set the inode number for this directory entry.
121    ///
122    /// # Arguments
123    ///
124    /// * `Inode` - The new inode number to assign
125    pub fn set_inode(&mut self, inode: Inode) {
126        self.inode = inode;
127    }
128
129    /// Set the name for this directory entry.
130    ///
131    /// # Arguments
132    ///
133    /// * `Name` - The new name to assign to this entry
134    pub fn set_name(&mut self, name: String) {
135        self.name = name;
136    }
137
138    /// Set the type for this directory entry.
139    ///
140    /// # Arguments
141    ///
142    /// * `Type` - The new file system object type
143    pub fn set_type(&mut self, r#type: Kind) {
144        self.r#type = r#type;
145    }
146
147    pub fn set_size(&mut self, size: Size) {
148        self.size = size;
149    }
150}
151
152impl AsMut<[u8]> for Entry {
153    fn as_mut(&mut self) -> &mut [u8] {
154        unsafe {
155            core::slice::from_raw_parts_mut(
156                self as *mut Entry as *mut u8,
157                core::mem::size_of::<Entry>(),
158            )
159        }
160    }
161}
162
163impl TryFrom<&mut [u8]> for &mut Entry {
164    type Error = ();
165
166    fn try_from(value: &mut [u8]) -> Result<Self, Self::Error> {
167        if value.len() != core::mem::size_of::<Entry>() {
168            return Err(());
169        }
170        if !(value.as_ptr() as usize).is_multiple_of(core::mem::align_of::<Entry>()) {
171            return Err(());
172        }
173
174        #[allow(clippy::transmute_ptr_to_ref)]
175        Ok(unsafe { core::mem::transmute::<*mut u8, &mut Entry>(value.as_mut_ptr()) })
176    }
177}
178
179#[cfg(test)]
180mod tests {
181    use super::*;
182    use alloc::string::String;
183
184    #[test]
185    fn test_entry_creation() {
186        let entry = Entry::new(
187            Inode::new(42),
188            String::from("test.txt"),
189            Kind::File,
190            Size::new(1024),
191        );
192
193        assert_eq!(entry.get_inode(), Inode::new(42));
194        assert_eq!(entry.get_name(), "test.txt");
195        assert_eq!(entry.get_type(), Kind::File);
196        assert_eq!(entry.get_size(), Size::new(1024));
197    }
198
199    #[test]
200    fn test_entry_getters() {
201        let entry = Entry::new(
202            Inode::new(123),
203            String::from("directory"),
204            Kind::Directory,
205            Size::new(0),
206        );
207
208        // Test individual getters
209        assert_eq!(entry.get_inode().as_u64(), 123);
210        assert_eq!(entry.get_name(), "directory");
211        assert_eq!(entry.get_type(), Kind::Directory);
212        assert_eq!(entry.get_size().as_u64(), 0);
213    }
214
215    #[test]
216    fn test_entry_setters() {
217        let mut entry = Entry::new(
218            Inode::new(1),
219            String::from("old_name"),
220            Kind::File,
221            Size::new(100),
222        );
223
224        // Test setters
225        entry.set_inode(Inode::new(999));
226        entry.set_name(String::from("new_name.txt"));
227        entry.set_type(Kind::Directory);
228        entry.set_size(Size::new(2048));
229
230        // Verify changes
231        assert_eq!(entry.get_inode().as_u64(), 999);
232        assert_eq!(entry.get_name(), "new_name.txt");
233        assert_eq!(entry.get_type(), Kind::Directory);
234        assert_eq!(entry.get_size().as_u64(), 2048);
235    }
236
237    #[test]
238    fn test_entry_clone() {
239        let original = Entry::new(
240            Inode::new(456),
241            String::from("clone_test.dat"),
242            Kind::File,
243            Size::new(512),
244        );
245
246        let cloned = original.clone();
247
248        // Verify clone has same values
249        assert_eq!(original.get_inode(), cloned.get_inode());
250        assert_eq!(original.get_name(), cloned.get_name());
251        assert_eq!(original.get_type(), cloned.get_type());
252        assert_eq!(original.get_size(), cloned.get_size());
253
254        // Verify they are equal
255        assert_eq!(original, cloned);
256    }
257
258    #[test]
259    fn test_entry_debug() {
260        let entry = Entry::new(
261            Inode::new(789),
262            String::from("debug_test"),
263            Kind::SymbolicLink,
264            Size::new(64),
265        );
266
267        let debug_str = alloc::format!("{entry:?}");
268        assert!(debug_str.contains("Entry_type"));
269        assert!(debug_str.contains("789"));
270        assert!(debug_str.contains("debug_test"));
271    }
272
273    #[test]
274    fn test_entry_equality() {
275        let entry1 = Entry::new(
276            Inode::new(100),
277            String::from("same.txt"),
278            Kind::File,
279            Size::new(200),
280        );
281
282        let entry2 = Entry::new(
283            Inode::new(100),
284            String::from("same.txt"),
285            Kind::File,
286            Size::new(200),
287        );
288
289        let entry3 = Entry::new(
290            Inode::new(101),
291            String::from("different.txt"),
292            Kind::File,
293            Size::new(200),
294        );
295
296        assert_eq!(entry1, entry2);
297        assert_ne!(entry1, entry3);
298    }
299
300    #[test]
301    fn test_entry_different_types() {
302        // Test entries with different file types
303        let file_entry = Entry::new(
304            Inode::new(1),
305            String::from("file.txt"),
306            Kind::File,
307            Size::new(1024),
308        );
309
310        let dir_entry = Entry::new(
311            Inode::new(2),
312            String::from("directory"),
313            Kind::Directory,
314            Size::new(0),
315        );
316
317        let symlink_entry = Entry::new(
318            Inode::new(3),
319            String::from("link"),
320            Kind::SymbolicLink,
321            Size::new(32),
322        );
323
324        assert_eq!(file_entry.get_type(), Kind::File);
325        assert_eq!(dir_entry.get_type(), Kind::Directory);
326        assert_eq!(symlink_entry.get_type(), Kind::SymbolicLink);
327
328        assert_ne!(file_entry, dir_entry);
329        assert_ne!(dir_entry, symlink_entry);
330        assert_ne!(file_entry, symlink_entry);
331    }
332
333    #[test]
334    fn test_entry_empty_name() {
335        let entry = Entry::new(Inode::new(0), String::new(), Kind::File, Size::new(0));
336
337        assert_eq!(entry.get_name(), "");
338        assert_eq!(entry.get_name().len(), 0);
339    }
340
341    #[test]
342    fn test_entry_large_values() {
343        let entry = Entry::new(
344            Inode::new(u64::MAX),
345            String::from("large_file.bin"),
346            Kind::File,
347            Size::new(u64::MAX),
348        );
349
350        assert_eq!(entry.get_inode().as_u64(), u64::MAX);
351        assert_eq!(entry.get_size().as_u64(), u64::MAX);
352    }
353
354    #[test]
355    fn test_entry_as_mut_slice() {
356        let mut entry = Entry::new(
357            Inode::new(42),
358            String::from("test"),
359            Kind::File,
360            Size::new(100),
361        );
362
363        let slice = entry.as_mut();
364        assert_eq!(slice.len(), core::mem::size_of::<Entry>());
365    }
366
367    #[test]
368    fn test_entry_from_slice_invalid_size() {
369        let mut buffer = [0u8; 10]; // Too small
370        let result = <&mut Entry>::try_from(buffer.as_mut_slice());
371        assert!(result.is_err());
372    }
373
374    #[test]
375    fn test_entry_from_slice_valid() {
376        // Create a properly sized and aligned buffer
377        let mut buffer = alloc::vec![0u8; core::mem::size_of::<Entry>()];
378
379        // Ensure proper alignment by using Vec which should be properly aligned for any type
380        let result = <&mut Entry>::try_from(buffer.as_mut_slice());
381
382        // This might fail due to alignment requirements, which is expected behavior
383        // The important thing is that we're testing the code path
384        let _ = result; // We don't assert success since alignment isn't guaranteed
385    }
386
387    #[test]
388    fn test_entry_modification_after_creation() {
389        let mut entry = Entry::new(
390            Inode::new(1),
391            String::from("initial"),
392            Kind::File,
393            Size::new(0),
394        );
395
396        // Modify multiple times
397        for i in 1..=5 {
398            entry.set_inode(Inode::new(i));
399            entry.set_name(alloc::format!("name_{i}"));
400            entry.set_size(Size::new(i * 100));
401
402            assert_eq!(entry.get_inode().as_u64(), i);
403            assert_eq!(entry.get_name(), &alloc::format!("name_{i}"));
404            assert_eq!(entry.get_size().as_u64(), i * 100);
405        }
406    }
407
408    #[test]
409    fn test_entry_unicode_names() {
410        let entry = Entry::new(
411            Inode::new(1),
412            String::from("файл.txt"), // Cyrillic
413            Kind::File,
414            Size::new(256),
415        );
416
417        assert_eq!(entry.get_name(), "файл.txt");
418
419        let entry2 = Entry::new(
420            Inode::new(2),
421            String::from("文件.dat"), // Chinese
422            Kind::File,
423            Size::new(512),
424        );
425
426        assert_eq!(entry2.get_name(), "文件.dat");
427    }
428}