use pyo3::exceptions::PyValueError;
use pyo3::prelude::*;
use pyo3::types::{PyDict, PyList};
use toml;
pub fn toml2py_val_with_bound<'py>(
py: Python<'py>,
value: &toml::Value,
) -> PyResult<Bound<'py, PyAny>> {
match value {
toml::Value::String(s) => Ok(s.into_pyobject(py)?.into_any()),
toml::Value::Integer(i) => Ok(i.into_pyobject(py)?.into_any()),
toml::Value::Float(f) => Ok(f.into_pyobject(py)?.into_any()),
toml::Value::Boolean(b) => Ok(b.into_pyobject(py)?.to_owned().into_any()),
toml::Value::Datetime(dt) => Ok(dt.to_string().into_pyobject(py)?.into_any()),
toml::Value::Array(arr) => {
let py_list = PyList::empty(py);
for item in arr {
let py_item = toml2py_val_with_bound(py, item)?;
py_list.append(py_item)?;
}
Ok(py_list.into_any())
},
toml::Value::Table(table) => {
let py_dict = PyDict::new(py);
for (key, value) in table.iter() {
let py_value = toml2py_val_with_bound(py, value)?;
py_dict.set_item(key, py_value)?;
}
Ok(py_dict.into_any())
},
}
}
pub fn toml2py_val(value: &toml::Value) -> PyResult<Py<PyAny>> {
Python::with_gil(|py| Ok(toml2py_val_with_bound(py, value)?.unbind()))
}
pub fn toml2py(toml: &toml::Value) -> PyResult<Py<PyDict>> {
Python::with_gil(|py| match toml {
toml::Value::Table(table) => {
let py_dict = PyDict::new(py);
for (key, value) in table.iter() {
let py_value = toml2py_val(value)?;
py_dict.set_item(key, py_value)?;
}
Ok(py_dict.unbind())
},
_ => Err(PyValueError::new_err("TOML value must represent a table")),
})
}
pub fn tomlstr2py(toml_str: &str) -> PyResult<Py<PyDict>> {
let value: toml::Value = toml::de::from_str(toml_str)
.map_err(|e| PyValueError::new_err(format!("Failed to parse TOML string: {}", e)))?;
toml2py(&value)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_toml2py() {
pyo3::prepare_freethreaded_python();
let toml_str = r#"
[package]
name = "example"
version = "0.1.0"
authors = ["Alice", "Bob"]
license = "MIT"
description = "An example package"
keywords = ["example", "rust", "toml"]
homepage = "https://example.com"
repository = ""
[dependencies]
pyo3 = { version = "0.15", features = ["extension-module"] }
numpy = "1.21"
[features]
default = ["numpy"]
optional = ["numpy"]
[build]
build = "build.rs"
"#;
let py_obj = tomlstr2py(toml_str).unwrap();
Python::with_gil(|py| {
let dict = py_obj.into_bound(py);
println!("Converted TOML to PyObject: {:?}", dict);
});
}
#[test]
fn test_toml2py_2() {
pyo3::prepare_freethreaded_python();
let toml_str = r#"
convergence_energy = 1.0e-8
convergence_grms = 1.0e-6
convergence_gmax = 1.0e-6
convergence_drms = 1.0e-4
convergence_dmax = 1.0e-4
"#;
let py_obj = tomlstr2py(toml_str).unwrap();
Python::with_gil(|py| {
let dict = py_obj.into_bound(py);
println!("Converted TOML to PyObject: {:?}", dict);
});
}
}