use std::io; use tokio::{io::{AsyncRead, AsyncSeek, AsyncWrite, AsyncWriteExt}, sync::mpsc}; use yazi_macro::ok_or_not_found; use yazi_shared::{path::{DynPath, PathBufDyn}, strand::{AsStrand, StrandCow}, url::{AsUrl, Url, UrlBuf}}; use crate::{cha::{Cha, ChaType}, engine::{Attrs, Capabilities}, file::{File, FileExtra}}; pub trait Engine: Sized { type File: AsyncRead + AsyncSeek + AsyncWrite + Unpin; type Demand: FileBuilder; type ReadDir: DirReader + 'static; type UrlCow; type Me<'a>: Engine; fn absolute(&self) -> impl Future>; fn canonicalize(&self) -> impl Future>; fn capabilities(&self) -> impl Future>; fn casefold(&self) -> impl Future>; fn copy

(&self, to: P, attrs: Attrs) -> impl Future> where P: DynPath; fn copy_progressive(&self, to: P, attrs: A) -> io::Result>> where P: DynPath, A: Into; fn create(&self) -> impl Future> { async move { self.demand().write(true).create(true).truncate(true).open(self.url()).await } } fn create_dir(&self) -> impl Future>; fn create_dir_all(&self) -> impl Future> { async move { let mut url = self.url(); if url.loc().is_empty() { return Ok(()); } let mut stack = Vec::new(); loop { match Self::new(url).await?.create_dir().await { Ok(()) => break, Err(e) if e.kind() == io::ErrorKind::NotFound => { if let Some(parent) = url.parent() { stack.push(url); url = parent; } else { return Err(io::Error::other("failed to create whole tree")); } } Err(_) if Self::new(url).await?.metadata().await.is_ok_and(|m| m.is_dir()) => break, Err(e) => return Err(e), } } while let Some(u) = stack.pop() { match Self::new(u).await?.create_dir().await { Ok(()) => {} Err(_) if Self::new(u).await?.metadata().await.is_ok_and(|m| m.is_dir()) => {} Err(e) => return Err(e), } } Ok(()) } } fn create_new(&self) -> impl Future> { async move { self.demand().write(true).create_new(true).open(self.url()).await } } fn demand(&self) -> Self::Demand { Self::Demand::default() } fn file(&self) -> impl Future> { async move { let cha = self.symlink_metadata().await?; let (mut followed, mut link_to) = (None, None); if cha.is_link() { followed = self.metadata().await.ok(); link_to = self.read_link().await.ok(); } Ok(File { url: self.url().to_owned(), cha: cha.follow(followed), extra: FileExtra::new(link_to, None), }) } } fn hard_link

(&self, to: P) -> impl Future> where P: DynPath; fn metadata(&self) -> impl Future>; fn new<'a>(url: Url<'a>) -> impl Future>>; fn open(&self) -> impl Future> { async move { self.demand().read(true).open(self.url()).await } } fn read_dir(self) -> impl Future>; fn read_link(&self) -> impl Future>; fn revalidate(&self, file: File) -> impl Future>> { async move { let new = self.file().await?; if new.cha.hits(file.cha) { Ok(None) } else { Ok(Some(new)) } } } fn remove_dir(&self) -> impl Future>; fn remove_dir_all(&self) -> impl Future> { async fn remove_dir_all_impl

(url: Url<'_>) -> io::Result<()> where P: Engine, { let mut it = ok_or_not_found!(P::new(url).await?.read_dir().await, return Ok(())); while let Some(child) = it.next().await? { let ft = ok_or_not_found!(child.file_type().await, continue); let result = if ft.is_dir() { Box::pin(remove_dir_all_impl::

(child.url().as_url())).await } else { P::new(child.url().as_url()).await?.remove_file().await }; () = ok_or_not_found!(result); } Ok(ok_or_not_found!(P::new(url).await?.remove_dir().await)) } async move { let cha = ok_or_not_found!(self.symlink_metadata().await, return Ok(())); if cha.is_link() { self.remove_file().await } else { remove_dir_all_impl::(self.url()).await } } } fn remove_dir_clean(&self) -> impl Future> { let root = self.url().to_owned(); async move { let mut stack = vec![(root, false)]; let mut result = Ok(()); while let Some((dir, visited)) = stack.pop() { let Ok(engine) = Self::new(dir.as_url()).await else { continue; }; if visited { result = engine.remove_dir().await; } else if let Ok(mut it) = engine.read_dir().await { stack.push((dir, true)); while let Ok(Some(ent)) = it.next().await { if ent.file_type().await.is_ok_and(|t| t.is_dir()) { stack.push((ent.url(), false)); } } } } result } } fn remove_file(&self) -> impl Future>; fn rename

(&self, to: P) -> impl Future> where P: DynPath; fn set_attrs(&self, attrs: Attrs) -> impl Future>; fn symlink(&self, original: S, _is_dir: F) -> impl Future> where S: AsStrand, F: AsyncFnOnce() -> io::Result; fn symlink_dir(&self, original: S) -> impl Future> where S: AsStrand, { self.symlink(original, async || Ok(true)) } fn symlink_file(&self, original: S) -> impl Future> where S: AsStrand, { self.symlink(original, async || Ok(false)) } fn symlink_metadata(&self) -> impl Future>; fn trash(&self) -> impl Future>; fn url(&self) -> Url<'_>; fn write(&self, contents: C) -> impl Future> where C: AsRef<[u8]>, { async move { self.create().await?.write_all(contents.as_ref()).await } } } // --- DirReader pub trait DirReader { type Entry: FileHolder; #[must_use] fn next(&mut self) -> impl Future>>; } // --- FileHolder pub trait FileHolder { #[must_use] fn file(&self) -> impl Future>; #[must_use] fn file_type(&self) -> impl Future>; #[must_use] fn metadata(&self) -> impl Future>; #[must_use] fn name(&self) -> StrandCow<'_>; #[must_use] fn path(&self) -> PathBufDyn; #[must_use] fn url(&self) -> UrlBuf; } // --- FileBuilder pub trait FileBuilder: Sized + Default { type File: AsyncRead + AsyncSeek + AsyncWrite + Unpin; fn append(&mut self, append: bool) -> &mut Self; fn attrs(&mut self, attrs: Attrs) -> &mut Self; fn create(&mut self, create: bool) -> &mut Self; fn create_new(&mut self, create_new: bool) -> &mut Self; fn open(&self, url: U) -> impl Future> where U: AsUrl; fn read(&mut self, read: bool) -> &mut Self; fn truncate(&mut self, truncate: bool) -> &mut Self; fn write(&mut self, write: bool) -> &mut Self; }