use std::io; use tokio::{select, sync::mpsc::{self, UnboundedReceiver}}; use yazi_fs::{Entries, FilesOp, cha::Cha, engine::{DirReader, FileHolder}, file::File, mounts::PARTITIONS}; use yazi_shared::url::UrlBuf; use crate::{VfsCha, VfsFile, VfsFilesOp, engine::{self, DirEntry}}; pub trait VfsEntries { fn from_dir(dir: &UrlBuf) -> impl Future>>; fn from_dir_bulk(dir: &UrlBuf) -> impl Future>>; fn assert_stale(dir: &UrlBuf, cha: Cha) -> impl Future>; } impl VfsEntries for Entries { async fn from_dir(dir: &UrlBuf) -> std::io::Result> { let mut it = engine::read_dir(dir).await?; let (tx, rx) = mpsc::unbounded_channel(); tokio::spawn(async move { while let Ok(Some(ent)) = it.next().await { select! { _ = tx.closed() => break, result = ent.metadata() => { let url = ent.url(); _ = tx.send(match result { Ok(cha) => File::from_follow(url, cha).await, Err(_) => File::from_dummy(url, ent.file_type().await.ok()) }); } } } }); Ok(rx) } async fn from_dir_bulk(dir: &UrlBuf) -> std::io::Result> { let mut it = engine::read_dir(dir).await?; let mut entries = Vec::new(); while let Ok(Some(entry)) = it.next().await { entries.push(entry); } let (first, rest) = entries.split_at(entries.len() / 3); let (second, third) = rest.split_at(entries.len() / 3); async fn go(entries: &[DirEntry]) -> Vec { let mut files = Vec::with_capacity(entries.len()); for ent in entries { let url = ent.url(); files.push(match ent.metadata().await { Ok(cha) => File::from_follow(url, cha).await, Err(_) => File::from_dummy(url, ent.file_type().await.ok()), }); } files } Ok( futures::future::join_all([go(first), go(second), go(third)]) .await .into_iter() .flatten() .collect(), ) } async fn assert_stale(dir: &UrlBuf, cha: Cha) -> Option { use std::io::ErrorKind; match Cha::from_url(dir).await { Ok(c) if !c.is_dir() => FilesOp::issue_error(dir, ErrorKind::NotADirectory).await, Ok(c) if c.hits(cha) && !PARTITIONS.read().timeless(cha) => {} Ok(c) => return Some(c), Err(e) => FilesOp::issue_error(dir, e).await, } None } }