// 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::{CStr, CString}; use std::os::raw::c_char; use mentat::Keyword; /// # Safety /// /// This function TODO pub unsafe fn c_char_to_string(cchar: *const c_char) -> &'static str { assert!(!cchar.is_null()); let c_str = CStr::from_ptr(cchar); c_str.to_str().unwrap_or("") } pub fn string_to_c_char(r_string: T) -> *mut c_char where T: Into, { 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_start_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::boxed::Box; use std::fmt::Display; use std::os::raw::c_char; 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 { 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, 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` /// # Safety /// Be afraid... TODO pub unsafe fn translate_result(result: Result, 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, 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` /// # Safety /// Be afraid... TODO pub unsafe fn translate_opt_result( result: Result, 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). /// # Safety /// Be afraid... TODO pub unsafe fn translate_void_result(result: Result<(), E>, error: *mut ExternError) where E: Display, { // Note that Box 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); } }