1use crate::{
2 config::{CertificateConfig, PrintConfig},
3 error::EricError,
4 error_code::ErrorCode,
5 response::{EricApiPayload, EricResponse, ResponseBuffer},
6 utils::ToCString,
7 ProcessingFlag,
8};
9use anyhow::{anyhow, Context};
10use eric_bindings::{
11 EricBearbeiteVorgang, EricBeende, EricCheckXML, EricDekodiereDaten, EricEntladePlugins,
12 EricHoleFehlerText, EricInitialisiere,
13};
14use std::{path::Path, ptr};
15use tracing::{debug, error, info};
16
17pub struct Eric;
21
22impl Eric {
23 pub fn new(log_path: Option<&Path>, plugin_path: Option<&Path>) -> Result<Self, EricError> {
29 info!("Initializing eric");
30
31 if let Some(log_path) = log_path {
32 info!(log_path = %log_path.display(), "Setting log path");
33 info!(log_file = %log_path.join("eric.log").display(), "Logging to file");
34 } else {
35 info!("No log path provided, using ERiC default temporary directory");
36 }
37
38 if let Some(plugin_path) = plugin_path {
39 info!(plugin_path = %plugin_path.display(), "Setting plugin path");
40 } else {
41 info!("No plugin path provided, using ERiC default plugin directory");
42 }
43
44 let plugin_path_cstring = plugin_path
46 .map(|plugin_path| plugin_path.try_to_cstring())
47 .transpose()
48 .context("failed to convert plugin path to CString")?;
49 let plugin_ptr = plugin_path_cstring
50 .as_deref()
51 .map_or(ptr::null(), |cstr| cstr.as_ptr());
52
53 let log_path_cstring = log_path
55 .map(|path| path.try_to_cstring())
56 .transpose()
57 .context("failed to convert log path to CString")?;
58 let log_path_ptr = log_path_cstring
59 .as_deref()
60 .map_or(ptr::null(), |cstr| cstr.as_ptr());
61
62 let error_code = unsafe { EricInitialisiere(plugin_ptr, log_path_ptr) };
63
64 match error_code {
65 x if x == ErrorCode::ERIC_OK as i32 => Ok(Eric),
66 error_code => Err(EricError::Internal(anyhow!(
67 "Can't init eric: {}",
68 error_code
69 ))),
70 }
71 }
72
73 pub fn validate(
77 &self,
78 xml: String,
79 taxonomy_type: &str,
80 taxonomy_version: &str,
81 pdf_path: Option<&str>,
82 ) -> Result<EricResponse, EricError> {
83 let processing_flag: ProcessingFlag;
84 let type_version = format!("{}_{}", taxonomy_type, taxonomy_version);
85 let print_config = if let Some(pdf_path) = pdf_path {
86 processing_flag = ProcessingFlag::Print;
87 Some(PrintConfig::new(pdf_path, &processing_flag)?)
88 } else {
89 processing_flag = ProcessingFlag::Validate;
90 None
91 };
92 Self::process(xml, type_version, processing_flag, print_config, None, None)
93 }
94
95 pub fn send(
102 &self,
103 xml: String,
104 taxonomy_type: &str,
105 taxonomy_version: &str,
106 certificate_path: &Path,
107 certificate_password: &str,
108 pdf_path: Option<&str>,
109 ) -> Result<EricResponse, EricError> {
110 let certificate_path = certificate_path
111 .to_str()
112 .context("failed to convert path to string")?;
113 let processing_flag: ProcessingFlag;
114 let type_version = format!("{}_{}", taxonomy_type, taxonomy_version);
115 let print_config = if let Some(pdf_path) = pdf_path {
116 processing_flag = ProcessingFlag::SendAndPrint;
117 Some(PrintConfig::new(pdf_path, &processing_flag)?)
118 } else {
119 processing_flag = ProcessingFlag::Send;
120 None
121 };
122 let certificate_config = CertificateConfig::new(certificate_path, certificate_password)?;
123 Self::process(
124 xml,
125 type_version,
126 processing_flag,
127 print_config,
128 Some(certificate_config),
129 None,
130 )
131 }
132
133 pub fn check_xml(
142 &self,
143 xml: String,
144 taxonomy_type: &str,
145 taxonomy_version: &str,
146 ) -> Result<EricResponse, EricError> {
147 let type_version = format!("{}_{}", taxonomy_type, taxonomy_version);
148 let xml = xml.try_to_cstring()?;
149 let type_version = type_version.try_to_cstring()?;
150
151 let validation_response_buffer = ResponseBuffer::new()?;
152
153 let error_code = unsafe {
154 EricCheckXML(
155 xml.as_ptr(),
156 type_version.as_ptr(),
157 validation_response_buffer.as_ptr(),
158 )
159 };
160
161 let validation_response = validation_response_buffer.read()?;
162 let payload = EricApiPayload::new(validation_response.to_string(), String::new());
163
164 if error_code == ErrorCode::ERIC_OK as i32 {
165 Ok(EricResponse::new(payload))
166 } else {
167 let response_buffer = ResponseBuffer::new()?;
168
169 unsafe {
170 EricHoleFehlerText(error_code, response_buffer.as_ptr());
171 }
172
173 let error_text = response_buffer.read()?;
174
175 Err(EricError::ApiError {
176 code: error_code,
177 message: error_text.to_string(),
178 payload,
179 })
180 }
181 }
182
183 pub fn get_error_text(&self, error_code: i32) -> Result<String, EricError> {
185 let response_buffer = ResponseBuffer::new()?;
186
187 unsafe {
188 EricHoleFehlerText(error_code, response_buffer.as_ptr());
189 }
190
191 Ok(response_buffer.read()?.to_string())
192 }
193
194 #[allow(dead_code)]
195 fn decrypt(
196 &self,
197 encrypted_file: &str,
198 certificate_config: CertificateConfig,
199 ) -> Result<i32, EricError> {
200 let encrypted_data = encrypted_file.try_to_cstring()?;
201 let response_buffer = ResponseBuffer::new()?;
202
203 let error_code = unsafe {
204 EricDekodiereDaten(
205 certificate_config.certificate.handle,
206 certificate_config.password.as_ptr(),
207 encrypted_data.as_ptr(),
208 response_buffer.as_ptr(),
209 )
210 };
211
212 Ok(error_code)
213 }
214
215 fn process(
216 xml: String,
217 type_version: String,
218 processing_flag: ProcessingFlag,
219 print_config: Option<PrintConfig>,
220 certificate_config: Option<CertificateConfig>,
221 transfer_code: Option<u32>,
222 ) -> Result<EricResponse, EricError> {
223 debug!("Processing xml file");
224
225 match processing_flag {
226 ProcessingFlag::Validate => debug!("Validating xml file"),
227 ProcessingFlag::Print => debug!("Validating xml file"),
228 ProcessingFlag::Send => debug!("Sending xml file"),
229 ProcessingFlag::SendAndPrint => debug!("Send and print"),
230 ProcessingFlag::CheckHints => debug!("Check hints"),
231 ProcessingFlag::ValidateWithoutDate => debug!("Validate without release date"),
232 }
233
234 let xml = xml.try_to_cstring()?;
235 let type_version = type_version.try_to_cstring()?;
236
237 let mut transfer_code_storage = transfer_code;
241 let transfer_code_ptr: *mut u32 = transfer_code_storage
242 .as_mut()
243 .map_or(ptr::null_mut(), |c| c as *mut u32);
244
245 if let Some(print_config) = &print_config {
246 info!(
247 pdf_path = %print_config
248 .pdf_path
249 .to_str()
250 .context("failed to convert path to string")?,
251 "Printing confirmation to file"
252 )
253 }
254
255 let validation_response_buffer = ResponseBuffer::new()?;
256 let server_response_buffer = ResponseBuffer::new()?;
257
258 let error_code = unsafe {
259 EricBearbeiteVorgang(
260 xml.as_ptr(),
261 type_version.as_ptr(),
262 processing_flag.into_u32(),
263 match &print_config {
267 Some(el) => el.print_parameter.as_ptr(),
268 None => ptr::null(),
269 },
270 match &certificate_config {
274 Some(config) => config.certificate_parameter.as_ptr(),
275 None => ptr::null(),
276 },
277 transfer_code_ptr,
278 validation_response_buffer.as_ptr(),
279 server_response_buffer.as_ptr(),
280 )
281 };
282
283 let transfer_code = unsafe { transfer_code_ptr.as_ref() };
284
285 if let Some(code) = transfer_code {
286 debug!(transfer_code = %code, "Transfer code received")
287 }
288
289 let validation_response = validation_response_buffer.read()?;
290 let server_response = server_response_buffer.read()?;
292 let payload =
293 EricApiPayload::new(validation_response.to_string(), server_response.to_string());
294
295 if error_code == ErrorCode::ERIC_OK as i32 {
296 Ok(EricResponse::new(payload))
297 } else {
298 let response_buffer = ResponseBuffer::new()?;
299
300 unsafe {
301 EricHoleFehlerText(error_code, response_buffer.as_ptr());
302 }
303
304 let error_text = response_buffer.read()?;
305
306 Err(EricError::ApiError {
307 code: error_code,
308 message: error_text.to_string(),
309 payload,
310 })
311 }
312 }
313}
314
315impl Drop for Eric {
316 fn drop(&mut self) {
317 info!("Closing eric");
318
319 let error_code = unsafe { EricEntladePlugins() };
320
321 if error_code != ErrorCode::ERIC_OK as i32 {
322 error!(error_code = %error_code, "Error while unloading plugins");
323 }
324
325 let error_code = unsafe { EricBeende() };
326
327 if error_code != ErrorCode::ERIC_OK as i32 {
328 error!(error_code = %error_code, "Can't close eric");
329 }
330 }
331}