use crate::tree::{PathBuf, PyTree};
use pyo3::exceptions::PyValueError;
use pyo3::prelude::*;
use pyo3::types::PyTuple;
use pyo3::types::PyTupleMethods;
pub struct TreeTransform(Py<PyAny>);
#[derive(Clone)]
pub struct TreeChange {}
impl From<Py<PyAny>> for TreeChange {
fn from(_ob: Py<PyAny>) -> Self {
TreeChange {}
}
}
impl<'a, 'py> FromPyObject<'a, 'py> for TreeChange {
type Error = PyErr;
fn extract(_ob: Borrowed<'a, 'py, PyAny>) -> PyResult<Self> {
Ok(TreeChange {})
}
}
pub struct Conflict(Py<PyAny>);
impl Clone for Conflict {
fn clone(&self) -> Self {
Python::attach(|py| Conflict(self.0.clone_ref(py)))
}
}
impl Conflict {
pub fn associated_filenames(&self) -> Result<Vec<PathBuf>, crate::error::Error> {
let mut v: Vec<PathBuf> = vec![];
Python::attach(|py| {
let ret = self.0.getattr(py, "associated_filenames")?;
for item in ret.bind(py).try_iter()? {
v.push(item?.extract()?);
}
Ok(v)
})
}
pub fn describe(&self) -> Result<String, crate::error::Error> {
Python::attach(|py| {
let ret = self.0.call_method0(py, "describe")?;
Ok(ret.extract(py)?)
})
}
pub fn cleanup<T: PyTree>(&self, tree: &T) -> Result<(), crate::error::Error> {
Python::attach(|py| {
self.0.call_method1(py, "cleanup", (tree.to_object(py),))?;
Ok(())
})
}
}
pub struct PreviewTree(Py<PyAny>);
impl<'py> IntoPyObject<'py> for PreviewTree {
type Target = PyAny;
type Output = Bound<'py, Self::Target>;
type Error = std::convert::Infallible;
fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
Ok(self.0.into_bound(py))
}
}
impl From<Py<PyAny>> for PreviewTree {
fn from(ob: Py<PyAny>) -> Self {
PreviewTree(ob)
}
}
impl TreeTransform {
fn to_object(&self) -> &Py<PyAny> {
&self.0
}
pub(crate) fn as_pyobject(&self) -> &Py<PyAny> {
&self.0
}
pub fn finalize(&self) -> Result<(), crate::error::Error> {
Python::attach(|py| {
self.to_object().call_method0(py, "finalize")?;
Ok(())
})
}
pub fn iter_changes(
&self,
) -> Result<Box<dyn Iterator<Item = TreeChange>>, crate::error::Error> {
let mut v: Vec<TreeChange> = vec![];
Python::attach(|py| {
let ret = self.to_object().call_method0(py, "iter_changes")?;
for item in ret.bind(py).try_iter()? {
v.push(item?.extract()?);
}
Ok(Box::new(v.into_iter()) as Box<dyn Iterator<Item = TreeChange>>)
})
}
pub fn cooked_conflicts(&self) -> Result<Vec<Conflict>, crate::error::Error> {
let mut v: Vec<Conflict> = vec![];
Python::attach(|py| {
let ret = self.to_object().getattr(py, "cooked_conflicts")?;
for item in ret.bind(py).try_iter()? {
v.push(Conflict(item?.into()));
}
Ok(v)
})
}
pub fn get_preview_tree(&self) -> Result<PreviewTree, crate::error::Error> {
Python::attach(|py| {
let ret = self.to_object().getattr(py, "preview_tree")?;
Ok(PreviewTree(ret))
})
}
}
impl From<Py<PyAny>> for TreeTransform {
fn from(ob: Py<PyAny>) -> Self {
TreeTransform(ob)
}
}
impl<'py> IntoPyObject<'py> for TreeTransform {
type Target = PyAny;
type Output = Bound<'py, Self::Target>;
type Error = std::convert::Infallible;
fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
Ok(self.0.into_bound(py))
}
}
impl<'a, 'py> FromPyObject<'a, 'py> for TreeTransform {
type Error = PyErr;
fn extract(ob: Borrowed<'a, 'py, PyAny>) -> PyResult<Self> {
Ok(TreeTransform(ob.to_owned().unbind()))
}
}
#[derive(PartialEq, Eq, Hash, Clone, Debug)]
pub struct TransId(String);
impl<'a, 'py> FromPyObject<'a, 'py> for TransId {
type Error = PyErr;
fn extract(ob: Borrowed<'a, 'py, PyAny>) -> PyResult<Self> {
Ok(TransId(ob.extract::<String>()?))
}
}
impl<'py> IntoPyObject<'py> for TransId {
type Target = PyAny;
type Output = Bound<'py, Self::Target>;
type Error = std::convert::Infallible;
fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
Ok(self.0.into_pyobject(py)?.into_any())
}
}
#[derive(PartialEq, Eq, Hash, Clone, Debug)]
pub enum RawConflict {
UnversionedExecutability(TransId),
NonFileExecutability(TransId),
Overwrite(TransId, String),
ParentLoop(TransId),
UnversionedParent(TransId),
VersioningNoContents(TransId),
VersioningBadKind(TransId),
Duplicate(TransId, TransId, String),
MissingParent(TransId),
NonDirectoryParent(TransId),
}
impl<'py> IntoPyObject<'py> for RawConflict {
type Target = PyAny;
type Output = Bound<'py, Self::Target>;
type Error = std::convert::Infallible;
fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
Ok(match self {
RawConflict::UnversionedExecutability(id) => {
PyTuple::new(py, [("unversioned executability", id)])
.unwrap()
.into_any()
}
RawConflict::NonFileExecutability(id) => {
PyTuple::new(py, [("non-file executability", id)])
.unwrap()
.into_any()
}
RawConflict::Overwrite(id, path) => PyTuple::new(py, [("overwrite", id, path)])
.unwrap()
.into_any(),
RawConflict::ParentLoop(id) => {
PyTuple::new(py, [("parent loop", id)]).unwrap().into_any()
}
RawConflict::UnversionedParent(id) => PyTuple::new(py, [("unversioned parent", id)])
.unwrap()
.into_any(),
RawConflict::VersioningNoContents(id) => {
PyTuple::new(py, [("versioning no contents", id)])
.unwrap()
.into_any()
}
RawConflict::VersioningBadKind(id) => PyTuple::new(py, [("versioning bad kind", id)])
.unwrap()
.into_any(),
RawConflict::Duplicate(id1, id2, path) => {
PyTuple::new(py, [("duplicate", id1, id2, path)])
.unwrap()
.into_any()
}
RawConflict::MissingParent(id) => PyTuple::new(py, [("missing parent", id)])
.unwrap()
.into_any(),
RawConflict::NonDirectoryParent(id) => PyTuple::new(py, [("non-directory parent", id)])
.unwrap()
.into_any(),
})
}
}
impl<'a, 'py> FromPyObject<'a, 'py> for RawConflict {
type Error = PyErr;
fn extract(ob: Borrowed<'a, 'py, PyAny>) -> PyResult<Self> {
let tuple = ob.extract::<Bound<PyTuple>>()?;
match tuple.get_item(0)?.extract::<String>()?.as_str() {
"unversioned executability" => Ok(Self::UnversionedExecutability(TransId(
tuple.get_item(1)?.extract::<String>()?,
))),
"non-file executability" => Ok(Self::NonFileExecutability(TransId(
tuple.get_item(1)?.extract::<String>()?,
))),
"overwrite" => Ok(Self::Overwrite(
TransId(tuple.get_item(1)?.extract::<String>()?),
tuple.get_item(2)?.extract::<String>()?,
)),
"parent loop" => Ok(Self::ParentLoop(TransId(
tuple.get_item(1)?.extract::<String>()?,
))),
"unversioned parent" => Ok(Self::UnversionedParent(TransId(
tuple.get_item(1)?.extract::<String>()?,
))),
"versioning no contents" => Ok(Self::VersioningNoContents(TransId(
tuple.get_item(1)?.extract::<String>()?,
))),
"versioning bad kind" => Ok(Self::VersioningBadKind(TransId(
tuple.get_item(1)?.extract::<String>()?,
))),
"duplicate" => Ok(Self::Duplicate(
TransId(tuple.get_item(1)?.extract::<String>()?),
TransId(tuple.get_item(2)?.extract::<String>()?),
tuple.get_item(3)?.extract::<String>()?,
)),
"missing parent" => Ok(Self::MissingParent(TransId(
tuple.get_item(1)?.extract::<String>()?,
))),
"non-directory parent" => Ok(Self::NonDirectoryParent(TransId(
tuple.get_item(1)?.extract::<String>()?,
))),
_ => Err(PyErr::new::<PyValueError, _>(format!(
"Unknown conflict type: {}",
tuple.get_item(0)?.extract::<String>()?
))),
}
}
}