#![warn(rust_2018_idioms, unused_qualifications)]
#[cfg(feature = "ffi-trace")]
#[macro_export]
macro_rules! trace {
($($tt:tt)*) => {
::std::println!($($tt)*);
}
}
#[cfg(not(feature = "ffi-trace"))]
#[macro_export]
macro_rules! trace {
($($tt:tt)*) => {};
}
#[cfg(feature = "ffi-trace")]
#[macro_export]
macro_rules! trace_and_return {
($expr:expr, $($tt:tt)*) => {
{
::std::println!($($tt)*);
$expr
}
}
}
#[cfg(not(feature = "ffi-trace"))]
#[macro_export]
macro_rules! trace_and_return {
($expr:expr, $($tt:tt)*) => {
#[allow(clippy::let_and_return)]
$expr
};
}
use anyhow::bail;
use bytes::buf::Buf;
pub use anyhow::Result;
pub mod ffi;
mod ffi_converter_impls;
mod ffi_converter_traits;
pub mod metadata;
mod oneshot;
#[cfg(feature = "scaffolding-ffi-buffer-fns")]
pub use ffi::ffiserialize::FfiBufferElement;
pub use ffi::*;
pub use ffi_converter_traits::{
ConvertError, FfiConverter, FfiConverterArc, HandleAlloc, Lift, LiftRef, LiftReturn, Lower,
LowerError, LowerReturn, TypeId,
};
pub use metadata::*;
pub mod deps {
pub use crate::trace;
pub use anyhow;
#[cfg(feature = "tokio")]
pub use async_compat;
pub use bytes;
pub use static_assertions;
}
const PACKAGE_VERSION: &str = env!("CARGO_PKG_VERSION");
static_assertions::const_assert!(PACKAGE_VERSION.len() < 10);
#[allow(clippy::len_zero)]
pub const fn check_compatible_version(bindgen_version: &'static str) -> bool {
let package_version = PACKAGE_VERSION.as_bytes();
let bindgen_version = bindgen_version.as_bytes();
package_version.len() == bindgen_version.len()
&& (package_version.len() == 0 || package_version[0] == bindgen_version[0])
&& (package_version.len() <= 1 || package_version[1] == bindgen_version[1])
&& (package_version.len() <= 2 || package_version[2] == bindgen_version[2])
&& (package_version.len() <= 3 || package_version[3] == bindgen_version[3])
&& (package_version.len() <= 4 || package_version[4] == bindgen_version[4])
&& (package_version.len() <= 5 || package_version[5] == bindgen_version[5])
&& (package_version.len() <= 6 || package_version[6] == bindgen_version[6])
&& (package_version.len() <= 7 || package_version[7] == bindgen_version[7])
&& (package_version.len() <= 8 || package_version[8] == bindgen_version[8])
&& (package_version.len() <= 9 || package_version[9] == bindgen_version[9])
&& package_version.len() < 10
}
#[macro_export]
macro_rules! assert_compatible_version {
($v:expr $(,)?) => {
uniffi::deps::static_assertions::const_assert!(uniffi::check_compatible_version($v));
};
}
struct UniFfiTag;
pub fn check_remaining(buf: &[u8], num_bytes: usize) -> Result<()> {
if buf.remaining() < num_bytes {
bail!(
"not enough bytes remaining in buffer ({} < {num_bytes})",
buf.remaining(),
);
}
Ok(())
}
#[macro_export]
macro_rules! ffi_converter_rust_buffer_lift_and_lower {
($uniffi_tag:ty) => {
type FfiType = $crate::RustBuffer;
fn lower(v: Self) -> $crate::RustBuffer {
let mut buf = ::std::vec::Vec::new();
<Self as $crate::FfiConverter<$uniffi_tag>>::write(v, &mut buf);
$crate::RustBuffer::from_vec(buf)
}
fn try_lift(buf: $crate::RustBuffer) -> $crate::Result<Self> {
let vec = buf.destroy_into_vec();
let mut buf = vec.as_slice();
let value = <Self as $crate::FfiConverter<$uniffi_tag>>::try_read(&mut buf)?;
match $crate::deps::bytes::Buf::remaining(&buf) {
0 => ::std::result::Result::Ok(value),
n => $crate::deps::anyhow::bail!(
"junk data left in buffer after lifting (count: {n})",
),
}
}
};
}
#[cfg(test)]
mod test {
use super::{FfiConverter, UniFfiTag};
use std::time::{Duration, SystemTime};
#[test]
fn timestamp_roundtrip_post_epoch() {
let expected = SystemTime::UNIX_EPOCH + Duration::new(100, 100);
let result =
<SystemTime as FfiConverter<UniFfiTag>>::try_lift(<SystemTime as FfiConverter<
UniFfiTag,
>>::lower(expected))
.expect("Failed to lift!");
assert_eq!(expected, result)
}
#[test]
fn timestamp_roundtrip_pre_epoch() {
let expected = SystemTime::UNIX_EPOCH - Duration::new(100, 100);
let result =
<SystemTime as FfiConverter<UniFfiTag>>::try_lift(<SystemTime as FfiConverter<
UniFfiTag,
>>::lower(expected))
.expect("Failed to lift!");
assert_eq!(
expected, result,
"Expected results after lowering and lifting to be equal"
)
}
}
#[cfg(test)]
pub mod test_util {
use std::{error::Error, fmt};
use super::*;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct TestError(pub String);
unsafe impl<UT> FfiConverter<UT> for TestError {
ffi_converter_rust_buffer_lift_and_lower!(UniFfiTag);
fn write(obj: TestError, buf: &mut Vec<u8>) {
<String as FfiConverter<UniFfiTag>>::write(obj.0, buf);
}
fn try_read(buf: &mut &[u8]) -> Result<TestError> {
<String as FfiConverter<UniFfiTag>>::try_read(buf).map(TestError)
}
const TYPE_ID_META: MetadataBuffer = MetadataBuffer::new();
}
impl fmt::Display for TestError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl Error for TestError {}
impl<T: Into<String>> From<T> for TestError {
fn from(v: T) -> Self {
Self(v.into())
}
}
derive_ffi_traits!(blanket TestError);
}