use std::ffi::{CStr, CString};
use std::os::raw::{c_char, c_int};
use libc::{c_void, free};
use rand_core::{OsRng, RngCore};
use crate::error::{Error, LibraryError};
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum LogLevel {
Error,
Warning,
Info,
DebugL1,
Debug,
DebugL2,
}
pub fn str_from_ptr<'a>(ptr: *const c_char) -> Result<&'a str, Error> {
unsafe { CStr::from_ptr(ptr) }.to_str().map_err(Error::from)
}
pub fn owned_str_from_ptr(ptr: *const c_char) -> Result<String, Error> {
str_from_ptr(ptr).map(ToOwned::to_owned)
}
pub fn run_with_string<R, F>(ptr: *const c_char, op: F) -> Result<R, Error>
where
F: FnOnce(&str) -> Result<R, Error>,
{
if ptr.is_null() {
return Err(Error::UnexpectedError(
"libnitrokey returned a null pointer".to_owned(),
));
}
let result = str_from_ptr(ptr).and_then(op);
unsafe { free(ptr as *mut c_void) };
result
}
pub fn result_from_string(ptr: *const c_char) -> Result<String, Error> {
run_with_string(ptr, |s| {
if s.is_empty() {
get_last_result().map(|_| s.to_owned())
} else {
Ok(s.to_owned())
}
})
}
pub fn result_or_error<T>(value: T) -> Result<T, Error> {
get_last_result().and(Ok(value))
}
pub fn get_struct<R, T, F>(f: F) -> Result<R, Error>
where
R: From<T>,
T: Default,
F: Fn(&mut T) -> c_int,
{
let mut out = T::default();
get_command_result(f(&mut out))?;
Ok(out.into())
}
pub fn get_command_result(value: c_int) -> Result<(), Error> {
if value == 0 {
Ok(())
} else {
Err(Error::from(value))
}
}
pub fn get_last_result() -> Result<(), Error> {
get_command_result(unsafe { nitrokey_sys::NK_get_last_command_status() }.into())
}
pub fn get_last_error() -> Error {
get_last_result().err().unwrap_or_else(|| {
Error::UnexpectedError("Expected an error, but command status is zero".to_owned())
})
}
pub fn generate_password(length: usize) -> Result<CString, Error> {
loop {
let mut data = vec![0u8; length];
OsRng.fill_bytes(&mut data[..]);
if let Ok(s) = CString::new(data) {
return Ok(s);
}
}
}
pub fn get_cstring<T: Into<Vec<u8>>>(s: T) -> Result<CString, Error> {
CString::new(s).map_err(|_| LibraryError::InvalidString.into())
}
impl From<LogLevel> for i32 {
fn from(log_level: LogLevel) -> i32 {
match log_level {
LogLevel::Error => 0,
LogLevel::Warning => 1,
LogLevel::Info => 2,
LogLevel::DebugL1 => 3,
LogLevel::Debug => 4,
LogLevel::DebugL2 => 5,
}
}
}