use dusa_collection_utils::{
core::errors::{ErrorArrayItem, Errors, UnifiedResult as uf},
core::logger::LogLevel,
core::types::{pathtype::PathType, stringy::Stringy},
log,
platform::functions::{create_hash, del_dir, del_file},
};
use serde::{Deserialize, Serialize};
use std::{
fs::{File, OpenOptions},
io::{prelude::*, SeekFrom, Write},
str,
};
use crate::{
config::{ARRAY_LEN, CHUNK_SIZE},
encrypt::create_secure_chunk,
local_env::{SystemPaths, VERSION},
};
#[derive(Serialize, Deserialize, Debug)]
pub struct ChunkMap {
pub location: PathType,
pub version: String,
pub chunk_num: u32,
pub chunk_hsh: Stringy,
pub chunk_beg: u32,
pub chunk_end: u32,
}
const BEG_CHAR: u32 = 40;
const END_CHAR: u32 = 80984;
pub fn array_arimitics() -> u32 {
let chunk_data_len: u32 = ARRAY_LEN;
let total_chunks: u32 = chunk_data_len / CHUNK_SIZE as u32;
return total_chunks;
}
pub async fn generate_system_array() -> Result<(), ErrorArrayItem> {
let system_paths: SystemPaths = SystemPaths::read_current().await;
log!(LogLevel::Trace, "RECS: Generating system array");
if let Err(err) = del_dir(&system_paths.SYSTEM_ARRAY_LOCATION).uf_unwrap() {
log!(LogLevel::Error, "{}", err);
return Err(err);
};
let system_array_contents: Stringy = create_system_array_contents();
if let Err(err) = write_system_array_to_file(&system_array_contents).await {
return Err(err);
}
log!(LogLevel::Trace, "RECS: System array file created");
Ok(())
}
fn create_system_array_contents() -> Stringy {
let system_array_header = format!("<--REcS Array Version {}-->\n", VERSION);
let system_array_chunk = create_secure_chunk();
let system_array_footer = "\n</--REcS Array-->";
Stringy::from(format!(
"{}{}{}",
system_array_header, system_array_chunk, system_array_footer
))
}
pub async fn write_system_array_to_file(contents: &str) -> Result<(), ErrorArrayItem> {
let system_paths = SystemPaths::read_current().await;
let mut system_array_file = OpenOptions::new()
.create(true) .write(true)
.append(true)
.open(system_paths.SYSTEM_ARRAY_LOCATION.to_owned())
.map_err(ErrorArrayItem::from)?;
write!(system_array_file, "{}", contents).map_err(|e| {
log!(
LogLevel::Error,
"Error while writing to the system array file: {:?}",
e
);
ErrorArrayItem::from(e)
})?;
Ok(())
}
pub async fn index_system_array() -> uf<bool> {
let system_paths = SystemPaths::read_current().await;
let mut chunk_number: u32 = 1;
let mut range_start: u32 = BEG_CHAR;
let mut range_end: u32 = BEG_CHAR + CHUNK_SIZE as u32;
if (range_end - range_start) < CHUNK_SIZE as u32 {
return uf::new(Err(ErrorArrayItem::new(
Errors::GeneralError,
"Invalid chunk length".to_string(),
)));
}
let mut file = match File::open(system_paths.SYSTEM_ARRAY_LOCATION.clone()) {
Ok(file) => file,
Err(e) => {
return uf::new(Err(ErrorArrayItem::from(e)));
}
};
while range_start <= END_CHAR {
if let Err(err) = file.seek(SeekFrom::Start(range_start as u64)) {
return uf::new(Err(ErrorArrayItem::from(err)));
}
let mut buffer: Vec<u8> = vec![0; CHUNK_SIZE as usize];
match file.read_exact(&mut buffer) {
Ok(_) => {
let chunk: String = buffer.iter().map(|byte| format!("{:02X}", byte)).collect();
let chunk_hash: Stringy = create_hash(chunk);
let chunk_map = ChunkMap {
location: system_paths.SYSTEM_ARRAY_LOCATION.clone(),
version: VERSION.to_string(),
chunk_hsh: chunk_hash,
chunk_num: chunk_number,
chunk_beg: range_start,
chunk_end: range_end,
};
let chunk_map_path =
PathType::Content(format!("{}/chunk_{}.map", system_paths.MAPS, chunk_number));
if chunk_map_path.exists() {
if let Err(err) = del_file(&chunk_map_path.clone()).uf_unwrap() {
return uf::new(Err(err));
}
}
let pretty_chunk_map = match serde_json::to_string_pretty(&chunk_map) {
Ok(map) => map,
Err(e) => {
return uf::new(Err(ErrorArrayItem::from(e)));
}
};
if let Err(e) = OpenOptions::new()
.create_new(true)
.write(true)
.open(&chunk_map_path)
.and_then(|mut file| write!(file, "{}", pretty_chunk_map))
{
return uf::new(Err(ErrorArrayItem::from(e)));
}
}
Err(_) => break, }
chunk_number += 1;
range_start = range_end;
range_end += CHUNK_SIZE as u32;
}
log!(LogLevel::Trace, "RECS: Indexing completed successfully");
uf::new(Ok(true))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_create_system_array_contents() {
let expected_header = format!("<--REcS Array Version {}-->\n", VERSION);
let expected_footer = "\n</--REcS Array-->";
let result = create_system_array_contents();
assert!(result.starts_with(&expected_header));
assert!(result.ends_with(expected_footer));
}
}