use std::borrow::Cow; use std::{env, fs}; use std::fs::{File, OpenOptions}; use std::io::{BufReader, BufWriter, Read, Write}; use std::path::{Path, PathBuf}; use crate::tuliprox_error::str_to_io_error; use crate::utils::debug_if_enabled; use log::{debug, error}; use path_clean::PathClean; use crate::utils::{API_PROXY_FILE, CONFIG_FILE, CONFIG_PATH, MAPPING_FILE, SOURCE_FILE, USER_FILE}; pub fn file_writer(w: W) -> BufWriter where W: Write, { BufWriter::with_capacity(131_072, w) } pub fn file_reader(r: R) -> BufReader where R: Read, { BufReader::with_capacity(131_072, r) } pub fn get_exe_path() -> PathBuf { let default_path = std::path::PathBuf::from("./"); let current_exe = std::env::current_exe(); match current_exe { Ok(exe) => { match fs::read_link(&exe) { Ok(f) => f.parent().map_or(default_path, std::path::Path::to_path_buf), Err(_) => exe.parent().map_or(default_path, std::path::Path::to_path_buf) } } Err(_) => default_path } } fn get_default_path(file: &str) -> String { let path: PathBuf = get_exe_path(); let default_path = path.join(file); String::from(if default_path.exists() { default_path.to_str().unwrap_or(file) } else { file }) } pub fn get_default_file_path(config_path: &str, file: &str) -> String { let path: PathBuf = PathBuf::from(config_path); let default_path = path.join(file); String::from(if default_path.exists() { default_path.to_str().unwrap_or(file) } else { file }) } #[inline] pub fn get_default_user_file_path(config_path: &str) -> String { get_default_file_path(config_path, USER_FILE) } #[inline] pub fn get_default_config_path() -> String { get_default_path(CONFIG_PATH) } #[inline] pub fn get_default_config_file_path(config_path: &str) -> String { get_default_file_path(config_path, CONFIG_FILE) } #[inline] pub fn get_default_sources_file_path(config_path: &str) -> String { get_default_file_path(config_path, SOURCE_FILE) } #[inline] pub fn get_default_mappings_path(config_path: &str) -> String { get_default_file_path(config_path, MAPPING_FILE) } #[inline] pub fn get_default_api_proxy_config_path(config_path: &str) -> String { get_default_file_path(config_path, API_PROXY_FILE) } pub fn get_working_path(wd: &str) -> String { let current_dir = std::env::current_dir().unwrap_or_default(); if wd.is_empty() { String::from(current_dir.to_str().unwrap_or(".")) } else { let work_path = std::path::PathBuf::from(wd); let _ = fs::create_dir_all(&work_path); let wdpath = fs::metadata(&work_path).map_or(None, |md| if md.is_dir() && !md.permissions().readonly() { work_path.canonicalize().ok() } else { error!("Path not found {:?}", &work_path); None }); let rp: PathBuf = wdpath.map_or_else(|| current_dir.join(wd), |d| d); rp.canonicalize().map_or_else(|_| { error!("Path not found {:?}", &rp); String::from("./") }, |ap| String::from(ap.to_str().unwrap_or("./"))) } } #[inline] pub fn open_file(file_name: &Path) -> Result { File::open(file_name) } pub fn persist_file(persist_file: Option, text: &str) { if let Some(path_buf) = persist_file { let filename = &path_buf.to_str().unwrap_or("?"); match File::create(&path_buf) { Ok(mut file) => match file.write_all(text.as_bytes()) { Ok(()) => debug!("persisted: {filename}"), Err(e) => error!("failed to persist file {filename}, {e}") }, Err(e) => error!("failed to persist file {filename}, {e}") } } } pub fn prepare_persist_path(file_name: &str, date_prefix: &str) -> PathBuf { let now = chrono::Local::now(); let persist_filename = file_name.replace("{}", format!("{date_prefix}{}", now.format("%Y%m%d_%H%M%S").to_string().as_str()).as_str()); std::path::PathBuf::from(persist_filename) } pub fn get_file_path(wd: &str, path: Option) -> Option { path.map(|p| if p.is_relative() { let pb = PathBuf::from(wd); pb.join(&p).clean() } else { p }) } pub fn add_prefix_to_filename(path: &Path, prefix: &str, ext: Option<&str>) -> PathBuf { let file_name = path.file_name().unwrap_or_default(); let new_file_name = format!("{}{}", prefix, file_name.to_string_lossy()); let result = path.with_file_name(new_file_name); match ext { None => result, Some(extension) => result.with_extension(extension) } } pub fn path_exists(file_path: &Path) -> bool { if let Ok(metadata) = fs::metadata(file_path) { return metadata.is_file(); } false } pub fn check_write(res: &std::io::Result<()>) -> Result<(), std::io::Error> { match res { Ok(()) => Ok(()), Err(_) => Err(str_to_io_error("Unable to write file")), } } // pub fn append_extension(path: &Path, ext: &str) -> PathBuf { // let extension = path.extension().map(|ext| ext.to_str().unwrap_or("")); // path.with_extension(format!("{}{ext}", &extension.unwrap_or_default())) // } #[inline] pub fn sanitize_filename(file_name: &str) -> String { file_name .chars() .map(|c| if c.is_alphanumeric() || c == '_' || c == '-' { c } else { '_' }) .collect() } #[inline] pub fn append_or_crate_file(path: &Path) -> std::io::Result { OpenOptions::new().create(true).append(true).open(path) } #[inline] pub fn create_new_file_for_write(path: &Path) -> std::io::Result { OpenOptions::new().write(true).create(true).truncate(true).open(path) } #[inline] pub fn create_new_file_for_read_write(path: &Path) -> std::io::Result { OpenOptions::new().read(true).write(true).create(true).truncate(true).open(path) } #[inline] pub fn open_read_write_file(path: &Path) -> std::io::Result { OpenOptions::new().read(true).write(true).create(false).truncate(false).open(path) } #[inline] pub fn open_readonly_file(path: &Path) -> std::io::Result { OpenOptions::new().read(true).write(false).truncate(false).create(false).open(path) } pub fn rename_or_copy(src: &Path, dest: &Path, remove_old: bool) -> std::io::Result<()> { // Try to rename the file if fs::rename(src, dest).is_err() { fs::copy(src, dest)?; if remove_old { if let Err(err) = fs::remove_file(src) { error!("Could not delete file {} {err}", src.to_string_lossy()); } } } Ok(()) } pub fn traverse_dir(path: &Path, visit: &mut F) -> std::io::Result<()> where F: FnMut(&std::fs::DirEntry, &std::fs::Metadata), { for entry in fs::read_dir(path)? { let entry = entry?; let metadata = entry.metadata()?; if metadata.is_dir() { traverse_dir(&entry.path(), visit)?; } else { visit(&entry, &metadata); } } Ok(()) } pub fn prepare_file_path(persist: Option<&str>, working_dir: &str, action: &str) -> Option { let persist_file: Option = persist.map(|persist_path| prepare_persist_path(persist_path, action)); if persist_file.is_some() { let file_path = get_file_path(working_dir, persist_file); debug_if_enabled!("persist to file: {}", file_path.as_ref().map_or(Cow::from("?"), |p| p.to_string_lossy())); file_path } else { None } } pub fn read_file_as_bytes(path: &Path) -> std::io::Result> { let mut file = File::open(path)?; let mut buffer = Vec::new(); file.read_to_end(&mut buffer)?; Ok(buffer) } pub fn make_absolute_path(path: &str, working_dir: &str) -> String { let rpb = std::path::PathBuf::from(path); if rpb.is_relative() { let mut rpb2 = std::path::PathBuf::from(working_dir).join(&rpb); if !rpb2.exists() { rpb2 = get_exe_path().join(&rpb); } if !rpb2.exists() { let cwd = std::env::current_dir(); if let Ok(cwd_path) = cwd { rpb2 = cwd_path.join(&rpb); } } if rpb2.exists() { return String::from(rpb2.clean().to_str().unwrap_or_default()); } } path.to_string() } pub fn resolve_relative_path(relative: &str) -> std::io::Result { let current_dir = env::current_dir()?; Ok(current_dir.join(relative)) }