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
// Copyright 2018 Mozilla
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use
// this file except in compliance with the License. You may obtain a copy of the
// License at http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.

pub mod strings {
    use std::ffi::{
        CString,
        CStr
    };
    use std::os::raw::c_char;

    use mentat::{
        Keyword,
    };

    pub fn c_char_to_string(cchar: *const c_char) -> &'static str {
        assert!(!cchar.is_null());
        let c_str = unsafe { CStr::from_ptr(cchar) };
        c_str.to_str().unwrap_or("")
    }

    pub fn string_to_c_char<T>(r_string: T) -> *mut c_char where T: Into<String> {
        CString::new(r_string.into()).unwrap().into_raw()
    }

    pub fn kw_from_string(keyword_string: &'static str) -> Keyword {
        // TODO: validate. The input might not be a keyword!
        let attr_name = keyword_string.trim_left_matches(":");
        let parts: Vec<&str> = attr_name.split("/").collect();
        Keyword::namespaced(parts[0], parts[1])
    }
}

pub mod log {
    #[cfg(all(target_os="android", not(test)))]
    use std::ffi::CString;

    #[cfg(all(target_os="android", not(test)))]
    use android;

    // TODO far from ideal. And, we might actually want to println in tests.
    #[cfg(all(not(target_os="android"), not(target_os="ios")))]
    pub fn d(_: &str) {}

    #[cfg(all(target_os="ios", not(test)))]
    pub fn d(message: &str) {
        eprintln!("{}", message);
    }

    #[cfg(all(target_os="android", not(test)))]
    pub fn d(message: &str) {
        let message = CString::new(message).unwrap();
        let message = message.as_ptr();
        let tag = CString::new("Mentat").unwrap();
        let tag = tag.as_ptr();
        unsafe { android::__android_log_write(android::LogLevel::Debug as i32, tag, message) };
    }
}

pub mod error {
    use super::strings::string_to_c_char;
    use std::os::raw::c_char;
    use std::boxed::Box;
    use std::fmt::Display;
    use std::ptr;

    /// Represents an error that occurred on the mentat side. Many mentat FFI functions take a
    /// `*mut ExternError` as the last argument. This is an out parameter that indicates an
    /// error that occurred during that function's execution (if any).
    ///
    /// For functions that use this pattern, if the ExternError's message property is null, then no
    /// error occurred. If the message is non-null then it contains a string description of the
    /// error that occurred.
    ///
    /// Important: This message is allocated on the heap and it is the consumer's responsibility to
    /// free it using `destroy_mentat_string`!
    ///
    /// While this pattern is not ergonomic in Rust, it offers two main benefits:
    ///
    /// 1. It avoids defining a large number of `Result`-shaped types in the FFI consumer, as would
    ///    be required with something like an `struct ExternResult<T> { ok: *mut T, err:... }`
    /// 2. It offers additional type safety over `struct ExternResult { ok: *mut c_void, err:... }`,
    ///    which helps avoid memory safety errors.
    #[repr(C)]
    #[derive(Debug)]
    pub struct ExternError {
        pub message: *mut c_char,
        // TODO: Include an error code here.
    }

    impl Default for ExternError {
        fn default() -> ExternError {
            ExternError { message: ptr::null_mut() }
        }
    }

    /// Translate Result<T, E>, into something C can understand, when T is not `#[repr(C)]`
    ///
    /// - If `result` is `Ok(v)`, moves `v` to the heap and returns a pointer to it, and sets
    ///   `error` to a state indicating that no error occurred (`message` is null).
    /// - If `result` is `Err(e)`, returns a null pointer and stores a string representing the error
    ///   message (which was allocated on the heap and should eventually be freed) into
    ///   `error.message`
    pub unsafe fn translate_result<T, E>(result: Result<T, E>, error: *mut ExternError) -> *mut T
    where E: Display {
        // TODO: can't unwind across FFI...
        assert!(!error.is_null(), "Error output parameter is not optional");
        let error = &mut *error;
        error.message = ptr::null_mut();
        match result {
            Ok(val) => Box::into_raw(Box::new(val)),
            Err(e) => {
                error.message = string_to_c_char(e.to_string());
                ptr::null_mut()
            }
        }
    }

    /// Translate Result<Option<T>, E> into something C can understand, when T is not `#[repr(C)]`.
    ///
    /// - If `result` is `Ok(Some(v))`, moves `v` to the heap and returns a pointer to it, and
    ///   sets `error` to a state indicating that no error occurred (`message` is null).
    /// - If `result` is `Ok(None)` returns a null pointer, but sets `error` to a state indicating
    ///   that no error occurred (`message` is null).
    /// - If `result` is `Err(e)`, returns a null pointer and stores a string representing the error
    ///   message (which was allocated on the heap and should eventually be freed) into
    ///   `error.message`
    pub unsafe fn translate_opt_result<T, E>(result: Result<Option<T>, E>, error: *mut ExternError) -> *mut T
    where E: Display {
        assert!(!error.is_null(), "Error output parameter is not optional");
        let error = &mut *error;
        error.message = ptr::null_mut();
        match result {
            Ok(Some(val)) => Box::into_raw(Box::new(val)),
            Ok(None) => ptr::null_mut(),
            Err(e) => {
                error.message = string_to_c_char(e.to_string());
                ptr::null_mut()
            }
        }
    }

    /// Identical to `translate_result`, but with additional type checking for the case that we have
    /// a `Result<(), E>` (which we're about to drop on the floor).
    pub unsafe fn translate_void_result<E>(result: Result<(), E>, error: *mut ExternError) where E: Display {
        // Note that Box<T> guarantees that if T is zero sized, it's not heap allocated. So not
        // only do we never need to free the return value of this, it would be a problem if someone did.
        translate_result(result, error);
    }
}