rust/hg-cpython/src/dirstate/item.rs
author Pierre-Yves David <pierre-yves.david@octobus.net>
Mon, 18 Oct 2021 20:02:15 +0200
changeset 48252 602c8e8411f5
parent 48194 1000db4a71f1
child 48253 948570aa7630
permissions -rw-r--r--
dirstate: add a concept of "fallback" flags to dirstate item The concept is defined and "used" by the flag code, but it is neither persisted nor set anywhere yet. We currently focus on defining the semantic of the attribute. More to come in the next changesets Check the inline documentation for details. Differential Revision: https://phab.mercurial-scm.org/D11686

use cpython::exc;
use cpython::ObjectProtocol;
use cpython::PyBytes;
use cpython::PyErr;
use cpython::PyNone;
use cpython::PyObject;
use cpython::PyResult;
use cpython::Python;
use cpython::PythonObject;
use hg::dirstate::DirstateEntry;
use hg::dirstate::EntryState;
use std::cell::Cell;
use std::convert::TryFrom;

py_class!(pub class DirstateItem |py| {
    data entry: Cell<DirstateEntry>;

    def __new__(
        _cls,
        wc_tracked: bool = false,
        p1_tracked: bool = false,
        p2_info: bool = false,
        has_meaningful_data: bool = true,
        has_meaningful_mtime: bool = true,
        parentfiledata: Option<(u32, u32, u32)> = None,

    ) -> PyResult<DirstateItem> {
        let mut mode_size_opt = None;
        let mut mtime_opt = None;
        if let Some((mode, size, mtime)) = parentfiledata {
            if has_meaningful_data {
                mode_size_opt = Some((mode, size))
            }
            if has_meaningful_mtime {
                mtime_opt = Some(mtime)
            }
        }
        let entry = DirstateEntry::from_v2_data(
            wc_tracked, p1_tracked, p2_info, mode_size_opt, mtime_opt,
        );
        DirstateItem::create_instance(py, Cell::new(entry))
    }

    @property
    def state(&self) -> PyResult<PyBytes> {
        let state_byte: u8 = self.entry(py).get().state().into();
        Ok(PyBytes::new(py, &[state_byte]))
    }

    @property
    def mode(&self) -> PyResult<i32> {
        Ok(self.entry(py).get().mode())
    }

    @property
    def size(&self) -> PyResult<i32> {
        Ok(self.entry(py).get().size())
    }

    @property
    def mtime(&self) -> PyResult<i32> {
        Ok(self.entry(py).get().mtime())
    }

    @property
    def has_fallback_exec(&self) -> PyResult<bool> {
        match self.entry(py).get().get_fallback_exec() {
            Some(_) => Ok(true),
            None => Ok(false),
        }
    }

    @property
    def fallback_exec(&self) -> PyResult<Option<bool>> {
        match self.entry(py).get().get_fallback_exec() {
            Some(exec) => Ok(Some(exec)),
            None => Ok(None),
        }
    }

    @fallback_exec.setter
    def set_fallback_exec(&self, value: Option<PyObject>) -> PyResult<()> {
        match value {
            None => {self.entry(py).get().set_fallback_exec(None);},
            Some(value) => {
            if value.is_none(py) {
                self.entry(py).get().set_fallback_exec(None);
            } else {
                self.entry(py).get().set_fallback_exec(
                    Some(value.is_true(py)?)
                );
            }},
        }
        Ok(())
    }

    @property
    def has_fallback_symlink(&self) -> PyResult<bool> {
        match self.entry(py).get().get_fallback_symlink() {
            Some(_) => Ok(true),
            None => Ok(false),
        }
    }

    @property
    def fallback_symlink(&self) -> PyResult<Option<bool>> {
        match self.entry(py).get().get_fallback_symlink() {
            Some(symlink) => Ok(Some(symlink)),
            None => Ok(None),
        }
    }

    @fallback_symlink.setter
    def set_fallback_symlink(&self, value: Option<PyObject>) -> PyResult<()> {
        match value {
            None => {self.entry(py).get().set_fallback_symlink(None);},
            Some(value) => {
            if value.is_none(py) {
                self.entry(py).get().set_fallback_symlink(None);
            } else {
                self.entry(py).get().set_fallback_symlink(
                    Some(value.is_true(py)?)
                );
            }},
        }
        Ok(())
    }

    @property
    def tracked(&self) -> PyResult<bool> {
        Ok(self.entry(py).get().tracked())
    }

    @property
    def p1_tracked(&self) -> PyResult<bool> {
        Ok(self.entry(py).get().p1_tracked())
    }

    @property
    def added(&self) -> PyResult<bool> {
        Ok(self.entry(py).get().added())
    }


    @property
    def p2_info(&self) -> PyResult<bool> {
        Ok(self.entry(py).get().p2_info())
    }

    @property
    def removed(&self) -> PyResult<bool> {
        Ok(self.entry(py).get().removed())
    }

    @property
    def maybe_clean(&self) -> PyResult<bool> {
        Ok(self.entry(py).get().maybe_clean())
    }

    @property
    def any_tracked(&self) -> PyResult<bool> {
        Ok(self.entry(py).get().any_tracked())
    }

    def v1_state(&self) -> PyResult<PyBytes> {
        let (state, _mode, _size, _mtime) = self.entry(py).get().v1_data();
        let state_byte: u8 = state.into();
        Ok(PyBytes::new(py, &[state_byte]))
    }

    def v1_mode(&self) -> PyResult<i32> {
        let (_state, mode, _size, _mtime) = self.entry(py).get().v1_data();
        Ok(mode)
    }

    def v1_size(&self) -> PyResult<i32> {
        let (_state, _mode, size, _mtime) = self.entry(py).get().v1_data();
        Ok(size)
    }

    def v1_mtime(&self) -> PyResult<i32> {
        let (_state, _mode, _size, mtime) = self.entry(py).get().v1_data();
        Ok(mtime)
    }

    def need_delay(&self, now: i32) -> PyResult<bool> {
        Ok(self.entry(py).get().mtime_is_ambiguous(now))
    }

    @classmethod
    def from_v1_data(
        _cls,
        state: PyBytes,
        mode: i32,
        size: i32,
        mtime: i32,
    ) -> PyResult<Self> {
        let state = <[u8; 1]>::try_from(state.data(py))
            .ok()
            .and_then(|state| EntryState::try_from(state[0]).ok())
            .ok_or_else(|| PyErr::new::<exc::ValueError, _>(py, "invalid state"))?;
        let entry = DirstateEntry::from_v1_data(state, mode, size, mtime);
        DirstateItem::create_instance(py, Cell::new(entry))
    }

    def drop_merge_data(&self) -> PyResult<PyNone> {
        self.update(py, |entry| entry.drop_merge_data());
        Ok(PyNone)
    }

    def set_clean(
        &self,
        mode: u32,
        size: u32,
        mtime: u32,
    ) -> PyResult<PyNone> {
        self.update(py, |entry| entry.set_clean(mode, size, mtime));
        Ok(PyNone)
    }

    def set_possibly_dirty(&self) -> PyResult<PyNone> {
        self.update(py, |entry| entry.set_possibly_dirty());
        Ok(PyNone)
    }

    def set_tracked(&self) -> PyResult<PyNone> {
        self.update(py, |entry| entry.set_tracked());
        Ok(PyNone)
    }

    def set_untracked(&self) -> PyResult<PyNone> {
        self.update(py, |entry| entry.set_untracked());
        Ok(PyNone)
    }
});

impl DirstateItem {
    pub fn new_as_pyobject(
        py: Python<'_>,
        entry: DirstateEntry,
    ) -> PyResult<PyObject> {
        Ok(DirstateItem::create_instance(py, Cell::new(entry))?.into_object())
    }

    pub fn get_entry(&self, py: Python<'_>) -> DirstateEntry {
        self.entry(py).get()
    }

    // TODO: Use https://doc.rust-lang.org/std/cell/struct.Cell.html#method.update instead when it’s stable
    pub fn update(&self, py: Python<'_>, f: impl FnOnce(&mut DirstateEntry)) {
        let mut entry = self.entry(py).get();
        f(&mut entry);
        self.entry(py).set(entry)
    }
}