#![allow(unused_labels)]
#![allow(unused_variables)]



use std::{
    ffi::OsStr, fs, io::Read,
    path::{self,Path,PathBuf},time::Instant,
};


#[cfg(all(any(target_os = "macos", target_os = "linux")))]
use std::os::unix::fs::{FileTypeExt, MetadataExt};
#[cfg(all(any(target_os = "windows")))]
use std::os::windows::fs::MetadataExt;

use fs_extra;
use mime_guess;
use mime_sniffer;

use async_fs;
use lwleen_rpc::futures::{AsyncReadExt, StreamExt};



use notify::{self,INotifyWatcher, Watcher};


use crate::prelude::*;
use super::type_file::*;
pub static 全局_文件移动管理: OnceCell<文件移动管理> = OnceCell::new();
pub static 全局_文件监控管理: OnceCell<文件监控管理> = OnceCell::new();


// static 全局_通用文件项数组_操作序号:AtomicUsize = AtomicUsize::new(0);
// Some(全局_通用文件项数组_操作序号.fetch_add(1, Ordering::AcqRel)+1) 
// if let Some(序号) = 操作序号 && 序号 != 全局_通用文件项数组_操作序号.load(Ordering::Acquire){   break;  }


#[gen_特征_derive(MOD特征_file)]
impl 信令路由{
    async fn exec文本信令_file(&self,函数名:&str, mut 参数数组:Vec<String>)-> anyhow_Result<(bool,Value,Vec<String>)>{
        let 信令路由ref = self;
        let 结果 = match 函数名{
            "文件_存在"=>{
                if 参数数组.len() < 1 {  return Err(anyhow_err!("文件_通用文件项, 缺少参数::  <路径>")) }
                json!(信令路由ref.文件_存在(参数数组.remove(0)).await)
            }
            "文件_是目录"=>{
                if 参数数组.len() < 1 {  return Err(anyhow_err!("文件_通用文件项, 缺少参数::  <路径>")) }
                json!(信令路由ref.文件_是目录(参数数组.remove(0)).await)
            }
            "文件_统计路径大小"=>{   
                //   go 文件_统计路径大小 /home/lwl/Desktop/新文件.txt 3000
                //   go 文件_统计路径大小 /home/lwl/Desktop/  3000
                if 参数数组.len() < 1 {  return Err(anyhow_err!("文件_统计路径大小, 缺少参数::  <路径> ... <限时ms>")) }
                let 限时ms = if let Some(last) = 参数数组.last() && let Ok(限定时间) = last.parse::<u128>(){ 参数数组.pop(); Some(限定时间)   }else{ None };
                json!(信令路由ref.文件_统计路径大小(参数数组, 限时ms).await?)
            }
            "文件_提取下层文件"=>{
                if 参数数组.len() < 3 {  return Err(anyhow_err!("文件_提取下层文件, 缺少参数::<应用分类> <深度> <路径数组> ")) }
                let 路径数组= 参数数组[2..].to_vec();
                let 深度 = 参数数组[1].parse::<u64>().unwrap_or(1);
                json!(信令路由ref.文件_提取下层文件(路径数组, 参数数组.remove(0), Some(深度), None ).await?)
            },
            "文件_新建空文件"=>{
                if 参数数组.len() < 2 {  return Err(anyhow_err!("文件_新建空文件, 缺少参数::  <路径> <类型or后缀>")) }  
                let 后缀 = if 参数数组.len() >=2 { 参数数组.remove(1)  } else{ "".to_string() };
                json!(信令路由ref.文件_新建空文件(参数数组.remove(0), 后缀).await?)
            }
            "文件_新建软连接"=>{
                if 参数数组.len() < 2 {  return Err(anyhow_err!("文件_新建软连接, 缺少参数::  <路径> <目标路径>")) }
                let 目标路径 = 参数数组.remove(1);
                json!(信令路由ref.文件_新建软连接(参数数组.remove(0), 目标路径).await?)
            }
            "文件_读文本"=>{
                if 参数数组.len() < 1 {  return Err(anyhow_err!("文件_读文本, 缺少参数::  <路径>")) }
                json!(信令路由ref.文件_读文本(参数数组.remove(0)).await?)
            }
            "文件_读文件头" =>{
                if 参数数组.len() < 1 {  return Err(anyhow_err!("文件_读文件头, 缺少参数::  <路径>")) }
                json!(信令路由ref.文件_读文件头(参数数组.remove(0),None).await?)
            }
            "文件_读文件mime"=>{
                if 参数数组.len() < 1 {  return Err(anyhow_err!("文件_读文件mime, 缺少参数::  <路径>")) }
                json!(信令路由ref.文件_读文件mime(参数数组.remove(0)).await?)
            }
            "文件_写文本"=>{
                if 参数数组.len() < 2 {  return Err(anyhow_err!("文件_写文本, 缺少参数::  <路径> <内容>")) }
                let 内容 = 参数数组.remove(1);
                信令路由ref.文件_写文本(参数数组.remove(0),内容).await?;
                json!(true)
            }
            "文件_重命名"=>{
                if 参数数组.len() < 2 {  return Err(anyhow_err!("文件_重命名, 缺少参数:: <新文件名>  <路径>")) }
                let 新文件名 = 参数数组.remove(0);
                json!(信令路由ref.文件_重命名(参数数组.remove(0),新文件名).await?)
            }
            "文件_重命名_多文件"=>{
                if 参数数组.len() < 2 {  return Err(anyhow_err!("文件_重命名, 缺少参数::  <新文件名>  <路径>...")) }
                let 新文件名 = 参数数组.remove(0);
                json!(信令路由ref.文件_重命名_多文件(参数数组, 新文件名).await?)
            }
            "文件_删除"=>{
                if 参数数组.len() < 1 {  return Err(anyhow_err!("文件_删除, 缺少参数::  <路径>")) }
                json!(信令路由ref.文件_删除(参数数组.remove(0)).await?)
            }
            "file" | "文件_通用文件项"=>{
                if 参数数组.len() < 1 {  return Err(anyhow_err!("文件_通用文件项, 缺少参数::  <路径>")) }
                json!(信令路由ref.通用文件项(参数数组.remove(0)).await)
            }
            "文件_通用文件项_路径数组"=>{
                if 参数数组.len() < 1 {  return Err(anyhow_err!("文件_通用文件项_路径数组, 缺少参数::  <路径> <路径> ...")) }
                json!(信令路由ref.通用文件项_路径数组(参数数组).await?)
            }
            "dir" | "文件_通用文件项数组"=>{
                // 如果最后一项是 true false 会去除 pop
                if 参数数组.len() < 1 {  return Err(anyhow_err!("文件_通用文件项数组, 缺少参数::  <路径>")) }
                json!(信令路由ref.通用文件项数组(参数数组.remove(0)).await?)
            }
            "文件移动过程_控制"=>{
                if 参数数组.len() < 2 {  return Err(anyhow_err!("文件移动过程_控制, 缺少参数::  <copy_id> <操作>")) }
                let 操作 = 参数数组.remove(1);
                信令路由ref.文件移动过程_控制(参数数组[0].parse::<u64>()?, 操作).await?;
                json!(true)
            }
            "文件移动过程_新建"=>{
                if 参数数组.len() < 3 {  return Err(anyhow_err!("文件移动过程_新建, 缺少参数::<文件操作> <path_to> <src> <src>...")); }
                json!(信令路由ref.文件移动过程_新建(参数数组.remove(0), 参数数组.remove(0), 参数数组).await?)
            }
            /*
            "文件_移动文件过程"=>{
                if 参数数组.len() < 3 {  return Err(anyhow_err!("移动文件过程, 缺少参数::<复制_覆盖> <path_to> <src> <src>...")); } 
                let 复制id = 信令路由.文件移动过程_新建(  参数数组[0].to_string(), 参数数组[1].to_string(), 参数数组[2..].to_vec()).await?;
                
                //---------------持续输出终端内容-----------------------------
                信令路由.on_文件移动过程数据流(复制id,|copy_id,输出数据|{
                    let json文本 = json!(&输出数据).to_string();
                    print!("_开🌷_{}_🍊束_\n",json文本); 
                });
                //--------- 接管输入   子终端的输入输出走 stderr 通道  ------------
                let loop退出 = loop_stdin阻塞等待用户输入(|stdin文本:String|{
                    for 文本 in  解码文本信令(stdin文本){
                        #[derive(Debug,Clone, serde::Deserialize)]
                        struct 控制指令{  控制指令:String  }
                        if let Ok(对象) =  serde_json::from_str::<控制指令>(&文本){ 
                            信令路由.文件移动过程_控制(复制id, &对象.控制指令 ).block阻塞完成().ok();
                        }
                    }
                    Ok(())
                });
                信令路由.文件移动过程_控制(复制id, "强制结束" ).block阻塞完成().ok();
                match  loop退出 {
                    Ok(())=>{ std::process::exit(0);  }
                    Err(err)=>{ std::process::exit(1); }
                }
                json!(复制id);
            }
            */
            "文件监控_新建"=>{
                let watch_id = 信令路由ref.文件监控_新建()?;
                for 路径 in 参数数组.iter(){
                    信令路由ref.文件监控_添加路径(watch_id, 路径)?;
                }
                信令路由ref.on_文件监控数据流(watch_id, move|watch_id, 文件变动|{
                    let json文本 = json!(&文件变动).to_string();
                    print!("_开🌷_{}_🍊束_\n",json文本);
                });
                json!(watch_id)
            },
            "文件监控_添加路径"=>{
                if 参数数组.len() < 2 {  return Err(anyhow_err!("文件_删除, 文件监控_添加路径:: <watch_id> <路径>")) }
                let watch_id = 参数数组[0].parse::<u64>()?;
                for 路径 in 参数数组[1..].iter(){    信令路由ref.文件监控_添加路径(watch_id, 路径)?;   }
                json!(true)
            },
            "文件监控_移除路径"=>{ 
                if 参数数组.len() < 2 {  return Err(anyhow_err!("文件_删除, 文件监控_移除路径:: <watch_id> <路径>")) }
                let watch_id = 参数数组[0].parse::<u64>()?;
                for 路径 in 参数数组[1..].iter(){    信令路由ref.文件监控_移除路径(watch_id, 路径)?;   }
                json!(true)
            },
            "文件监控_清空路径"=>{
                if 参数数组.len() < 1 {  return Err(anyhow_err!("文件_删除, 文件监控_清空路径:: <watch_id>")) }
                json!( 信令路由ref.文件监控_清空路径(参数数组[0].parse::<u64>()?)? )
            },
            "文件监控_删除单个"=>{ 
                if 参数数组.len() < 1 {  return Err(anyhow_err!("文件_删除, 文件监控_清空路径:: <watch_id>")) }
                json!( 信令路由ref.文件监控_删除单个(参数数组[0].parse::<u64>()?)?  )
            },
            "文件监控_删除所有"=>{   json!(信令路由ref.文件监控_删除所有()?)   }
            _=>{ return Ok((false, json!(""), 参数数组)); }
        };
        Ok((true, 结果, Vec::new()))
    }

    
    fn 文件_统计路径大小(&self,路径数组:Vec<String>, 限时ms:Option<u128>)->impl 信令任务<文件统计信息>{   
        core发布信令任务!(self, 信令表::文件_统计路径大小 { 路径数组,限时ms },  信令表::文件_统计路径大小_回复 => 文件统计信息 )
    }
    /// ##  文件操作 =>    "复制覆盖"  "复制不覆盖"   "复制共存"    "剪切覆盖"  "剪切不覆盖"  "剪切共存" 
    fn 文件移动过程_新建<T:AsRef<str>>(&self, 文件操作:T,目的地:T, 源文件数组:Vec<String> )-> impl 信令任务<原子ID>{ 
        core发布信令任务!(self, 信令表::文件移动过程_新建 {  文件操作:文件操作.as_ref().to_string(), 目的地:目的地.as_ref().to_string(), 源文件数组  },  信令表::文件移动过程_新建_回复 => copy_id )
    }
    /// ##  控制指令 =>    "正常"   "暂停"   "强制结束"   "删除复制任务"
    fn 文件移动过程_控制(&self,   copy_id:原子ID,控制指令:impl AsRef<str>)-> impl 信令任务<原子ID>{
        core发布信令任务!(self, 信令表::文件移动过程_控制 { copy_id, 控制指令:控制指令.as_ref().to_string() },  信令表::文件移动过程_控制_回复 => copy_id )
    }
    fn emit_文件移动过程_已退出Exit(&self, copy_id: 原子ID, 退出原因:String) {
        self.task发布信令_单向(信令表::文件移动过程_已退出 { copy_id , 退出原因 });
        self.core_chan数据流通道表().remove(&copy_id);
    }
    fn emit_文件移动过程_输出数据流(&self,  copy_id: 原子ID, 输出数据:文件复制过程状态 ){ 
        if let Some(通道数组) = self.core_chan数据流通道表().get(&copy_id){
            if 通道数组.len()==1{  通道数组[0].force_send(Box::new(信令表::文件移动过程_输出数据流 { copy_id, 输出数据   })).ok();   }
            else{ for 通道 in 通道数组.iter(){  通道.force_send(Box::new(信令表::文件移动过程_输出数据流 { copy_id, 输出数据:输出数据.clone()   })).ok();  }     }
        }
    }
    fn on_文件移动过程数据流<F>(&self,        copy_id: 原子ID, 外部回调:F) where F:Fn(原子ID,文件复制过程状态) ->() + Send + 'static  {
        let (发送tx,接收rx) =  async_unbounded();
        self.core_chan数据流通道表().entry(copy_id)
                        .or_insert(Vec::new())
                        .push(发送tx);
        let _ = self.pool异步任务(async move{
            while let Ok(信令) = 接收rx.recv().await{
                if let Ok(信令表::文件移动过程_输出数据流 { copy_id, 输出数据  }) = 信令.try_into(){
                    外部回调(copy_id, 输出数据); 
                }else{ break; };
            }
        });
    }

    async fn 通用文件项(&self,路径:impl AsRef<str>)->文件项{  文件::new(路径).通用文件项().await  }
    async fn 通用文件项_路径数组<T:AsRef<str>>(&self,路径数组:Vec<T>)->anyhow_Result<Vec<文件项>>{  通用文件项_路径数组(路径数组).await  }
    /// ## 目录下 文件项 数组
    async fn 通用文件项数组(&self,路径:impl AsRef<str>)->anyhow_Result<Vec<文件项>>{  文件::new(路径).通用文件项数组().await  }
    fn 通用文件项_同步(&self,路径:impl AsRef<str>)->文件项{  文件::new(路径).通用文件项_同步()     }
    async fn 通用文件项_路径数组_同步<T:AsRef<str>>(&self,路径数组:Vec<T>, 是否唯一:bool)->anyhow_Result<Vec<文件项>>{  通用文件项_路径数组_同步(路径数组)  }
    fn 通用文件项数组_同步(&self,路径:impl AsRef<str>, 是否唯一:bool)->anyhow_Result<Vec<文件项>>{ 文件::new(路径).通用文件项数组_同步() }
    
    async fn 文件_是目录(&self,路径:impl AsRef<str>)->bool{  文件::new(路径).文件_是目录().await  }
    async fn 文件_存在(&self,路径:impl AsRef<str>)->bool{  文件::new(路径).文件_存在().await  }
    async fn 文件_写文本(&self,路径:impl AsRef<str>,文本: impl AsRef<str> )->anyhow_Result<()>{  文件::new(路径).文件_写文本(文本).await  }
    async fn 文件_写(&self,路径:impl AsRef<str>,原始数据: impl AsRef<[u8]>)->anyhow_Result<()>{  文件::new(路径).文件_写(原始数据).await  }
    async fn 文件_读(&self,路径:impl AsRef<str>)->anyhow_Result<Vec<u8>>{  文件::new(路径).文件_读().await  }
    async fn 文件_读文件头(&self,路径:impl AsRef<str>,字节数:Option<usize>)->anyhow_Result<Vec<u8>>{  文件::new(路径).文件_读文件头(字节数).await }
    async fn 文件_读文件mime(&self,路径:impl AsRef<str>)->anyhow_Result<String>{  文件::new(路径).文件_读文件mime().await  }
    async fn 文件_读文本(&self,路径:impl AsRef<str>)->anyhow_Result<String>{  文件::new(路径).文件_读文本().await  }
    async fn 文件_删除(&self,路径:impl AsRef<str>)->anyhow_Result<()>{  文件::new(路径).文件_删除().await  }
    async fn 文件_重命名(&self,路径:impl AsRef<str>, 新文件名:impl AsRef<str>)->anyhow_Result<String>{  文件::new(路径).文件_重命名(新文件名).await  }
    async fn 文件_重命名_多文件<T:AsRef<str>>(&self,路径数组:Vec<T>, 新文件名:T)->anyhow_Result<()> {  文件_重命名_多文件(路径数组,新文件名).await  }
    async fn 文件_新建空文件(&self,路径:impl AsRef<str>, 后缀:impl AsRef<str>)->anyhow_Result<String>{  文件::new(路径).文件_新建空文件(后缀).await  }
    async fn 文件_新建软连接(&self,路径:impl AsRef<str>, 目标目录:impl AsRef<str>)->anyhow_Result<String>{ 文件::new(路径).文件_新建软连接(目标目录).await }
    async fn 文件_提取下层文件(&self,路径数组:Vec<String>, 应用分类:String, 深度:Option<u64>, 存放目录:Option<String>)->anyhow_Result<()>{  文件::文件_提取下层文件(路径数组,应用分类,深度,存放目录).await  }
    


    fn 文件监控_新建(&self)->anyhow_Result<原子ID>{
        let 全局监控 = 全局_文件监控管理.get_or_init(||文件监控管理::new(self.clone()));
        全局监控.新建文件监听器()
    }
    fn 文件监控_添加路径(&self,watch_id:原子ID,路径:impl AsRef<str>)->anyhow_Result<()>{
        let 全局监控 = 全局_文件监控管理.get_or_init(||文件监控管理::new(self.clone()));
        全局监控.添加路径(watch_id, 路径)
    }
    fn 文件监控_移除路径(&self,watch_id:原子ID,路径:impl AsRef<str>)->anyhow_Result<()>{
        let 全局监控 = 全局_文件监控管理.get_or_init(||文件监控管理::new(self.clone()));
        全局监控.移除路径(watch_id,路径)
    }
    /// 清空 watch_id 监听的路径
    fn 文件监控_清空路径(&self,watch_id:原子ID)->anyhow_Result<()>{
        let 全局监控 = 全局_文件监控管理.get_or_init(||文件监控管理::new(self.clone()));
        全局监控.清空路径(watch_id)
    }
    /// 删除 watch_id
    fn 文件监控_删除单个(&self,watch_id:原子ID)->anyhow_Result<()>{
        let 全局监控 = 全局_文件监控管理.get_or_init(||文件监控管理::new(self.clone()));
        全局监控.删除单个(watch_id)
    }
    fn 文件监控_删除所有(&self)->anyhow_Result<()>{
        let 全局监控 = 全局_文件监控管理.get_or_init(||文件监控管理::new(self.clone()));
        全局监控.删除所有()
    }
    fn emit_文件监控_输出数据流(&self, watch_id: 原子ID, 输出数据:文件变动信令){
        if let Some(通道数组) = self.core_chan数据流通道表().get(&watch_id){
            if 通道数组.len()==1{  通道数组[0].force_send(Box::new(信令表::文件监控变动_输出数据流 { watch_id, 文件变动:输出数据   })).ok();   }
            else{ for 通道 in 通道数组.iter(){  通道.force_send(Box::new(信令表::文件监控变动_输出数据流 { watch_id, 文件变动:输出数据.clone()   })).ok();  }     }
        }
    }
    fn on_文件监控数据流<F>(&self,watch_id: 原子ID, 外部回调:F) where F:Fn(原子ID,文件变动信令) ->() + Send + 'static  {
        let (发送tx,接收rx) =  async_unbounded();
        self.core_chan数据流通道表().entry(watch_id)
                        .or_insert(Vec::new())
                        .push(发送tx);
        let _ = self.pool异步任务(async move{
            while let Ok(信令) = 接收rx.recv().await{
                if let Ok(信令表::文件监控变动_输出数据流 { watch_id, 文件变动  }) = 信令.try_into(){
                    外部回调(watch_id, 文件变动); 
                }else{ break; };
            }
        });
    }
}



#[derive(Clone, Serialize, Deserialize)]//可选特征
enum 信令表{
    文件_统计路径大小{      路径数组:Vec<String>, 限时ms:Option<u128> },
    文件_统计路径大小_回复{  文件统计信息:文件统计信息 },

    文件移动过程_新建{      文件操作:String, 源文件数组:Vec<String>, 目的地:String },
    文件移动过程_新建_回复{  copy_id:原子ID  },
    文件移动过程_控制{      copy_id:原子ID , 控制指令: String  },
    文件移动过程_控制_回复{  copy_id:原子ID  },
    文件移动过程_输出数据流{ copy_id:原子ID , 输出数据: 文件复制过程状态  },
    文件移动过程_已退出 {    copy_id:原子ID , 退出原因:String },

    文件监控变动_输出数据流{ watch_id:原子ID ,文件变动:文件变动信令  },

}
impl信令特征!(信令表, 执行函数);




async fn 执行函数(信令路由:信令路由,id:信令ID,单个信令:信令表)->anyhow_Result<信令表>{
    let 文件移动管理 = 全局_文件移动管理.get_or_init(||文件移动管理::new(信令路由.clone()));
    
    match 单个信令{
        信令表::文件移动过程_新建 { 文件操作,目的地, 源文件数组}=>{
            let 结果 = 文件移动管理.新建文件移动过程(文件操作,目的地,源文件数组)?;
            anyhow_Ok信令!(信令表::文件移动过程_新建_回复{ copy_id:结果 })
        }
        信令表::文件移动过程_控制{ copy_id, 控制指令  }=>{
            let 结果 = match 字符串转_文件移动_控制指令(控制指令) {
                Ok(指令)=> 文件移动管理.文件移动过程_控制(copy_id,指令).map(|_|  信令表::文件移动过程_控制_回复{ copy_id } ),
                Err(err)=> anyhow_Err信令!("{err}"),
            }?;
            anyhow_Ok信令!(结果)
        }
        信令表::文件移动过程_已退出{ copy_id, 退出原因  }=>{
            anyhow_Err信令!("复制过程退出 {copy_id}  {退出原因}")
        }
        信令表::文件_统计路径大小{ 路径数组,限时ms }=>{
            let res =  信令路由.pool同步任务(move ||{  文件::统计_路径大小_同步(路径数组,限时ms) }).await??;
            anyhow_Ok信令!(信令表::文件_统计路径大小_回复 { 文件统计信息:res })
        }
        _ =>{ return Err(anyhow_err!("信令不存在")); }
    }
}





type 可存储_文件监听回调函数 = Box<dyn Fn(原子ID, 文件变动信令) ->() + Send + Sync + 'static>  ;
#[derive(Default,Clone, Serialize,Deserialize,Debug)]
pub struct 文件变动信令 {
    pub watch_id:原子ID,
    pub 路径数组: Vec<String>,
    pub 变动类型: notify::event::EventKind,
    pub err:String,
}
#[derive(Clone)]
pub struct  文件监控管理{
    pub 信令路由:信令路由,
    pub 监控器Watcher: Arc<DashMap<原子ID,INotifyWatcher>>,
    pub 路径集合:Arc<DashMap<原子ID,DashSet<String>>>,  // 每个 watch_id  对应一个路径数组 
}
impl 文件监控管理{
    pub fn new(信令路由:信令路由)-> Self{
        Self { 信令路由,  监控器Watcher: Arc::new(DashMap::new()) , 路径集合:Arc::new(DashMap::new())  }
    }
    pub fn 新建文件监听器(&self)->anyhow_Result<原子ID>{
        let watch_id = 原子ID对象::next().0;
        self.路径集合.entry(watch_id).or_insert(DashSet::new());
        let 信令路由 = self.信令路由.clone();
        let 监控器 = notify::recommended_watcher(move|res: Result<notify::Event, notify::Error>|{
            match res {
                Ok(event) =>{
                    let 路径数组:Vec<String> = event.paths.into_iter().map(|path|{path.display().to_string()}).collect();
                    let 文件监听信令 = 文件变动信令 {watch_id, 路径数组,  变动类型:event.kind, err:"".into()  };
                    信令路由.emit_文件监控_输出数据流(watch_id,文件监听信令);
                },
                Err(err) =>{
                    let 路径数组:Vec<String> = err.paths.into_iter().map(|path|{path.display().to_string()}).collect();
                    let 错误类型 = match err.kind {
                        notify::ErrorKind::Generic(错误)=>错误.to_string(),
                        notify::ErrorKind::Io(错误)=>错误.to_string(),
                        notify::ErrorKind::InvalidConfig(错误)=>"配置错误".to_string(),
                        notify::ErrorKind::MaxFilesWatch=>"超过最大监控文件数目".to_string(),
                        notify::ErrorKind::PathNotFound=>"找不到监控路径".to_string(),
                        notify::ErrorKind::WatchNotFound=>"监控器不存在".to_string(),
                    };
                    let 文件监听信令 = 文件变动信令 {watch_id, 路径数组,  变动类型:notify::event::EventKind::Other, err:错误类型 };
                    信令路由.emit_文件监控_输出数据流(watch_id,文件监听信令);
                },
            };
        })?;
        self.监控器Watcher.entry(watch_id).or_insert(监控器);
        Ok(watch_id)
    }
    pub fn 添加路径(&self,watch_id:原子ID, 路径:impl AsRef<str>)->anyhow_Result<()>{
        let 可用路径 = 路径.as_ref().to_string();
        let 集合 = self.路径集合.get(&watch_id).ok_or(anyhow_err!("不存在此 watch_id {watch_id}"))?;

        if ! 集合.contains(&可用路径){
            if let Some(监听器) = self.监控器Watcher.get_mut(&watch_id).as_mut(){
                监听器.watch(Path::new(路径.as_ref()), notify::RecursiveMode::NonRecursive )?;
                集合.insert(可用路径);
            }
        }
        Ok(())
    }
    pub fn 移除路径(&self,watch_id:原子ID, 路径:impl AsRef<str>)->anyhow_Result<()>{
        let 可用路径 = 路径.as_ref().to_string();
        let 集合 = self.路径集合.get(&watch_id).ok_or(anyhow_err!("不存在此 watch_id {watch_id}"))?;
        
        if let Some(监听器) = self.监控器Watcher.get_mut(&watch_id).as_mut(){
            监听器.unwatch(Path::new(路径.as_ref()))?;
            集合.remove(&可用路径);
            Ok(())
        }else{
            Err(anyhow_err!("文件监控管理 移除路径 [错误]: 此监听器不存在 {watch_id}"))
        }
    }
    pub fn 清空路径(&self,watch_id:原子ID)->anyhow_Result<()>{
        let 集合 = self.路径集合.get(&watch_id).ok_or(anyhow_err!("不存在此 watch_id {watch_id}"))?;
        for 路径 in 集合.iter(){
            self.移除路径(watch_id, 路径.clone())?;
        }
        Ok(())
    }
    
    pub fn 删除单个(&self, watch_id:原子ID)->anyhow_Result<()>{
        self.监控器Watcher.remove(&watch_id);
        self.路径集合.remove(&watch_id);
        Ok(())
    }
    pub fn 删除所有(&self) -> anyhow_Result<()>{
        for item in self.监控器Watcher.iter(){ self.删除单个(item.key().to_owned())?; }
        for item in self.路径集合.iter(){  self.删除单个(item.key().to_owned())?; }
        Ok(())
    }

}








#[derive(Clone)]
pub struct  文件移动管理{
    信令路由:信令路由,
    记录文件移动过程数组: Arc<DashMap<原子ID, 线程同步控制>>,
}

impl 文件移动管理{
    pub fn new(信令路由:信令路由)->Self{ 
        Self { 
            信令路由,
            记录文件移动过程数组: Arc::new(DashMap::new()),
        }
    }
    pub fn 新建文件移动过程(&self, 文件操作:String,目的地:String, 源文件数组:Vec<String>)->anyhow_Result<原子ID>{
        let copy_id = 原子ID对象::next().0;
        //线程间多控制权
        let 线程控制 = 线程同步控制::new("正常");
        let 线程控制_clone = 线程控制.clone();
        self.记录文件移动过程数组.insert(copy_id, 线程控制);
        
        let (信令路由_c1,信令路由_c2) = (self.信令路由.clone(),self.信令路由.clone());
        std::thread::spawn(move ||{
            let 记录复制输出 = Arc::new(Mutex::new(文件复制过程状态::default()));
            let 记录复制输出c1 = 记录复制输出.clone();
            let 移动文件过程 = 文件移动管理::移动文件过程(目的地,源文件数组, 文件操作, 线程控制_clone , move | 复制过程结果|{
                *记录复制输出c1.lock() = 复制过程结果.clone();
                信令路由_c1.emit_文件移动过程_输出数据流(copy_id, 复制过程结果);
            });
            
            
            {   //------- 处理退出
                let 退出原因 = 移动文件过程.map_or_else(|err|err.to_string(),|_|{ "".to_string() }) ; 
                let mut 复制结果 = 记录复制输出.lock().clone(); 
                if 退出原因!=""{ 复制结果.err = 退出原因.clone() };
                信令路由_c2.emit_文件移动过程_输出数据流(copy_id, 复制结果);
                信令路由_c2.emit_文件移动过程_已退出Exit(copy_id, 退出原因); 
            }
        });
        Ok(copy_id)
    }
    pub fn 文件移动过程_控制(&self, copy_id:原子ID, 控制指令:文件移动控制指令)->anyhow_Result<()>{
        let 线程控制 = {
            self.记录文件移动过程数组.get(&copy_id) 
                .map(|a| { a.clone() })
                .ok_or(anyhow_err!("\n文件移动过程_控制  🍎🍎🍎文件移动过程未创建或已销毁  copy_id: {copy_id}"))?
        };
        线程控制.广播多线程信号(控制指令.to_string());
        Ok(())  
    }

    /// - 统计所有的文件项  src_array中所有文件 ->   复制或移动到   ->   目标文件项
    fn sub分流标记文件项(src_array:&Vec<String> ,目标文件项: &文件项,
                    输出状态:&mut 文件复制过程状态,
                    立即报错退出: &mut impl FnMut(&mut 文件复制过程状态, String) ->anyhow_Result<()> ,
                    控制线程启停_发送输出:&mut impl FnMut(&mut 文件复制过程状态) -> anyhow_Result<u128> ,
    ) -> anyhow_Result<Vec<文件项>>{
        let mut 所有src文件项 = Vec::new();
        'top: for src in src_array{
            let mut 记录文件夹: HashMap<String, 文件项> = HashMap::new();     // 真实路径   -   可能是副本目标项
            let mut src根目录 = std::path::Path::new(src).parent().ok_or(anyhow_err!("此目录没有父路径"))?.display().to_string();
            let mut 计算目标文件项 = 目标文件项.clone();

            for entry in walkdir::WalkDir::new(src).follow_links(输出状态.跟随链接).into_iter(){
                        let 此路径 = match entry {
                            Ok(direntry)=>{ direntry.path().to_owned() }
                            Err(err)=>{
                                match err.path() {
                                    Some(path)=>{ path.to_owned()  }
                                    None=>{ 立即报错退出(输出状态,format!("🍁🍁🍁移动文件错误, sub分流标记文件项 【目标文件不可读】"))?; std::path::Path::new("").to_owned()  }
                                }
                            }
                        };
                        
                        let 此路径Str = 此路径.display().to_string();
                        if let Some(文件夹路径) = 记录文件夹.keys().find(|路径|{    // 特殊情况同级文件  /home/12345   /home/123    
                            此路径.starts_with(*路径)}              //  区分 (/home/123)/ddd   /home/123     
                            && if let Some(上级路径) = 此路径.parent(){ 上级路径.starts_with(*路径) }else{ false }
                        ){
                            // 文件夹 下的路径
                            src根目录 = 文件夹路径.clone();                           //真实路径
                            计算目标文件项 = 记录文件夹.get(文件夹路径).unwrap().clone(); 
                        };
                        let mut src文件项 =  文件::new(此路径Str).通用文件项_同步();
                        src文件项.根目录   = src根目录.clone();
                        src文件项.相对路径 = src文件项.路径[src根目录.len() ..] .to_string();
                        src文件项.目标路径 = path_join(&计算目标文件项.路径, &src文件项.相对路径); 
                        输出状态.总文件数 += 1;
                        输出状态.总量字节 += src文件项.文件大小size;
                        if 输出状态.总文件数 % 10000 == 1 {  
                            输出状态.来源路径  = src文件项.路径.clone();
                            输出状态.目标路径 = format!("统计总数: {}",输出状态.总文件数);
                            控制线程启停_发送输出(输出状态)?;  
                        };
                        let 重名存在 = fs::symlink_metadata(&src文件项.目标路径).is_ok();     //🌷不检测源文件存在
                        

                        if 输出状态.操作=="剪切" && ( (src文件项.源类型 ==""  && 计算目标文件项.源路径.starts_with(&src文件项.路径) ) 
                                                 || (src文件项.源类型 !=""  && 计算目标文件项.源路径.starts_with(&src文件项.源路径) )  )
                        {
                            // 剪切   ( (源文件不存在  文件移动到子目录) || (源文件存在   源文件移动到子目录) )
                            立即报错退出(输出状态,format!("🍁🍁🍁父目录不能移到子目录"))?;  
                        }
                        else if (输出状态.覆盖标志=="不覆盖" && 重名存在 && (输出状态.操作=="剪切" || (输出状态.操作=="复制" && src文件项.源类型!="文件夹")))
                            ||  (输出状态.覆盖标志!="共存" && (src文件项.路径 == src文件项.目标路径 || src文件项.源路径 == src文件项.目标路径))
                        {
                            // (不覆盖  重名  剪切  ||  非文件复制 )  ||  (非共存)文件自己覆盖自己       --> 跳过   (剪切跳过整个文件夹    复制时只跳过文件)
                            src文件项.描述 = "跳过此文件".into();
                        }
                        else if 输出状态.操作=="剪切" && (!重名存在) && src文件项.dev == 计算目标文件项.源dev 
                            && (src文件项.源dev == 计算目标文件项.源dev || src文件项.源类型 =="") 
                        {
                            // 剪切  不存在重名  同磁盘文件   && (同磁盘源文件 或 源文件不存在 )            --> 同磁盘剪切文件
                            src文件项.描述 = "rename移动此文件".into();
                        }
                        else if src文件项.类型=="文件夹"    //     -->  文件夹  ||  复制链接文件夹
                            || (输出状态.操作=="复制" && src文件项.类型=="链接文件" && src文件项.源类型 == "文件夹" && 输出状态.跟随链接)
                        {
                            src文件项.描述 = "创建文件夹".into();
                        }
                        else if src文件项.类型=="链接文件" && ( 输出状态.操作=="剪切"         //剪切时  只移动链接  不移动内部文件
                                                            || src文件项.源类型==""        //无效链接文件保留链接文件
                                                            || !输出状态.跟随链接 )        //不跟随时   复制链接
                        {
                            src文件项.描述 = "link创建链接".into();  
                        }
                        else{ src文件项.描述 = "复制此文件".into(); }   //  --> 需复制文件
                        

                        // - 只有在 共存 时， 需要考虑已经存在的文件
                        if 输出状态.覆盖标志=="共存"{
                            //文件不能自己覆盖自己  自己删除自己   ->  标记此文件不能被删除
                            if src文件项.路径 == src文件项.目标路径 || src文件项.源路径 == src文件项.目标路径{  src文件项.源err = "保留".into();    }
                            if 计算目标文件项.源err == "保留" { src文件项.源err = "保留".into();  };  //继承上级
                            
                            let 新目标路径 = 文件::计算生成不存在路径_同步(&src文件项.目标路径,"副本");
                            if 新目标路径 != src文件项.目标路径{   src文件项.目标路径 = 新目标路径;   }  //文件是否存在
                            if src文件项.类型=="文件夹"{ // 真实路径   -   可能是副本目标项
                                let mut 新目标文件项 = 文件::new(&src文件项.目标路径).通用文件项_同步();
                                新目标文件项.源err = src文件项.源err.clone();                         //传递给下级
                                记录文件夹.insert(src文件项.路径.clone(), 新目标文件项);
                            }
                        }
                        所有src文件项.push(src文件项);  
                    
                
            }
        }

        Ok(所有src文件项)
    }

    
    fn 移动文件过程<F>(目标路径:String,src_array:Vec<String>,传入文件操作:String,线程控制:线程同步控制,mut 外部回调:F )-> anyhow_Result<()>
    where F : FnMut(文件复制过程状态) -> ()             // + std::marker::Send + 'static    //捕获外面的可变变量
    {
        
        let 跟随链接 = true;                         // 不跟随链接   链接文件夹则只算一个普通链接文件 
        let path_to = 文件::new(目标路径.clone());  path_to.创建文件夹_同步().ok();
        let 目标文件项 = path_to.通用文件项_同步();
        let (发送者, 接收者) =  async_unbounded::<文件复制过程状态>();   // let (发送者, 接收者) = std::sync::mpsc::channel();  //多发送者，单接收者
        
        let _ = std::thread::Builder::new().name("线程_移动文件过程".to_string()).spawn(move || ->anyhow_Result<()>{
            let mut 输出状态 = 文件复制过程状态::default();
            输出状态.uuid = 信令路由::fn_uuid随机().to_string();

            let mut 立即报错退出 = |输出状态:&mut 文件复制过程状态, 原因:String|->anyhow_Result<()>{
                    输出状态.err = 原因.clone();
                    发送者.force_send(输出状态.clone())?;
                    return Err(anyhow_err!("{原因}"))
            };
            let mut 控制线程启停_发送输出 = | 输出状态:&mut 文件复制过程状态| ->anyhow_Result<u128> {
                输出状态.工作状态 =线程控制.读取多线程共享变量();
                发送者.force_send(输出状态.clone())?;
                //4种状态  正常   暂停   正常结束   强制结束
                线程控制.阻塞线程_尝试("暂停");
                let 新工作状态 = 线程控制.读取多线程共享变量();
                if 输出状态.工作状态 != 新工作状态{
                    输出状态.工作状态 = 新工作状态;
                    发送者.force_send(输出状态.clone())?;
                };
                if 输出状态.工作状态 == "强制结束" {
                    输出状态.err = format!("🍁🍁🍁用户指令：【强制结束】 结束");
                    发送者.force_send(输出状态.clone())?;
                    return Err(anyhow_err!("{}",输出状态.err.clone()));
                }
                Ok(TIME时间::now_毫秒())   //返回时间戳
            };
            
            //第一个 剪切覆盖是对源文件    ,第二个覆盖共存是对目标文件
            let 文件操作 = match 字符串转_文件移动_操作指令(传入文件操作){
                Ok(操作) => { 操作 }
                Err(err)=>{ return 立即报错退出(&mut 输出状态,format!("🍁🍁🍁文件移动过程_新建, 无法识别 <文件操作> -->  {err}"));    }
            };
            let 共存 = match  文件操作{ 文件操作::剪切_共存=> true, 文件操作::复制_共存 => true,  _=> false };
            let 覆盖 = match  文件操作{ 文件操作::复制_覆盖=> true, 文件操作::剪切_覆盖 => true,  _=> false };
            let 复制 = match  文件操作{ 文件操作::复制_覆盖=> true, 文件操作::复制_不覆盖=> true, 文件操作::复制_共存=>true,  _=> false };  
            输出状态.操作 = (if 复制{"复制"}else{"剪切"}).to_string();
            输出状态.覆盖标志  = (if 共存{ "共存" }else if 覆盖 { "覆盖" }else{ "不覆盖" }).to_string();
            输出状态.跟随链接  = 跟随链接;
            输出状态.工作状态  = "正常".into();
            if 目标文件项.源类型 !="文件夹" {  立即报错退出(&mut 输出状态,format!("🍁🍁🍁目标文件项，不是文件夹"))?;  }
            
            let mut 所有src文件项 = 文件移动管理::sub分流标记文件项(&src_array, &目标文件项,&mut 输出状态, &mut  立即报错退出, &mut 控制线程启停_发送输出)?;
            
            for src文件项 in &mut 所有src文件项{ 
                输出状态.当前文件序号 +=1;
                输出状态.来源路径        = src文件项.路径.clone();
                输出状态.目标路径        = src文件项.目标路径.clone();
                let 记录总已复制字节 = 输出状态.总已复制字节;

                match src文件项.描述.as_ref(){
                    "跳过此文件"=>{
                        输出状态.总已复制字节 = 记录总已复制字节 + src文件项.文件大小size;         
                        输出状态.速度展示字符串 = "100%".into();
                    }
                    "创建文件夹"=>{
                        if let Err(err) = 文件::new(src文件项.目标路径.clone()).创建文件夹_同步(){
                            立即报错退出(&mut 输出状态,format!("🍁🍁🍁创建文件夹错误  {err}"))?;
                        }
                        输出状态.速度展示字符串 = "100%".into();
                        src文件项.描述 = if src文件项.源err == "保留" { "".into() }else{ "可删除".into()  };
                    }
                    "rename移动此文件"=>{
                        if let Err(err) = fs::rename(&src文件项.路径, &src文件项.目标路径){
                            立即报错退出(&mut 输出状态,format!("🍁🍁🍁rename移动文件错误  {err}"))?;
                        }
                        输出状态.总已复制字节 = 记录总已复制字节 + src文件项.文件大小size;  
                        输出状态.速度展示字符串 = "100%".into();
                    }
                    "link创建链接"=>{
                        let 文件项 = 文件::new(src文件项.目标路径.to_owned()).通用文件项_同步();
                        if 文件项.类型=="链接文件" { fs::remove_file(&src文件项.目标路径).ok();  }             //尝试删除 旧链接文件
                        if let Err(err) = std::os::unix::fs::symlink(&src文件项.源路径, &src文件项.目标路径){
                            立即报错退出(&mut 输出状态,format!("🍁🍁🍁link创建链接错误  {err}"))?;
                        }
                        输出状态.速度展示字符串 = "100%".into();
                        输出状态.总已复制字节 = 记录总已复制字节 + src文件项.文件大小size;  
                        
                        //文件不能自己覆盖自己   自己删除自己
                        src文件项.描述 = if src文件项.源err == "保留" { "".into() }else{ "可删除".into()  };
                    }
                    _=>{   // 复制此文件
                        let 临时路径 = if src文件项.文件大小size > 30_000_000 { format!("{}_复制中_{}", &src文件项.目标路径, 输出状态.当前文件序号) }else{ src文件项.目标路径.clone() } ;
                        let src有效路径 = if src文件项.类型=="链接文件" { src文件项.源路径.clone() } else { src文件项.路径.clone() };
    
                        let mut 记录上次时间ms = TIME时间::now_毫秒() -10;
                        let mut 记录上次复制 = 0_u64;
                        输出状态.存在复制流过程   = true;
                        let mut options = fs_extra::file::CopyOptions::new();
                        options.buffer_size = 30_000_000  ;  //30M 
                        options.overwrite = 覆盖;
                        options.skip_exist = !覆盖;
    
                        let 周期回调_复制过程 = | 输出:fs_extra::file::TransitProcess  |{
                            输出状态.单文件总量字节     = 输出.total_bytes;
                            输出状态.单文件已复制字节   = 输出.copied_bytes; 
                            输出状态.单文件已复制展示字符串   =  format!("{} / {}", 文件::字节转可读(输出.copied_bytes),文件::字节转可读(输出.total_bytes));
                            输出状态.总已复制字节      = 记录总已复制字节 + 输出.copied_bytes;
                            输出状态.总百分比          = (100f64 * 输出状态.总已复制字节 as f64 / 输出状态.总量字节 as f64).round() as u64;
                            
                            输出状态.速度              = ((输出.copied_bytes - 记录上次复制)/((TIME时间::now_毫秒() - 记录上次时间ms) as u64))*1000_u64;
                            输出状态.速度展示字符串     =  format!("{} / 秒", 文件::字节转可读(输出状态.速度));   //  3M / s
                            
                            if let Err(err) = 控制线程启停_发送输出(&mut 输出状态){
                                std::fs::remove_file(&临时路径).ok();  //为了退出，主动删除目标文件，引发 copy_with_progress 复制错误
                                std::panic::panic_any("fs_extra::file::copy_with_progress  >  退出");
                            };
                            记录上次时间ms            = TIME时间::now_毫秒() -10;
                            记录上次复制              = 输出.copied_bytes;
                        };
                        
                        if let Err(err) = fs_extra::file::copy_with_progress(&src有效路径, &临时路径, &options, 周期回调_复制过程){
                            立即报错退出(&mut 输出状态,format!("🍁🍁🍁移动文件错误, 异磁盘文件数组，fs_extra 无法复制单文件  {err}"))?;
                        };
                        if 临时路径 != src文件项.目标路径{    //不完整的文件可能被访问,导致复制失败, 所以用临时路径
                            if let Err(err) = fs::rename(&临时路径,&src文件项.目标路径){
                                    立即报错退出(&mut 输出状态,format!("🍁🍁🍁移动文件错误, 异磁盘文件数组，无法恢复文件名  {err}"))?;
                            }
                        }
    
                        //文件不能自己覆盖自己   自己删除自己
                        src文件项.描述 = if src文件项.源err == "保留" { "".into() }else{ "可删除".into()  };
                    }
                }
                
                输出状态.单文件已复制展示字符串 =  format!("{} / {}", src文件项.文件大小,src文件项.文件大小);  
                输出状态.总百分比 = (100f64 * 输出状态.总已复制字节 as f64 / 输出状态.总量字节 as f64).round() as u64;
                控制线程启停_发送输出(&mut 输出状态)?; 
            }

            
            //------------------------------ //运行到这里无错误  删除src文件
            输出状态.总百分比 = 100;    
            控制线程启停_发送输出(&mut 输出状态)?; 
            if 输出状态.操作=="剪切" {
                所有src文件项.retain(|文件|{ 文件.描述=="可删除" });
                let mut 记录文件夹:Vec<String>  = Vec::new();
                for src in &mut 所有src文件项{
                        if 记录文件夹.iter().any(|路径|{ src.路径.starts_with(路径) }){
                            //是子文件 
                            src.描述 = "父文件夹已经删除，此文件跳过".into(); 
                        }else if src.源类型=="文件夹"{ 
                            记录文件夹.push(src.路径.clone()); 
                            src.描述 = "删除".into(); 
                        }else{   src.描述 = "删除".into();   }

                        if src.描述 == "删除"{
                            if let Err(err) = 文件::new(src.路径.clone()).文件同步_删除(){
                                输出状态.来源路径 = src.路径.clone();
                                输出状态.目标路径 = format!("尝试删除文件: {}",src.文件名);
                                立即报错退出(&mut 输出状态,format!("🍁🍁🍁 文件移动完成，但无法删除源文件  {err}"))?;
                            }
                        }
                } 
            }
            Ok(())
        });
    
        {  
            let mut 记录 = 文件复制过程状态::default(); 记录.工作状态 = "正常".into(); 
            
            while let Ok(输出状态_out) = 接收者.recv_blocking() {            //阻塞进程等待消息 
                外部回调(输出状态_out.clone()) ; 记录 = 输出状态_out;
            };                                                   // 结算状态     4种状态  正常   暂停   正常结束   强制结束
            if 记录.工作状态 == "正常" && 记录.err == "" { 记录.工作状态 = "正常结束".into() } else { 记录.工作状态 = "强制结束".into() } 
            外部回调(记录);
        };
        Ok(())
    }

}









pub struct 文件{
    路径:String,
}

impl 文件 {
    pub fn new(路径:impl AsRef<str>)->Self{   Self{ 路径:路径.as_ref().to_string() }   } 
    pub fn path_join(&self, path2: impl AsRef<str>) -> String {   path_join(&self.路径, path2) }
    pub fn parent(&self)->Option<String>{  path::Path::new(&self.路径).parent().map(|路径|{ 路径.display().to_string() })   }
    fn 字节转可读(size: u64)->String{ 信令路由::fn_字节to可读(size)  }
    fn 计算文件时间_同步(文件属性:& fs::Metadata,类型:&str)->anyhow_Result<u128> {
        Ok(match 类型 {
            "创建时间"=>{ 文件属性.created()?.duration_since(std::time::UNIX_EPOCH).unwrap_or_default().as_millis() }
            "修改时间"=>{ 文件属性.modified()?.duration_since(std::time::UNIX_EPOCH).unwrap_or_default().as_millis() }
            _=>        { 0 }
        })
    }
    fn osStr转String(osstr:Option<&OsStr>)->String{  osstr.unwrap_or_default().to_string_lossy().to_string() }
    fn 计算文件类型(文件属性:& fs::Metadata)->String{    // async_fs::Metadata
        let 文件类型 = 文件属性.file_type();
        cfg_if!{
            if #[cfg(all(any(target_os = "macos", target_os = "linux")))]{
                if 文件类型.is_block_device()       {  return 文件类型::块设备文件.to_string();     } 
                else if 文件类型.is_char_device()   {  return 文件类型::字符设备文件.to_string();   }
                else if 文件类型.is_fifo()          {  return 文件类型::管道文件.to_string();       }
                else if 文件类型.is_socket()        {  return 文件类型::套接字文件.to_string();     }
            }
        }
        if 文件类型.is_dir()                {   return 文件类型::文件夹.to_string();        }
        else if 文件类型.is_symlink()       {   return 文件类型::链接文件.to_string();      } 
        else if 文件类型.is_file()          {   return 文件类型::文件.to_string();          } 
        else                               {   return "".to_string();                    }
    }
    fn 计算mime类型_应用分类(文件项:&mut 文件项){
        match mime_guess::from_ext(&文件项.扩展名).first() {
            Some(mime)=>{
                文件项.类型mime = mime.as_ref().to_string();
                match mime.type_().as_ref(){
                    "text"=>        文件项.应用分类 = 文件应用分类::文本.to_string(),
                    "image"=>       文件项.应用分类 = 文件应用分类::图像.to_string(),
                    "audio"=>       文件项.应用分类 = 文件应用分类::音频.to_string(),
                    "video"=>       文件项.应用分类 = 文件应用分类::视频.to_string(),
                    "application"=> 文件项.应用分类 = 文件应用分类::应用.to_string(),
                    "font"=>        文件项.应用分类 = 文件应用分类::字体.to_string(),
                    "multipart"=>   文件项.应用分类 = 文件应用分类::表单数据.to_string(),
                    _=>             文件项.应用分类 = if 文件项.源类型=="文件夹"{ "文件夹".to_string() }else{ 文件项.类型mime.clone() },
                }
            },
            None=>{
                文件项.类型mime = "".to_string();
                文件项.应用分类  = if 文件项.源类型=="文件夹"{ "文件夹".to_string() }else{ 文件项.类型mime.clone() };
            }
        }
    }
    
    pub fn 通用文件项_同步(&self) -> 文件项{
        let mut 文件项 = 文件项::default();
        文件项.路径 = self.路径.clone();
        
        let 路径对象 = path::Path::new(&self.路径);
        match 路径对象.symlink_metadata(){                    //不跟随链接 without following symlinks
            Err(e)=> 文件项.err = e.to_string(),
            Ok(文件属性)=>{
                文件项.文件名 = Self::osStr转String(路径对象.file_name());
                文件项.扩展名 = Self::osStr转String(路径对象.extension()).to_ascii_lowercase();
                文件项.创建时间ms = Self::计算文件时间_同步(&文件属性, "创建时间").unwrap_or(0);
                文件项.修改时间ms = Self::计算文件时间_同步(&文件属性, "修改时间").unwrap_or(0);
                文件项.创建时间 = TIME时间::time_毫秒转日期(文件项.创建时间ms).unwrap_or("".into());
                文件项.修改时间 = TIME时间::time_毫秒转日期(文件项.修改时间ms).unwrap_or("".into());
                文件项.id = format!("id_{}_{}",文件项.修改时间ms,&文件项.路径);
                cfg_if!{
                    if #[cfg(all(any(target_os = "macos", target_os = "linux")))]{
                        文件项.dev = 文件属性.dev(); 
                        文件项.权限mode = 文件属性.mode(); 
                        文件项.用户uid = 文件属性.uid();
                    }else if #[cfg(all(any(target_os = "windows")))]{
                        文件项.dev = 文件属性.volume_serial_number().unwrap_or(0) as u64;
                    }
                }
                文件项.文件大小size = 文件属性.len() as u64;
                文件项.文件大小 = Self::字节转可读(文件项.文件大小size);
                文件项.隐藏文件 = 文件项.文件名.starts_with(".");
                文件项.类型 = Self::计算文件类型(&文件属性);
                
                if 文件项.类型 == "链接文件"{
                        let 源路径path_可用 = match fs::canonicalize(&文件项.路径){  // 会递归解析找到真实路径, 源文件被删除,报错
                            Ok(path)=>{ Some(path) }
                            Err(_)=>{
                                match fs::read_link(&文件项.路径){                       //这个是链接路径, 不会报错
                                    Err(err)=>{    文件项.源err = err.to_string(); None } 
                                    Ok(path)=>{  Some(path)    }
                               }
                            }
                        };
                        if let Some(源路径path) =  源路径path_可用{ 
                            let 源路径对象 = 源路径path.as_path();
                            文件项.源路径 = 源路径对象.display().to_string();
                            文件项.源文件名 = Self::osStr转String(源路径对象.file_name());
                            文件项.扩展名 = Self::osStr转String(源路径对象.extension()).to_ascii_lowercase();
                            match 源路径对象.symlink_metadata() {
                                Err(e)=> 文件项.源err = e.to_string(),
                                Ok(源文件属性)=>{
                                    文件项.源文件大小size = 源文件属性.len() as u64;
                                    文件项.源文件大小 = Self::字节转可读(文件项.源文件大小size);
                                    文件项.源类型 = Self::计算文件类型(&源文件属性);
                                    cfg_if!{
                                        if #[cfg(all(any(target_os = "macos", target_os = "linux")))]{
                                            文件项.源dev = 源文件属性.dev();
                                        }else if #[cfg(all(any(target_os = "windows")))]{
                                            文件项.源dev = 源文件属性.volume_serial_number().unwrap_or(0) as u64;
                                        }
                                    }
                                   
                                }
                            }
                        }
                }else{
                    文件项.源路径 = 文件项.路径.clone();
                    文件项.源类型 = 文件项.类型.clone();
                    文件项.源文件名 = 文件项.文件名.clone();
                    文件项.源文件大小 = 文件项.文件大小.clone();
                    文件项.源文件大小size = 文件项.文件大小size;
                    文件项.源dev = 文件项.dev;
                };
            }
        }
        Self::计算mime类型_应用分类(&mut 文件项);
        
        文件项
    } 
    /// - 唯一序号标记操作id  , 当标记改变时,立即退出
    pub fn 通用文件项数组_同步(&self)-> anyhow_Result<Vec<文件项>>{
        let mut 数组: Vec<文件项> = Vec::new();
        if self.文件_是目录_同步(){
            let 目录 = fs::read_dir(&self.路径)?;
            for 路径对象 in 目录 {
                match 路径对象{
                    Ok(入口) =>{
                        数组.push(文件::new(入口.path().display().to_string()).通用文件项_同步());
                    }
                    Err(err)=>{ return Err(anyhow_err!("通用文件项数组错误：{err}")); }
                }
            }
        }
        else{   return Err(anyhow_err!("通用文件项数组错误：传入的不是文件夹 {}",self.路径));   }
        Ok(数组)
    }
    
    // pub fn 文件删除到回收站_同步(&self)-> anyhow_Result<()>{
    //     Err(anyhow_err!("文件删除到回收站  未实现"))
    // }
    // pub fn 文件监控<F>(监控回调:impl notify::EventHandler + 'static )->anyhow_Result<notify::INotifyWatcher> {
    //     let 监控: notify::INotifyWatcher = notify::recommended_watcher(监控回调)?;
    //     Ok(监控)
    // }
    pub fn 创建文件_同步(&self)->anyhow_Result<()>{     fs::File::create_new(&self.路径)?; Ok(())  }   //父文件夹必须存在
    pub fn 创建文件夹_同步(&self)->anyhow_Result<()>{   fs::create_dir_all(&self.路径)?;  Ok(())   }
    
    
    

    pub fn 文件同步_存在(&self)-> bool{  fs::metadata(&self.路径).is_ok()  }              //🌷源文件存在
    pub fn 文件同步_重命名(&self, name:&str)->anyhow_Result<()>{                               //🌷不检测源文件存在
        let 文件项 = self.通用文件项_同步();
        let 路径长 = 文件项.路径.len() - 文件项.文件名.len();
        let 目录 = &文件项.路径[0..路径长];             //切片
        // eprintln!("重命名: {目录}");
        let 新路径 = path_join(目录, name);

        if fs::symlink_metadata(&self.路径).is_ok() {  return Err(anyhow_err!("重命名错误:: 文件已经存在"));  }
        fs::rename(文件项.路径, 新路径)?;
        Ok(())
    }
    pub fn 文件同步_是目录(&self)->bool{  if let Ok(meta) = fs::metadata(&self.路径){ meta.is_dir()  }else{ false }  }
    pub fn 文件同步_读文本(&self)-> anyhow_Result<String>{ Ok(fs::read_to_string(&self.路径)?) }
    pub fn 文件同步_读(&self)-> anyhow_Result<Vec<u8>>{    Ok(fs::read(&self.路径)?)  }
    pub fn 文件同步_写(&self, 内容:String)-> anyhow_Result<()>{ fs::write(&self.路径, 内容)?;  Ok(())   }
    pub fn 文件同步_删除(&self)-> anyhow_Result<()>{
        if Path::new(&self.路径).is_dir(){  fs::remove_dir_all(&self.路径)?;  }
        else{  fs::remove_file(&self.路径)?;  }
        Ok(())
    }

    //============= 异步 ==================
    fn 计算文件时间(文件属性:& async_fs::Metadata,类型:&str)->anyhow_Result<u128> {
        Ok(match 类型 {
            "创建时间"=>{ 文件属性.created()?.duration_since(std::time::UNIX_EPOCH).unwrap_or_default().as_millis() }
            "修改时间"=>{ 文件属性.modified()?.duration_since(std::time::UNIX_EPOCH).unwrap_or_default().as_millis() }
            _=>        { 0 }
        })
    }
    
    pub async fn rename(&self,目标路径:impl AsRef<str> )->anyhow_Result<()>{  async_fs::rename(&self.路径, 目标路径.as_ref()).await.map_err(|err|{ anyhow_err!("文件 rename失败: {err}")  })   }   
    pub async fn 创建文件(&self)->anyhow_Result<()>{     async_fs::File::create(&self.路径).await?; Ok(())     }   //父文件夹必须存在
    pub async fn 创建文件夹(&self)->anyhow_Result<()>{   async_fs::create_dir_all(&self.路径).await?;  Ok(())   }
    

    pub fn 计算生成不存在路径_同步(路径:impl AsRef<str>, 文件名后缀:impl AsRef<str>)->String{    //🌷不检测源文件存在
        let 路径 = 路径.as_ref();
        let 原始文件 = std::path::Path::new(路径);
        
        let 文件名 = 原始文件.file_name().unwrap_or_default().to_str().unwrap_or_default();
        let 无文件名路径 = &路径[..(路径.len()-文件名.len())];
        let 纯文件名 =  原始文件.file_stem().unwrap_or_default().to_str().unwrap_or_default();
        let 带点扩展名 =  &文件名[(纯文件名.len())..];
        let mut num=1;
        let 后缀 = if "" == 文件名后缀.as_ref(){ "".to_string() }else{  format!("_{}",文件名后缀.as_ref())  };
        let mut 计算路径 = format!("{无文件名路径}{纯文件名}{带点扩展名}");     //最开始等于 传入的路径
        loop{
            if fs::symlink_metadata(&计算路径).is_ok(){                  
                计算路径 = format!("{无文件名路径}{纯文件名}{后缀}{num}{带点扩展名}");   num +=1;  
            }else{  break;  }
        }
        计算路径
    }
    pub async fn 计算生成不存在路径(路径:impl AsRef<str>,文件名后缀:impl AsRef<str>)->String{   //🌷不检测源文件存在
        let 路径 = 路径.as_ref();
        let 原始文件 = std::path::Path::new(路径);

        let 文件名 = 原始文件.file_name().unwrap_or_default().to_str().unwrap_or_default();
        let 无文件名路径 = &路径[..(路径.len()-文件名.len())];
        let 纯文件名 =  原始文件.file_stem().unwrap_or_default().to_str().unwrap_or_default();
        let 带点扩展名 =  &文件名[(纯文件名.len())..];
        let mut num=1;
        let 后缀 = if "" == 文件名后缀.as_ref(){ "".to_string() }else{  format!("_{}",文件名后缀.as_ref())  };
        let mut 计算路径 = format!("{无文件名路径}{纯文件名}{带点扩展名}");     //最开始等于 传入的路径
        loop{
            if async_fs::symlink_metadata(&计算路径).await.is_ok(){  
                计算路径 = format!("{无文件名路径}{纯文件名}{后缀}{num}{带点扩展名}");   num +=1; 
            }else{  break;  }
        }
        计算路径
    }
    
    pub async fn 计算生成不存在路径_数字递增(路径:impl AsRef<str>,文件名后缀:impl AsRef<str>)->String{
        let 路径 = 路径.as_ref();
        let 原始文件 = std::path::Path::new(路径);
        // 新文件.txt     纯文件名: 新文件     带点扩展名:  .txt

        let 文件名 = 原始文件.file_name().unwrap_or_default().to_str().unwrap_or_default();
        let 无文件名路径 = &路径[..(路径.len()-文件名.len())];
        let 纯文件名 =  原始文件.file_stem().unwrap_or_default().to_str().unwrap_or_default();
        let 带点扩展名 =  &文件名[(纯文件名.len())..];
        // 读取文件名 最后的数字
        let (数字字符,mut num)= if 纯文件名.len() > 0{
            let (mut temp,mut 数字字符,mut 存在数字,mut 标记找到 ) = (1,"",0,false);
            loop{
                if temp <= 纯文件名.len() && 纯文件名.is_char_boundary(纯文件名.len()-temp){
                    let 最后字符 = &纯文件名[(纯文件名.len()-temp)..];             //最后一位
                    if let Ok(数字) = 最后字符.parse::<u64>(){ 标记找到 = true;数字字符 = 最后字符;存在数字 = 数字;temp +=1; }
                    else{ break; }   //已经找到后面已经不是数字
                }else{ break; }
            }
            if 标记找到 { (数字字符, 存在数字) }else{  ("", 1)   }  // 有数字按数字递增,   无时从1递增
        }else{ ("", 1) };
        
        let 纯文件名无数字 = &纯文件名[..(纯文件名.len() - 数字字符.len())];
        let 后缀 = if "" == 文件名后缀.as_ref(){ "" }else{  &format!("_{}",文件名后缀.as_ref())  };
        let mut 计算路径 = format!("{无文件名路径}{纯文件名无数字}{num}{后缀}{带点扩展名}");     //最开始等于 传入的路径
        loop{
            if async_fs::symlink_metadata(&计算路径).await.is_ok(){  
                计算路径 = format!("{无文件名路径}{纯文件名无数字}{num}{后缀}{带点扩展名}");   num +=1; 
            }else{  break;  }
        }
        计算路径
    }


    pub async fn 通用文件项(&self) -> 文件项{
         let mut 文件项 = 文件项::default();
        文件项.路径 = self.路径.clone();
        
        match async_fs::symlink_metadata(&self.路径).await{                  //不跟随链接 without following symlinks
            Err(e)=> 文件项.err = e.to_string(),
            Ok(文件属性)=>{
                let 路径对象 = std::path::Path::new(&self.路径);
                文件项.文件名 = Self::osStr转String(路径对象.file_name());
                文件项.扩展名 = Self::osStr转String(路径对象.extension()).to_ascii_lowercase();
                文件项.创建时间ms = Self::计算文件时间(&文件属性, "创建时间").unwrap_or(0);
                文件项.修改时间ms = Self::计算文件时间(&文件属性, "修改时间").unwrap_or(0);
                文件项.创建时间 = TIME时间::time_毫秒转日期(文件项.创建时间ms).unwrap_or("".into());
                文件项.修改时间 = TIME时间::time_毫秒转日期(文件项.修改时间ms).unwrap_or("".into());
                文件项.id = format!("id_{}_{}",文件项.修改时间ms,&文件项.路径);
                cfg_if!{
                    if #[cfg(all(any(target_os = "macos", target_os = "linux")))]{
                        文件项.dev = 文件属性.dev();
                        文件项.权限mode = 文件属性.mode();
                        文件项.用户uid = 文件属性.uid();
                    }else if #[cfg(all(any(target_os = "windows")))]{
                        文件项.dev = 文件属性.volume_serial_number().unwrap_or(0) as u64;
                    }
                }
                文件项.文件大小size = 文件属性.len() as u64;
                文件项.文件大小 = Self::字节转可读(文件项.文件大小size);
                文件项.隐藏文件 = 文件项.文件名.starts_with(".");
                文件项.类型 = Self::计算文件类型(&文件属性);


                if 文件项.类型 == "链接文件"{
                        let 源路径path_可用 = match async_fs::canonicalize(&文件项.路径).await{  // 会递归解析找到真实路径, 源文件被删除,报错
                            Ok(path)=>{ Some(path) }
                            Err(_)=>{
                                match async_fs::read_link(&文件项.路径).await{                       //这个是链接路径, 不会报错
                                    Err(err)=>{    文件项.源err = err.to_string(); None } 
                                    Ok(path)=>{  Some(path)    }
                                }
                            }
                        };
                        if let Some(源路径path) =  源路径path_可用{ 
                            let 源路径对象 = 源路径path.as_path();
                            文件项.源路径 = 源路径对象.display().to_string();
                            文件项.源文件名 = Self::osStr转String(源路径对象.file_name()); 
                            文件项.扩展名 = Self::osStr转String(源路径对象.extension()).to_ascii_lowercase();
                            match async_fs::symlink_metadata(&文件项.源路径).await{
                                Err(e)=> 文件项.源err = e.to_string(),
                                Ok(源文件属性)=>{
                                    文件项.源文件大小size = 源文件属性.len() as u64;
                                    文件项.源文件大小 = Self::字节转可读(文件项.源文件大小size);
                                    文件项.源类型 = Self::计算文件类型(&源文件属性);
                                    cfg_if!{
                                        if #[cfg(all(any(target_os = "macos", target_os = "linux")))]{
                                            文件项.源dev = 源文件属性.dev();
                                        }else if #[cfg(all(any(target_os = "windows")))]{
                                            文件项.源dev = 源文件属性.volume_serial_number().unwrap_or(0) as u64;
                                        }
                                    }
                                }
                            }
                        }
                }else{
                    文件项.源路径 = 文件项.路径.clone();
                    文件项.源类型 = 文件项.类型.clone();
                    文件项.源文件名 = 文件项.文件名.clone();
                    文件项.源文件大小 = 文件项.文件大小.clone();
                    文件项.源文件大小size = 文件项.文件大小size;
                    文件项.源dev = 文件项.dev;
                };
            }
        }
        Self::计算mime类型_应用分类(&mut 文件项);
        
        文件项
    }
    /// - 唯一序号标记操作id  , 当标记改变时,立即退出
    pub async fn 通用文件项数组(&self)->anyhow_Result<Vec<文件项>>{
        let mut 数组: Vec<文件项> = Vec::new();
        if self.文件_是目录().await{
            let mut 目录 = async_fs::read_dir(&self.路径).await.map_err(|err|{ anyhow_err!("通用文件项数组_错误：无法读取此目录 {err}") })?;

            while let Some(路径对象) = 目录.next().await{
                match 路径对象{
                    Ok(入口) =>{
                        数组.push(文件::new(入口.path().display().to_string()).通用文件项().await);
                    }
                    Err(err)=>{ return Err(anyhow_err!("通用文件项数组_错误：{err}")); }
                }
            }
        }
        else{   return Err(anyhow_err!("通用文件项数组错误：传入的不是文件夹 {}",self.路径));   }
        Ok(数组)
    }

    
    /// - metadata 跟随链接，检测 源文件 是否存在
    /// - symlink_metadata 不跟随链接
    pub async fn 文件_存在(&self)->bool{  async_fs::metadata(&self.路径).await.is_ok()  }    //🌷源文件存在
    pub async fn 文件_重命名(&self,新文件名:impl AsRef<str>)->anyhow_Result<String>{  //🌷不检测源文件存在
        let 路径对象 = std::path::Path::new(&self.路径);
        let 新路径对象 = 路径对象.with_file_name(新文件名.as_ref());
        let 新路径 = 新路径对象.display().to_string();
        if async_fs::symlink_metadata(&新路径).await.is_ok(){ return Err(anyhow_err!("文件_重命名失败, 路径已经存在:  {新路径}")); }
        
        async_fs::rename(&self.路径, &新路径).await.map_err(|err|{ anyhow_err!("文件_重命名 失败: {err}")  })?;
        Ok(新路径)
    }
    pub async fn 文件_是目录(&self)->bool{  if let Ok(meta) = async_fs::metadata(&self.路径).await{ meta.is_dir()  }else{ false }  }
    pub fn 文件_是目录_同步(&self)->bool{  if let Ok(meta) = fs::metadata(&self.路径){ meta.is_dir()  }else{ false }   }
    /// - 默认 512 字节
    /// - go 文件_读文件头   /home/lwl/Desktop/🌷pic
    pub async fn 文件_读文件头(&self,字节数:Option<usize>)->anyhow_Result<Vec<u8>>{
        let mut file = async_fs::File::open(&self.路径).await?;
        let mut buffer = vec![0; 字节数.unwrap_or(512)];               // 预分配   字节的缓冲区
        match file.read_exact(&mut buffer).await{
            Ok(_)=>{ Ok(buffer)  }
            Err(err)=>{              
                match err.kind() {
                    std::io::ErrorKind::UnexpectedEof =>{
                        // let mut buffer = vec![0; 字节数.unwrap_or(512)]; 
                        // let bytes = file.read(&mut buffer).await?;
                        // buffer.truncate(bytes);
                        Ok(buffer)
                    }
                    _=>{ Err(anyhow_err!("{err}"))  }
                }
            }
        }  
    }
    /// - 根据文件头 读取文件 mime类型
    pub async fn 文件_读文件mime(&self)->anyhow_Result<String>{
        use mime_sniffer::MimeTypeSniffer;
        let 文件头 = self.文件_读文件头(None).await?;
        let mime = 文件头.sniff_mime_type().ok_or(anyhow_err!("无法解析 mime {}",self.路径))?;
        Ok(mime.to_string())
    }
    pub async fn 文件_读文本(&self)->anyhow_Result<String>{ async_fs::read_to_string(&self.路径).await.map_err(|err|anyhow_err!("文件_读文本 错误: {err}"))  } 
    pub async fn 文件_读(&self)->anyhow_Result<Vec<u8>>{ async_fs::read(&self.路径).await.map_err(|err|anyhow_err!("文件_读 错误: {err}"))  }
    pub async fn 文件_写文本(&self, 文本: impl AsRef<str>)->anyhow_Result<()>{  async_fs::write(&self.路径, 文本.as_ref()).await.map_err(|err|anyhow_err!("文件_写文本 错误: {err}"))  }   
    pub async fn 文件_写(&self, 原始数据: impl AsRef<[u8]>)->anyhow_Result<()>{  async_fs::write(&self.路径, 原始数据).await.map_err(|err|anyhow_err!("文件_写 错误: {err}"))  }   
    pub async fn 文件_删除(&self)->anyhow_Result<()>{
        let 文件项 = self.通用文件项().await;
        if 文件项.类型 =="文件夹"{ async_fs::remove_dir_all(&文件项.路径).await?;  }else{ async_fs::remove_file(&文件项.路径).await?;   };
        Ok(())
    }
    pub async fn 文件_新建空文件(&self, 类型or文件扩展名:impl AsRef<str>)->anyhow_Result<String>{
        let 后缀 = 类型or文件扩展名.as_ref();
        let 文件项 = self.通用文件项().await;
        let 上级路径 = if 文件项.类型=="文件夹"{ self.路径.clone() }else{  self.parent().unwrap_or(self.路径.clone())  };
        let 最终路径:String;
        if 后缀 == "文件夹"{
            let 新路径 =  path_join(上级路径,"新文件夹");
            最终路径 = Self::计算生成不存在路径(新路径,"").await;
            async_fs::create_dir_all(&最终路径).await?
        }
        else{
            let 文件名 = if 后缀 == "" { "新文件"  }else{  &format!("新文件.{后缀}") };
            let 新路径 =  path_join(上级路径,文件名);
            最终路径 = Self::计算生成不存在路径(新路径,"").await;
            async_fs::File::create(&最终路径).await?;
        }
        Ok(最终路径)
    }
    pub async fn 文件_新建软连接(&self, 目的目录:impl AsRef<str>) -> anyhow_Result<String>{
        let 源文件项 = self.通用文件项().await;
        let 目标文件项 = Self::new(&目的目录).通用文件项().await;
        let 可用目标路径 = if 目标文件项.类型=="文件夹"{ 目标文件项.路径.clone() }else{ Self::new(&目的目录).parent().ok_or(anyhow_err!("文件新建软连接 上级目录"))? };
        let mut 新路径 =  Self::new(可用目标路径).path_join(&源文件项.文件名);   
        新路径 = Self::计算生成不存在路径(&新路径,"快捷方式").await;

        #[cfg(any(target_os = "windows"))]
        std::os::windows::fs::symlink_file(&源文件项.源路径,  &新路径)?;
        #[cfg(not(any(target_os = "windows")))]
        std::os::unix::fs::symlink(&源文件项.源路径, &新路径)?;
        Ok(新路径)
    }


    /// 异步递归 需要加 Box::pin
    /// - 移动 符合的文件到 存放目录
    pub async fn 文件_提取下层文件(路径数组:Vec<String>, 应用分类:String, 深度:Option<u64>, 存放目录:Option<String>)->anyhow_Result<()>{
        let 深度 = if let Some(deep) = 深度{ deep }else{ 1 };
        if 深度 <=0 { return Err(anyhow_err!("文件_提取下层文件 参数 深度不能为0")); };
        let 存放目录 = if let Some(目录) = 存放目录{  目录  }else{ Self::new(&路径数组[0]).parent().ok_or(anyhow_err!("文件_提取下层文件 无法找到上层目录"))?  };
        let mut 存放目录文件 = Self::new(存放目录);
        if 存放目录文件.通用文件项().await.类型 != "文件夹"{
            存放目录文件 = Self::new(存放目录文件.parent().ok_or(anyhow_err!("文件_提取下层文件 无法找到上层目录"))? );
        }
        
        for 路径 in 路径数组{
            let 文件 = Self::new(路径);
            if 文件.通用文件项().await.类型 == "文件夹"{
                if let Ok(文件项数组) = 文件.通用文件项数组().await{
                    for 子文件项 in 文件项数组{
                        if 子文件项.类型=="文件夹"{
                            Box::pin(Self::文件_提取下层文件(vec![子文件项.路径.clone()],应用分类.clone(),Some(深度-1), Some(存放目录文件.路径.clone()))).await.ok();
                        }
                        else if 子文件项.应用分类 == 应用分类{
                            let mut 新路径 = 存放目录文件.path_join(子文件项.文件名);
                            新路径 = Self::计算生成不存在路径(新路径,"重名").await;
                            Self::new(子文件项.路径).rename(新路径).await.ok();    //移动文件
                        }
                    }
                }
            }
        }
        Ok(())
    }
    
    pub fn 读取目录下路径数组_同步(&self)->anyhow_Result<Vec<String>>{
        let mut 结果数组:Vec<String> = Vec::new();
        let 路径对象 = path::Path::new(&self.路径);
        if 路径对象.is_dir(){
            let 目录 = 路径对象.read_dir()?;
            for 路径 in 目录{
                let path = 路径?.path().display().to_string();
                结果数组.push(path);
            }
        } 
        Ok(结果数组)
    }
    pub fn 读取目录下路径_同步(&self)-> anyhow_Result<Vec<String>>{
        let mut 结果数组:Vec<String> = Vec::new();
        let 路径对象 = path::Path::new(&self.路径);
        if 路径对象.is_dir(){
            let 目录 = 路径对象.read_dir()?;
            for 路径 in 目录{
                let path = 路径.unwrap().path().to_str().unwrap().to_string();
                结果数组.push(path);
            }
        }
        Ok(结果数组)
    }
    fn sub遍历生成文件项_同步(结果数组:&mut Vec<文件项>, 文件项:文件项, 跟随链接:bool)->anyhow_Result<()>{
        结果数组.push(文件项.clone());
        if 文件项.类型=="文件夹" || (文件项.类型=="链接文件" && 文件项.源类型 == "文件夹" && 跟随链接){ 
            let 数组 = Self::new(文件项.路径.clone()).通用文件项数组_同步()?;
            for 下级文件项 in 数组{
                Self::sub遍历生成文件项_同步(结果数组,下级文件项, 跟随链接)?;
            }
        }
        Ok(())
    }
    pub fn 读取目录下所有文件项_同步(&self, 跟随链接:bool) -> anyhow_Result<Vec<文件项>>{
        let mut 结果数组:Vec<文件项> = Vec::new();
        
        Self::sub遍历生成文件项_同步(&mut 结果数组, self.通用文件项_同步(), 跟随链接)?;   

        let 根目录 = std::path::Path::new(&self.路径).parent().ok_or(anyhow_err!("此目录没有父路径"))?.display().to_string();
        for 文件项 in &mut 结果数组{
            文件项.根目录 = 根目录.clone();
            文件项.相对路径 = 文件项.路径[根目录.len() ..] .to_string();
        }

        Ok(结果数组)
    }

    
    pub fn 统计目录下所有文件_同步(&self) -> anyhow_Result<所有文件>{
        let mut 结果对象 = 所有文件::default();
        let 路径对象 = path::Path::new(&self.路径);
        if 路径对象.is_dir(){
            let 深度配置 = fs_extra::dir::DirOptions::default();
            let 目录对象 = fs_extra::dir::get_dir_content2(路径对象, &深度配置)?;
            结果对象.总量字节 = 目录对象.dir_size;
            结果对象.文件数组 = 目录对象.files;
            结果对象.目录数组 = 目录对象.directories;
        }else{
            结果对象.总量字节 = if let Ok(路径meta) = 路径对象.metadata(){ 路径meta.len() }else{ 0 };
            结果对象.文件数组.push(路径对象.to_str().unwrap().to_string());
        }
        结果对象.总量 = Self::字节转可读(结果对象.总量字节);
        Ok(结果对象)
    }
    pub fn 统计_路径大小_同步(路径数组:Vec<String>, 限时ms:Option<u128>)-> anyhow_Result<文件统计信息> {   //🌷不检测源文件存在
        // let 路径数组 : Vec<std::path::PathBuf> = 路径数组.iter().map(|路径|{ std::path::Path::new(路径).to_path_buf() }).collect();
        // let walk = diskus::Walk::new(&路径数组,1, diskus::FilesizeType::DiskUsage);
        // let (size_in_bytes, errors) = walk.run();
        // size_in_bytes
        let 记录时间 = Instant::now();
        use std::os::unix::fs::MetadataExt;  //引入特征
        let mut 统计文件 = 文件统计信息::default();
        'top: for 路径 in 路径数组{
            for entry in walkdir::WalkDir::new(路径).follow_links(false).into_iter(){
                match entry{
                    Ok(direntry)=>{ 
                        if let Ok(metadata) = direntry.path().symlink_metadata(){
                            统计文件.个数 +=1;
                            统计文件.总量size += metadata.len();  // metadata.len()  -> 这个是实际大小     metadata.blocks() * 512 ->占用磁盘
                        }
                        else{  统计文件.err = format!("无法获取文件 metadata 信息 {:?}",direntry.path().display() );  break 'top;  } 
                    }
                    Err(err)=>{ 统计文件.err = format!("遍历文件错误 {:?}", err); break 'top;  }
                }

                if let Some(限时) = 限时ms && 统计文件.个数 % 10000 ==1{
                    let 耗时ms = 记录时间.elapsed().as_millis();  
                    if 耗时ms > 限时{ 统计文件.err = format!("统计文件限定 {限时} ms，已经超时"); break 'top; }
                }
            }
        }
        if 统计文件.err != ""{  Err(anyhow_err!("统计_路径数组错误: {}",统计文件.err))  }  //出错
        else { 统计文件.总量 = Self::字节转可读(统计文件.总量size);   Ok(统计文件)      }  
        
    }

}




/// - 唯一序号标记操作id  , 当标记改变时,立即退出
pub async fn 通用文件项_路径数组<T:AsRef<str>>(路径数组:Vec<T>)->anyhow_Result<Vec<文件项>>{
    let mut 数组: Vec<文件项> = Vec::new();
    for path in 路径数组{  
        数组.push(文件::new(path).通用文件项().await);   
    }
    Ok(数组)
}



/// - 唯一序号标记操作id  , 当标记改变时,立即退出
pub fn 通用文件项_路径数组_同步<T:AsRef<str>>(路径数组:Vec<T>)->anyhow_Result<Vec<文件项>>{
    let mut 数组: Vec<文件项> = Vec::new();
    for path in 路径数组{ 
        数组.push(文件::new(path).通用文件项_同步());    
    }
    Ok(数组)
}


pub async fn 文件_重命名_多文件<T:AsRef<str>>(路径数组:Vec<T>, 新文件名:T)->anyhow_Result<()> {  
    let 新文件名 = 新文件名.as_ref();
    for 路径 in 路径数组{
        let 原路径 = std::path::PathBuf::from(路径.as_ref());
        let 原扩展名 = 原路径.extension().unwrap_or_default().display().to_string();
        let 新路径 = 原路径.with_file_name(新文件名).with_extension(原扩展名);   //扩展名保持不变
        let 不重名路径 = 文件::计算生成不存在路径_数字递增(新路径.display().to_string(),"").await;
        文件::new(路径.as_ref()).文件_重命名(不重名路径).await?;
    }
    Ok(())
}



pub fn path_join(path1: impl AsRef<str>, path2: impl AsRef<str>) -> String {
    let 可用path1 = path::Path::new(path1.as_ref());
    let mut 可用paht2 = path::Path::new(path2.as_ref());
    if let Ok(path2) = 可用paht2.strip_prefix("/"){  可用paht2 = path2; }  //去除开头的字符    或者 可用paht2.split_off(1)
    可用path1.join( 可用paht2) .display().to_string()
    // path::Path::new(path1.as_ref()).join(path::Path::new(path2.as_ref())).display().to_string()
}




#[cfg(all(test,feature = "mod_file"))]
#[cfg(test)]
mod 测试生成_ffmpeg_mod {  
    use super::*;
    
    #[cfg(all(test,feature = "mod_file"))]
    #[test]
    fn 测试_() { 
        let a = path_join("/dddd/", "//342");
        assert_eq!(a,"/dddd/342");
    }
}



/*
                    let 变动类型 = match event.kind{
                        notify::event::EventKind::Access(类型)=>"访问文件",
                        notify::event::EventKind::Create(类型)=>"创建文件" ,
                        notify::event::EventKind::Modify(类型)=>"修改文件" ,
                        notify::event::EventKind::Remove(类型)=>"移除文件" ,
                        notify::event::EventKind::Other =>"Other",
                        notify::event::EventKind::Any =>"Any",
                    };
*/