Mercurial > hg
view rust/hg-cpython/src/dirstate/dirs_multiset.rs @ 42851:64e28b891796
rust-cpython: mark unsafe functions as such
It wasn't trivial to fix leak_immutable() to be safe since we have to
allow immutable operations (e.g. iter()) on the leaked reference. So
let's mark it unsafe for now. Callers must take care of the returned
object to guarantee the memory safety.
I'll revisit this later. I think $leaked<T: 'static> could have a function
that converts itself into $leaked<U: 'static> with a given FnOnce(&T) -> &U,
where T is $inner_struct, and U is $iterator_type for example.
author | Yuya Nishihara <yuya@tcha.org> |
---|---|
date | Sun, 01 Sep 2019 18:06:31 +0900 |
parents | 8f549c46bc64 |
children | 706104dcb2c8 |
line wrap: on
line source
// dirs_multiset.rs // // Copyright 2019 Raphaël Gomès <rgomes@octobus.net> // // This software may be used and distributed according to the terms of the // GNU General Public License version 2 or any later version. //! Bindings for the `hg::dirstate::dirs_multiset` file provided by the //! `hg-core` package. use std::cell::RefCell; use std::convert::TryInto; use cpython::{ exc, ObjectProtocol, PyBytes, PyClone, PyDict, PyErr, PyObject, PyResult, Python, }; use crate::dirstate::extract_dirstate; use crate::ref_sharing::{PySharedRefCell, PySharedState}; use hg::{DirsMultiset, DirstateMapError, DirstateParseError, EntryState}; py_class!(pub class Dirs |py| { data inner: PySharedRefCell<DirsMultiset>; data py_shared_state: PySharedState; // `map` is either a `dict` or a flat iterator (usually a `set`, sometimes // a `list`) def __new__( _cls, map: PyObject, skip: Option<PyObject> = None ) -> PyResult<Self> { let mut skip_state: Option<EntryState> = None; if let Some(skip) = skip { skip_state = Some( skip.extract::<PyBytes>(py)?.data(py)[0] .try_into() .map_err(|e: DirstateParseError| { PyErr::new::<exc::ValueError, _>(py, e.to_string()) })?, ); } let inner = if let Ok(map) = map.cast_as::<PyDict>(py) { let dirstate = extract_dirstate(py, &map)?; DirsMultiset::from_dirstate(&dirstate, skip_state) } else { let map: Result<Vec<Vec<u8>>, PyErr> = map .iter(py)? .map(|o| Ok(o?.extract::<PyBytes>(py)?.data(py).to_owned())) .collect(); DirsMultiset::from_manifest(&map?) }; Self::create_instance( py, PySharedRefCell::new(inner), PySharedState::default() ) } def addpath(&self, path: PyObject) -> PyResult<PyObject> { self.borrow_mut(py)?.add_path( path.extract::<PyBytes>(py)?.data(py), ); Ok(py.None()) } def delpath(&self, path: PyObject) -> PyResult<PyObject> { self.borrow_mut(py)?.delete_path( path.extract::<PyBytes>(py)?.data(py), ) .and(Ok(py.None())) .or_else(|e| { match e { DirstateMapError::PathNotFound(_p) => { Err(PyErr::new::<exc::ValueError, _>( py, "expected a value, found none".to_string(), )) } DirstateMapError::EmptyPath => { Ok(py.None()) } } }) } def __iter__(&self) -> PyResult<DirsMultisetKeysIterator> { let (leak_handle, leaked_ref) = unsafe { self.leak_immutable(py)? }; DirsMultisetKeysIterator::create_instance( py, RefCell::new(Some(leak_handle)), RefCell::new(Box::new(leaked_ref.iter())), ) } def __contains__(&self, item: PyObject) -> PyResult<bool> { Ok(self .inner(py) .borrow() .contains(item.extract::<PyBytes>(py)?.data(py).as_ref())) } }); py_shared_ref!(Dirs, DirsMultiset, inner, DirsMultisetLeakedRef,); impl Dirs { pub fn from_inner(py: Python, d: DirsMultiset) -> PyResult<Self> { Self::create_instance( py, PySharedRefCell::new(d), PySharedState::default(), ) } fn translate_key(py: Python, res: &Vec<u8>) -> PyResult<Option<PyBytes>> { Ok(Some(PyBytes::new(py, res))) } } py_shared_sequence_iterator!( DirsMultisetKeysIterator, DirsMultisetLeakedRef, Vec<u8>, Dirs::translate_key, Option<PyBytes> );