fast-fs 0.2.1

High-speed async file system traversal library with batteries-included file browser component
Documentation
// <FILE>crates/fast-fs/src/models/cls_file_list.rs</FILE> - <DESC>FileList struct</DESC>
// <VERS>VERSION: 0.2.0 - 2025-12-07T16:58:06Z</VERS>
// <WCTX>Renamed from files.rs</WCTX>
// <CLOG>Moved to models/cls_file_list.rs</CLOG>

//! Versioned file list with deferred sorting
use crate::models::cls_file_entry::FileEntry;
use crate::models::cls_sort_by::SortBy;
use std::ops::{Deref, DerefMut};
/// A versioned file list with deferred sorting
#[derive(Debug, Default)]
pub struct FileList {
    items: Vec<FileEntry>,
    hidden: Vec<FileEntry>,
    /// Incremented on every modification
    revision: u64,
    /// Last sorted revision
    version: u64,
    sort_by: SortBy,
    show_hidden: bool,
}
impl Deref for FileList {
    type Target = Vec<FileEntry>;
    fn deref(&self) -> &Self::Target {
        &self.items
    }
}
impl DerefMut for FileList {
    fn deref_mut(&mut self) -> &mut Self::Target {
        self.revision += 1;
        &mut self.items
    }
}
impl FileList {
    /// Create a new empty file list
    pub fn new() -> Self {
        Self::default()
    }
    /// Create a file list from entries
    pub fn from_entries(entries: Vec<FileEntry>) -> Self {
        let mut list = Self::new();
        list.update_full(entries);
        list
    }
    /// Replace all entries
    pub fn update_full(&mut self, entries: Vec<FileEntry>) {
        self.revision += 1;
        if self.show_hidden {
            self.items = entries;
            self.hidden.clear();
        } else {
            let (hidden, visible): (Vec<_>, Vec<_>) =
                entries.into_iter().partition(|e| e.is_hidden);
            self.items = visible;
            self.hidden = hidden;
        }
    }
    /// Add entries to the list
    pub fn push_batch(&mut self, entries: Vec<FileEntry>) {
        self.revision += 1;
        if self.show_hidden {
            self.items.extend(entries);
        } else {
            for entry in entries {
                if entry.is_hidden {
                    self.hidden.push(entry);
                } else {
                    self.items.push(entry);
                }
            }
        }
    }
    /// Check if sorting is needed and perform it
    pub fn catchup(&mut self) -> bool {
        if self.version == self.revision {
            return false;
        }
        self.version = self.revision;
        self.sort_items();
        true
    }
    /// Set the sort order
    pub fn set_sort(&mut self, sort_by: SortBy) {
        if self.sort_by != sort_by {
            self.sort_by = sort_by;
            self.revision += 1;
        }
    }
    /// Toggle hidden file visibility
    pub fn set_show_hidden(&mut self, show: bool) {
        if self.show_hidden == show {
            return;
        }
        self.show_hidden = show;
        self.revision += 1;
        if show {
            self.items.append(&mut self.hidden);
        } else {
            let items = std::mem::take(&mut self.items);
            let (hidden, visible): (Vec<_>, Vec<_>) = items.into_iter().partition(|e| e.is_hidden);
            self.items = visible;
            self.hidden = hidden;
        }
    }
    /// Get the current revision number (incremented on each mutation)
    pub fn revision(&self) -> u64 {
        self.revision
    }

    /// Check if the list needs sorting (deferred sort pending)
    pub fn needs_sort(&self) -> bool {
        self.version != self.revision
    }

    /// Get the current sort order
    pub fn sort_by(&self) -> SortBy {
        self.sort_by
    }

    /// Get the count of hidden files (not in visible list)
    pub fn hidden_count(&self) -> usize {
        self.hidden.len()
    }
    fn sort_items(&mut self) {
        match self.sort_by {
            SortBy::Name => {
                self.items
                    .sort_by(|a, b| a.name.to_lowercase().cmp(&b.name.to_lowercase()));
            }
            SortBy::NameDesc => {
                self.items
                    .sort_by(|a, b| b.name.to_lowercase().cmp(&a.name.to_lowercase()));
            }
            SortBy::Size => {
                self.items.sort_by_key(|e| e.size);
            }
            SortBy::SizeDesc => {
                self.items.sort_by(|a, b| b.size.cmp(&a.size));
            }
            SortBy::Modified => {
                self.items.sort_by_key(|e| e.modified);
            }
            SortBy::ModifiedDesc => {
                self.items.sort_by(|a, b| b.modified.cmp(&a.modified));
            }
            SortBy::Extension => {
                self.items.sort_by(|a, b| {
                    let ext_cmp = a.extension().cmp(&b.extension());
                    if ext_cmp == std::cmp::Ordering::Equal {
                        a.name.to_lowercase().cmp(&b.name.to_lowercase())
                    } else {
                        ext_cmp
                    }
                });
            }
            SortBy::DirsFirst => {
                self.items.sort_by(|a, b| match (a.is_dir, b.is_dir) {
                    (true, false) => std::cmp::Ordering::Less,
                    (false, true) => std::cmp::Ordering::Greater,
                    _ => a.name.to_lowercase().cmp(&b.name.to_lowercase()),
                });
            }
        }
    }
}

// <FILE>crates/fast-fs/src/models/cls_file_list.rs</FILE> - <DESC>FileList struct</DESC>
// <VERS>END OF VERSION: 0.2.0 - 2025-12-07T16:58:06Z</VERS>