util.rs
1 //! Utility functions. 2 3 use std::{ 4 fs::{copy, create_dir_all, metadata, read, set_permissions, write, File}, 5 os::unix::fs::PermissionsExt, 6 path::{Path, PathBuf}, 7 time::SystemTime, 8 }; 9 10 use clingwrap::runner::CommandError; 11 use time::{macros::format_description, OffsetDateTime}; 12 13 /// Create an empty file. 14 pub fn create_file(filename: &Path) -> Result<PathBuf, UtilError> { 15 File::create(filename).map_err(|e| UtilError::CreateFile(filename.into(), e))?; 16 Ok(filename.into()) 17 } 18 19 /// Create a directory. 20 pub fn mkdir(dirname: &Path) -> Result<(), UtilError> { 21 create_dir_all(dirname).map_err(|e| UtilError::CreateDir(dirname.into(), e))?; 22 Ok(()) 23 } 24 25 /// Create a subdirectory. 26 pub fn mkdir_child(parent: &Path, subdir: &str) -> Result<PathBuf, UtilError> { 27 let pathname = parent.join(subdir); 28 create_dir_all(&pathname).map_err(|e| UtilError::CreateDir(pathname.clone(), e))?; 29 Ok(pathname) 30 } 31 32 /// Make sure a directory exists and is empty. 33 pub fn recreate_dir(dirname: &Path) -> Result<(), UtilError> { 34 if dirname.exists() { 35 std::fs::remove_dir_all(dirname).map_err(|e| UtilError::RemoveDir(dirname.into(), e))?; 36 } 37 mkdir(dirname)?; 38 Ok(()) 39 } 40 41 /// Read a text file. 42 pub fn cat_text_file(filename: &Path) -> Result<String, UtilError> { 43 let data = read(filename).map_err(|err| UtilError::Read(filename.into(), err))?; 44 let text = String::from_utf8(data).map_err(|err| UtilError::Utf8(filename.into(), err))?; 45 Ok(text) 46 } 47 48 /// Write a file. 49 pub fn write_file(filename: &Path, data: &[u8]) -> Result<(), UtilError> { 50 write(filename, data).map_err(|err| UtilError::WriteFile(filename.into(), err)) 51 } 52 53 /// Copy a file. 54 pub fn copy_file(src: &Path, dst: &Path) -> Result<(), UtilError> { 55 copy(src, dst).map_err(|err| UtilError::Copy(src.into(), dst.into(), err))?; 56 Ok(()) 57 } 58 59 /// Copy a file, make sure user has read and write permission to the copy. 60 pub fn copy_file_rw(src: &Path, dst: &Path) -> Result<(), UtilError> { 61 copy_file(src, dst)?; 62 let mut perms = std::fs::metadata(dst) 63 .map_err(|err| UtilError::GetMetadata(dst.into(), err))? 64 .permissions(); 65 perms.set_mode(0o644); 66 std::fs::set_permissions(dst, perms) 67 .map_err(|err| UtilError::SetPermissions(dst.into(), err))?; 68 Ok(()) 69 } 70 71 /// Write a file, make it executable. 72 pub fn write_executable(filename: &Path, data: &[u8]) -> Result<(), UtilError> { 73 // Unix mode bits for an executable file: read/write/exec for 74 // owner, read/exec for group and others 75 const EXECUTABLE: u32 = 0o755; 76 77 write(filename, data).map_err(|err| UtilError::WriteFile(filename.into(), err))?; 78 let meta = metadata(filename).map_err(|err| UtilError::GetMetadata(filename.into(), err))?; 79 let mut perm = meta.permissions(); 80 perm.set_mode(EXECUTABLE); 81 set_permissions(filename, perm).map_err(|err| UtilError::MakeExec(filename.into(), err))?; 82 Ok(()) 83 } 84 85 /// Current time as a string. 86 pub fn now() -> Result<String, UtilError> { 87 let fmt = format_description!("[year]-[month]-[day] [hour]:[minute]:[second]Z"); 88 OffsetDateTime::now_utc() 89 .format(fmt) 90 .map_err(UtilError::TimeFormat) 91 } 92 93 /// Format a time stamp. 94 pub fn format_timestamp(time: SystemTime) -> Result<String, UtilError> { 95 let fmt = format_description!("[year]-[month]-[day] [hour]:[minute]:[second]Z"); 96 OffsetDateTime::from(time) 97 .format(fmt) 98 .map_err(UtilError::TimeFormat) 99 } 100 101 /// Errors from utility functions. 102 #[derive(Debug, thiserror::Error)] 103 pub enum UtilError { 104 /// Can't create directory. 105 #[error("failed to create directory {0}")] 106 CreateDir(PathBuf, #[source] std::io::Error), 107 108 /// Can't remove directory. 109 #[error("failed to remove directory {0}")] 110 RemoveDir(PathBuf, #[source] std::io::Error), 111 112 /// Can't write file. 113 #[error("failed to write file {0}")] 114 WriteFile(PathBuf, #[source] std::io::Error), 115 116 /// Can't get file metadata. 117 #[error("failed to get metadata for file: {0}")] 118 GetMetadata(PathBuf, #[source] std::io::Error), 119 120 /// Can't make executable. 121 #[error("failed to make a file executable: {0}")] 122 MakeExec(PathBuf, #[source] std::io::Error), 123 124 /// Can't copy file. 125 #[error("failed to copy file {0} to {1}")] 126 Copy(PathBuf, PathBuf, #[source] std::io::Error), 127 128 /// Can't set file permissions. 129 #[error("failed to set permissions for file {0}")] 130 SetPermissions(PathBuf, #[source] std::io::Error), 131 132 /// Can't read file. 133 #[error("failed to read file {0}")] 134 Read(PathBuf, #[source] std::io::Error), 135 136 /// Can't read file as UTF-8. 137 #[error("failed to understand file {0} into UTF8")] 138 Utf8(PathBuf, #[source] std::string::FromUtf8Error), 139 140 /// Can't format time as string. 141 #[error("failed to format time stamp")] 142 TimeFormat(#[source] time::error::Format), 143 144 /// Can't create file. 145 #[error("failed to create file {0}")] 146 CreateFile(PathBuf, #[source] std::io::Error), 147 148 /// Can't run program. 149 #[error("failed to run program {0}")] 150 Execute(&'static str, #[source] CommandError), 151 }