use crate::{
config::{CertificateConfig, PrintConfig},
error::EricError,
error_code::ErrorCode,
response::{EricApiPayload, EricResponse, ResponseBuffer},
utils::ToCString,
ProcessingFlag,
};
use anyhow::{anyhow, Context};
use eric_bindings::{
EricBearbeiteVorgang, EricBeende, EricCheckXML, EricDekodiereDaten, EricEntladePlugins,
EricHoleFehlerText, EricInitialisiere,
};
use std::{path::Path, ptr};
use tracing::{debug, error, info};
pub struct Eric;
impl Eric {
pub fn new(log_path: Option<&Path>, plugin_path: Option<&Path>) -> Result<Self, EricError> {
info!("Initializing eric");
if let Some(log_path) = log_path {
info!(log_path = %log_path.display(), "Setting log path");
info!(log_file = %log_path.join("eric.log").display(), "Logging to file");
} else {
info!("No log path provided, using ERiC default temporary directory");
}
if let Some(plugin_path) = plugin_path {
info!(plugin_path = %plugin_path.display(), "Setting plugin path");
} else {
info!("No plugin path provided, using ERiC default plugin directory");
}
let plugin_path_cstring = plugin_path
.map(|plugin_path| plugin_path.try_to_cstring())
.transpose()
.context("failed to convert plugin path to CString")?;
let plugin_ptr = plugin_path_cstring
.as_deref()
.map_or(ptr::null(), |cstr| cstr.as_ptr());
let log_path_cstring = log_path
.map(|path| path.try_to_cstring())
.transpose()
.context("failed to convert log path to CString")?;
let log_path_ptr = log_path_cstring
.as_deref()
.map_or(ptr::null(), |cstr| cstr.as_ptr());
let error_code = unsafe { EricInitialisiere(plugin_ptr, log_path_ptr) };
match error_code {
x if x == ErrorCode::ERIC_OK as i32 => Ok(Eric),
error_code => Err(EricError::Internal(anyhow!(
"Can't init eric: {}",
error_code
))),
}
}
pub fn validate(
&self,
xml: String,
taxonomy_type: &str,
taxonomy_version: &str,
pdf_path: Option<&str>,
) -> Result<EricResponse, EricError> {
let processing_flag: ProcessingFlag;
let type_version = format!("{}_{}", taxonomy_type, taxonomy_version);
let print_config = if let Some(pdf_path) = pdf_path {
processing_flag = ProcessingFlag::Print;
Some(PrintConfig::new(pdf_path, &processing_flag)?)
} else {
processing_flag = ProcessingFlag::Validate;
None
};
Self::process(xml, type_version, processing_flag, print_config, None, None)
}
pub fn send(
&self,
xml: String,
taxonomy_type: &str,
taxonomy_version: &str,
certificate_path: &Path,
certificate_password: &str,
pdf_path: Option<&str>,
) -> Result<EricResponse, EricError> {
let certificate_path = certificate_path
.to_str()
.context("failed to convert path to string")?;
let processing_flag: ProcessingFlag;
let type_version = format!("{}_{}", taxonomy_type, taxonomy_version);
let print_config = if let Some(pdf_path) = pdf_path {
processing_flag = ProcessingFlag::SendAndPrint;
Some(PrintConfig::new(pdf_path, &processing_flag)?)
} else {
processing_flag = ProcessingFlag::Send;
None
};
let certificate_config = CertificateConfig::new(certificate_path, certificate_password)?;
Self::process(
xml,
type_version,
processing_flag,
print_config,
Some(certificate_config),
None,
)
}
pub fn check_xml(
&self,
xml: String,
taxonomy_type: &str,
taxonomy_version: &str,
) -> Result<EricResponse, EricError> {
let type_version = format!("{}_{}", taxonomy_type, taxonomy_version);
let xml = xml.try_to_cstring()?;
let type_version = type_version.try_to_cstring()?;
let validation_response_buffer = ResponseBuffer::new()?;
let error_code = unsafe {
EricCheckXML(
xml.as_ptr(),
type_version.as_ptr(),
validation_response_buffer.as_ptr(),
)
};
let validation_response = validation_response_buffer.read()?;
let payload = EricApiPayload::new(validation_response.to_string(), String::new());
if error_code == ErrorCode::ERIC_OK as i32 {
Ok(EricResponse::new(payload))
} else {
let response_buffer = ResponseBuffer::new()?;
unsafe {
EricHoleFehlerText(error_code, response_buffer.as_ptr());
}
let error_text = response_buffer.read()?;
Err(EricError::ApiError {
code: error_code,
message: error_text.to_string(),
payload,
})
}
}
pub fn get_error_text(&self, error_code: i32) -> Result<String, EricError> {
let response_buffer = ResponseBuffer::new()?;
unsafe {
EricHoleFehlerText(error_code, response_buffer.as_ptr());
}
Ok(response_buffer.read()?.to_string())
}
#[allow(dead_code)]
fn decrypt(
&self,
encrypted_file: &str,
certificate_config: CertificateConfig,
) -> Result<i32, EricError> {
let encrypted_data = encrypted_file.try_to_cstring()?;
let response_buffer = ResponseBuffer::new()?;
let error_code = unsafe {
EricDekodiereDaten(
certificate_config.certificate.handle,
certificate_config.password.as_ptr(),
encrypted_data.as_ptr(),
response_buffer.as_ptr(),
)
};
Ok(error_code)
}
fn process(
xml: String,
type_version: String,
processing_flag: ProcessingFlag,
print_config: Option<PrintConfig>,
certificate_config: Option<CertificateConfig>,
transfer_code: Option<u32>,
) -> Result<EricResponse, EricError> {
debug!("Processing xml file");
match processing_flag {
ProcessingFlag::Validate => debug!("Validating xml file"),
ProcessingFlag::Print => debug!("Validating xml file"),
ProcessingFlag::Send => debug!("Sending xml file"),
ProcessingFlag::SendAndPrint => debug!("Send and print"),
ProcessingFlag::CheckHints => debug!("Check hints"),
ProcessingFlag::ValidateWithoutDate => debug!("Validate without release date"),
}
let xml = xml.try_to_cstring()?;
let type_version = type_version.try_to_cstring()?;
let mut transfer_code_storage = transfer_code;
let transfer_code_ptr: *mut u32 = transfer_code_storage
.as_mut()
.map_or(ptr::null_mut(), |c| c as *mut u32);
if let Some(print_config) = &print_config {
info!(
pdf_path = %print_config
.pdf_path
.to_str()
.context("failed to convert path to string")?,
"Printing confirmation to file"
)
}
let validation_response_buffer = ResponseBuffer::new()?;
let server_response_buffer = ResponseBuffer::new()?;
let error_code = unsafe {
EricBearbeiteVorgang(
xml.as_ptr(),
type_version.as_ptr(),
processing_flag.into_u32(),
match &print_config {
Some(el) => el.print_parameter.as_ptr(),
None => ptr::null(),
},
match &certificate_config {
Some(config) => config.certificate_parameter.as_ptr(),
None => ptr::null(),
},
transfer_code_ptr,
validation_response_buffer.as_ptr(),
server_response_buffer.as_ptr(),
)
};
let transfer_code = unsafe { transfer_code_ptr.as_ref() };
if let Some(code) = transfer_code {
debug!(transfer_code = %code, "Transfer code received")
}
let validation_response = validation_response_buffer.read()?;
let server_response = server_response_buffer.read()?;
let payload =
EricApiPayload::new(validation_response.to_string(), server_response.to_string());
if error_code == ErrorCode::ERIC_OK as i32 {
Ok(EricResponse::new(payload))
} else {
let response_buffer = ResponseBuffer::new()?;
unsafe {
EricHoleFehlerText(error_code, response_buffer.as_ptr());
}
let error_text = response_buffer.read()?;
Err(EricError::ApiError {
code: error_code,
message: error_text.to_string(),
payload,
})
}
}
}
impl Drop for Eric {
fn drop(&mut self) {
info!("Closing eric");
let error_code = unsafe { EricEntladePlugins() };
if error_code != ErrorCode::ERIC_OK as i32 {
error!(error_code = %error_code, "Error while unloading plugins");
}
let error_code = unsafe { EricBeende() };
if error_code != ErrorCode::ERIC_OK as i32 {
error!(error_code = %error_code, "Can't close eric");
}
}
}