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
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
//! Bundles function with common receiver into a service or 'class' in object oriented languages.
//!
//! Services provide a convenient way to manage state and memory between method invocations.
//! They are similar to classes in object oriented languages, but we refrained from naming them
//! as such because for lifetime and memory-safety reasons it would be best practice to only have
//! "a few" well-defined instances around at any given time, not millions with arbitrary
//! inter-dependencies.
//!
//! That said, services usually translate to classes in languages supporting them, automatically
//! guard against panics (prevent them from bubbling into C which would be undefined behavior)
//! and can provide transparent error handling.
//!
//! In short, if your library offers a "service", the _service pattern_ might give you a noticeable
//! quality of life improvement.
//!
//! # Defining Services
//!
//! To define a service you need the following parts:
//!
//! - An `opaque` type; the instance of a service
//! - A Rust `Error` type mappable to an [FFIError](crate::patterns::result::FFIError) enum via `From<Error>`
//! - Some methods on the opaque type.
//!
//! # Example
//!
//! In this example we define a service called `SimpleService` with a constructor and two methods.
//! The type `MyFFIError` is not shown, but implemented as in the [FFIError](crate::patterns::result::FFIError) example.
//!
//! ```
//! # use std::fmt::{Display, Formatter};
//! #
//! # #[derive(Debug)]
//! # pub enum Error {
//! #     Bad,
//! # }
//! #
//! # impl Display for Error {
//! #     fn fmt(&self, _: &mut Formatter<'_>) -> std::fmt::Result {
//! #         Ok(())
//! #     }
//! # }
//! #
//! # impl std::error::Error for Error {}
//! #
//! # #[ffi_type(patterns(ffi_error))]
//! # #[repr(C)]
//! # pub enum MyFFIError {
//! #     Ok = 0,
//! #     NullPassed = 1,
//! #     Panic = 2,
//! #     OtherError = 3,
//! # }
//! #
//! # impl FFIError for MyFFIError {
//! #     const SUCCESS: Self = Self::Ok;
//! #     const NULL: Self = Self::NullPassed;
//! #     const PANIC: Self = Self::Panic;
//! # }
//! #
//! # impl From<Error> for MyFFIError {
//! #     fn from(x: Error) -> Self {
//! #         match x {
//! #             Error::Bad => Self::OtherError,
//! #         }
//! #     }
//! # }
//! #
//! use interoptopus::{ffi_type, ffi_service, ffi_service_ctor, ffi_service_method};
//! use interoptopus::patterns::result::FFIError;
//!
//! #[ffi_type(opaque)]
//! pub struct SimpleService {
//!     pub some_value: u32,
//! }
//!
//! #[ffi_service(error = "MyFFIError", prefix = "simple_service_")]
//! impl SimpleService {
//!
//!     #[ffi_service_ctor]
//!     pub fn new_with(some_value: u32) -> Result<Self, Error> {
//!         Ok(Self { some_value })
//!     }
//!
//!     pub fn maybe_fails(&self, x: u32) -> Result<(), Error> {
//!         Ok(())
//!     }
//!
//!     #[ffi_service_method(direct)]
//!     pub fn just_return_value(&self) -> u32 {
//!         self.some_value
//!     }
//! }
//! ```
//!
//! In languages supporting this pattern bindings will be generated allowing the service to be
//! instantiated and called like this:
//!
//! ```csharp
//! var x = new SimpleService(123);
//! x.MaybeFails(456);
//! x.JustReturnValue();
//! x.Dispose();
//! ```
//!
//! In other languages and on the C FFI level the following methods would be emitted:
//!
//! ```c
//! myffierror simple_service_new_with(simpleservice** context, uint32_t some_value);
//! myffierror simple_service_destroy(simpleservice** context);
//! myffierror simple_service_maybe_fails(simpleservice* context, uint32_t x);
//! uint32_t simple_service_just_return_value(simpleservice* context);
//! ```
//!

use crate::lang::c::{CType, Function, OpaqueType};
use crate::patterns::TypePattern;
use std::fmt::Debug;

/// Combines a receiver, constructor, destructor and multiple methods in one entity.
#[derive(Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Hash)]
pub struct Service {
    the_type: OpaqueType,
    constructors: Vec<Function>,
    destructor: Function,
    methods: Vec<Function>,
}

impl Service {
    pub fn new(constructors: Vec<Function>, destructor: Function, methods: Vec<Function>) -> Self {
        let the_type = extract_obvious_opaque_from_parameter(
            constructors
                .first()
                .expect("Must have at least one constructor")
                .signature()
                .params()
                .first()
                .expect("Constructor must have at least one parameter")
                .the_type(),
        )
        .expect("First parameter must point to opaque.");

        Self {
            the_type,
            constructors,
            destructor,
            methods,
        }
    }

    /// Checks if the signature of this service is compatible with the `Service` pattern, panic with
    /// error message otherwise.
    ///
    /// This function is mainly called during compile time therefore panicking with a good error
    /// message is beneficial.
    pub fn assert_valid(&self) {
        let constructor_fist_parameter = self
            .constructors
            .first()
            .expect("Must have at least one constructor")
            .signature()
            .params()
            .get(0)
            .expect("Constructor for must have at least one parameter.");

        match &constructor_fist_parameter.the_type() {
            CType::ReadWritePointer(x) => match **x {
                CType::ReadWritePointer(ref x) => match **x {
                    CType::Opaque(_) => {}
                    _ => panic!("First parameter must be opaque type"),
                },
                _ => panic!("First parameter must be opaque type"),
            },
            CType::Opaque(_) => {}
            _ => panic!("First parameter must be RwPointer(RwPointer(Opaque)) type"),
        }

        let destructor_first_parameter = self
            .destructor
            .signature()
            .params()
            .get(0)
            .expect("Constructor for must have at least one parameter.");

        match &destructor_first_parameter.the_type() {
            CType::ReadWritePointer(x) => match **x {
                CType::ReadWritePointer(ref x) => match **x {
                    CType::Opaque(_) => {}
                    _ => panic!("First parameter must be opaque type"),
                },
                _ => panic!("First parameter must be opaque type"),
            },
            CType::Opaque(_) => {}
            _ => panic!("First parameter must be RwPointer(RwPointer(Opaque)) type"),
        }

        for constructor in self.constructors.iter() {
            match constructor.signature().rval() {
                CType::Pattern(TypePattern::FFIErrorEnum(_)) => {}
                _ => panic!("Constructor must return a `ffi_error` type pattern."),
            }
        }

        match self.destructor.signature().rval() {
            CType::Pattern(TypePattern::FFIErrorEnum(_)) => {}
            _ => panic!("Destructor must return a `ffi_error` type pattern."),
        }
    }

    pub fn the_type(&self) -> &OpaqueType {
        &self.the_type
    }

    pub fn constructors(&self) -> &[Function] {
        &self.constructors
    }

    pub fn destructor(&self) -> &Function {
        &self.destructor
    }

    pub fn methods(&self) -> &[Function] {
        &self.methods
    }
}

/// Walks the type until it finds the first "obvious" Opaque.
///
/// An Opaque is obvious if it is at a singular position (e.g., `*const Opaque`),
/// but not within the fields of a struct.
fn extract_obvious_opaque_from_parameter(param: &CType) -> Option<OpaqueType> {
    match param {
        CType::Primitive(_) => None,
        CType::Enum(_) => None,
        CType::Opaque(x) => Some(x.clone()),
        CType::Composite(_) => None,
        CType::FnPointer(_) => None,
        CType::ReadPointer(x) => extract_obvious_opaque_from_parameter(x),
        CType::ReadWritePointer(x) => extract_obvious_opaque_from_parameter(x),
        CType::Pattern(_) => None,
        CType::Array(_) => None,
    }
}