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)
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 )
130 }
131
132 pub fn check_xml(
141 &self,
142 xml: String,
143 taxonomy_type: &str,
144 taxonomy_version: &str,
145 ) -> Result<EricResponse, EricError> {
146 let type_version = format!("{}_{}", taxonomy_type, taxonomy_version);
147 let xml = xml.try_to_cstring()?;
148 let type_version = type_version.try_to_cstring()?;
149
150 let validation_response_buffer = ResponseBuffer::new()?;
151
152 let error_code = unsafe {
153 EricCheckXML(
154 xml.as_ptr(),
155 type_version.as_ptr(),
156 validation_response_buffer.as_ptr(),
157 )
158 };
159
160 let validation_response = validation_response_buffer.read()?;
161 let payload = EricApiPayload::new(validation_response.to_string(), String::new());
162
163 if error_code == ErrorCode::ERIC_OK as i32 {
164 Ok(EricResponse::new(payload))
165 } else {
166 let response_buffer = ResponseBuffer::new()?;
167
168 unsafe {
169 EricHoleFehlerText(error_code, response_buffer.as_ptr());
170 }
171
172 let error_text = response_buffer.read()?;
173
174 Err(EricError::ApiError {
175 code: error_code,
176 message: error_text.to_string(),
177 payload,
178 })
179 }
180 }
181
182 pub fn get_error_text(&self, error_code: i32) -> Result<String, EricError> {
184 let response_buffer = ResponseBuffer::new()?;
185
186 unsafe {
187 EricHoleFehlerText(error_code, response_buffer.as_ptr());
188 }
189
190 Ok(response_buffer.read()?.to_string())
191 }
192
193 #[allow(dead_code)]
194 fn decrypt(
195 &self,
196 encrypted_file: &str,
197 certificate_config: CertificateConfig,
198 ) -> Result<i32, EricError> {
199 let encrypted_data = encrypted_file.try_to_cstring()?;
200 let response_buffer = ResponseBuffer::new()?;
201
202 let error_code = unsafe {
203 EricDekodiereDaten(
204 certificate_config.certificate.handle,
205 certificate_config.password.as_ptr(),
206 encrypted_data.as_ptr(),
207 response_buffer.as_ptr(),
208 )
209 };
210
211 Ok(error_code)
212 }
213
214 fn process(
215 xml: String,
216 type_version: String,
217 processing_flag: ProcessingFlag,
218 print_config: Option<PrintConfig>,
219 certificate_config: Option<CertificateConfig>,
220 ) -> Result<EricResponse, EricError> {
221 debug!("Processing xml file");
222
223 match processing_flag {
224 ProcessingFlag::Validate => debug!("Validating xml file"),
225 ProcessingFlag::Print => debug!("Validating xml file"),
226 ProcessingFlag::Send => debug!("Sending xml file"),
227 ProcessingFlag::SendAndPrint => debug!("Send and print"),
228 ProcessingFlag::CheckHints => debug!("Check hints"),
229 ProcessingFlag::ValidateWithoutDate => debug!("Validate without release date"),
230 }
231
232 let xml = xml.try_to_cstring()?;
233 let type_version = type_version.try_to_cstring()?;
234
235 if let Some(print_config) = &print_config {
236 info!(
237 pdf_path = %print_config
238 .pdf_path
239 .to_str()
240 .context("failed to convert path to string")?,
241 "Printing confirmation to file"
242 )
243 }
244
245 let validation_response_buffer = ResponseBuffer::new()?;
246 let server_response_buffer = ResponseBuffer::new()?;
247
248 let error_code = unsafe {
249 EricBearbeiteVorgang(
250 xml.as_ptr(),
251 type_version.as_ptr(),
252 processing_flag.into_u32(),
253 match &print_config {
257 Some(el) => el.print_parameter.as_ptr(),
258 None => ptr::null(),
259 },
260 match &certificate_config {
264 Some(config) => config.certificate_parameter.as_ptr(),
265 None => ptr::null(),
266 },
267 validation_response_buffer.as_ptr(),
268 server_response_buffer.as_ptr(),
269 )
270 };
271
272 let validation_response = validation_response_buffer.read()?;
273 let server_response = server_response_buffer.read()?;
275 let payload =
276 EricApiPayload::new(validation_response.to_string(), server_response.to_string());
277
278 if error_code == ErrorCode::ERIC_OK as i32 {
279 Ok(EricResponse::new(payload))
280 } else {
281 let response_buffer = ResponseBuffer::new()?;
282
283 unsafe {
284 EricHoleFehlerText(error_code, response_buffer.as_ptr());
285 }
286
287 let error_text = response_buffer.read()?;
288
289 Err(EricError::ApiError {
290 code: error_code,
291 message: error_text.to_string(),
292 payload,
293 })
294 }
295 }
296}
297
298impl Drop for Eric {
299 fn drop(&mut self) {
300 info!("Closing eric");
301
302 let error_code = unsafe { EricEntladePlugins() };
303
304 if error_code != ErrorCode::ERIC_OK as i32 {
305 error!(error_code = %error_code, "Error while unloading plugins");
306 }
307
308 let error_code = unsafe { EricBeende() };
309
310 if error_code != ErrorCode::ERIC_OK as i32 {
311 error!(error_code = %error_code, "Can't close eric");
312 }
313 }
314}