use std::{env::consts::EXE_EXTENSION, path::Path, str::FromStr};
use tokio::fs::{metadata, read_to_string, write};
use tracing::{error, warn};
use crate::result::{RokitError, RokitResult};
/**
Loads the given type from the file at the given path.
Will return an error if the file does not exist or could not be parsed.
*/
pub(crate) async fn load_from_file
(path: P) -> RokitResult
where
P: AsRef,
T: FromStr,
E: Into,
{
let path = path.as_ref();
match read_to_string(path).await {
Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
Err(RokitError::FileNotFound(path.into()))
}
Err(e) => Err(e.into()),
Ok(s) => match s.parse() {
Ok(t) => Ok(t),
Err(e) => Err(e.into()),
},
}
}
/**
Saves the given data, stringified, to the file at the given path.
*/
pub(crate) async fn save_to_file(path: P, data: T) -> RokitResult<()>
where
P: AsRef,
T: Clone + ToString,
{
let path = path.as_ref();
write(path, data.to_string()).await?;
Ok(())
}
/**
Checks if the given path exists.
Note that this may return `false` if the caller
does not have permissions to access the given path.
*/
pub async fn path_exists(path: impl AsRef) -> bool {
metadata(path).await.is_ok()
}
/**
Writes the given contents to the file at the
given path, and adds executable permissions to it.
*/
pub async fn write_executable_file(
path: impl AsRef,
contents: impl AsRef<[u8]>,
) -> RokitResult<()> {
let path = path.as_ref();
if !EXE_EXTENSION.is_empty() {
match path.extension() {
Some(extension) if extension == EXE_EXTENSION => {}
_ => warn!(
"An executable file was written without an executable extension!\
\nThe file at '{path:?}' may not be usable.\
\nThis is most likely a bug in Rokit, please report it at {}",
env!("CARGO_PKG_REPOSITORY").trim_end_matches(".git")
),
}
}
if let Err(e) = write(path, contents).await {
error!("Failed to write executable to {path:?}:\n{e}");
return Err(e.into());
}
add_executable_permissions(path).await?;
Ok(())
}
#[cfg(unix)]
async fn add_executable_permissions(path: impl AsRef) -> RokitResult<()> {
use std::fs::Permissions;
use std::os::unix::fs::PermissionsExt;
use tokio::fs::set_permissions;
let path = path.as_ref();
if let Err(e) = set_permissions(path, Permissions::from_mode(0o755)).await {
error!("Failed to set executable permissions on {path:?}:\n{e}");
return Err(e.into());
}
Ok(())
}
#[cfg(not(unix))]
async fn add_executable_permissions(_path: impl AsRef) -> RokitResult<()> {
Ok(())
}