use std::result::Result;
use abe_policy::{
Attribute as AttributeRust, EncryptionHint, Policy as PolicyRust, PolicyAxis as PolicyAxisRust,
};
use pyo3::{
exceptions::{PyException, PyTypeError, PyValueError},
prelude::*,
types::{PyBytes, PyList},
};
#[pyclass]
pub struct Attribute(AttributeRust);
#[pymethods]
impl Attribute {
#[new]
pub fn new(axis: &str, name: &str) -> Self {
Self(AttributeRust::new(axis, name))
}
pub fn get_axis(&self) -> &str {
&self.0.axis
}
pub fn get_name(&self) -> &str {
&self.0.name
}
#[allow(clippy::inherent_to_string)]
pub fn to_string(&self) -> String {
format!("{}", self.0)
}
#[staticmethod]
pub fn from_string(string: &str) -> PyResult<Self> {
match AttributeRust::try_from(string) {
Ok(inner) => Ok(Self(inner)),
Err(e) => Err(PyException::new_err(e.to_string())),
}
}
}
#[pyclass]
pub struct PolicyAxis(PolicyAxisRust);
#[pymethods]
impl PolicyAxis {
#[new]
fn new(name: &str, attributes: &PyList, hierarchical: bool) -> PyResult<Self> {
let attributes = attributes
.into_iter()
.map(|attr| {
if let Ok(name) = attr.extract::<&str>() {
Ok((name, EncryptionHint::Classic))
} else if let Ok((name, is_hybridized)) = attr.extract::<(&str, bool)>() {
if is_hybridized {
Ok((name, EncryptionHint::Hybridized))
} else {
Ok((name, EncryptionHint::Classic))
}
} else {
Err(PyValueError::new_err(
"Attributes should be of type List[str] or List[Tuple[str, bool]].",
))
}
})
.collect::<Result<_, _>>()?;
Ok(Self(PolicyAxisRust::new(name, attributes, hierarchical)))
}
pub fn len(&self) -> usize {
self.0.len()
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn get_name(&self) -> &str {
&self.0.name
}
pub fn get_attributes(&self) -> Vec<(String, bool)> {
self.0
.attributes_properties
.iter()
.map(|attribute_properties| {
(
attribute_properties.name.clone(),
attribute_properties.encryption_hint == EncryptionHint::Hybridized,
)
})
.collect()
}
pub fn is_hierarchical(&self) -> bool {
self.0.hierarchical
}
#[allow(clippy::inherent_to_string)]
pub fn to_string(&self) -> String {
format!(
"{}: {:?}, hierarchical: {}",
&self.0.name, &self.0.attributes_properties, &self.0.hierarchical
)
}
}
#[pyclass]
pub struct Policy(pub(super) PolicyRust);
#[pymethods]
impl Policy {
#[new]
#[args(max_attribute_creations = "4294967295")]
fn new(max_attribute_creations: u32) -> Self {
Self(PolicyRust::new(max_attribute_creations))
}
pub fn add_axis(&mut self, axis: &PolicyAxis) -> PyResult<()> {
self.0
.add_axis(axis.0.clone())
.map_err(|e| PyException::new_err(e.to_string()))
}
pub fn rotate(&mut self, attribute: &Attribute) -> PyResult<()> {
self.0
.rotate(&attribute.0)
.map_err(|e| PyException::new_err(e.to_string()))
}
pub fn attributes(&self) -> Vec<Attribute> {
self.0.attributes().into_iter().map(Attribute).collect()
}
pub fn attribute_values(&self, attribute: &Attribute) -> PyResult<Vec<u32>> {
self.0
.attribute_values(&attribute.0)
.map_err(|e| PyException::new_err(e.to_string()))
}
pub fn attribute_current_value(&self, attribute: &Attribute) -> PyResult<u32> {
self.0
.attribute_current_value(&attribute.0)
.map_err(|e| PyException::new_err(e.to_string()))
}
pub fn to_bytes(&self, py: Python) -> PyResult<Py<PyBytes>> {
serde_json::to_vec(&self.0)
.map(|bytes| PyBytes::new(py, bytes.as_slice()).into())
.map_err(|e| PyException::new_err(e.to_string()))
}
#[staticmethod]
pub fn from_bytes(bytes: &PyBytes) -> PyResult<Self> {
serde_json::from_slice(bytes.as_bytes())
.map(Self)
.map_err(|e| PyTypeError::new_err(format!("Error deserializing attributes: {e}")))
}
fn __repr__(&self) -> String {
format!("{}", &self.0)
}
pub fn deep_copy(&self) -> Self {
Self(self.0.clone())
}
}