dusk_api/lib.rs
1//! Crate, that is used while building a plugin system as a common
2//! dependency by both plugin and plugin user to define the plugin
3//! behavior and safely import and use the plugin
4//!
5//! # Plugin Side
6//!
7//! To quickly learn how to create a plugin and export functions from it see
8//! [`export_freight!`] macro documentation
9//!
10//! # Importer Side
11//!
12//! To quickly learn how to import and use plugins see [`FreightProxy`]
13//! documentation
14
15use std::any::{Any, TypeId};
16
17/// Api version parameter, passed from the build script.
18///
19/// For the program that uses the plugin to work correctly it
20/// has to use the same version of api, which is ensured by embedding
21/// it as a static variable
22pub static API_VERSION: &str = env!("CARGO_PKG_VERSION");
23
24/// Rust compiler version parameter, passed from the compiler.
25///
26/// If plugin is compiled with a different rust compiler version
27/// it may be incompatible with the program using it, so before
28/// proceeding to use the plugin a version check is needed.
29///
30/// for this to work, build script should set this environmental
31/// variable, which can be done like this
32///
33/// # build.rs
34/// ```
35/// extern crate rustc_version;
36///
37/// fn main() {
38/// let version = rustc_version::version().unwrap();
39/// println!("cargo:rustc-env=RUSTC_VERSION={}", version);
40/// }
41/// ```
42///
43/// # Cargo.toml
44/// ```
45/// [build-dependencies]
46/// rustc_version = "0.3.0"
47/// ```
48pub static RUSTC_VERSION: &str = env!("RUSTC_VERSION");
49
50/// A macro, which can be used to make exporting of a struct
51/// easier
52///
53/// # Example
54///
55/// ```
56/// dusk_api::export_plugin!("test", "0.1.0", MyFreight);
57///
58/// pub struct MyFreight;
59///
60/// impl Freight for MyFreight {
61/// // Your implementation here
62/// }
63/// ```
64#[macro_export]
65macro_rules! export_plugin {
66 ($name: expr, $version: expr, $freight: ident) => {
67 $crate::register_freight!($freight, freight_registry_function);
68 $crate::export_freight!($name, $version, freight_registry_function);
69 };
70}
71
72/// A macro, which must be used for exporting a struct.
73///
74/// Every export must be done using this macro, to make sure
75/// the plugin is compatible with the program using it
76///
77/// To learn more about structure, required to register the
78/// plugins behavior, see [`Freight`] trait documentation
79///
80/// To learn how to do the same job easier automatically
81/// see [`register_freight!`] macro documentation and
82/// [`export_plugin!`] macro documentation
83///
84/// # Example
85/// ```
86/// dusk_api::export_freight!("test", "0.1.0", register);
87///
88/// pub fn register (registrar: &mut dyn FreightRegistrar) {
89/// registrar.register_freight(Box::new(MyFreight));
90/// }
91///
92/// pub struct MyFreight;
93///
94/// impl Freight for MyFreight {
95/// // Your implementation here
96/// }
97/// ```
98#[macro_export]
99macro_rules! export_freight {
100 ($name:expr, $version:expr, $register:expr) => {
101 #[doc(hidden)]
102 #[no_mangle]
103 pub static freight_declaration: $crate::FreightDeclaration
104 = $crate::FreightDeclaration {
105 rustc_version: $crate::RUSTC_VERSION,
106 api_version: $crate::API_VERSION,
107 freight_version: $version,
108 name: $name,
109 register: $register,
110 };
111 };
112}
113
114/// A macro, which can be used to create a registry function
115/// easier
116///
117/// # Example
118///
119/// ```
120/// dusk_api::register_freight!(MyFreight, my_reg_fn);
121/// dusk_api::export_freight!("test", "0.1.0", my_reg_fn);
122///
123/// pub struct MyFreight;
124///
125/// impl Freight for MyFreight {
126/// // Your implementation here
127/// }
128/// ```
129#[macro_export]
130macro_rules! register_freight {
131 ($freight: expr, $name: ident) => {
132 #[doc(hidden)]
133 pub fn $name (registrar: &mut dyn $crate::FreightRegistrar) {
134 registrar.register_freight(Box::new($freight));
135 }
136 };
137}
138
139/// A macro, that makes plugin importing a little bit easier
140///
141/// # Example
142///
143/// ```
144/// let mut my_f_proxy: FreightProxy =
145/// import_plugin!("/bin/libtest-plug.so").unwrap();
146///
147/// println!("{}, {}", my_f_proxy.name, my_f_proxy.version);
148/// let fnlist: Vec<Function> = my_f_proxy.get_function_list();
149/// for func in fnlist {
150/// println!("{}, {}", func.name, func.number);
151/// }
152/// ```
153#[macro_export]
154macro_rules! import_plugin {
155 ($lib: expr) => {
156 unsafe{
157 $crate::FreightProxy::load($lib)
158 }
159 };
160}
161
162
163/// Enum, that represents a message passed to the program using the
164/// plugin when the function fails
165#[derive(Debug)]
166pub enum RuntimeError {
167
168 /// Send a message, representing the runtime error, that occured
169 /// while using a function.
170 ///
171 /// # Example
172 /// ```
173 /// fn call_function (
174 /// self: &mut Self,
175 /// _function_number: u64,
176 /// _args: Vec<&mut Box<dyn Any>>
177 /// ) -> Result<Box<dyn Any>, RuntimeError> {
178 /// Err(RuntimeError::Message{
179 /// msg: "You can't call an empty freight"
180 /// })
181 /// }
182 /// ```
183 Message { msg: &'static str },
184}
185
186/// Enum, that represents an interplugin request and either contains
187/// a [`InterplugRequest::Crucial`] plugin request (must be provided
188/// in order for the plugin to work or an
189/// [`InterplugRequest::Optional`] plugin request which may be
190/// denied
191///
192/// For more complex situations, when several plugins might provide
193/// similar functionality, [`InterplugRequest::Either`] may be used
194/// to provide several requests, each of which may be fulfilled
195/// for the plugin to work correctly. In case this functionality
196/// may also be provided by several different plugins together,
197/// [`InterplugRequest::Each`] should be used.
198///
199/// If the request is optional, the final decision to provide it
200/// or not to provide it is supposed to be made by the user. For
201/// example, if user needs some function from a plugin, that
202/// requires an optional interplug request to be fulfilled, they
203/// just add it to the dependencies, so the program, that provides
204/// the dependencies, when seeing this request finds out that
205/// the plugin that was requested was already loaded earlier,
206/// so it might as well provide it to the requesting plugin.
207pub enum InterplugRequest {
208
209 /// An interplug request that *MUST* be fulfilled in order
210 /// for the plugin to work at all
211 Crucial {
212 plugin: &'static str,
213 version: &'static str,
214 },
215
216 /// An interplug request that must be fulfilled in order for
217 /// the plugin to fully work, which means that without it
218 /// some functions will be unavailable
219 Optional {
220 plugin: &'static str,
221 version: &'static str,
222 },
223
224 /// An interlplug request that contains several interlplug
225 /// requests, either of which *MUST* be fulfilled for the
226 /// plugin to work at all
227 Either {
228 requests: Vec<InterplugRequest>,
229 },
230
231 /// An interlplug request that contains several interplug
232 /// requests, either of which should be fulfilled for the
233 /// plugin to fully work.
234 OptionalEither {
235 requests: Vec<InterplugRequest>,
236 },
237
238 /// An interplug request that contains several interplug
239 /// requests, each of which *MUST* be fulfilled in order for
240 /// the plugin to work
241 Each {
242 requests: Vec<InterplugRequest>,
243 },
244
245 /// An interplug request that contains several interplug
246 /// requests, each of which should be fulfilled in for
247 /// the plugin to fully work
248 OptionalEach {
249 requests: Vec<InterplugRequest>,
250 },
251}
252
253/// Enum that represents a system limitation, that a plugin either
254/// needs to know to work correctly, or should be notified of in
255/// case main program wants to limit some settings
256///
257/// When initiating the plugin, using [`Freight::init`], a vector
258/// of limitations can be passed to the plugin, to set such limits
259/// as number of cpu threads, memory working directories, etc.
260/// If for example the main program started to do some multithreading
261/// itself, it may notify the plugin using [`Freight::update_limitations`]
262/// that the maximum amount of threads it can use was lowered from
263/// the previous amount to 1, or if the main program does not care
264/// about the amount of threads anymore, and lets the plugin decide
265/// by itself which amount it wants to use, it can send a
266/// [`Limitation::Reset`] to it.
267pub enum Limitation {
268
269 /// Set the maximum allowed number, represetting some setting
270 Top {
271 setting: &'static str,
272 limit: isize,
273 },
274
275 /// Set the minimum allowed number, representing some setting
276 Bottom {
277 setting: &'static str,
278 limit: isize,
279 },
280
281 /// Reset the setting to default value (as if the main program
282 /// has never set any value to the setting at all)
283 Reset {
284 setting: &'static str,
285 },
286}
287
288/// Structure representing main characteristics of a function needed
289/// for the program using a plugin, which implements it
290///
291/// A Function object contains
292/// * function name
293/// * its id number to be used when calling the function
294/// * argument [`TypeId`]s it takes
295/// * its return [`TypeId`]
296pub struct Function {
297
298 /// Function name, as a reference to a static string. Mainly
299 /// used to give user the ability to choose the function they
300 /// want to use
301 pub name: &'static str,
302
303 /// Function ID, used to call this function
304 ///
305 /// **Should always be the same for same functions in the newer
306 /// releases, unless a new plugin version is submitted**
307 pub number: u64,
308
309 /// [`TypeId`]s of arguments, this function expects to find
310 /// inside of a Vector of [`Any`] trait implementors
311 ///
312 /// See [`std::any::Any`] documentation to find out more about
313 /// storing an [`Any`] trait implementor and getting back
314 /// from a [`Box<dyn Any>`]
315 pub arg_types: Vec<TypeId>,
316
317 /// The [`TypeId`] of the returned [`Any`] trait implementor
318 ///
319 /// See [`std::any::Any`] documentation to find out more about
320 /// storing an [`Any`] trait implementor and getting back
321 /// from a [`Box<dyn Any>`]
322 pub return_type: TypeId,
323
324 /// If the function can not work without some optional
325 /// interplug requests fulfilled, they must be included in
326 /// this field when providing the function to the
327 /// program that is using the plugin, so it knows if this
328 /// function is available in the current setup or not.
329 pub dependencies: Option<Vec<InterplugRequest>>,
330}
331
332/// Structure representing main characteristics of an object type
333/// needed for the program, using the plugin, that either imports
334/// or defines this type in case this type is not present in
335/// the user program itself
336///
337/// A Type object contains
338/// * type name, used for identifying this type
339/// * its [`TypeId`] for Any trait to work properly
340pub struct Type {
341
342 /// Name for the [`TypeId`] owner to be reffered to as a static
343 /// string
344 pub name: &'static str,
345
346 /// If an object of this type should have some functions, that
347 /// can be called on it, they should be provided here. The
348 /// functions provided here should be called using the same
349 /// [`Freight::call_function`] function, so they should
350 /// all have unique numbers
351 pub methods : Option<Vec<Function>>,
352
353 /// All fields of an object of this type, user needs to be able
354 /// to access, should be located here. The field name then
355 /// will be the function name, functions return type is the
356 /// field type and the only argument of the function should
357 /// be of the type, the field is owned by. These functions
358 /// are once again called by the same [`Freight::call_function`]
359 /// function and should all have unique numbers over all functions
360 /// called by [`Freight::call_function`]
361 pub fields : Option<Vec<Function>>,
362
363 /// [`TypeId`] object, gotten from the structure, being
364 /// provided to the program, that is using the plugin
365 ///
366 /// See [`std::any::TypeId`] documentation to find out how
367 /// to get a type id of a type
368 pub type_id: TypeId,
369}
370
371/// Trait, that defines the plugin behavior
372///
373/// This trait must be implemented in plugins to allow the program,
374/// that uses them to call any internal function of choice. For that
375/// the trait has a method [`Freight::get_function_list`], that
376/// provides argument types and return types, actually being used
377/// under Any trait as well as the function name to refer to it and
378/// its identification number, which is needed to call this function
379///
380/// The [`Freight::call_function`] method actually calls the function,
381/// which matches the provided id.
382///
383/// # Example
384/// ```
385/// pub struct Test;
386///
387/// impl Freight for Test {
388/// fn call_function (
389/// self: &mut Self,
390/// function_number: u64,
391/// mut args: Vec<&mut Box<dyn Any>>
392/// ) -> Result<Box<dyn Any>, RuntimeError> {
393///
394/// match function_number {
395/// 0 => return Ok(Box::new(
396/// args[0].downcast_mut::<String>()
397/// .unwrap()
398/// .clone())
399/// ),
400/// _ => return Err(RuntimeError::Message{
401/// msg: "bad fn number"
402/// }),
403/// }
404/// }
405///
406/// fn get_function_list (self: &mut Self) -> Vec<Function> {
407/// let mut result: Vec<Function> = Vec::new();
408/// result.push(Function{
409/// name: "copy",
410/// number: 0,
411/// arg_types: vec![TypeId::of::<String>()],
412/// return_type: TypeId::of::<String>(),
413/// });
414/// return result;
415/// }
416///
417/// fn get_type_list (self: &mut Self) -> Vec<Type> {
418/// return Vec::new();
419/// }
420/// }
421/// ```
422pub trait Freight {
423
424 /// Function that is ran when importing the plugin, which
425 /// may be reimplememented in a plugin if it needs to set up
426 /// some things before doing any other actions
427 ///
428 /// If some system limitations should be applied, they must be
429 /// included as an argument. If the plugin needs to use other
430 /// plugins, it should request them as a Vector of
431 /// [`InterplugRequest`]
432 fn init (self: &mut Self, _limitations: &Option<Vec<Limitation>>)
433 -> Vec<InterplugRequest> {
434 Vec::new()
435 }
436
437 /// Function that updates system limitations
438 fn update_limitations (self: &mut Self, _limitations: &Vec<Limitation>) {
439 ()
440 }
441
442 /// Function that replies to the interplugin request by
443 /// providing the requested plugin
444 fn interplug_provide (
445 self: &mut Self,
446 _request: InterplugRequest,
447 _freight_proxy: std::rc::Rc<FreightProxy>,
448 ) {}
449
450 /// Function that replies to the interplugin request by
451 /// by informing it that the request was denied
452 fn interplug_deny (
453 self: &mut Self,
454 _request: InterplugRequest,
455 ) {}
456
457 /// Function that is used to provide information about
458 /// non standard types, a function from this plugin might take
459 /// as an argument or return, so that the program using the
460 /// plugin can take such non-standard objects from one
461 /// function implemented in this plugin and pass it on into
462 /// another function in this plugin
463 ///
464 /// To use it, just reimplement it to return a vector of
465 /// such [`Type`] structure descriptions
466 fn get_type_list (self: &mut Self) -> Vec<Type> {
467 Vec::new()
468 }
469
470 /// Function that is used to provide information about internal
471 /// functions of a plugin to the program using it, so it can
472 /// choose the function it needs either by its name, argument
473 /// types, return type or all of the above
474 fn get_function_list (self: &mut Self) -> Vec<Function>;
475
476 /// Function that is used to provide information about internal
477 /// functions of a plugin that are named after binary operators
478 /// and should be treated as such. These functions have to
479 /// always get exactly two arguments and they are called by the
480 /// same function that calls any function [`Freight::call_function`]
481 fn get_operator_list (self: &mut Self) -> Vec<Function> {
482 Vec::new()
483 }
484
485 /// Function that is used to call proxy the calls from the
486 /// outside of a plugin to the internal functions and must
487 /// implement function calling, by its number arguments,
488 /// contained inside of [`Vec<Box<dyn Any>>`] and must return
489 /// either a [`Box<dyn Any>`] representing the returned value
490 /// or a [`RuntimeError`]
491 fn call_function (
492 self: &mut Self,
493 function_number: u64,
494 args: Vec<&mut Box<dyn Any>>
495 ) -> Result<Box<dyn Any>, RuntimeError>;
496}
497
498/// Trait to be implemented on structs, which are used to register
499/// or store the imported plugins
500///
501/// This trait is only needed for internal usage and as a reference
502/// for the plugins, so that they can define a function that takes a
503/// [`FreightRegistrar`] implementor as an argument, so that when
504/// the plugin is imported the function is called on it and
505/// some unexportable things such as structures could be provided,
506/// which in this particular case is a [`Freight`] implementor
507/// structure
508pub trait FreightRegistrar {
509
510 /// Function that gets a [`Freight`] implementor passed as an
511 /// argument and is used to use it wherever it is needed in the
512 /// [`FreightRegistrar`] implementor
513 fn register_freight (
514 self: &mut Self,
515 freight: Box<dyn Freight>,
516 );
517}
518
519/// A structure, exported by plugin, containing some package details
520/// and register function
521///
522/// This structure contains the rust compiler version, the plugin was
523/// compiled with, api version it uses, the plugin name and version
524/// and the actual function, that is used to register the plugin.
525///
526/// The function is only needed to pass a structure, that implements
527/// trait Freight to the [`FreightRegistrar::register_freight`] as
528/// structures can not be put into static variables, but static
529/// functions can.
530///
531/// This structure must only be built by [`export_freight!`] macro
532/// in plugins. And its fields are only read by
533/// [`FreightProxy::load`] function when loading the plugin
534pub struct FreightDeclaration {
535
536 /// Rust compiler version as a static string
537 pub rustc_version: &'static str,
538
539 /// Api version as a static string
540 pub api_version: &'static str,
541
542 /// Version of the freight being imported
543 pub freight_version: &'static str,
544
545 /// Name of the freight being imported
546 pub name: &'static str,
547
548 /// Function that gets a [`FreightRegistrar`] trait implementor
549 /// as an argument and calls its freight_register function
550 /// to provide unexportable things, such as structs, in
551 /// particular, [`Freight`] implementor structures
552 pub register: fn (&mut dyn FreightRegistrar),
553}
554
555/// A structure, that contains a Freight object and is used to import
556/// and use it safely
557///
558/// This structure is a [`Freight`] trait implementor and
559/// [`FreightRegistrar`] trait implementor. It provides
560/// [`FreightProxy::load`] function that is used to build the
561/// [`FreightProxy`] from a library path
562///
563/// To learn more about the functions you may call on the
564/// [`FreightProxy`], see [`Freight`] trait documentation
565///
566/// # Example
567///
568/// ```
569/// let mut my_f_proxy: FreightProxy = unsafe{
570/// FreightProxy::load("/bin/libtest_plug.so").expect("fail")
571/// };
572/// println!("{}, {}", my_f_proxy.name, my_f_proxy.version);
573/// let fnlist: Vec<Function> = my_f_proxy.get_function_list();
574/// for func in fnlist {
575/// println!("{}, {}", func.name, func.number);
576/// }
577/// ```
578pub struct FreightProxy {
579
580 /// Imported freight, solely for internal purposes
581 pub freight: Box<dyn Freight>,
582
583 /// Lib this freight was imported from to make sure this
584 /// structure does not outlive the library it was imported from
585 pub lib: std::rc::Rc<libloading::Library>,
586
587 /// Imported freights name as a static string
588 pub name: &'static str,
589
590 /// Imported freights version as a static string
591 pub version: &'static str,
592}
593
594/// Structure representing an empty [`Freight`] implementor, needed
595/// only for [`FreightProxy`] configuration
596pub struct EmptyFreight;
597impl Freight for EmptyFreight {
598 fn call_function (
599 self: &mut Self,
600 _function_number: u64,
601 _args: Vec<&mut Box<dyn Any>>
602 ) -> Result<Box<dyn Any>, RuntimeError> {
603
604 Err(RuntimeError::Message{
605 msg: "You can't call an empty freight"
606 })
607 }
608
609 fn get_function_list (self: &mut Self) -> Vec<Function> {
610 Vec::new()
611 }
612}
613
614/// Functions, needed to configure [`FreightProxy`] structure
615/// initially
616impl FreightProxy {
617
618 /// Function, used to build a [`FreightProxy`] object from a
619 /// library path
620 pub unsafe fn load (lib_path: &str)
621 -> Result<FreightProxy, RuntimeError> {
622
623 // Import the library
624 // *FIXME* Get rid of unwrap
625 let lib : std::rc::Rc<libloading::Library>
626 = std::rc::Rc::new(
627 libloading::Library::new(lib_path).unwrap());
628
629 // Get the plugin declaration structure from this lib
630 // *FIXME* Get rid of unwrap
631 let declaration: FreightDeclaration = lib
632 .get::<*mut FreightDeclaration>(b"freight_declaration\0")
633 .unwrap()
634 .read();
635
636 // Check if the compiler and api versions match
637 // If not -- immediately return an error
638 if declaration.rustc_version != RUSTC_VERSION
639 || declaration.api_version != API_VERSION
640 {
641 return Err(RuntimeError::Message{
642 msg: "Version mismatch"
643 });
644 }
645
646 // Make a new FreightProxy with all values that are
647 // already available
648 let mut result: FreightProxy = FreightProxy {
649 freight: Box::new(EmptyFreight{}),
650 lib: lib,
651 name: declaration.name,
652 version: declaration.freight_version,
653 };
654
655 // Call the function, imported in the plugin declaration
656 // and pass the FreightProxy to it as an argument
657 // so it sets the internal freight variable to a
658 // correct value
659 (declaration.register)(&mut result);
660
661 // Return the result
662 Ok(result)
663 }
664}
665
666// Implementation of trait Freight for the proxy structure, so we
667// can call exact same functions from it
668impl Freight for FreightProxy {
669
670 // Proxy function, that takes everything needed to call a function
671 // and passes it on to the freight inside
672 fn call_function (
673 self: &mut Self,
674 function_number: u64,
675 args: Vec<&mut Box<dyn Any>>
676 ) -> Result<Box<dyn Any>, RuntimeError> {
677 self.freight.call_function(function_number, args)
678 }
679
680 // Proxy function, that calls the internal freights init function
681 // and returns its plugin dependencies
682 fn init (self: &mut Self, limitations: &Option<Vec<Limitation>>)
683 -> Vec<InterplugRequest> {
684 self.freight.init(limitations)
685 }
686
687 // Proxy function that takes the list of new system limitations
688 // and passes it to the plugin
689 fn update_limitations (self: &mut Self, limitations: &Vec<Limitation>) {
690 self.freight.update_limitations(limitations)
691 }
692
693 // Proxy function for replying to an interplugin dependency
694 // request by providing the requested plugin
695 fn interplug_provide (
696 self: &mut Self,
697 request: InterplugRequest,
698 freight_proxy: std::rc::Rc<FreightProxy>,
699 ) {
700 self.freight.interplug_provide(request, freight_proxy);
701 }
702
703 // Proxy function for replying to an interplugin dependency
704 // request by informing it of request denial
705 fn interplug_deny (
706 self: &mut Self,
707 request: InterplugRequest,
708 ) {
709 self.freight.interplug_deny(request);
710 }
711
712 // Proxy function, that calls the function that gets function
713 // list from the inside freight and returns the result
714 fn get_function_list (self: &mut Self) -> Vec<Function> {
715 self.freight.get_function_list()
716 }
717
718 // Proxy function, that calls the function that gets type
719 // list from the inside freight and returns the result
720 fn get_type_list (self: &mut Self) -> Vec<Type> {
721 self.freight.get_type_list()
722 }
723}
724
725// Implementation of FreightRegistrar trait for the proxy
726// structure, so that we can call register function on it without
727// any third party structure
728impl FreightRegistrar for FreightProxy {
729
730 // The function that simply takes a freight implementor
731 // as an argument and passes it into the inside freight
732 // variable
733 fn register_freight (
734 self: &mut Self,
735 freight: Box<dyn Freight>,
736 ) {
737 self.freight = freight;
738 }
739}