use std::sync::Arc;
use crate::v0::Visibility;
use super::{Module, Node, Operation, Package, Param, Region, SeqPart, Symbol, Term};
use pyo3::{
PyAny,
exceptions::PyTypeError,
types::{PyAnyMethods, PyStringMethods as _, PyTypeMethods as _},
};
impl<'py> pyo3::FromPyObject<'_, 'py> for Term {
type Error = pyo3::PyErr;
fn extract(term: pyo3::Borrowed<'_, 'py, PyAny>) -> pyo3::PyResult<Self> {
let name = term.get_type().name()?;
Ok(match name.to_cow()?.as_ref() {
"Wildcard" => Self::Wildcard,
"Var" => {
let name = term.getattr("name")?.extract()?;
Self::Var(name)
}
"Apply" => {
let symbol = term.getattr("symbol")?.extract()?;
let args: Vec<_> = term.getattr("args")?.extract()?;
Self::Apply(symbol, args.into())
}
"List" => {
let parts: Vec<_> = term.getattr("parts")?.extract()?;
Self::List(parts.into())
}
"Tuple" => {
let parts: Vec<_> = term.getattr("parts")?.extract()?;
Self::Tuple(parts.into())
}
"Literal" => {
let literal = term.getattr("value")?.extract()?;
Self::Literal(literal)
}
"Func" => {
let region = term.getattr("region")?.extract()?;
Self::Func(Arc::new(region))
}
_ => {
return Err(PyTypeError::new_err(format!(
"Unknown Term type: {}.",
name.to_cow()?
)));
}
})
}
}
impl<'py> pyo3::IntoPyObject<'py> for &Term {
type Target = pyo3::PyAny;
type Output = pyo3::Bound<'py, Self::Target>;
type Error = pyo3::PyErr;
fn into_pyobject(self, py: pyo3::Python<'py>) -> Result<Self::Output, Self::Error> {
let py_module = py.import("hugr.model")?;
match self {
Term::Wildcard => {
let py_class = py_module.getattr("Wildcard")?;
py_class.call0()
}
Term::Var(var_name) => {
let py_class = py_module.getattr("Var")?;
py_class.call1((var_name.as_ref(),))
}
Term::Apply(symbol_name, terms) => {
let py_class = py_module.getattr("Apply")?;
py_class.call1((symbol_name.as_ref(), terms.as_ref()))
}
Term::List(parts) => {
let py_class = py_module.getattr("List")?;
py_class.call1((parts.as_ref(),))
}
Term::Literal(literal) => {
let py_class = py_module.getattr("Literal")?;
py_class.call1((literal,))
}
Term::Tuple(parts) => {
let py_class = py_module.getattr("Tuple")?;
py_class.call1((parts.as_ref(),))
}
Term::Func(region) => {
let py_class = py_module.getattr("Func")?;
py_class.call1((region.as_ref(),))
}
}
}
}
impl<'py> pyo3::FromPyObject<'_, 'py> for SeqPart {
type Error = pyo3::PyErr;
fn extract(part: pyo3::Borrowed<'_, 'py, PyAny>) -> pyo3::PyResult<Self> {
let name = part.get_type().name()?;
if name.to_cow()?.as_ref() == "Splice" {
let term = part.getattr("seq")?.extract()?;
Ok(Self::Splice(term))
} else {
let term = part.extract()?;
Ok(Self::Item(term))
}
}
}
impl<'py> pyo3::IntoPyObject<'py> for &SeqPart {
type Target = pyo3::PyAny;
type Output = pyo3::Bound<'py, Self::Target>;
type Error = pyo3::PyErr;
fn into_pyobject(self, py: pyo3::Python<'py>) -> Result<Self::Output, Self::Error> {
let py_module = py.import("hugr.model")?;
match self {
SeqPart::Item(term) => term.into_pyobject(py),
SeqPart::Splice(term) => {
let py_class = py_module.getattr("Splice")?;
py_class.call1((term,))
}
}
}
}
impl<'py> pyo3::FromPyObject<'_, 'py> for Param {
type Error = pyo3::PyErr;
fn extract(symbol: pyo3::Borrowed<'_, 'py, PyAny>) -> pyo3::PyResult<Self> {
let name = symbol.getattr("name")?.extract()?;
let r#type = symbol.getattr("type")?.extract()?;
Ok(Self { name, r#type })
}
}
impl<'py> pyo3::IntoPyObject<'py> for &Param {
type Target = pyo3::PyAny;
type Output = pyo3::Bound<'py, Self::Target>;
type Error = pyo3::PyErr;
fn into_pyobject(self, py: pyo3::Python<'py>) -> Result<Self::Output, Self::Error> {
let py_module = py.import("hugr.model")?;
let py_class = py_module.getattr("Param")?;
py_class.call1((self.name.as_ref(), &self.r#type))
}
}
impl<'py> pyo3::FromPyObject<'_, 'py> for Visibility {
type Error = pyo3::PyErr;
fn extract(ob: pyo3::Borrowed<'_, 'py, PyAny>) -> pyo3::PyResult<Self> {
match ob.str()?.to_cow()?.as_ref() {
"Public" => Ok(Visibility::Public),
"Private" => Ok(Visibility::Private),
s => Err(PyTypeError::new_err(format!(
"Expected \"Public\" or \"Private\", got {s}",
))),
}
}
}
impl<'py> pyo3::IntoPyObject<'py> for &Visibility {
type Target = pyo3::types::PyAny;
type Output = pyo3::Bound<'py, Self::Target>;
type Error = pyo3::PyErr;
fn into_pyobject(self, py: pyo3::Python<'py>) -> Result<Self::Output, Self::Error> {
let s = match self {
Visibility::Private => "Private",
Visibility::Public => "Public",
};
Ok(pyo3::types::PyString::new(py, s).into_any())
}
}
impl<'py> pyo3::FromPyObject<'_, 'py> for Symbol {
type Error = pyo3::PyErr;
fn extract(symbol: pyo3::Borrowed<'_, 'py, PyAny>) -> pyo3::PyResult<Self> {
let name = symbol.getattr("name")?.extract()?;
let params: Vec<_> = symbol.getattr("params")?.extract()?;
let visibility = symbol.getattr("visibility")?.extract()?;
let constraints: Vec<_> = symbol.getattr("constraints")?.extract()?;
let signature = symbol.getattr("signature")?.extract()?;
Ok(Self {
visibility,
name,
signature,
params: params.into(),
constraints: constraints.into(),
})
}
}
impl<'py> pyo3::IntoPyObject<'py> for &Symbol {
type Target = pyo3::PyAny;
type Output = pyo3::Bound<'py, Self::Target>;
type Error = pyo3::PyErr;
fn into_pyobject(self, py: pyo3::Python<'py>) -> Result<Self::Output, Self::Error> {
let py_module = py.import("hugr.model")?;
let py_class = py_module.getattr("Symbol")?;
py_class.call1((
self.name.as_ref(),
&self.visibility,
self.params.as_ref(),
self.constraints.as_ref(),
&self.signature,
))
}
}
impl<'py> pyo3::FromPyObject<'_, 'py> for Node {
type Error = pyo3::PyErr;
fn extract(node: pyo3::Borrowed<'_, 'py, PyAny>) -> pyo3::PyResult<Self> {
let operation = node.getattr("operation")?.extract()?;
let inputs: Vec<_> = node.getattr("inputs")?.extract()?;
let outputs: Vec<_> = node.getattr("outputs")?.extract()?;
let regions: Vec<_> = node.getattr("regions")?.extract()?;
let meta: Vec<_> = node.getattr("meta")?.extract()?;
let signature = node.getattr("signature")?.extract()?;
Ok(Self {
operation,
inputs: inputs.into(),
outputs: outputs.into(),
regions: regions.into(),
meta: meta.into(),
signature,
})
}
}
impl<'py> pyo3::IntoPyObject<'py> for &Node {
type Target = pyo3::PyAny;
type Output = pyo3::Bound<'py, Self::Target>;
type Error = pyo3::PyErr;
fn into_pyobject(self, py: pyo3::Python<'py>) -> Result<Self::Output, Self::Error> {
let py_module = py.import("hugr.model")?;
let py_class = py_module.getattr("Node")?;
py_class.call1((
&self.operation,
self.inputs.as_ref(),
self.outputs.as_ref(),
self.regions.as_ref(),
self.meta.as_ref(),
&self.signature,
))
}
}
impl<'py> pyo3::FromPyObject<'_, 'py> for Operation {
type Error = pyo3::PyErr;
fn extract(op: pyo3::Borrowed<'_, 'py, PyAny>) -> pyo3::PyResult<Self> {
let name = op.get_type().name()?;
Ok(match name.to_cow()?.as_ref() {
"InvalidOp" => Self::Invalid,
"Dfg" => Self::Dfg,
"Cfg" => Self::Cfg,
"Block" => Self::Block,
"DefineFunc" => {
let symbol = op.getattr("symbol")?.extract()?;
Self::DefineFunc(Box::new(symbol))
}
"DeclareFunc" => {
let symbol = op.getattr("symbol")?.extract()?;
Self::DeclareFunc(Box::new(symbol))
}
"DeclareConstructor" => {
let symbol = op.getattr("symbol")?.extract()?;
Self::DeclareConstructor(Box::new(symbol))
}
"DeclareOperation" => {
let symbol = op.getattr("symbol")?.extract()?;
Self::DeclareOperation(Box::new(symbol))
}
"DeclareAlias" => {
let symbol = op.getattr("symbol")?.extract()?;
Self::DeclareAlias(Box::new(symbol))
}
"DefineAlias" => {
let symbol = op.getattr("symbol")?.extract()?;
let value = op.getattr("value")?.extract()?;
Self::DefineAlias(Box::new(symbol), value)
}
"TailLoop" => Self::TailLoop,
"Conditional" => Self::Conditional,
"Import" => {
let name = op.getattr("name")?.extract()?;
Self::Import(name)
}
"CustomOp" => {
let operation = op.getattr("operation")?.extract()?;
Self::Custom(operation)
}
_ => return Err(PyTypeError::new_err("Unknown Operation type.")),
})
}
}
impl<'py> pyo3::IntoPyObject<'py> for &Operation {
type Target = pyo3::PyAny;
type Output = pyo3::Bound<'py, Self::Target>;
type Error = pyo3::PyErr;
fn into_pyobject(self, py: pyo3::Python<'py>) -> Result<Self::Output, Self::Error> {
let py_module = py.import("hugr.model")?;
match self {
Operation::Invalid => {
let py_class = py_module.getattr("InvalidOp")?;
py_class.call0()
}
Operation::Dfg => {
let py_class = py_module.getattr("Dfg")?;
py_class.call0()
}
Operation::Cfg => {
let py_class = py_module.getattr("Cfg")?;
py_class.call0()
}
Operation::Block => {
let py_class = py_module.getattr("Block")?;
py_class.call0()
}
Operation::DefineFunc(symbol) => {
let py_class = py_module.getattr("DefineFunc")?;
py_class.call1((symbol.as_ref(),))
}
Operation::DeclareFunc(symbol) => {
let py_class = py_module.getattr("DeclareFunc")?;
py_class.call1((symbol.as_ref(),))
}
Operation::DeclareConstructor(symbol) => {
let py_class = py_module.getattr("DeclareConstructor")?;
py_class.call1((symbol.as_ref(),))
}
Operation::DeclareOperation(symbol) => {
let py_class = py_module.getattr("DeclareOperation")?;
py_class.call1((symbol.as_ref(),))
}
Operation::DeclareAlias(symbol) => {
let py_class = py_module.getattr("DeclareAlias")?;
py_class.call1((symbol.as_ref(),))
}
Operation::DefineAlias(symbol, value) => {
let py_class = py_module.getattr("DefineAlias")?;
py_class.call1((symbol.as_ref(), value))
}
Operation::TailLoop => {
let py_class = py_module.getattr("TailLoop")?;
py_class.call0()
}
Operation::Conditional => {
let py_class = py_module.getattr("Conditional")?;
py_class.call0()
}
Operation::Import(name) => {
let py_class = py_module.getattr("Import")?;
py_class.call1((name.as_ref(),))
}
Operation::Custom(term) => {
let py_class = py_module.getattr("CustomOp")?;
py_class.call1((term,))
}
}
}
}
impl<'py> pyo3::FromPyObject<'_, 'py> for Region {
type Error = pyo3::PyErr;
fn extract(region: pyo3::Borrowed<'_, 'py, PyAny>) -> pyo3::PyResult<Self> {
let kind = region.getattr("kind")?.extract()?;
let sources: Vec<_> = region.getattr("sources")?.extract()?;
let targets: Vec<_> = region.getattr("targets")?.extract()?;
let children: Vec<_> = region.getattr("children")?.extract()?;
let meta: Vec<_> = region.getattr("meta")?.extract()?;
let signature = region.getattr("signature")?.extract()?;
Ok(Self {
kind,
sources: sources.into(),
targets: targets.into(),
children: children.into(),
meta: meta.into(),
signature,
})
}
}
impl<'py> pyo3::IntoPyObject<'py> for &Region {
type Target = pyo3::PyAny;
type Output = pyo3::Bound<'py, Self::Target>;
type Error = pyo3::PyErr;
fn into_pyobject(self, py: pyo3::Python<'py>) -> Result<Self::Output, Self::Error> {
let py_module = py.import("hugr.model")?;
let py_class = py_module.getattr("Region")?;
py_class.call1((
self.kind,
self.sources.as_ref(),
self.targets.as_ref(),
self.children.as_ref(),
self.meta.as_ref(),
&self.signature,
))
}
}
impl<'py> pyo3::FromPyObject<'_, 'py> for Module {
type Error = pyo3::PyErr;
fn extract(module: pyo3::Borrowed<'_, 'py, PyAny>) -> pyo3::PyResult<Self> {
let root = module.getattr("root")?.extract()?;
Ok(Self { root })
}
}
impl<'py> pyo3::IntoPyObject<'py> for &Module {
type Target = pyo3::PyAny;
type Output = pyo3::Bound<'py, Self::Target>;
type Error = pyo3::PyErr;
fn into_pyobject(self, py: pyo3::Python<'py>) -> Result<Self::Output, Self::Error> {
let py_module = py.import("hugr.model")?;
let py_class = py_module.getattr("Module")?;
py_class.call1((&self.root,))
}
}
impl<'py> pyo3::FromPyObject<'_, 'py> for Package {
type Error = pyo3::PyErr;
fn extract(package: pyo3::Borrowed<'_, 'py, PyAny>) -> pyo3::PyResult<Self> {
let modules = package.getattr("modules")?.extract()?;
Ok(Self { modules })
}
}
impl<'py> pyo3::IntoPyObject<'py> for &Package {
type Target = pyo3::PyAny;
type Output = pyo3::Bound<'py, Self::Target>;
type Error = pyo3::PyErr;
fn into_pyobject(self, py: pyo3::Python<'py>) -> Result<Self::Output, Self::Error> {
let py_module = py.import("hugr.model")?;
let py_class = py_module.getattr("Package")?;
py_class.call1((&self.modules,))
}
}
macro_rules! impl_into_pyobject_owned {
($ident:ty) => {
impl<'py> pyo3::IntoPyObject<'py> for $ident {
type Target = pyo3::PyAny;
type Output = pyo3::Bound<'py, Self::Target>;
type Error = pyo3::PyErr;
fn into_pyobject(self, py: pyo3::Python<'py>) -> Result<Self::Output, Self::Error> {
(&self).into_pyobject(py)
}
}
};
}
impl_into_pyobject_owned!(Term);
impl_into_pyobject_owned!(SeqPart);
impl_into_pyobject_owned!(Param);
impl_into_pyobject_owned!(Symbol);
impl_into_pyobject_owned!(Module);
impl_into_pyobject_owned!(Package);
impl_into_pyobject_owned!(Node);
impl_into_pyobject_owned!(Visibility);
impl_into_pyobject_owned!(Region);
impl_into_pyobject_owned!(Operation);