use std::{
path::{PathBuf},
io::{Write},
fs::{self, File},
};
use crate::error::CryptError;
use crate::key_control::*;
#[derive(PartialEq, Debug, Clone)]
pub struct FileMetadata {
location: PathBuf,
file_type: FileTypes,
file_state: FileState,
}
impl FileMetadata {
pub fn new() -> Self {
FileMetadata {
location: PathBuf::new(),
file_type: FileTypes::Other,
file_state: FileState::Other,
}
}
pub fn from(location: PathBuf, file_type: FileTypes, file_state: FileState) -> Self {
FileMetadata {
location,
file_type,
file_state,
}
}
pub fn set_location(&mut self, location: PathBuf) {
self.location = location;
}
pub fn set_file_type(&mut self, file_type: FileTypes) {
self.file_type = file_type;
}
pub fn set_file_state(&mut self, file_state: FileState) {
self.file_state = file_state;
}
pub fn file_type(&self) -> &FileTypes {
&self.file_type
}
pub fn file_state(&self) -> &FileState {
&self.file_state
}
pub fn location(&self) -> Result<PathBuf, CryptError> {
let dir_str = &self.location.as_os_str().to_str().unwrap();
let dir = PathBuf::from(dir_str);
Ok(dir)
}
pub fn tags(&self) -> Result<(String, String), CryptError> {
let (start_label, end_label) = match self.file_type {
FileTypes::PublicKey => ("-----BEGIN PUBLIC KEY-----\n", "\n-----END PUBLIC KEY-----"),
FileTypes::SecretKey => ("-----BEGIN SECRET KEY-----\n", "\n-----END SECRET KEY-----"),
FileTypes::Message => ("-----BEGIN MESSAGE-----\n", "\n-----END MESSAGE-----"),
FileTypes::Ciphertext => ("-----BEGIN CIPHERTEXT-----\n", "\n-----END CIPHERTEXT-----"),
FileTypes::File => unreachable!(),
FileTypes::Other => unreachable!(),
};
Ok((start_label.to_string(), end_label.to_string()))
}
pub fn load(&self) -> Result<Vec<u8>, CryptError> {
let file_content = fs::read_to_string(&self.location).map_err(CryptError::from)?;
let (start_label, end_label) = self.tags()?;
let start = file_content.find(&start_label)
.ok_or(CryptError::InvalidMessageFormat)? + start_label.len();
let end = file_content.rfind(&end_label)
.ok_or(CryptError::InvalidMessageFormat)?;
let content = &file_content[start..end].trim();
hex::decode(content).map_err(|_| CryptError::HexDecodingError("Invalid hex format".into()))
}
pub fn parent(&self) -> Result<PathBuf, CryptError> {
let parent = self.location.parent();
let parent = match parent {
Some(parent) => PathBuf::from(parent),
_ => {PathBuf::new()}
};
Ok(parent)
}
pub fn save(&self, content: &[u8]) -> Result<(), CryptError> {
if let Some(parent_dir) = self.location.parent() {
if !parent_dir.is_dir() {
std::fs::create_dir_all(parent_dir).map_err(|_| CryptError::WriteError)?;
}
}
let (start_label, end_label) = self.tags()?;
let content = format!("{}{}{}", start_label, hex::encode(content), end_label);
let mut buffer = File::create(&self.location).map_err(|_| CryptError::WriteError)?;
buffer.write_all(content.as_bytes()).map_err(|_| CryptError::WriteError)?;
Ok(())
}
pub fn read(&self) -> Result<Vec<u8>, CryptError> {
fs::read(&self.location).map_err(CryptError::from)
}
}