use std::path::Path;
use std::fs;
use std::time::{SystemTime, UNIX_EPOCH};
use crate::hashinterface::HashAlgorithm;
use crate::sha256hasher::Sha256Hasher;
use crate::md5hasher::Md5Hasher;
use crate::response::{Request, Response, OutputJsonMessage};
use crate::error::{JsonHashError, ErrorMessage};
use crate::cli::Cli;
#[derive(Debug, Clone, Copy)]
pub enum Algorithm {
Sha256,
Md5,
}
impl Algorithm {
pub fn from_str(s: &str) -> Result<Self, JsonHashError> {
match s.to_lowercase().as_str() {
"sha256" => Ok(Algorithm::Sha256),
"md5" => Ok(Algorithm::Md5),
_ => Err(JsonHashError::HashAlgorithmInvalid),
}
}
pub fn create_hasher(&self) -> Box<dyn HashAlgorithm> {
match self {
Algorithm::Sha256 => Box::new(Sha256Hasher::new()),
Algorithm::Md5 => Box::new(Md5Hasher::new()),
}
}
}
pub fn process_file_path(filepath: &str) -> (String, String, String) {
let path = Path::new(filepath);
let absfilepath = std::fs::canonicalize(path)
.unwrap_or_else(|_| path.to_path_buf())
.to_string_lossy()
.into_owned();
let filename = path
.file_name()
.unwrap_or_default()
.to_string_lossy()
.into_owned();
let current_dir = std::env::current_dir()
.unwrap_or_else(|_| std::path::PathBuf::from("."))
.file_name()
.unwrap_or_default()
.to_string_lossy()
.into_owned();
let parent_dir = path.parent().unwrap_or_else(|| Path::new(""));
let parent_folder = parent_dir
.file_name()
.unwrap_or_default()
.to_string_lossy()
.into_owned();
let shortpath = if parent_folder.is_empty() {
format!("{}/{}", current_dir, filename)
} else {
format!("{}/{}/{}", current_dir, parent_folder, filename)
};
(absfilepath, filename, shortpath)
}
pub fn compute_digests(filepath: &str, content: &[u8], algorithm: Algorithm) -> Result<(String, String, String), JsonHashError> {
let mut hasher = algorithm.create_hasher();
hasher.update(content);
let hash = hasher.finalize();
let (absfilepath, _, _) = process_file_path(filepath);
let mut pathid_hasher = algorithm.create_hasher();
pathid_hasher.update(absfilepath.as_bytes());
let pathid = pathid_hasher.finalize();
let (_, _, shortpath) = process_file_path(filepath);
let mut shortpathid_hasher = algorithm.create_hasher();
shortpathid_hasher.update(shortpath.as_bytes());
let shortpathid = shortpathid_hasher.finalize();
Ok((hash, pathid, shortpathid))
}
pub fn process_file(cli: &Cli) -> OutputJsonMessage {
let start_time = SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("Time went backwards")
.as_nanos();
let mut params = std::collections::HashMap::new();
params.insert("filepath".to_string(), serde_json::Value::String(cli.filepath.clone()));
params.insert("alg".to_string(), serde_json::Value::String(cli.alg.clone()));
if let Some(id) = &cli.id {
params.insert("id".to_string(), serde_json::Value::String(id.clone()));
} else {
params.insert("id".to_string(), serde_json::Value::Null);
}
let request = Request {
method: "jsonhash".to_string(),
params,
ts: start_time,
version: env!("CARGO_PKG_VERSION").to_string(),
};
let algorithm = match Algorithm::from_str(&cli.alg) {
Ok(alg) => alg,
Err(e) => return OutputJsonMessage::error(request, ErrorMessage::from(e)),
};
let path = Path::new(&cli.filepath);
if !path.exists() {
return OutputJsonMessage::error(request, ErrorMessage::from(JsonHashError::FileDoesNotExist));
}
if !path.is_file() {
return OutputJsonMessage::error(request, ErrorMessage::from(JsonHashError::NotAFile));
}
let content = match fs::read(&cli.filepath) {
Ok(content) => content,
Err(_) => return OutputJsonMessage::error(request, ErrorMessage::from(JsonHashError::FileOpeningError)),
};
let (absfilepath, filename, shortpath) = process_file_path(&cli.filepath);
let (hash, pathid, shortpathid) = match compute_digests(&cli.filepath, &content, algorithm) {
Ok(digests) => digests,
Err(e) => return OutputJsonMessage::error(request, ErrorMessage::from(e)),
};
let end_time = SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("Time went backwards")
.as_nanos();
let response = Response {
ts: end_time,
absfilepath,
hash,
filename,
shortpath,
pathid,
shortpathid,
};
OutputJsonMessage::success(request, response)
}