cobia/lib.rs
1//! # Crate cobia - RUST Language binding for CO-LaN's COBIA middleware.
2//!
3//! CAPE-OPEN consists of a series of specifications to expand the range of application of
4//! process simulation technologies. The CAPE-OPEN specifications specify a set of software interfaces
5//! that allow plug and play inter-operability between a given process modelling environment (PME)
6//! and a third-party process modelling component (PMC).
7//!
8//! The CAPE-OPEN specifications are supported by the non-profit organization CO-LaN:
9//! [http://www.colan.org](http://www.colan.org)
10//!
11//! The COBIA middle-ware is a platform independent object model and request broker implementation
12//! to facilitate the inter-operation of CAPE-OPEN compliant PMEs and PMCs.
13//!
14//! This create provides the following:
15//! - Safe Rust wrappers around the raw C FFI bindings to COBIA's API routines
16//! - Rust based implementation of the CAPE-OPEN data types
17//! - Rust based wrappers around externally implemented CAPE-OPEN data types
18//! - A Rust code generator utility that utilizes COBIA's public type API to generate Rust bindings
19//! - Rust traits representing COBIA interfaces
20//! - Rust native implementations of COBIA based interfaces using objects that provide the Rust traits
21//! - Smart pointers to COBIA objects
22//! - Rust definitions for externall implemented COBIA objects
23//! - Macros to make implementing CAPE-OPEN components in Rust
24//!
25//! # Prerequisites
26//!
27//! To compile against this create, the following prerequisites must be installed:
28//! - The COBIA SDK (available for download from [https://colan.repositoryhosting.com/trac/colan_cobia/downloads](https://colan.repositoryhosting.com/trac/colan_cobia/downloads)
29//! - The required ingredients for bindgen, including a valid CLANG installation, see [https://rust-lang.github.io/rust-bindgen/requirements.html](https://rust-lang.github.io/rust-bindgen/requirements.html)
30//!
31//! This package uses bindgen to generate Rust access to the C-API of COBIA. As with most Rust implementations that
32//! refer to C/C++ code, all external access is unsafe, and marked as such in the cobia crate itself. Typically one
33//! does not need to use the unsafe keyword in implementations that use cobia, with a few exceptions.
34//!
35//! # Interface implementation considerations
36//!
37//! COBIA is a middle-ware that allows objects implemented in different languages to inter-operate. They
38//! do so through the use of reference counted interfaces, much like other middle-wares such as COM or CORBA.
39//!
40//! To allow the Rust compiler to check the life time and validity of pointers, interface pointers are passed by
41//! reference. A Rust object that implements one or more COBIA interface is allocated on the heap by boxing it
42//! as part of its creation. Once the object is boxed, its vTable pointers are initialized to point to the correct
43//! memory location. The boxed object is then converted to a raw pointer; this raw pointer is the interface pointer's
44//! 'me' member, and is used by the raw interface methods to access the Rust object. The raw interface methods
45//! require Rust traits to be implemented on the Rust object, to provide the actual functionality.
46//!
47//! Data that requires heap allocations (strings, arrays, ...) are handled a little differently. They too have a
48//! vTable, but at the design of this crate it was decided not to box the data objects, as the heap allocation
49//! for the object itself may well not be needed and may hamper performance.
50//!
51//! As a result, it is quite possible, and even likely, for data object implementations to be moved around after
52//! its creation. When passed to a foreign function, the vTable for the data object is created on the fly, pointing
53//! to where the data object is currently located. The reference to the vTable requires a reference to the data object
54//! implementation, so Rust's life span checking is used to ensure that the data object implementation remains valid
55//! during the life span of the vTable, and the vTable remains valid for the duration of the external call.
56//!
57//! To further leverage Rust's safety features, data objects have In and Out versions. The In version cannot be modified
58//! by the receiver, whereas the intent of the Out version is exactly to be modified. Functions that require an In data
59//! type, can do so by a reference to it. Function that require an Out data type on the other hand, require a mutable reference;
60//! this ensures that an Out data object is not modified unexpectedly.
61//!
62//! # Rust code generation
63//!
64//! To generate Rust bindings for interfaces described by CAPE-OPEN IDL (either in a .cidl file or a library in the
65//! COBIA registry), the cidl.exe tool can be used. This tool is part of the current crate. This tool is used during
66//! the build process to generate the bindings for the default cape_open and cape_open_1_2 modules, containing
67//! all known CAPE-OPEN interfaces in the installed COBIA SDK.
68//!
69//! The cidl tool can also be used to generate bindings for custom CAPE-OPEN interfaces; this functionality is currently
70//! untested, arising a business case.
71//!
72//! # CAPE-OPEN object implementations
73//!
74//! To implement a CAPE-OPEN object in rust, one can use the `cape_object_implementation` macro. This macro takes as
75//! arguments the interfaces that are implemented by the object, as well as some optional constructor details.
76//!
77//! To implement the entry points for a COBIA dynamic link library, one can use the `pmc_entry_points` macro.
78//!
79//! The reader is directed to the Salt Water Property Package example and the Distillation Shortcut Unit Operation
80//! examples in the repository for further details.
81//!
82//! Extenally implemented COBIA objects are accessed through smart pointers; an interface specific smart pointer is
83//! generated for each known COBIA interface, along with the Rust methods that represent the interface methods.
84//!
85//! # Acknowledgements
86//!
87//! This package uses process_path, to get the path of the currently executing process or dynamic library, (C) Copyright Wesley Wiser and process_path contributors
88//!
89//! This package uses bindgen, to automatically generates Rust FFI bindings to C and C++ libraries, (C) Jyun-Yan You
90
91//todo: build bindings for custom interfaces using cidl.exe and update above documentation
92//todo: IDL interfaces
93//todo: Marshal interfaces (for a C++ client these are not directly exposed, as they are used via implementation classes that provide this functionality)
94//todo: CapeResult cobiaDepersistFromTransitionFormat(ICapeInterface *reader,ICapeInterface **transitionFormat,CapeInteger majorVersion,CapeInteger minorVersion);
95//todo: CapeResult cobiaDepersistPMCFromTransitionFormat(ICapeInterface *PMC, ICapeInterface *reader,CapeInteger majorVersion,CapeInteger minorVersion);
96
97//To build documentation:
98// cargo doc --no-deps --package cobia --open
99
100#![allow(non_upper_case_globals)]
101#![allow(non_camel_case_types)]
102#![allow(non_snake_case)]
103#![allow(unused_imports)]
104#![allow(dead_code)]
105pub mod C;
106
107mod cobia_error;
108pub use cobia_error::COBIAError;
109mod cape_data_traits;
110pub mod prelude;
111pub use cape_data_traits::*;
112mod cape_data_from_provider;
113pub use cape_data_from_provider::*;
114mod cape_array_object_vec;
115#[cfg(doc)] pub use cape_array_object_vec::CapeArrayObjectVec;
116pub use cape_array_object_vec::{CapeArrayStringVec,CapeArrayValueVec};
117mod cobia_collection;
118pub use cobia_collection::CobiaCollection;
119mod cobia_identification;
120pub use cobia_identification::CobiaIdentification;
121pub use C::CapeBoolean;
122pub use C::CapeReal;
123pub use C::CapeInteger;
124pub use C::CapeResult;
125pub use C::CapeCharacter;
126pub use C::CapeByte;
127pub use C::CapeEnumeration;
128#[cfg_attr(not(target_os="windows"), path = "cape_string_posix.rs")]
129#[cfg_attr(target_os="windows", path = "cape_string_win32.rs")]
130mod cape_string;
131pub use cape_string::{CapeStringIn,CapeStringOut};
132mod cape_string_impl;
133pub use cape_string_impl::CapeStringImpl;
134mod cape_string_const;
135pub use cape_string_const::CapeStringConstNoCase;
136pub use cape_string_const::CapeStringHashKey;
137mod cape_string_map;
138pub use cape_string_map::CapeOpenMap;
139mod cape_array_vec;
140#[cfg(doc)] pub use cape_array_vec::CapeArrayVec;
141pub use cape_array_vec::{CapeArrayRealVec,CapeArrayIntegerVec,CapeArrayByteVec,CapeArrayBooleanVec,CapeArrayEnumerationVec};
142mod cape_array_slice;
143#[cfg(doc)] pub use cape_array_slice::CapeArraySlice;
144pub use cape_array_slice::{CapeArrayRealSlice,CapeArrayIntegerSlice,CapeArrayByteSlice,CapeArrayBooleanSlice,CapeArrayEnumerationSlice};
145mod cape_array_real_scalar;
146pub use cape_array_real_scalar::CapeArrayRealScalar;
147pub mod cape_value_impl;
148pub use cape_value_impl::{CapeValueImpl,CapeValueContent};
149mod cape_value;
150pub use cape_value::{CapeValueIn,CapeValueOut};
151mod cape_array;
152#[cfg(doc)] pub use cape_array::{CapeArrayIn,CapeArrayOut};
153mod cape_array_real;
154pub use cape_array_real::{CapeArrayRealIn,CapeArrayRealOut};
155mod cape_array_integer;
156pub use cape_array_integer::{CapeArrayIntegerIn,CapeArrayIntegerOut};
157mod cape_array_byte;
158pub use cape_array_byte::{CapeArrayByteIn,CapeArrayByteOut};
159mod cape_array_boolean;
160pub use cape_array_boolean::{CapeArrayBooleanIn,CapeArrayBooleanOut};
161mod cape_array_enumeration;
162pub use cape_array_enumeration::{CapeArrayEnumerationIn,CapeArrayEnumerationOut};
163mod cape_array_string;
164pub use cape_array_string::{CapeArrayStringIn,CapeArrayStringOut};
165mod cape_array_value;
166pub use cape_array_value::{CapeArrayValueIn,CapeArrayValueOut};
167mod cape_object;
168pub use cape_object::CapeObject;
169mod cape_error;
170pub use cape_error::CapeError;
171mod cape_error_impl;
172pub use cape_error_impl::CapeErrorImpl;
173mod cape_result_value;
174mod cobia_enums;
175pub use cobia_enums::*;
176mod cape_registry_key;
177pub use cape_registry_key::*;
178mod cape_registry_key_writer;
179pub use cape_registry_key_writer::CapeRegistryKeyWriter;
180mod cape_registrar;
181pub use cape_registrar::CapeRegistrar;
182mod cape_registry_writer;
183pub use cape_registry_writer::CapeRegistryWriter;
184mod cape_pmc_registration_details;
185pub use cape_pmc_registration_details::CapePMCRegistrationDetails;
186mod cape_pmc_enumerator;
187pub use cape_pmc_enumerator::CapePMCEnumerator;
188mod cape_type_library_details;
189pub use cape_type_library_details::CapeLibraryDetails;
190mod cape_type_library_enumerator;
191pub use cape_type_library_enumerator::CapeTypeLibraries;
192mod cobia_pmc_helpers;
193pub use cobia_pmc_helpers::*;
194mod cape_object_impl;
195pub use cape_object_impl::*;
196mod cape_smart_pointer;
197#[cfg_attr(not(target_os="windows"), path = "cape_window_id_posix.rs")]
198#[cfg_attr(target_os="windows", path = "cape_window_id_win32.rs")]
199mod cape_window_id;
200pub use cape_window_id::{CapeWindowId,CapeWindowIdToRaw,CapeWindowIdFromRaw};
201
202pub mod cape_open; //types common to all CAPE-OPEN versions
203pub mod cape_open_1_2; //types specific to CAPE-OPEN 1.2
204
205use core::hash::Hash;
206use std::error;
207use std::fmt;
208use std::hash::Hasher;
209use std::path::PathBuf;
210
211//macros
212pub use cobia_macro::*;
213
214pub use cape_result_value::*;
215
216// //! # Rust COBIA binding
217// //!
218// //! 'cobia' is a Rust binding for the COBIA library.
219// //!
220
221/// #COBIA initialization routine
222///
223/// Must be called prior to calling any COBIA routine.
224///
225/// # Examples
226///
227/// ```
228/// use cobia;
229/// cobia::cape_open_initialize().unwrap();
230/// ```
231
232#[must_use]
233pub fn cape_open_initialize() -> Result<(), COBIAError> {
234 let mut s = CapeStringImpl::new();
235 unsafe {
236 if !C::capeInitialize((&s.as_cape_string_out() as *const C::ICapeString).cast_mut()) {
237 Err(COBIAError::Message(s.as_string()))
238 } else {
239 Ok(())
240 }
241 }
242}
243
244/// #COBIA clean-up routine
245///
246/// Deallocates COBIA resources. Should be called when COBIA is no longer needed.
247/// No COBIA routines should be called after this routine is called.
248///
249/// # Examples
250///
251/// ```
252/// use cobia;
253/// cobia::cape_open_initialize().unwrap();
254/// //use COBIA and do some stuff
255/// cobia::cape_open_cleanup()
256/// ```
257
258pub fn cape_open_cleanup() {
259 unsafe { C::capeCleanup() }
260}
261
262/// #get COBIA version
263///
264/// Returns the COBIA version
265///
266/// # Examples
267///
268/// ```
269/// use cobia;
270/// cobia::cape_open_initialize().unwrap();
271/// println!("Cobia version: {}",cobia::get_cobia_version());
272/// ```
273
274pub fn get_cobia_version() -> String {
275 let mut s = CapeStringImpl::new();
276 unsafe {
277 C::capeGetCobiaVersion((&s.as_cape_string_out() as *const C::ICapeString).cast_mut());
278 }
279 s.as_string()
280}
281
282/// #get COBIA language
283///
284/// Returns the COBIA language
285///
286/// # Examples
287///
288/// ```
289/// use cobia;
290/// cobia::cape_open_initialize().unwrap();
291/// println!("Cobia language: {}",cobia::get_cobia_language());
292/// ```
293
294pub fn get_cobia_language() -> String {
295 let mut s = CapeStringImpl::new();
296 unsafe {
297 C::capeGetCobiaLanguage((&s.as_cape_string_out() as *const C::ICapeString).cast_mut());
298 }
299 s.as_string()
300}
301
302/// Wrapper class around native CapeUUID
303pub use C::CapeUUID;
304
305impl CapeUUID {
306 /// #Create a new CapeUUID
307 ///
308 /// Creates a new CapeUUID, and generates unique content
309 ///
310 /// # Examples
311 ///
312 /// ```
313 /// use cobia;
314 /// cobia::cape_open_initialize().unwrap();
315 /// let uuid=cobia::CapeUUID::new();
316 /// ```
317 pub fn new() -> Self {
318 unsafe {
319 C::capeGenerateUUID()
320 }
321 }
322
323 /// #Create a null CapeUUID
324 ///
325 /// Creates a null CapeUUID
326 ///
327 /// # Examples
328 ///
329 /// ```
330 /// use cobia;
331 /// cobia::cape_open_initialize().unwrap();
332 /// let uuid=cobia::CapeUUID::null();
333 /// let uuid_1=cobia::CapeUUID::from_string("{00000000-0000-0000-0000-000000000000}").unwrap();
334 /// assert_eq!(uuid_1,uuid);
335 /// ```
336 pub const fn null() -> Self {
337 CapeUUID { data: [0; 16] }
338 }
339
340 /// #Create a CapeUUID from character slice
341 ///
342 /// Creates a new CapeUUID from a character slice
343 ///
344 /// # Arguments
345 ///
346 /// * `slice` - A character slice to be converted to a CapeUUID
347 ///
348 /// # Examples
349 ///
350 /// ```
351 /// use cobia;
352 /// cobia::cape_open_initialize().unwrap();
353 /// let uuid=cobia::CapeUUID::from_slice(&[0x12u8,0x34,0x56,0x78,0x9a,0xbc,0xde,0xf0,0x12,0x34,0x56,0x78,0x90,0xab,0xcd,0xef]);
354 /// let uuid_1=cobia::CapeUUID::from_string("{12345678-9abc-def0-1234-567890abcdef}").unwrap();
355 /// assert_eq!(uuid_1,uuid);
356 /// ```
357 pub const fn from_slice(slice: &[u8; 16]) -> Self {
358 Self {data: *slice}
359 }
360
361 /// #Create a new CapeUUID from a string
362 ///
363 /// Creates a new CapeUUID from a string
364 ///
365 /// # Arguments
366 ///
367 /// * `s` - A string slice to be converted to a CapeUUID
368 ///
369 /// # Examples
370 ///
371 /// ```
372 /// use cobia;
373 /// cobia::cape_open_initialize().unwrap();
374 /// let uuid_1=cobia::CapeUUID::from_string("{12345678-9abc-def0-1234-567890abcdef}").unwrap();
375 /// let uuid_2=cobia::CapeUUID::from_string("{12345678-9ABC-DEF0-1234-567890ABCDEF}").unwrap();
376 /// assert_eq!(uuid_1,uuid_2);
377 /// ```
378 pub fn from_string(s: &str) -> Result<Self, COBIAError> {
379 let mut uuid = CapeUUID::null();
380 let str_uuid = CapeStringImpl::from_string(s);
381 let res = unsafe { C::capeUUIDFromString(str_uuid.as_capechar_const(), &mut uuid) };
382 if res == COBIAERR_NOERROR {
383 Ok(uuid)
384 } else {
385 Err(COBIAError::Code(res))
386 }
387 }
388
389 /// #Create a string from a CapeUUID
390 ///
391 /// Creates a string from a CapeUUID
392 ///
393 /// # Examples
394 ///
395 /// ```
396 /// use cobia;
397 /// cobia::cape_open_initialize().unwrap();
398 /// let uuid=cobia::CapeUUID::from_string("{12345678-9ABC-DEF0-1234-567890ABCDEF}").unwrap();
399 /// let s=uuid.as_string();
400 /// assert_eq!(&s,"{12345678-9abc-def0-1234-567890abcdef}");
401 /// ```
402 pub fn as_string(&self) -> String {
403 let mut s = CapeStringImpl::new();
404 unsafe {
405 C::capeStringFromUUID(self as *const CapeUUID, (&s.as_cape_string_out() as *const C::ICapeString).cast_mut());
406 }
407 s.as_string()
408 }
409
410 /// #Compare two CapeUUIDs
411 ///
412 /// Compares two CapeUUIDs
413 ///
414 /// # Arguments
415 ///
416 /// * `other` - The other CapeUUID to compare to
417 ///
418 /// # Returns
419 ///
420 /// * -1 if self is less than other
421 /// * 0 if self is equal to other
422 /// * 1 if self is greater than other
423 ///
424 /// # Examples
425 ///
426 /// ```
427 /// use cobia;
428 /// cobia::cape_open_initialize().unwrap();
429 /// let uuid_1=cobia::CapeUUID::from_string("{12345678-9abc-def0-1234-567890abcdee}").unwrap();
430 /// let uuid_2=cobia::CapeUUID::from_string("{12345678-9abc-def0-1234-567890abcdef}").unwrap();
431 /// assert_eq!(uuid_1.compare(&uuid_2),-1);
432 /// assert_eq!(uuid_2.compare(&uuid_1),1);
433 /// assert_eq!(uuid_2.compare(&uuid_2),0);
434 /// ```
435 pub fn compare(&self, other: &Self) -> i32 {
436 unsafe {
437 C::capeUUID_Compare(
438 self as *const CapeUUID,
439 other as *const CapeUUID,
440 )
441 }
442 }
443}
444
445impl From<&[u8; 16]> for CapeUUID {
446 fn from(slice: &[u8; 16]) -> Self {
447 CapeUUID::from_slice(slice)
448 }
449}
450
451impl From<&str> for CapeUUID {
452 fn from(s: &str) -> Self {
453 CapeUUID::from_string(s).unwrap()
454 }
455}
456
457impl PartialEq for CapeUUID {
458 /// #Compare two CapeUUIDs
459 ///
460 /// Compares two CapeUUIDs
461 ///
462 /// # Arguments
463 ///
464 /// * `other` - The other CapeUUID to compare to
465 ///
466 /// # Returns
467 ///
468 /// * true if self is equal to other
469 /// * false if self is not equal to other
470 ///
471 /// # Examples
472 ///
473 /// ```
474 /// use cobia;
475 /// cobia::cape_open_initialize().unwrap();
476 /// let uuid_1=cobia::CapeUUID::from_string("{12345678-9abc-def0-1234-567890abcdef}").unwrap();
477 /// let uuid_2=cobia::CapeUUID::from_string("{12345678-9abc-def0-1234-567890abcdef}").unwrap();
478 /// assert_eq!(uuid_1,uuid_2);
479 /// ```
480 ///
481 /// ```
482 /// use cobia;
483 /// cobia::cape_open_initialize().unwrap();
484 /// let uuid_1=cobia::CapeUUID::from_string("{12345678-9abc-def0-1234-567890abcdef}").unwrap();
485 /// let uuid_2=cobia::CapeUUID::from_string("{87654321-9abc-def0-1234-567890abcdef}").unwrap();
486 /// assert!(uuid_1!=uuid_2);
487 /// ```
488
489 fn eq(&self, other: &Self) -> bool {
490 unsafe {
491 C::capeUUID_Equal(
492 self as *const CapeUUID,
493 other as *const CapeUUID,
494 )
495 }
496 }
497}
498
499impl Eq for CapeUUID {}
500
501impl Hash for CapeUUID {
502 fn hash<H: Hasher>(&self, state: &mut H) {
503 state.write(&self.data);
504 }
505}
506
507impl std::fmt::Display for CapeUUID {
508 /// Formats the CapeUUID using the given formatter.
509 ///
510 /// # Examples
511 ///
512 /// ```
513 /// use cobia;
514 /// cobia::cape_open_initialize().unwrap();
515 /// let uuid=cobia::CapeUUID::from_string("{12345678-9abc-def0-1234-567890abcdef}").unwrap();
516 /// assert_eq!(format!("{}",uuid),"{12345678-9abc-def0-1234-567890abcdef}");
517 /// ```
518
519 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
520 write!(f, "{}", self.as_string())
521 }
522}
523
524/// #get COBIA installation folder
525///
526/// Returns the COBIA installation folder of the currently loaded COBIA
527///
528/// # Examples
529///
530/// ```
531/// use cobia;
532/// cobia::cape_open_initialize().unwrap();
533/// println!("Cobia installation folder: {}",cobia::get_cobia_folder().to_str().unwrap());
534/// ```
535pub fn get_cobia_folder() -> PathBuf {
536 let mut s = CapeStringImpl::new();
537 unsafe {
538 C::capeGetCOBIAFolder((&s.as_cape_string_out() as *const C::ICapeString).cast_mut());
539 }
540 PathBuf::from(s.as_string())
541}
542
543/// #get COBIA user data folder
544///
545/// Returns the folder where COBIA per-user data is located (e.g. registry)
546///
547/// # Examples
548///
549/// ```
550/// use cobia;
551/// cobia::cape_open_initialize().unwrap();
552/// println!("Cobia user data folder: {}",cobia::get_cobia_user_data_folder().to_str().unwrap());
553/// ```
554pub fn get_cobia_user_data_folder() -> PathBuf {
555 let mut s = CapeStringImpl::new();
556 unsafe {
557 C::capeGetCOBIAUserDataFolder((&s.as_cape_string_out() as *const C::ICapeString).cast_mut());
558 }
559 PathBuf::from(s.as_string())
560}
561
562/// #get COBIA system data folder
563///
564/// Returns the folder where COBIA all-users data is located (e.g. registry)
565///
566/// For a per-user installation, this is the same folder as the user data folder.
567///
568/// # Examples
569///
570/// ```
571/// use cobia;
572/// cobia::cape_open_initialize().unwrap();
573/// println!("Cobia system data folder: {}",cobia::get_cobia_system_data_folder());
574/// ```
575pub fn get_cobia_system_data_folder() -> String {
576 let mut s = CapeStringImpl::new();
577 unsafe {
578 C::capeGetCOBIAUserDataFolder((&s.as_cape_string_out() as *const C::ICapeString).cast_mut());
579 }
580 s.as_string()
581}
582
583/// Service function to get in-process service type for current bitness
584#[cfg(target_pointer_width = "64")]
585pub fn inproc_service_type() -> CapePMCServiceType {
586 CapePMCServiceType::Inproc64
587}
588
589/// Service function to get in-process service type for current bitness
590#[cfg(target_pointer_width = "32")]
591pub fn inproc_service_type() -> CapePMCServiceType {
592 CapePMCServiceType::Inproc32
593}
594
595
596#[cfg(test)]
597mod tests {
598 use crate::*;
599 use regex::Regex;
600
601 #[test]
602 fn get_version() {
603 cape_open_initialize().unwrap();
604 let version = get_cobia_version();
605 let re = Regex::new(r"\d+\.\d+\.\d+\.\d+").unwrap();
606 assert!(re.is_match(&version));
607 }
608
609 #[test]
610 fn cobia_folder_exists() {
611 cape_open_initialize().unwrap();
612 let folder = get_cobia_folder();
613 assert!(folder.exists());
614 }
615}