use std::error::Error;
use std::ffi::c_int;
use std::ffi::CString;
use std::fmt::Display;
use std::path::Path;
use std::{os::raw::c_char, ptr::null};
#[link(name = "msoc")]
extern "C" {
fn MSOC_encryptA(
outFile: *const c_char,
inFile: *const c_char,
pass: *const c_char,
opt: *const usize,
) -> c_int;
fn MSOC_decryptA(
outFile: *const c_char,
inFile: *const c_char,
pass: *const c_char,
opt: *const usize,
) -> c_int;
}
#[derive(Debug, PartialEq, Eq)]
pub enum CryptError {
InputFileNotExist,
NotSupportFormat,
AlreadyEncryped,
BadPassword,
UnknownError(i32),
}
impl Display for CryptError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
CryptError::InputFileNotExist => f.write_str("The input file is not exist!"),
CryptError::NotSupportFormat => {
f.write_str("The input file's format is not supported!")
}
CryptError::AlreadyEncryped => f.write_str("The input file had encryped!"),
CryptError::BadPassword => f.write_str("The password is incorrect"),
CryptError::UnknownError(ret) => write!(f, "unknown error,ret code {ret}"),
}
}
}
impl From<CryptError> for String {
fn from(value: CryptError) -> Self {
value.to_string()
}
}
impl Error for CryptError {}
pub fn encrypt(in_file: &str, password: &str, out_file: Option<&str>) -> Result<(), CryptError> {
if !Path::new(in_file).exists() {
return Err(CryptError::InputFileNotExist);
}
unsafe {
let input = CString::new(in_file).unwrap();
let output = CString::new(out_file.unwrap_or(in_file)).unwrap();
let password = CString::new(password).unwrap();
let ret = MSOC_encryptA(output.as_ptr(), input.as_ptr(), password.as_ptr(), null());
match ret {
0 => Ok(()),
1 => Err(CryptError::NotSupportFormat),
2 => Err(CryptError::AlreadyEncryped),
3 => Err(CryptError::BadPassword),
8 => Err(CryptError::InputFileNotExist),
_ => Err(CryptError::UnknownError(ret)),
}
}
}
pub fn decrypt(in_file: &str, password: &str, out_file: Option<&str>) -> Result<(), CryptError> {
if !Path::new(in_file).exists() {
return Err(CryptError::InputFileNotExist);
}
unsafe {
let input = CString::new(in_file).unwrap();
let output = CString::new(out_file.unwrap_or(in_file)).unwrap();
let password = CString::new(password).unwrap();
let ret = MSOC_decryptA(output.as_ptr(), input.as_ptr(), password.as_ptr(), null());
match ret {
0 => Ok(()),
1 => Err(CryptError::NotSupportFormat),
2 => Err(CryptError::AlreadyEncryped),
3 => Err(CryptError::BadPassword),
8 => Err(CryptError::InputFileNotExist),
_ => Err(CryptError::UnknownError(ret)),
}
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_encrypt() {
let input = "/home/feiy/Desktop/1.xlsx";
let output = "/home/feiy/Desktop/output.xlsx";
let password = "test";
let ret = encrypt(input, password, Some(output));
assert_eq!(ret, Ok(()));
}
#[test]
fn test_decrypt() {
let input = "/home/feiy/Desktop/output.xlsx";
let password = "test";
let ret = decrypt(input, password, None);
assert_eq!(ret, Ok(()));
}
}