use std::io::{Read, Write};
use std::os::unix::prelude::OpenOptionsExt;
use std::path::PathBuf;
use wildland_corex::api::{SfoError, SfoResult, SpecialFileOperations, SpecialFileType};
#[derive(Clone)]
pub struct SfoForLocalFS {
path: String,
}
impl SfoForLocalFS {
pub fn new(sfo_path: &str) -> Self {
let stub = SfoForLocalFS {
path: sfo_path.to_owned(),
};
stub.ensure_file_dirs_are_created().unwrap();
stub
}
fn ensure_file_dirs_are_created(&self) -> SfoResult<()> {
let file_types = [
SpecialFileType::Data,
SpecialFileType::Cache,
SpecialFileType::Temporary,
];
file_types.into_iter().for_each(|ft| {
if !self
.get_special_file_path_from_type(&ft)
.try_exists()
.unwrap()
{
std::fs::create_dir_all(self.get_special_file_path_from_type(&ft))
.map_err(|e| SfoError::Generic(e.to_string()))
.unwrap();
}
});
Ok(())
}
fn get_special_file_path_from_type(&self, file_type: &SpecialFileType) -> PathBuf {
match file_type {
SpecialFileType::Data => PathBuf::from(format!("{}/data", self.path)),
SpecialFileType::Cache => PathBuf::from(format!("{}/cache", self.path)),
SpecialFileType::Temporary => PathBuf::from(format!("{}/tmp", self.path)),
}
}
}
impl SpecialFileOperations for SfoForLocalFS {
fn create_special_file(
&mut self,
file_name: String,
file_type: SpecialFileType,
input_stream: Option<Vec<u8>>,
) -> SfoResult<()> {
let mut file_location = self.get_special_file_path_from_type(&file_type);
std::fs::create_dir_all(&file_location).map_err(|e| SfoError::Generic(e.to_string()))?;
file_location.push(&file_name);
if file_location.try_exists().unwrap() {
return Err(SfoError::FileExists);
}
let mut f = std::fs::OpenOptions::new()
.create(true)
.write(true)
.truncate(true)
.mode(0o600)
.open(&file_location)
.map_err(|e| SfoError::Generic(e.to_string()))?;
if let Some(stream) = input_stream {
tracing::debug!(
"Written {} bytes to {}",
f.write(&stream).unwrap(),
file_name
);
}
Ok(())
}
fn remove_special_file(
&mut self,
file_name: String,
file_type: SpecialFileType,
) -> SfoResult<()> {
let mut file_path = self.get_special_file_path_from_type(&file_type);
file_path.push(file_name);
if !file_path.try_exists().unwrap() {
Err(SfoError::FileNotFound)
} else {
std::fs::remove_file(file_path).map_err(|e| SfoError::Generic(e.to_string()))
}
}
fn write_to_special_file(
&self,
file_name: String,
file_type: SpecialFileType,
contents: Vec<u8>,
append: bool,
) -> SfoResult<()> {
let mut file_path = self.get_special_file_path_from_type(&file_type);
file_path.push(&file_name);
if !file_path.try_exists().unwrap() {
Err(SfoError::FileNotFound)
} else {
let mut f = std::fs::OpenOptions::new()
.write(true)
.truncate(!append)
.append(append)
.open(&file_path)
.map_err(|e| SfoError::Generic(e.to_string()))?;
tracing::debug!(
"Written {} bytes to {}",
f.write(&contents).unwrap(),
file_name
);
Ok(())
}
}
fn read_from_special_file(
&self,
file_name: String,
file_type: SpecialFileType,
) -> SfoResult<Vec<u8>> {
let mut file_path = self.get_special_file_path_from_type(&file_type);
file_path.push(&file_name);
if !file_path.try_exists().unwrap() {
Err(SfoError::FileNotFound)
} else {
let mut f = std::fs::OpenOptions::new()
.read(true)
.open(&file_path)
.map_err(|e| SfoError::Generic(e.to_string()))?;
let mut buffer = vec![];
tracing::debug!(
"Read {} bytes from {}",
f.read_to_end(&mut buffer)
.map_err(|e| SfoError::Generic(e.to_string()))?,
file_name
);
Ok(buffer)
}
}
fn get_special_file_path(
&self,
file_name: String,
file_type: SpecialFileType,
) -> SfoResult<String> {
let mut path = self.get_special_file_path_from_type(&file_type);
path.push(file_name);
let str_path = path.to_str().unwrap().to_string();
Ok(str_path)
}
}