|
|
// ref_sharing.rs
|
|
|
//
|
|
|
// Copyright 2019 Raphaël Gomès <rgomes@octobus.net>
|
|
|
//
|
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
|
// of this software and associated documentation files (the "Software"), to
|
|
|
// deal in the Software without restriction, including without limitation the
|
|
|
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
|
|
// sell copies of the Software, and to permit persons to whom the Software is
|
|
|
// furnished to do so, subject to the following conditions:
|
|
|
//
|
|
|
// The above copyright notice and this permission notice shall be included in
|
|
|
// all copies or substantial portions of the Software.
|
|
|
//
|
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
|
|
// IN THE SOFTWARE.
|
|
|
|
|
|
//! Macros for use in the `hg-cpython` bridge library.
|
|
|
|
|
|
/// Defines a `py_class!` that acts as a Python iterator over a Rust iterator.
|
|
|
///
|
|
|
/// TODO: this is a bit awkward to use, and a better (more complicated)
|
|
|
/// procedural macro would simplify the interface a lot.
|
|
|
///
|
|
|
/// # Parameters
|
|
|
///
|
|
|
/// * `$name` is the identifier to give to the resulting Rust struct.
|
|
|
/// * `$leaked` corresponds to `UnsafePyLeaked` in the matching `@shared data`
|
|
|
/// declaration.
|
|
|
/// * `$iterator_type` is the type of the Rust iterator.
|
|
|
/// * `$success_func` is a function for processing the Rust `(key, value)`
|
|
|
/// tuple on iteration success, turning it into something Python understands.
|
|
|
/// * `$success_func` is the return type of `$success_func`
|
|
|
///
|
|
|
/// # Safety
|
|
|
///
|
|
|
/// `$success_func` may take a reference, but it's lifetime may be cheated.
|
|
|
/// Do not copy it out of the function call.
|
|
|
///
|
|
|
/// # Example
|
|
|
///
|
|
|
/// ```
|
|
|
/// struct MyStruct {
|
|
|
/// inner: HashMap<Vec<u8>, Vec<u8>>;
|
|
|
/// }
|
|
|
///
|
|
|
/// py_class!(pub class MyType |py| {
|
|
|
/// @shared data inner: MyStruct;
|
|
|
///
|
|
|
/// def __iter__(&self) -> PyResult<MyTypeItemsIterator> {
|
|
|
/// let leaked_ref = self.inner_shared(py).leak_immutable();
|
|
|
/// MyTypeItemsIterator::from_inner(
|
|
|
/// py,
|
|
|
/// unsafe { leaked_ref.map(py, |o| o.iter()) },
|
|
|
/// )
|
|
|
/// }
|
|
|
/// });
|
|
|
///
|
|
|
/// impl MyType {
|
|
|
/// fn translate_key_value(
|
|
|
/// py: Python,
|
|
|
/// res: (&Vec<u8>, &Vec<u8>),
|
|
|
/// ) -> PyResult<Option<(PyBytes, PyBytes)>> {
|
|
|
/// let (f, entry) = res;
|
|
|
/// Ok(Some((
|
|
|
/// PyBytes::new(py, f),
|
|
|
/// PyBytes::new(py, entry),
|
|
|
/// )))
|
|
|
/// }
|
|
|
/// }
|
|
|
///
|
|
|
/// py_shared_iterator!(
|
|
|
/// MyTypeItemsIterator,
|
|
|
/// UnsafePyLeaked<HashMap<'static, Vec<u8>, Vec<u8>>>,
|
|
|
/// MyType::translate_key_value,
|
|
|
/// Option<(PyBytes, PyBytes)>
|
|
|
/// );
|
|
|
/// ```
|
|
|
macro_rules! py_shared_iterator {
|
|
|
(
|
|
|
$name: ident,
|
|
|
$leaked: ty,
|
|
|
$success_func: expr,
|
|
|
$success_type: ty
|
|
|
) => {
|
|
|
py_class!(pub class $name |py| {
|
|
|
data inner: RefCell<$leaked>;
|
|
|
|
|
|
def __next__(&self) -> PyResult<$success_type> {
|
|
|
let mut leaked = self.inner(py).borrow_mut();
|
|
|
let mut iter = unsafe { leaked.try_borrow_mut(py)? };
|
|
|
match iter.next() {
|
|
|
None => Ok(None),
|
|
|
// res may be a reference of cheated 'static lifetime
|
|
|
Some(res) => $success_func(py, res),
|
|
|
}
|
|
|
}
|
|
|
|
|
|
def __iter__(&self) -> PyResult<Self> {
|
|
|
Ok(self.clone_ref(py))
|
|
|
}
|
|
|
});
|
|
|
|
|
|
impl $name {
|
|
|
pub fn from_inner(
|
|
|
py: Python,
|
|
|
leaked: $leaked,
|
|
|
) -> PyResult<Self> {
|
|
|
Self::create_instance(
|
|
|
py,
|
|
|
RefCell::new(leaked),
|
|
|
)
|
|
|
}
|
|
|
}
|
|
|
};
|
|
|
}
|
|
|
|