lief 1.0.0

Official Rust bindings for LIEF
use cxx::memory::UniquePtrTarget;
use lief_ffi as ffi;
use std::marker::PhantomData;

use crate::Range;

#[doc(hidden)]
pub trait FromFFI<T: UniquePtrTarget> {
    fn from_ffi(ptr: cxx::UniquePtr<T>) -> Self;
}

#[doc(hidden)]
pub trait AsFFI<U> {
    fn as_ffi(&self) -> &U;

    #[allow(dead_code)]
    fn as_mut_ffi(&mut self) -> std::pin::Pin<&mut U>;
}

#[doc(hidden)]
pub fn into_optional<T: FromFFI<U>, U: UniquePtrTarget>(raw_ffi: cxx::UniquePtr<U>) -> Option<T> {
    if raw_ffi.is_null() {
        None
    } else {
        Some(T::from_ffi(raw_ffi))
    }
}

#[doc(hidden)]
pub fn into_ranges(raw_ffi: cxx::UniquePtr<cxx::CxxVector<ffi::Range>>) -> Vec<Range> {
    let cxx_vec = raw_ffi.as_ref().unwrap();
    if cxx_vec.is_empty() {
        return Vec::new();
    }

    let mut rust_range: Vec<Range> = Vec::with_capacity(cxx_vec.len());

    for ffi_range in cxx_vec {
        rust_range.push(Range::from_ffi(ffi_range));
    }

    rust_range
}

pub struct Iterator<'a, Parent: UniquePtrTarget, It: UniquePtrTarget> {
    #[doc(hidden)]
    pub it: cxx::UniquePtr<It>,
    _owner: PhantomData<&'a Parent>,
}

impl<'a, Parent: UniquePtrTarget, It: UniquePtrTarget> Iterator<'a, Parent, It> {
    #[doc(hidden)]
    pub fn new(it: cxx::UniquePtr<It>) -> Self {
        Self {
            it,
            _owner: PhantomData,
        }
    }
}

#[doc(hidden)]
#[macro_export]
macro_rules! declare_iterator_conv {
    ($name:ident, $from:ty, $ffi:ty, $parent:ty, $ffi_iterator:ty, $conv: expr) => {
        pub type $name<'a> = $crate::common::Iterator<'a, $parent, $ffi_iterator>;
        impl<'a> Iterator for $name<'a> {
            type Item = $from;
            fn next(&mut self) -> Option<Self::Item> {
                let next = self.it.as_mut().unwrap().next();
                if next.is_null() {
                    None
                } else {
                    Some($conv(next))
                }
            }
        }
        impl<'a> ExactSizeIterator for $name<'a> {
            fn len(&self) -> usize {
                self.it.as_ref().unwrap().size().try_into().unwrap()
            }
        }
    };
}

#[doc(hidden)]
#[macro_export]
macro_rules! declare_iterator {
    ($name:ident, $from:ty, $ffi:ty, $parent:ty, $ffi_iterator:ty) => {
        $crate::declare_iterator_conv!($name, $from, $ffi, $parent, $ffi_iterator, |n| {
            Self::Item::from_ffi(n)
        });
    };
}

pub struct ForwardIterator<'a, Parent: UniquePtrTarget, It: UniquePtrTarget> {
    #[doc(hidden)]
    pub it: cxx::UniquePtr<It>,
    _owner: PhantomData<&'a Parent>,
}

impl<'a, Parent: UniquePtrTarget, It: UniquePtrTarget> ForwardIterator<'a, Parent, It> {
    #[doc(hidden)]
    pub fn new(it: cxx::UniquePtr<It>) -> Self {
        Self {
            it,
            _owner: PhantomData,
        }
    }
}

#[doc(hidden)]
#[macro_export]
macro_rules! declare_fwd_iterator_conv {
    ($name:ident, $from:ty, $ffi:ty, $parent:ty, $ffi_iterator:ty, $conv: expr) => {
        pub type $name<'a> = $crate::common::ForwardIterator<'a, $parent, $ffi_iterator>;
        impl<'a> Iterator for $name<'a> {
            type Item = $from;
            fn next(&mut self) -> Option<Self::Item> {
                let next = self.it.as_mut().unwrap().next();
                if next.is_null() {
                    None
                } else {
                    Some($conv(next))
                }
            }

            fn size_hint(&self) -> (usize, Option<usize>) {
                let hint = self.it.as_ref().unwrap().size().try_into().unwrap();
                (hint, Some(hint))
            }
        }
    };
}

#[doc(hidden)]
#[macro_export]
macro_rules! declare_fwd_iterator {
    ($name:ident, $from:ty, $ffi:ty, $parent:ty, $ffi_iterator:ty) => {
        $crate::declare_fwd_iterator_conv!($name, $from, $ffi, $parent, $ffi_iterator, |n| {
            Self::Item::from_ffi(n)
        });
    };
}

#[doc(hidden)]
#[macro_export]
macro_rules! declare_lazy_iterator_conv {
    ($name:ident, $from:ty, $ffi:ty, $parent:ty, $ffi_iterator:ty, $conv: expr) => {
        pub type $name<'a> = $crate::common::ForwardIterator<'a, $parent, $ffi_iterator>;
        impl<'a> Iterator for $name<'a> {
            type Item = $from;
            fn next(&mut self) -> Option<Self::Item> {
                let next = self.it.as_mut().unwrap().next();
                if next.is_null() {
                    None
                } else {
                    Some($conv(next))
                }
            }
        }
    };
}

#[doc(hidden)]
#[macro_export]
macro_rules! declare_lazy_iterator {
    ($name:ident, $from:ty, $ffi:ty, $parent:ty, $ffi_iterator:ty) => {
        $crate::declare_lazy_iterator_conv!($name, $from, $ffi, $parent, $ffi_iterator, |n| {
            Self::Item::from_ffi(n)
        });
    };
}

#[doc(hidden)]
#[macro_export]
macro_rules! to_slice {
    ($e:expr) => {
        let content_ptr = $e;
        unsafe {
            if content_ptr.size > 0 {
                return std::slice::from_raw_parts_mut(content_ptr.ptr, content_ptr.size as usize);
            }
            return &[];
        }
    };
}

#[doc(hidden)]
#[macro_export]
macro_rules! __to_result {
    ($func: expr, $self: expr, $conv: expr $(, $args:tt)*) => {
        let mut err: u32 = 0;

        let value = $func($self, $($args,)* std::pin::Pin::new(&mut err));
        if err > 0 {
            return Err($crate::Error::from(err));
        }
        return Ok($conv(value));
    };
}

#[doc(hidden)]
#[macro_export]
macro_rules! to_result {
    ($func: expr, $self: expr $(, $args:tt)*) => {
        $crate::__to_result!($func, &$self.ptr, |x| x $(, $args)*)
    };
}

#[doc(hidden)]
#[macro_export]
macro_rules! to_conv_result {
    ($func: expr, $self: expr, $conv: expr $(, $args:tt)*) => {
        $crate::__to_result!($func, $self, $conv $(, $args)*)
    };
}

#[doc(hidden)]
#[macro_export]
macro_rules! __to_opt {
    ($func: expr, $self: expr, $conv: expr $(, $args:tt)*) => {
        let mut _is_set: u32 = 0;

        let value = $func(&$self, $($args,)* std::pin::Pin::new(&mut _is_set));
        if _is_set == 0 {
            return None;
        }
        return Some($conv(value.into()));
    };
}

#[doc(hidden)]
#[macro_export]
macro_rules! to_opt {
    ($func: expr, $self: expr $(, $args:tt)*) => {
        $crate::__to_opt!($func, $self.ptr, |x| x $(, $args)*)
    };
}

#[doc(hidden)]
#[macro_export]
macro_rules! to_conv_opt {
    ($func: expr, $self: expr, $conv: expr $(, $args:tt)*) => {
        $crate::__to_opt!($func, $self.ptr, $conv $(, $args)*)
    };
}

#[doc(hidden)]
#[macro_export]
macro_rules! to_opt_trait {
    ($func: expr, $self: expr $(, $args:tt)*) => {
        $crate::__to_opt!($func, $self, |x| x $(, $args)*)
    };
}

#[doc(hidden)]
#[macro_export]
macro_rules! to_opt_trait_conv {
    ($func: expr, $self: expr, $conv: expr $(, $args:tt)*) => {
        $crate::__to_opt!($func, $self, $conv $(, $args)*)
    };
}

pub struct StandaloneForwardIterator<It: UniquePtrTarget> {
    #[doc(hidden)]
    pub it: cxx::UniquePtr<It>,
}

impl<It: UniquePtrTarget> StandaloneForwardIterator<It> {
    #[doc(hidden)]
    pub fn new(it: cxx::UniquePtr<It>) -> Self {
        Self { it }
    }
}

#[doc(hidden)]
#[macro_export]
macro_rules! declare_standalone_fwd_iterator {
    ($name:ident, $from:ty, $ffi:ty, $ffi_iterator:ty) => {
        $crate::declare_standalone_fwd_iterator_conv!($name, $from, $ffi, $ffi_iterator, |n| {
            Self::Item::from_ffi(n)
        });
    };
}

#[doc(hidden)]
#[macro_export]
macro_rules! declare_standalone_fwd_iterator_conv {
    ($name:ident, $from:ty, $ffi:ty, $ffi_iterator:ty, $conv: expr) => {
        pub type $name = $crate::common::StandaloneForwardIterator<$ffi_iterator>;
        impl Iterator for $name {
            type Item = $from;
            fn next(&mut self) -> Option<Self::Item> {
                let next = self.it.as_mut().unwrap().next();
                if next.is_null() {
                    None
                } else {
                    Some($conv(next))
                }
            }
        }
    };
}

#[doc(hidden)]
#[macro_export]
macro_rules! __to_ok_result {
    ($func: expr $(, $args:expr)*) => {
        let mut err: u32 = 0;

        $func($($args,)* std::pin::Pin::new(&mut err));
        if err > 0 {
            return Err($crate::Error::from(err));
        }
        return Ok(());
    };
}

#[doc(hidden)]
#[macro_export]
macro_rules! to_ok_result {
    ($func: expr $(, $args:expr)*) => {
        $crate::__to_ok_result!($func $(, $args)*)
    };
}