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}