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
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
pub use crate::ll::bytecode::{FunctionParameterCount, MethodParameterCount};
use crate::{
    ll::{bytecode::Environment, gc::Memory, value::RawValue},
    Error, IntoValue, LanguageErrorKind, RawForeignFunction, TryFromValue, Value,
};

fn create_rawff(
    f: impl Fn(&Environment, &mut Memory, &[RawValue]) -> Result<RawValue, LanguageErrorKind> + 'static,
) -> RawForeignFunction {
    Box::new(f)
}

/// Arguments passed to a varargs function.
///
/// This is a wrapper that does things like type-checking and arity-checking.
#[derive(Debug)]
pub struct Arguments<'a> {
    this: RawValue,
    inner: &'a [RawValue],
    env: &'a Environment,
}

impl<'a> Arguments<'a> {
    /// Creates a new [`Arguments`] from a raw argument list, as is passed into a
    /// [raw function][RawForeignFunction].
    pub fn new(raw_arguments: &'a [RawValue], env: &'a Environment) -> Self {
        // Skip the first argument, which is `self` (or the currently called function).
        Self { this: raw_arguments[0], inner: &raw_arguments[1..], env }
    }

    /// Returns the number of arguments passed to the function.
    pub fn count(&self) -> usize {
        self.inner.len()
    }

    /// Raises an error if there weren't exactly `n` arguments passed to the function.
    pub fn expect_exactly(&self, n: usize) -> Result<(), Error> {
        if self.count() != n {
            Err(Error::ArgumentCount { expected: n, got: self.count() })
        } else {
            Ok(())
        }
    }

    /// Raises an error if there weren't at least `n` arguments passed to the function.
    pub fn expect_at_least(&self, n: usize) -> Result<(), Error> {
        if self.count() < n {
            Err(Error::ArgumentCount { expected: n, got: self.count() })
        } else {
            Ok(())
        }
    }

    /// Returns the `self` parameter as a value.
    pub fn raw_self(&self) -> &RawValue {
        &self.this
    }

    /// Returns the `n`th argument, or `None` if the argument is not present.
    pub fn nth(&self, n: usize) -> Option<&RawValue> {
        self.inner.get(n)
    }

    /// Returns the `n`th argument converted into the given type. Returns an error if there aren't
    /// enough arguments.
    pub fn get<T>(&self, n: usize) -> Result<T, Error>
    where
        T: TryFromValue,
    {
        let value = self.inner.get(n).cloned().unwrap_or(RawValue::from(()));
        T::try_from_value(&Value::from_raw(value), self.env).map_err(|error| {
            if let Error::TypeMismatch { expected, got } = error {
                Error::ArgumentTypeMismatch { index: n, expected, got }
            } else {
                error
            }
        })
    }

    /// Returns the raw array of arguments.
    pub fn array(&self) -> &[RawValue] {
        self.inner
    }
}

/// Wrapper struct for marking functions that use the with-raw-self calling convention.
///
/// This `Deref`s to the inner value.
#[derive(Debug)]
#[repr(transparent)]
pub struct RawSelf<'a>(pub(crate) &'a RawValue);

impl std::ops::Deref for RawSelf<'_> {
    type Target = RawValue;

    fn deref(&self) -> &Self::Target {
        self.0
    }
}

/// A Rust function that can be called from Mica.
///
/// To spare you the time needed to decipher the absolutely unreadable autogenerated
/// implementations of this trait, functions with the following signatures are supported:
/// - Constant number of type checked arguments
///   - `fn (A, B, C, ...) -> R` where
///     - Each argument: [`TryFromValue`]
///     - `R`: [`Into`]`<`[`Value`]`>`
///   - `fn (A, B, C, ...) -> Result<R, E>`
///     - Each argument: [`TryFromValue`]
///     - `R`: [`Into`]`<`[`Value`]`>`
///   - `fn (Self, A, B, C, ...) -> R` where
///     - `Self`: [`SelfFromRawValue`][`crate::SelfFromRawValue`] or
///       [`MutSelfFromRawValue`][`crate::MutSelfFromRawValue`]
///     - Each argument after `Self`: [`TryFromValue`]
///     - `R`: [`Into`]`<`[`Value`]`>`
///   - `fn (Self, A, B, C, ...) -> Result<R, E>`
///     - `Self`: [`SelfFromRawValue`][`crate::SelfFromRawValue`] or
///       [`MutSelfFromRawValue`][`crate::MutSelfFromRawValue`]
///     - Each argument after `Self`: [`TryFromValue`]
///     - `R`: [`Into`]`<`[`Value`]`>`
///   - Due to a limitation in Rust's type system, a maximum of 8 arguments is supported now. If
///     more is needed, use the varargs versions described below.
/// - Variable number of dynamically typed arguments
///   - `fn (`[`Arguments`]`) -> R` where `R`: [`Into`]`<`[`Value`]`>`
///   - `fn (`[`Arguments`]`) -> Result<R, E>` where
///     - `R`: [`Into`]`<`[`Value`]`>`
///     - `E`: [`std::error::Error`]
///
/// The generic parameter `V` is not used inside the trait. Its only purpose is to allow for
/// multiple overlapping implementations of a trait for the same type. See [`ffvariants`] for more
/// information.
pub trait ForeignFunction<V> {
    /// The type used for parameter counts in this kind of foreign functions.
    ///
    /// This is either [`FunctionParameterCount`] or [`MethodParameterCount`], depending on whether
    /// a function is usable as a bare function or a method.
    type ParameterCount;

    /// The number of parameters this function has, or `None` if the function accepts a variable
    /// number of arguments.
    ///
    /// The default implementation returns `None`.
    const PARAMETER_COUNT: Self::ParameterCount;

    /// Converts the function to a `RawForeignFunction`.
    fn into_raw_foreign_function(self) -> RawForeignFunction;
}

/// Variants of `ForeignFunction`.
///
/// This is a bit of a hack around Rust's type system not supporting disjoint generic
/// implementations. Each implementation has a corresponding marker type in this module, such that
/// eg. the traits `ForeignFunction<ffvariants::Fallible>` and
/// `ForeignFunction<ffvariants::Infallible>` are different, but can both be matched by using a
/// generic parameter.
///
/// As long as all implementations are disjoint, Rust will happily infer all the types. If any
/// implementations end up not being disjoint, that's bad - Rust will complain about types being
/// ambiguous, but that should be pretty easy to notice.
#[allow(missing_debug_implementations)]
pub mod ffvariants {
    use std::marker::PhantomData;

    // For the type system to accept our implemenations, the types of the function's parameters must
    // appear somewhere in the trait or the implemented self type. We can't control the self type,
    // so we put them into a generic parameter here.
    // The PhantomData inside suppresses an "unused generic parameter" error and prevents
    // construction of the two structs (because it's a private field).

    /// A bare fallible function.
    pub struct Fallible<Args>(PhantomData<Args>);
    /// A bare infallible function.
    pub struct Infallible<Args>(PhantomData<Args>);
    /// A bare varargs fallible function.
    pub enum VarargsFallible {}
    /// A bare varargs infallible function.
    pub enum VarargsInfallible {}

    mod bare {
        pub trait Sealed {}

        impl<Args> Sealed for super::Fallible<Args> {}
        impl<Args> Sealed for super::Infallible<Args> {}
        impl Sealed for super::VarargsFallible {}
        impl Sealed for super::VarargsInfallible {}
    }

    /// Marker trait for all functions that _don't_ accept a `self` reference as the first
    /// parameter, and may accept a variable number of arguments.
    ///
    /// See also [`Method`] and [`BareExactArgs`].
    pub trait BareMaybeVarargs: bare::Sealed {}

    impl<Args> BareMaybeVarargs for Fallible<Args> {}
    impl<Args> BareMaybeVarargs for Infallible<Args> {}
    impl BareMaybeVarargs for VarargsFallible {}
    impl BareMaybeVarargs for VarargsInfallible {}

    /// Marker trait for all functions that don't accept a `self` reference as the first
    /// parameter and do not accept a variable number of arguments.
    ///
    /// See also [`Method`] and [`BareMaybeVarargs`].
    pub trait BareExactArgs: bare::Sealed {}

    impl<Args> BareExactArgs for Fallible<Args> {}
    impl<Args> BareExactArgs for Infallible<Args> {}

    /// A fallible function with `RawSelf`.
    pub struct FallibleRawSelf<Args>(PhantomData<Args>);
    /// An infallible function with `RawSelf`.
    pub struct InfallibleRawSelf<Args>(PhantomData<Args>);

    // S is the self type (`ImmutableSelf` or `MutableSelf`).
    /// A fallible function with typed `self`.
    pub struct FallibleSelf<S, Args>(PhantomData<(S, Args)>);
    /// An infallible function with typed `self`.
    pub struct InfallibleSelf<S, Args>(PhantomData<(S, Args)>);

    mod method {
        pub trait Sealed {}

        impl<Args> Sealed for super::FallibleRawSelf<Args> {}
        impl<Args> Sealed for super::InfallibleRawSelf<Args> {}
        impl<S, Args> Sealed for super::FallibleSelf<S, Args> {}
        impl<S, Args> Sealed for super::InfallibleSelf<S, Args> {}
    }

    /// Defines the common [`Receiver`][`Self::Receiver`] type of [`ImmutableSelf`]
    /// and [`MutableSelf`].
    pub trait Receiver {
        /// The type that receives the method call.
        type Receiver;
    }

    /// Marker for a method that has an immutable `self` (`&self`).
    pub struct ImmutableSelf<S>(PhantomData<S>);
    /// Marker for a method that has a mutable `self` (`&mut self`).
    pub struct MutableSelf<S>(PhantomData<S>);

    impl<S> Receiver for ImmutableSelf<S> {
        type Receiver = S;
    }

    impl<S> Receiver for MutableSelf<S> {
        type Receiver = S;
    }

    /// Marker trait for all functions that accept a `self` reference as the first parameter.
    ///
    /// See also [`BareMaybeVarargs`] and [`BareExactArgs`].
    pub trait Method<S>: method::Sealed
    where
        S: ?Sized,
    {
    }

    impl<'s, Args> Method<super::RawSelf<'s>> for FallibleRawSelf<Args> {}
    impl<'s, Args> Method<super::RawSelf<'s>> for InfallibleRawSelf<Args> {}
    impl<S, Args> Method<S::Receiver> for FallibleSelf<S, Args> where S: Receiver {}
    impl<S, Args> Method<S::Receiver> for InfallibleSelf<S, Args> where S: Receiver {}
}

impl<Ret, Err, F> ForeignFunction<ffvariants::VarargsFallible> for F
where
    Ret: IntoValue + 'static,
    Err: std::error::Error + 'static,
    F: Fn(Arguments) -> Result<Ret, Err> + 'static,
{
    type ParameterCount = FunctionParameterCount;

    const PARAMETER_COUNT: Self::ParameterCount = FunctionParameterCount::Varargs;

    fn into_raw_foreign_function(self) -> RawForeignFunction {
        create_rawff(move |env, gc, args| {
            self(Arguments::new(args, env))
                .map(|value| value.into_value_with_environment(env).to_raw(gc))
                .map_err(|error| LanguageErrorKind::User(Box::new(error)))
        })
    }
}

impl<Ret, F> ForeignFunction<ffvariants::VarargsInfallible> for F
where
    Ret: IntoValue + 'static,
    F: Fn(Arguments) -> Ret + 'static,
{
    type ParameterCount = FunctionParameterCount;

    const PARAMETER_COUNT: Self::ParameterCount = FunctionParameterCount::Varargs;

    fn into_raw_foreign_function(self) -> RawForeignFunction {
        create_rawff(move |env, gc, args| {
            Ok(self(Arguments::new(args, env)).into_value_with_environment(env).to_raw(gc))
        })
    }
}