use crate::{
ebi_framework::{
ebi_command::{EBI_COMMANDS, EbiCommand, search_command_in_source_files},
ebi_output::EbiOutput,
},
python::python::pm4py_function_name,
};
use ebi_objects::anyhow::Result;
pub fn generate_pm4py_module() -> Result<EbiOutput> {
let imports = format!(
"#![allow(unsafe_op_in_unsafe_fn)]
#![allow(unused_variables)]
// This file has been automatically generated. Manual changes will be overridden.
use pyo3::prelude::*;
use pyo3::types::PyAny;
use super::{{python_link::import_or_load, python_export::ExportableToPM4Py}};
use crate::ebi_framework::ebi_command::EbiCommand;"
);
let mut functions = String::new();
let mut module = format!(
"#[pymodule]\npub fn ebi(_py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> {{"
);
for path in EBI_COMMANDS.get_command_paths() {
if path.last().unwrap().is_in_python() {
let (fn_name, body) = ebi_command_to_pm4py_function(&path)?;
functions.push_str(&body);
module.push_str(&format!(
" m.add_function(wrap_pyfunction!({}, m)?)?;\n",
fn_name
));
}
}
module.push_str(
" Ok(())
}
",
);
let result = format!("{}\n\n{}\n\n{}", imports, functions, module);
Ok(EbiOutput::String(result))
}
fn ebi_command_to_pm4py_function(path: &Vec<&EbiCommand>) -> Result<(String, String)> {
let fn_name = pm4py_function_name(path);
let library_name = search_command_in_source_files(path)?;
let (input_types, exact_arithmetic) = if let EbiCommand::Command {
input_types,
exact_arithmetic,
..
} = path[path.len() - 1]
{
(input_types, *exact_arithmetic)
} else {
return Ok((String::new(), String::new()));
};
let mut body = format!(
r###"#[pyfunction]
fn {fname}(py: Python<'_>, {args}) -> PyResult<Py<PyAny>> {{
{exact}
let command: &&EbiCommand = &&{library_name};
let input_types = match **command {{
EbiCommand::Command {{ input_types, .. }} => input_types,
_ => return Err(pyo3::exceptions::PyValueError::new_err("Expected a command.")),
}};
"###,
fname = fn_name,
args = (0..input_types.len())
.map(|i| format!("arg{}: &Bound<'_, PyAny>", i))
.collect::<Vec<_>>()
.join(", "),
exact = if exact_arithmetic {
"ebi_objects::ebi_arithmetic::exact::set_exact_globally(true);"
} else {
"ebi_objects::ebi_arithmetic::exact::set_exact_globally(false);"
},
library_name = library_name
);
for idx in 0..input_types.len() {
body.push_str(&format!(r###" let input{idx} = import_or_load(arg{idx}, input_types[{idx}], {idx})
.map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("Could not import argument {idx}: {{}}", e)))?;
"###,
idx = idx
));
}
let inputs = (0..input_types.len())
.map(|i| format!("input{}", i))
.collect::<Vec<_>>()
.join(", ");
body.push_str(&format!(
r###" let inputs = vec![{}];
// Execute the command.
let result = command.execute_with_inputs(inputs)
.map_err(|e| pyo3::exceptions::PyException::new_err(format!("Command error: {{}}", e)))?
.export_to_pm4py(py)
.map_err(|e| pyo3::exceptions::PyException::new_err(format!("Export error: {{}}", e)))?;
Ok(result)
}}
"###,
inputs
));
Ok((fn_name, body))
}