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}