1pub mod prt {
2 use std::{collections, fmt, path};
3 use std::ffi;
4 use std::fmt::{Display, Formatter};
5 use std::ptr;
6 use derive_builder::Builder;
7
8 #[derive(Debug)]
9 pub struct PrtError {
10 pub message: String,
11 pub status: Option<Status>,
12 }
13
14 pub struct PrtContext {
15 handle: *const prt_ffi::Object,
16 }
17
18 unsafe impl Sync for PrtContext {} impl Drop for PrtContext {
21 fn drop(&mut self) {
22 unsafe {
23 prt_ffi::Object::destroy(&*self.handle);
24 }
25 }
26 }
27
28 impl Display for PrtContext {
29 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
30 write!(f, "PrtContext native handle at {:p}", self.handle)
31 }
32 }
33
34 pub fn init(_extra_plugin_paths: Option<Vec<path::PathBuf>>,
35 initial_minimal_log_level: Option<LogLevel>) -> Result<Box<PrtContext>, PrtError>
36 {
37 let cesdk_lib_path = crate::helpers::get_cesdk_path().join("lib");
39 if !cesdk_lib_path.exists() {
40 return Err(PrtError {
41 message: format!("Error while loading built-in extensions: {}", get_status_description(Status::STATUS_FILE_NOT_FOUND)),
42 status: Some(Status::STATUS_FILE_NOT_FOUND),
43 });
44 }
45 let cesdk_lib_dir_wchar_vec = crate::helpers::from_string_to_wchar_vec(cesdk_lib_path.to_str().unwrap());
46
47 let plugins_dirs: [*const libc::wchar_t; 1] = [cesdk_lib_dir_wchar_vec.as_ptr()];
51 let log_level = initial_minimal_log_level.or(Some(LogLevel::LOG_WARNING));
52 unsafe {
53 let mut status = Status::STATUS_UNSPECIFIED_ERROR;
54 let prt_handle = prt_ffi::ffi_init(plugins_dirs.as_ptr(),
55 plugins_dirs.len(),
56 log_level.unwrap(),
57 ptr::addr_of_mut!(status));
58 return if (prt_handle != ptr::null()) && (status == Status::STATUS_OK) {
59 Ok(Box::new(PrtContext { handle: prt_handle }))
60 } else {
61 Err(PrtError {
63 message: get_status_description(Status::STATUS_UNSPECIFIED_ERROR),
64 status: Some(Status::STATUS_UNSPECIFIED_ERROR),
65 })
66 };
67 }
68 }
69
70 #[derive(PartialEq)]
71 #[derive(Debug)]
72 pub enum PrimitiveType {
73 Undefined(),
74 String(String),
75 Float(f64),
76 Bool(bool),
77 Int(i32),
78 StringArray(Vec<String>),
79 FloatArray(Vec<f64>),
80 BoolArray(Vec<bool>),
81 IntArray(Vec<i32>),
82 }
83
84 pub type EncoderOptions = collections::HashMap<String, PrimitiveType>;
85
86 #[derive(Clone, Debug)]
87 pub enum KeyOrUri {
88 Undefined,
89 Key(String),
90 Uri(String), }
92
93 impl Default for KeyOrUri {
94 fn default() -> Self { KeyOrUri::Undefined }
95 }
96
97 impl KeyOrUri {
98 fn ffi_to_cstring(&self) -> ffi::CString {
99 match self {
100 KeyOrUri::Undefined => panic!("Undefined ResolveMap key or Uri!"),
101 KeyOrUri::Key(k) => ffi::CString::new(k.as_str()).unwrap(),
102 KeyOrUri::Uri(u) => ffi::CString::new(u.as_str()).unwrap(),
103 }
104 }
105 }
106
107 impl fmt::Display for KeyOrUri {
108 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
109 match self {
110 KeyOrUri::Undefined => write!(f, "Undefined ResolveMap key or Uri!"),
111 KeyOrUri::Key(k) => write!(f, "{}", k),
112 KeyOrUri::Uri(u) => write!(f, "{}", u),
113 }
114 }
115 }
116
117 #[derive(Default, Builder, Debug)]
118 pub struct InitialShape {
119 vertex_coords: Vec<f64>,
120 indices: Vec<u32>,
121 face_counts: Vec<u32>,
122
123 rule_file: KeyOrUri,
124 start_rule: String,
125 random_seed: i32,
126 name: String,
127 }
128
129 pub trait Callbacks {}
130
131 #[derive(Default)]
132 pub struct FileCallbacks {}
133
134 impl Callbacks for FileCallbacks {}
135
136 pub fn generate<C>(initial_shapes: &Vec<Box<InitialShape>>,
137 encoders: &Vec<String>,
138 encoder_options: &Vec<EncoderOptions>,
139 callbacks: &mut Box<C>) -> Status where C: Callbacks
141 {
142 if encoders.len() != encoder_options.len() {
143 return Status::STATUS_ARGUMENTS_MISMATCH;
144 }
145
146 let mut initial_shape_adaptors: Vec<prt_ffi::InitialShapeAdaptor> = initial_shapes.iter()
149 .map(|x| prt_ffi::InitialShapeAdaptor::adapt(x))
150 .collect();
151
152 let initial_shape_wrappers: Vec<prt_ffi::InitialShapeWrapper> = initial_shape_adaptors.iter_mut()
153 .map(|x: &mut prt_ffi::InitialShapeAdaptor| x.get_ffi_wrapper())
154 .collect();
155
156 let initial_shape_wrapper_ptr_vec: Vec<*const prt_ffi::InitialShapeWrapper> = initial_shape_wrappers.iter()
157 .map(|x| &*x as *const prt_ffi::InitialShapeWrapper)
158 .collect();
159
160 let occlusion_handles: *const u64 = ptr::null();
161
162 let encoders_wchar_vec: Vec<Vec<libc::wchar_t>> = encoders.iter()
164 .map(|x| crate::helpers::from_string_to_wchar_vec(x.as_str()))
165 .collect();
166 let encoders_ptr_vec: Vec<*const libc::wchar_t> = encoders_wchar_vec.iter().map(|x| x.as_ptr()).collect();
167
168 let encoder_options_ptr_vec: *const prt_ffi::AttributeMap = ptr::null();
169
170 let callbacks_context: *mut C = callbacks.as_mut();
171 let callbacks_binding: Box<prt_ffi::AbstractCallbacksBinding<C>>
172 = Box::new(prt_ffi::AbstractCallbacksBinding { context: callbacks_context });
173 let callbacks_binding_ptr
174 = Box::into_raw(callbacks_binding) as *mut ffi::c_void;
175
176 let cache: *mut prt_ffi::Cache = ptr::null_mut();
177 let occl_set: *const prt_ffi::OcclusionSet = ptr::null();
178 let generate_options: *const prt_ffi::AttributeMap = ptr::null();
179
180 unsafe {
181 let status = prt_ffi::ffi_generate(initial_shape_wrapper_ptr_vec.as_ptr(),
182 initial_shape_wrapper_ptr_vec.len(),
183 occlusion_handles,
184 encoders_ptr_vec.as_ptr(), encoders_ptr_vec.len(),
185 encoder_options_ptr_vec,
186 callbacks_binding_ptr,
187 cache,
188 occl_set,
189 generate_options);
190
191 return status;
192 }
193 }
194
195 #[allow(non_camel_case_types)]
196 #[allow(dead_code)]
197 #[repr(C)]
198 pub enum LogLevel {
199 LOG_TRACE = 0,
200 LOG_DEBUG = 1,
201 LOG_INFO = 2,
202 LOG_WARNING = 3,
203 LOG_ERROR = 4,
204 LOG_FATAL = 5,
205 LOG_NO = 1000,
206 }
207
208 pub trait LogHandler {
209 fn handle_log_event(&mut self, msg: &str);
210 }
211
212 #[derive(Default)]
213 pub struct DefaultLogHandler {}
214
215 impl LogHandler for DefaultLogHandler {
216 fn handle_log_event(&mut self, msg: &str) {
217 println!("{}", msg);
218 }
219 }
220
221 pub fn add_log_handler<T>(log_handler: &mut Box<T>) where T: LogHandler {
222 unsafe extern "C" fn handle_log_event<T>(context: *mut T, cmsg: *const ffi::c_char)
223 where T: LogHandler
224 {
225 let handler_ref: &mut T = &mut *context;
226
227 let msg = ffi::CStr::from_ptr(cmsg).to_str().unwrap();
228 handler_ref.handle_log_event(msg);
229 }
230
231 let context: *mut T = log_handler.as_mut();
232 let binding: Box<prt_ffi::AbstractLogHandlerBinding<T>> = Box::new(prt_ffi::AbstractLogHandlerBinding {
233 handle_log_event,
234 context,
235 });
236
237 let binding_ptr: *mut ffi::c_void = Box::into_raw(binding) as *mut ffi::c_void;
238 unsafe {
239 prt_ffi::ffi_add_log_handler(binding_ptr);
240 }
241 }
242
243 pub fn remove_log_handler<T>(log_handler: &mut Box<T>) where T: LogHandler {
244 unsafe extern "C" fn handle_log_event<T>(_context: *mut T, _cmsg: *const ffi::c_char)
245 where T: LogHandler
246 {}
247
248 let context = log_handler.as_mut() as *mut T;
249 let binding: Box<prt_ffi::AbstractLogHandlerBinding<T>> = Box::new(prt_ffi::AbstractLogHandlerBinding {
250 handle_log_event,
251 context,
252 });
253
254 let binding_ptr: *mut ffi::c_void = Box::into_raw(binding) as *mut ffi::c_void;
255 unsafe {
256 prt_ffi::ffi_remove_log_handler(binding_ptr);
257 }
258 }
259
260 pub fn log(msg: &str, level: LogLevel) {
261 let cs_vec = crate::helpers::from_string_to_wchar_vec(msg);
262 unsafe {
263 prt_ffi::prt_log(cs_vec.as_ptr(), level);
264 }
265 }
266
267 #[allow(non_camel_case_types)]
268 #[allow(dead_code)]
269 #[derive(PartialEq)]
270 #[derive(Debug)]
271 #[repr(C)]
272 pub enum Status {
273 STATUS_OK,
274 STATUS_UNSPECIFIED_ERROR,
275 STATUS_OUT_OF_MEM,
276 STATUS_NO_LICENSE,
277
278 STATUS_NOT_ALL_IS_GENERATED,
279 STATUS_INCOMPATIBLE_IS,
280
281 STATUS_FILE_NOT_FOUND,
282 STATUS_FILE_ALREADY_EXISTS,
283 STATUS_COULD_NOT_OPEN_FILE,
284 STATUS_COULD_NOT_CLOSE_FILE,
285 STATUS_FILE_WRITE_FAILED,
286 STATUS_FILE_READ_FAILED,
287 STATUS_FILE_SEEK_FAILED,
288 STATUS_FILE_TELL_FAILED,
289 STATUS_NO_SEEK,
290 STATUS_EMPTY_FILE,
291 STATUS_INVALID_URI,
292
293 STATUS_STREAM_ADAPTOR_NOT_FOUND,
294 STATUS_RESOLVEMAP_PROVIDER_NOT_FOUND,
295 STATUS_DECODER_NOT_FOUND,
296 STATUS_ENCODER_NOT_FOUND,
297 STATUS_UNABLE_TO_RESOLVE,
298 STATUS_CHECK_ERROR_PARAM,
299 STATUS_KEY_NOT_FOUND,
300 STATUS_KEY_ALREADY_TAKEN,
301 STATUS_KEY_NOT_SUPPORTED,
302 STATUS_STRING_TRUNCATED,
303 STATUS_ILLEGAL_CALLBACK_OBJECT,
304 STATUS_ILLEGAL_LOG_HANDLER,
305 STATUS_ILLEGAL_LOG_LEVEL,
306 STATUS_ILLEGAL_VALUE,
307 STATUS_NO_RULEFILE,
308 STATUS_NO_INITIAL_SHAPE,
309 STATUS_CGB_ERROR,
310 STATUS_NOT_INITIALIZED,
311 STATUS_ALREADY_INITIALIZED,
312 STATUS_INCONSISTENT_TEXTURE_PARAMS,
313 STATUS_CANCELED,
314 STATUS_UNKNOWN_ATTRIBUTE,
315 STATUS_UNKNOWN_RULE,
316 STATUS_ARGUMENTS_MISMATCH,
317 STATUS_BUFFER_TO_SMALL,
318 STATUS_UNKNOWN_FORMAT,
319 STATUS_ENCODE_FAILED,
320 STATUS_ATTRIBUTES_ALREADY_SET,
321 STATUS_ATTRIBUTES_NOT_SET,
322 STATUS_GEOMETRY_ALREADY_SET,
323 STATUS_GEOMETRY_NOT_SET,
324 STATUS_ILLEGAL_GEOMETRY,
325 STATUS_NO_GEOMETRY,
326 }
327
328 pub fn get_status_description(status: Status) -> String {
329 unsafe {
330 let status_description_cchar_ptr = prt_ffi::ffi_get_status_description(status);
331 let status_description_cstr = ffi::CStr::from_ptr(status_description_cchar_ptr);
332 let status_description = status_description_cstr.to_str().unwrap_or_default();
333 return String::from(status_description);
334 }
335 }
336
337 pub struct PrtVersion {
338 pub version_major: i32,
339 pub version_minor: i32,
340 pub version_build: i32,
341 pub version_string: String,
342
343 pub name: String,
344 pub full_name: String,
345
346 pub build_config: String,
347 pub build_os: String,
348 pub build_arch: String,
349 pub build_tc: String,
350 pub build_date: String,
351
352 pub cga_version_major: i32,
353 pub cga_version_minor: i32,
354 pub cga_version_string: String,
355
356 pub cgac_version_major: i32,
357 pub cgac_version_minor: i32,
358 pub cgac_version_string: String,
359 }
360
361 pub fn get_version() -> Result<PrtVersion, PrtError> {
362 unsafe {
363 let version_ptr = prt_ffi::ffi_get_version();
364 if version_ptr == ptr::null() {
365 return Err(PrtError {
366 message: "Could not get PRT version info".to_string(),
367 status: None,
368 });
369 }
370 let version_ref = &*version_ptr;
371 let ver = PrtVersion {
372 version_major: version_ref.version_major,
373 version_minor: version_ref.version_minor,
374 version_build: version_ref.version_build,
375 version_string: crate::helpers::from_char_ptr_to_string(version_ref.version),
376 name: crate::helpers::from_char_ptr_to_string(version_ref.name),
377 full_name: crate::helpers::from_char_ptr_to_string(version_ref.full_name),
378 build_config: crate::helpers::from_char_ptr_to_string(version_ref.build_config),
379 build_os: crate::helpers::from_char_ptr_to_string(version_ref.build_os),
380 build_arch: crate::helpers::from_char_ptr_to_string(version_ref.build_arch),
381 build_tc: crate::helpers::from_char_ptr_to_string(version_ref.build_tc),
382 build_date: crate::helpers::from_char_ptr_to_string(version_ref.build_date),
383 cga_version_major: version_ref.cga_version_major,
384 cga_version_minor: version_ref.cga_version_minor,
385 cga_version_string: crate::helpers::from_char_ptr_to_string(version_ref.cga_version),
386 cgac_version_major: version_ref.cgac_version_major,
387 cgac_version_minor: version_ref.cgac_version_minor,
388 cgac_version_string: crate::helpers::from_char_ptr_to_string(version_ref.cgac_version),
389 };
390 Ok(ver)
391 }
392 }
393
394 mod prt_ffi {
395 use std::ffi;
396 use std::ptr::null;
397
398 #[repr(C)]
399 pub(crate) struct Object {
400 dummy: i8, }
402
403 impl Object {
404 pub(crate) fn destroy(&self) {}
405 }
406
407 extern "C" {
408 #[cfg(all(target_os = "linux", target_arch = "x86_64"))]
409 #[link_name = "\u{1}_ZN3prt4initEPKPKwmNS_8LogLevelEPNS_6StatusE"]
410 pub(crate) fn ffi_init(prt_plugins: *const *const libc::wchar_t,
411 prt_plugins_count: libc::size_t,
412 log_level: crate::prt::LogLevel,
413 status: *mut crate::prt::Status) -> *const Object;
414 }
415
416 #[repr(C)]
417 pub(crate) struct AttributeMap {
418 dummy: i32,
419 }
420
421 #[repr(C)]
422 struct ResolveMap {
423 dummy: i32,
424 }
425
426 #[repr(C)]
427 pub(crate) struct InitialShapeWrapper {
428 vertex_coords: *const f64,
430 vertex_coords_count: libc::size_t,
431 indices: *const u32,
432 indices_count: libc::size_t,
433 face_counts: *const u32,
434 face_counts_count: libc::size_t,
435
436 rule_file: *const ffi::c_char,
437 start_rule: *const ffi::c_char,
438 random_seed: i32,
439 name: *const ffi::c_char,
440 attributes: *const AttributeMap,
441 resolve_map: *const ResolveMap,
442 }
443
444 pub(crate) struct InitialShapeAdaptor<'a> {
445 initial_shape: &'a Box<crate::prt::InitialShape>,
446
447 ffi_rule_file_owner: ffi::CString,
448 ffi_start_rule_owner: ffi::CString,
449 ffi_name_owner: ffi::CString,
450 }
451
452 impl InitialShapeAdaptor<'_> {
453 pub(crate) fn adapt(initial_shape: &Box<crate::prt::InitialShape>) -> InitialShapeAdaptor {
454 InitialShapeAdaptor {
455 initial_shape,
456 ffi_rule_file_owner: initial_shape.rule_file.ffi_to_cstring(),
457 ffi_start_rule_owner: ffi::CString::new(initial_shape.start_rule.as_bytes()).unwrap(),
458 ffi_name_owner: ffi::CString::new(initial_shape.name.as_bytes()).unwrap(),
459 }
460 }
461
462 pub(crate) fn get_ffi_wrapper(&self) -> InitialShapeWrapper {
463 return InitialShapeWrapper {
464 vertex_coords: self.initial_shape.vertex_coords.as_ptr(),
465 vertex_coords_count: self.initial_shape.vertex_coords.len(),
466 indices: self.initial_shape.indices.as_ptr(),
467 indices_count: self.initial_shape.indices.len(),
468 face_counts: self.initial_shape.face_counts.as_ptr(),
469 face_counts_count: self.initial_shape.face_counts.len(),
470 rule_file: self.ffi_rule_file_owner.as_ptr(),
471 start_rule: self.ffi_start_rule_owner.as_ptr(),
472 random_seed: self.initial_shape.random_seed,
473 name: self.ffi_name_owner.as_ptr(),
474 attributes: null(), resolve_map: null(), };
477 }
478 }
479
480 #[repr(C)]
481 pub(crate) struct Cache {
482 dummy: i32,
483 }
484
485 #[repr(C)]
486 pub(crate) struct OcclusionSet {
487 dummy: i32,
488 }
489
490 #[repr(C)]
491 pub(crate) struct AbstractCallbacksBinding<T> where T: crate::prt::Callbacks {
492 pub(crate) context: *mut T,
493 }
494
495 #[link(name = "bindings", kind = "static")]
496 extern "C" {
497 pub(crate) fn ffi_generate(initial_shapes: *const *const InitialShapeWrapper,
498 initial_shapes_count: libc::size_t,
499 occlusion_handles: *const u64, encoders: *const *const libc::wchar_t,
501 encoders_count: libc::size_t,
502 encoder_options: *const AttributeMap,
503 callbacks: *mut ffi::c_void,
504 cache: *mut Cache,
505 occl_set: *const OcclusionSet,
506 generate_options: *const AttributeMap) -> crate::prt::Status;
507 }
508
509 #[repr(C)]
510 pub(crate) struct AbstractLogHandlerBinding<T> where T: crate::prt::LogHandler {
511 pub(crate) handle_log_event: unsafe extern fn(*mut T, msg: *const ffi::c_char),
512 pub(crate) context: *mut T,
513 }
514
515 #[link(name = "bindings", kind = "static")]
516 extern "C" {
517 pub(crate) fn ffi_add_log_handler(log_handler: *mut ffi::c_void);
518 pub(crate) fn ffi_remove_log_handler(log_handler: *mut ffi::c_void);
519 }
520
521 extern "C" {
522 #[cfg(all(target_os = "linux", target_arch = "x86_64"))]
523 #[link_name = "\u{1}_ZN3prt3logEPKwNS_8LogLevelE"]
524 pub(crate) fn prt_log(msg: *const libc::wchar_t, level: crate::prt::LogLevel);
525 }
526
527 #[repr(C)]
528 pub(crate) struct Version {
529 pub(crate) version_major: i32,
530 pub(crate) version_minor: i32,
531 pub(crate) version_build: i32,
532
533 pub(crate) name: *const ffi::c_char,
534 pub(crate) full_name: *const ffi::c_char,
535 pub(crate) version: *const ffi::c_char,
536 pub(crate) build_config: *const ffi::c_char,
537 pub(crate) build_os: *const ffi::c_char,
538 pub(crate) build_arch: *const ffi::c_char,
539 pub(crate) build_tc: *const ffi::c_char,
540 pub(crate) build_date: *const ffi::c_char,
541
542 pub(crate) name_w: *const libc::wchar_t,
543 full_name_w: *const libc::wchar_t,
544 version_w: *const libc::wchar_t,
545 build_config_w: *const libc::wchar_t,
546 build_os_w: *const libc::wchar_t,
547 build_arch_w: *const libc::wchar_t,
548 build_tc_w: *const libc::wchar_t,
549 pub(crate) build_date_w: *const libc::wchar_t,
550
551 pub(crate) cga_version_major: i32,
552 pub(crate) cga_version_minor: i32,
553 pub(crate) cga_version: *const ffi::c_char,
554 cga_version_w: *const i32,
555
556 pub(crate) cgac_version_major: i32,
557 pub(crate) cgac_version_minor: i32,
558 pub(crate) cgac_version: *const ffi::c_char,
559 cgac_version_w: *const i32,
560 }
561
562 extern "C" {
563 #[cfg(all(target_os = "linux", target_arch = "x86_64"))]
564 #[link_name = "\u{1}_ZN3prt10getVersionEv"]
565 pub(crate) fn ffi_get_version() -> *const Version;
566 }
567
568 extern "C" {
569 #[cfg(all(target_os = "linux", target_arch = "x86_64"))]
570 #[link_name = "\u{1}_ZN3prt20getStatusDescriptionENS_6StatusE"]
571 pub(crate) fn ffi_get_status_description(input: crate::prt::Status) -> *const ffi::c_char;
572 }
573 }
574
575 #[cfg(test)]
576 mod tests {
577 use super::*;
578
579 fn assert_cstring(prefix: &str, raw_val: *const ffi::c_char, expected_val: &str) {
580 let val = crate::helpers::from_char_ptr_to_string(raw_val);
581 assert_string(prefix, val, expected_val);
582 }
583
584 fn assert_wcstring(prefix: &str, raw_val: *const libc::wchar_t, expected_val: &str) {
585 let val = crate::helpers::from_wchar_ptr_to_string(raw_val);
586 assert_string(prefix, val, expected_val);
587 }
588
589 fn assert_string(prefix: &str, raw_val: String, expected_val: &str) {
590 assert_eq!(raw_val, expected_val, "{}: assertion error", prefix);
591 }
592
593 fn assert_int(prefix: &str, raw_val: i32, expected_val: i32) {
594 assert_eq!(raw_val, expected_val, "{}: assertion error", prefix);
595 }
596
597 #[test]
598 fn prt_get_version() {
599 unsafe {
600 let ver = &*prt_ffi::ffi_get_version();
601 assert_cstring("prt::Version::mName", ver.name, "ArcGIS Procedural Runtime");
602 assert_cstring("prt::Version::mVersion", ver.version, "2.7.8538");
603 assert_cstring("prt::Version::mBuildConfig", ver.build_config, "PRT_BC_REL");
604 assert_cstring("prt::Version::mBuildOS", ver.build_os, "linux");
605 assert_cstring("prt::Version::mBuildArch", ver.build_arch, "x86_64");
606 assert_cstring("prt::Version::mBuildTC", ver.build_tc, "PRT_TC_GCC93");
607 assert_cstring("prt::Version::mBuildDate", ver.build_date, "2022-10-04 15:48");
608
609 assert_wcstring("prt::Version::mwName", ver.name_w, "ArcGIS Procedural Runtime");
610 assert_wcstring("prt::Version::mwBuildDate", ver.build_date_w, "2022-10-04 15:48");
611
612 assert_int("prt::Version::mCGAVersionMajor", ver.cga_version_major, 2022);
613 assert_int("prt::Version::mCGAVersionMinor", ver.cga_version_minor, 1);
614
615 assert_int("prt::Version::mCGACVersionMajor", ver.cgac_version_major, 1);
616 assert_int("prt::Version::mCGACVersionMinor", ver.cgac_version_minor, 19);
617 }
618 }
619
620 #[test]
621 fn prt_get_status_description() {
622 unsafe {
623 let status_description_cchar_ptr = prt_ffi::ffi_get_status_description(Status::STATUS_OUT_OF_MEM);
624 let status_description = crate::helpers::from_char_ptr_to_string(status_description_cchar_ptr);
625 assert_eq!(status_description, "Out of memory.");
626 }
627 }
628
629 #[test]
630 fn create_attribute_map() {
631 let mut map = collections::HashMap::new();
632 map.insert("foo".to_string(), PrimitiveType::String("bar".to_string()));
633 assert_eq!(map.get("foo"), Some(&PrimitiveType::String("bar".to_string())));
634 }
635 }
636}
637
638mod helpers {
639 use std::{ffi, fs, path};
640
641 pub(crate) fn from_char_ptr_to_string(cchar_ptr: *const ffi::c_char) -> String {
642 let val_cstr = unsafe { ffi::CStr::from_ptr(cchar_ptr) };
643 return val_cstr.to_str().unwrap_or_default().to_string();
644 }
645
646 #[allow(dead_code)]
647 #[cfg(target_os = "linux")]
648 fn wchar_is_utf32() -> bool {
649 true
650 }
651
652 #[allow(dead_code)]
653 #[cfg(target_os = "windows")]
654 fn wchar_is_utf32() -> bool {
655 false
656 }
657
658 #[allow(dead_code)]
659 pub fn from_wchar_ptr_to_string(ptr: *const libc::wchar_t) -> String {
660 assert!(!ptr.is_null());
661 let ptr_len = unsafe { libc::wcslen(ptr) };
662 assert!(ptr_len > 0);
663 return if wchar_is_utf32() {
664 let widestring_result = unsafe { widestring::U32CString::from_ptr(ptr as *const u32, ptr_len) };
665 let cstring = widestring_result.expect("could not convert wchar_t array to UTF32 string");
666 cstring.to_string().expect("could not convert to Rust string")
667 } else {
668 let widestring_result = unsafe { widestring::U16CString::from_ptr(ptr as *const u16, ptr_len) };
669 let cstring = widestring_result.expect("could not convert wchar_t array to UTF16 string");
670 cstring.to_string().expect("could not convert to Rust string")
671 };
672 }
673
674 pub fn from_string_to_wchar_vec(msg: &str) -> Vec<libc::wchar_t> {
675 return if cfg!(linux) {
676 let wide_msg = widestring::U32CString::from_str(msg)
677 .expect("cannot convert to UTF-32/wchar_t");
678 wide_msg.into_vec_with_nul().iter().map(|&e| e as libc::wchar_t).collect()
679 } else {
680 let wide_msg = widestring::U16CString::from_str(msg)
681 .expect("cannot convert to UTF-16/wchar_t");
682 wide_msg.into_vec_with_nul().iter().map(|&e| e as libc::wchar_t).collect()
683 };
684 }
685
686 pub fn get_cesdk_path() -> path::PathBuf {
688 let out_dir = env!("OUT_DIR");
689 let crate_root = path::PathBuf::from(out_dir);
690 let deps_path = crate_root.join("prust_custom_deps");
691 let cesdk_path_entry = fs::read_dir(deps_path)
692 .expect("cannot read deps dir")
693 .filter(|p| p.is_ok() && p.as_ref().unwrap().file_type().unwrap().is_dir())
694 .find(|p| p.as_ref().unwrap()
695 .path().file_name().unwrap()
696 .to_str().unwrap().starts_with("esri_ce_sdk-"));
697 return cesdk_path_entry.expect("could not find cesdk lib dir").unwrap().path();
698 }
699}