graphrecords-python 0.5.0

High-performance graph-based data records
Documentation
use crate::{
    graphrecord::{attribute::PyGraphRecordAttribute, value::PyGraphRecordValue},
    querying::{endpoint::PyEdgeEndpointRole, failure_kind::PyFailureKind},
};
use graphrecords_core::graphrecord::{EdgeIndex, GraphRecordValue, NodeIndex};
use graphrecords_query::{
    AttributeName, FailureKind,
    dynamic::DynIndexOwned,
    index::{EdgeEndpointRole, Position, Positional},
    registry::IndexDescriptor,
};
use pyo3::{exceptions::PyTypeError, prelude::*};

pub(super) trait IndexConversion: Sized {
    fn from_python(object: &Bound<'_, PyAny>, descriptor: &IndexDescriptor) -> PyResult<Self>;

    fn to_python(&self, py: Python<'_>) -> PyResult<Py<PyAny>>;
}

impl IndexConversion for DynIndexOwned {
    fn from_python(object: &Bound<'_, PyAny>, descriptor: &IndexDescriptor) -> PyResult<Self> {
        let IndexDescriptor::Domain(domain) = descriptor else {
            return Err(PyTypeError::new_err(
                "an expanded index has no literal form",
            ));
        };

        if domain.is::<Positional>() {
            return object.extract::<Position>().map(Self::Positional);
        }
        if domain.is::<NodeIndex>() {
            return Ok(Self::Node(
                object.extract::<PyGraphRecordAttribute>()?.into(),
            ));
        }
        if domain.is::<EdgeIndex>() {
            return object.extract().map(Self::Edge);
        }
        if domain.is::<AttributeName>() {
            return Ok(Self::Attribute(
                object.extract::<PyGraphRecordAttribute>()?.into(),
            ));
        }
        if domain.is::<GraphRecordValue>() {
            return Ok(Self::Value(object.extract::<PyGraphRecordValue>()?.into()));
        }
        if domain.is::<bool>() {
            return object.extract().map(Self::Bool);
        }
        if domain.is::<EdgeEndpointRole>() {
            return Err(PyTypeError::new_err(
                "an edge-endpoint role has no literal form",
            ));
        }
        if domain.is::<FailureKind>() {
            return Ok(Self::FailureKind(object.extract::<PyFailureKind>()?.into()));
        }

        Err(PyTypeError::new_err(format!(
            "index domain `{}` has no literal form",
            domain.name()
        )))
    }

    fn to_python(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
        match self {
            Self::Positional(position) => Ok(position.into_pyobject(py)?.into_any().unbind()),
            Self::Node(index) | Self::Attribute(index) => {
                Ok(PyGraphRecordAttribute::from(index.clone())
                    .into_pyobject(py)?
                    .unbind())
            }
            Self::Edge(index) => Ok(index.into_pyobject(py)?.into_any().unbind()),
            Self::Value(value) => Ok(PyGraphRecordValue::from(value.clone())
                .into_pyobject(py)?
                .unbind()),
            Self::Bool(value) => Ok(value.into_pyobject(py)?.to_owned().into_any().unbind()),
            Self::EndpointRole(role) => Ok(PyEdgeEndpointRole::from(*role)
                .into_pyobject(py)?
                .into_any()
                .unbind()),
            Self::FailureKind(kind) => Ok(PyFailureKind::from(*kind)
                .into_pyobject(py)?
                .into_any()
                .unbind()),
            Self::Expanded(expanded) => {
                let parent = expanded.parent_index().to_python(py)?;
                let child = match expanded.child_index() {
                    Some(child) => child.to_python(py)?,
                    None => py.None(),
                };

                Ok((parent, child).into_pyobject(py)?.into_any().unbind())
            }
        }
    }
}