use crate::{parse::AutoPath, res, Error, Result};
use std::{
borrow::Cow,
fs,
io::{self, Write as _},
path::{Path, PathBuf},
};
#[cfg(feature = "encode")]
use crate::system::encode::auto_decode;
#[cfg(feature = "encode")]
pub fn auto_read<'a, P>(path: P) -> Result<String>
where
P: AsRef<Path>,
{
let data = fs::read(&path)?;
Ok(auto_decode(&data).unwrap_or(String::from_utf8_lossy(&data).to_string()))
}
#[cfg(feature = "encode")]
pub fn auto_read_gpk<P>(path: P) -> Result<String>
where
P: AsRef<Path>,
{
auto_read(path)
}
pub fn tree_folder2<P>(dir_path: P) -> Result<Vec<PathBuf>>
where
P: AsRef<Path>,
{
let mut result = Vec::new();
if dir_path.as_ref().is_dir() {
let entries = fs::read_dir(dir_path)?;
for entry in entries {
if let Ok(entry) = entry {
let file_path = entry.path();
if file_path.is_dir() {
let sub_directory_files = tree_folder2(&file_path)?;
result.extend(sub_directory_files);
} else {
result.push(file_path);
}
}
}
} else {
result.push(dir_path.as_ref().to_path_buf())
}
Ok(result)
}
pub fn tree_folder<P>(dir_path: P) -> Result<Vec<String>>
where
P: AsRef<Path>,
{
let mut result = Vec::new();
if dir_path.as_ref().is_dir() {
let entries = fs::read_dir(dir_path)?;
for entry in entries {
if let Ok(entry) = entry {
let file_path = entry.path();
if file_path.is_dir() {
let sub_directory_files = tree_folder(&file_path)?;
result.extend(sub_directory_files);
} else {
if let Some(file_name) = file_path.to_str() {
result.push(file_name.to_string());
}
}
}
}
} else {
result.push(dir_path.as_ref().display().to_string())
}
Ok(result)
}
pub fn rename_file<P, P2>(src: P, dst: P2) -> Result<()>
where
P: AsRef<Path>,
P2: AsRef<Path>,
{
let src = src.as_ref();
let dst = dst.as_ref();
if src.exists() && src.is_file() {
if dst.exists() && !dst.is_file() {
Err(format!("目标已存在,并非文件格式 {}", dst.display()).into())
} else {
fs::rename(src, dst)?;
if dst.exists() && dst.is_file() {
Ok(())
} else {
Err(format!("源{} 目标移动失败 {}", src.display(), dst.display()).into())
}
}
} else {
Err(format!("原始缓存文件不存在 {}", src.display()).into())
}
}
pub fn convert_path(path_str: &str) -> String {
if cfg!(target_os = "windows") {
path_str.replace('/', "\\")
} else {
String::from(path_str)
}
}
pub fn auto_copy<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> Result<()> {
let from_path = from.as_ref();
let to_path = to.as_ref();
if from_path.is_file() {
fs::copy(from_path, to_path)?;
} else if from_path.is_dir() {
to_path.auto_create_dir()?;
for entry in fs::read_dir(from_path)? {
let entry = entry?;
let from_entry_path = entry.path();
let to_entry_path = to_path.join(
from_entry_path
.file_name()
.ok_or(Error::Str("无法解析文件名".into()))?,
);
auto_copy(from_entry_path, to_entry_path)?;
}
} else {
return Err(res::Error::Io(io::Error::new(
io::ErrorKind::Other,
"Path is neither a file nor a directory",
)));
}
Ok(())
}
fn write<'a>(
path: &PathBuf,
bytes: Cow<'a, [u8]>,
is_sync: bool,
is_append: bool,
) -> crate::Result<()> {
if !is_append && path.exists() {
path.auto_remove_file()?;
}
let mut f = fs::OpenOptions::new()
.read(true)
.write(true)
.create(true)
.append(is_append)
.open(path)?;
f.write_all(&bytes)?;
if is_sync {
f.sync_data()?;
}
Ok(())
}
#[cfg(feature = "encode")]
pub fn write_gbk<'a>(
path: &PathBuf,
content: &'a str,
is_sync: bool,
is_append: bool,
) -> crate::Result<()> {
let (bytes, _encode, had_errors) = encoding_rs::GBK.encode(content);
if had_errors {
return Err("写入GBK失败".into());
} else {
write(path, bytes, is_sync, is_append)
}
}
#[cfg(feature = "encode")]
pub fn write_utf8<'a>(
path: &PathBuf,
content: &'a str,
is_sync: bool,
is_append: bool,
) -> crate::Result<()> {
let (bytes, _encode, had_errors) = encoding_rs::UTF_8.encode(content);
if had_errors {
return Err("写入UTF-8失败".into());
} else {
write(path, bytes, is_sync, is_append)
}
}
#[cfg(feature = "tokio")]
pub mod async_runtime {
use std::{borrow::Cow, path::PathBuf};
use tokio::{fs, io::AsyncWriteExt as _};
async fn async_write<'a>(
path: &PathBuf,
bytes: Cow<'a, [u8]>,
is_sync: bool,
is_append: bool,
) -> crate::Result<()> {
if !is_append && path.exists() {
fs::remove_file(path).await?
}
let mut f = fs::OpenOptions::new()
.read(true)
.write(true)
.create(true)
.append(is_append)
.open(path)
.await?;
f.write_all(&bytes).await?;
if is_sync {
f.sync_data().await?;
}
Ok(())
}
#[cfg(feature = "encode")]
pub async fn async_write_gbk<'a>(
path: &PathBuf,
content: &'a str,
is_sync: bool,
is_append: bool,
) -> crate::Result<()> {
let (bytes, _encode, had_errors) = encoding_rs::GBK.encode(content);
if had_errors {
return Err("异步写入GBK失败".into());
} else {
async_write(path, bytes, is_sync, is_append).await
}
}
#[cfg(feature = "encode")]
pub async fn async_write_utf8<'a>(
path: &PathBuf,
content: &'a str,
is_sync: bool,
is_append: bool,
) -> crate::Result<()> {
let (bytes, _encode, had_errors) = encoding_rs::UTF_8.encode(content);
if had_errors {
return Err("异步写入UTF-8失败".into());
} else {
async_write(path, bytes, is_sync, is_append).await
}
}
}