1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
#![allow(non_camel_case_types)] //! [Github](https://github.com/hansihe/Rustler) //! [Example](https://github.com/hansihe/Rustler_Example) //! //! Rustler is a library for writing Erlang NIFs in safe Rust code. That means there should be no //! ways to crash the BEAM (Erlang VM). The library provides facilities for generating the //! boilerplate for interacting with the BEAM, handles encoding and decoding of Erlang terms, and //! catches rust panics before they unwind into C. //! //! The library provides functionality for both Erlang and Elixir, however Elixir is favored as of //! now. //! //! This crate provides the entire runtime library for rustler. Code generators are located in the //! rustler_codegen library. //! //! # Getting Started //! There is a [`:rustler`](https://hex.pm/packages/rustler) package on hex.pm that provides //! functionality which makes working with Rustler easier, including project generators, an //! automatic NIF compiler for Mix, and utilities for loading the compiled NIF. //! //! For more information about this, see [the documentation for //! rustler_mix](https://hexdocs.pm/rustler/basics.html). #[macro_use(enif_snprintf)] extern crate erlang_nif_sys; use std::marker::PhantomData; mod wrapper; use wrapper::nif_interface::NIF_ENV; #[doc(hidden)] pub mod codegen_runtime; #[macro_use] extern crate lazy_static; #[macro_use] pub mod types; mod term; pub use term::{ NifTerm }; pub use types::{ NifEncoder, NifDecoder }; pub use wrapper::nif_interface::ErlNifTaskFlags; pub mod resource; #[doc(hidden)] pub mod dynamic; pub use dynamic::TermType; pub mod schedule; pub mod env; pub mod thread; mod export; pub type NifResult<T> = Result<T, NifError>; /// Private type system hack to help ensure that each environment exposed to safe Rust code is /// given a different lifetime. The size of this type is zero, so it costs nothing at run time. Its /// purpose is to make `NifEnv<'a>` and `NifTerm<'a>` *invariant* w.r.t. `'a`, so that Rust won't /// auto-convert a `NifEnv<'a>` to a `NifEnv<'b>`. type EnvId<'a> = PhantomData<*mut &'a u8>; /// On each NIF call, a NifEnv is passed in. The NifEnv is used for most operations that involve /// communicating with the BEAM, like decoding and encoding terms. /// /// There is no way to allocate a NifEnv at the moment, but this may be possible in the future. #[derive(Clone, Copy)] pub struct NifEnv<'a> { env: NIF_ENV, id: EnvId<'a> } /// Two environments are equal if they're the same `NIF_ENV` value. /// /// A `NifEnv<'a>` is equal to a `NifEnv<'b>` iff `'a` and `'b` are the same lifetime. impl<'a, 'b> PartialEq<NifEnv<'b>> for NifEnv<'a> { fn eq(&self, other: &NifEnv<'b>) -> bool { self.env == other.env } } impl<'a> NifEnv<'a> { /// Create a new NifEnv. For the `_lifetime_marker` argument, pass a /// reference to any local variable that has its own lifetime, different /// from all other `NifEnv` values. The purpose of the argument is to make /// it easier to know for sure that the `NifEnv` you're creating has a /// unique lifetime (i.e. that you're following the most important safety /// rule of Rustler). /// /// # Unsafe /// Don't create multiple `NifEnv`s with the same lifetime. unsafe fn new<T>(_lifetime_marker: &'a T, env: NIF_ENV) -> NifEnv<'a> { NifEnv { env: env, id: PhantomData } } pub fn as_c_arg(&self) -> NIF_ENV { self.env } /// Convenience method for building a tuple `{error, Reason}`. pub fn error_tuple<T>(self, reason: T) -> NifTerm<'a> where T: NifEncoder { let error = types::atom::error().to_term(self); (error, reason).encode(self) } } /// Represents usual errors that can happen in a nif. This enables you /// to return an error from anywhere, even places where you don't have /// an NifEnv availible. pub enum NifError { /// Returned when the NIF has been called with the wrong number or type of /// arguments. BadArg, /// Encodes the string into an atom and returns it from the NIF. Atom(&'static str), RaiseAtom(&'static str), RaiseTerm(Box<NifEncoder>), } impl NifError { /// # Unsafe /// /// If `self` is a `BadArg`, `RaiseAtom`, or `RaiseTerm` value, then the /// term returned from this method must not be used except as the return /// value from the calling NIF. unsafe fn encode<'a>(self, env: NifEnv<'a>) -> NifTerm<'a> { match self { NifError::BadArg => { let exception = wrapper::exception::raise_badarg(env.as_c_arg()); NifTerm::new(env, exception) }, NifError::Atom(atom_str) => { types::atom::NifAtom::from_str(env, atom_str) .ok().expect("NifError::Atom: bad atom").to_term(env) }, NifError::RaiseAtom(atom_str) => { let atom = types::atom::NifAtom::from_str(env, atom_str) .ok().expect("NifError::RaiseAtom: bad argument"); let exception = wrapper::exception::raise_exception( env.as_c_arg(), atom.as_c_arg()); NifTerm::new(env, exception) }, NifError::RaiseTerm(ref term_unencoded) => { let term = term_unencoded.encode(env); let exception = wrapper::exception::raise_exception( env.as_c_arg(), term.as_c_arg()); NifTerm::new(env, exception) }, } } }