#![allow(clippy::module_name_repetitions)]
use serde::{Deserialize, Serialize};
use super::attribute::Attribute;
use crate::error::Error;
use std::{
collections::HashMap,
fmt::Debug,
ops::{BitAnd, BitOr},
};
const OPERATOR_SIZE: usize = 2;
#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum AccessPolicy {
Attr(Attribute),
And(Box<AccessPolicy>, Box<AccessPolicy>),
Or(Box<AccessPolicy>, Box<AccessPolicy>),
All, }
impl PartialEq for AccessPolicy {
fn eq(&self, other: &Self) -> bool {
let mut attributes_mapping = HashMap::<Attribute, u32>::new();
let left_to_u32 = self.to_u32(&mut attributes_mapping);
let right_to_u32 = other.to_u32(&mut attributes_mapping);
if left_to_u32 != right_to_u32 {
false
} else {
self.attributes() == other.attributes()
}
}
}
impl AccessPolicy {
pub fn new(axis: &str, attribute: &str) -> Self {
Self::Attr(Attribute::new(axis, attribute))
}
fn to_u32(&self, attribute_mapping: &mut HashMap<Attribute, u32>) -> u32 {
match self {
Self::Attr(attr) => {
if let Some(integer_value) = attribute_mapping.get(attr) {
*integer_value
} else {
let max = (attribute_mapping.len() + 1) as u32;
attribute_mapping.insert(attr.clone(), max);
max
}
}
Self::And(l, r) => l.to_u32(attribute_mapping) * r.to_u32(attribute_mapping),
Self::Or(l, r) => l.to_u32(attribute_mapping) + r.to_u32(attribute_mapping),
Self::All => 0,
}
}
pub fn from_axes(axes_attributes: &HashMap<String, Vec<String>>) -> Result<Self, Error> {
let mut access_policies: Vec<Self> = Vec::with_capacity(axes_attributes.len());
for (axis, attributes) in axes_attributes {
access_policies.push(
attributes
.iter()
.map(|x| Attribute::new(axis, x).into())
.reduce(BitOr::bitor)
.ok_or_else(|| Error::MissingAttribute {
item: None,
axis_name: Some(axis.clone()),
})?,
);
}
let access_policy = access_policies
.iter()
.cloned()
.reduce(BitAnd::bitand)
.ok_or(Error::MissingAxis)?;
Ok(access_policy)
}
fn find_next_parenthesis(boolean_expression: &str) -> Result<usize, Error> {
let mut count = 0;
let mut right_closing_parenthesis = None;
for (index, c) in boolean_expression.chars().enumerate() {
match c {
'(' => count += 1,
')' => count -= 1,
_ => {}
};
if count < 0 {
right_closing_parenthesis = Some(index);
break;
}
}
right_closing_parenthesis.ok_or_else(|| {
Error::InvalidBooleanExpression(format!(
"Missing closing parenthesis in boolean expression {boolean_expression}"
))
})
}
fn sanitize_spaces(boolean_expression: &str) -> String {
let trim_closure = |expr: &str, separator: &str| -> String {
let expression = expr
.split(separator)
.collect::<Vec<_>>()
.into_iter()
.map(str::trim)
.collect::<Vec<_>>();
let mut expression_chars = Vec::<char>::new();
for (i, s) in expression.iter().enumerate() {
if i == 0 && s.is_empty() {
expression_chars.append(&mut separator.chars().collect::<Vec<_>>());
} else {
expression_chars.append(&mut s.chars().collect::<Vec<_>>());
if i != expression.len() - 1 {
expression_chars.append(&mut separator.chars().collect::<Vec<_>>());
}
}
}
expression_chars.iter().collect::<String>()
};
let mut output = boolean_expression.to_string();
for sep in ["(", ")", "||", "&&", "::"] {
output = trim_closure(output.as_str(), sep);
}
output
}
fn decompose_expression(
boolean_expression: &str,
split_position: usize,
) -> Result<(String, Option<String>, Option<String>), Error> {
if split_position > boolean_expression.len() {
return Err(Error::InvalidBooleanExpression(format!(
"Cannot split boolean expression {boolean_expression} at position \
{split_position} since {split_position} is greater than the size of \
{boolean_expression}"
)));
}
let left_part = &boolean_expression[..split_position];
if split_position == boolean_expression.len() {
return Ok((left_part.to_string(), None, None));
}
let next_char = boolean_expression
.chars()
.nth(split_position)
.unwrap_or_default();
let mut split_position = split_position;
if next_char == ')' {
split_position += 1;
}
if split_position == boolean_expression.len() {
return Ok((left_part.to_string(), None, None));
}
let operator = &boolean_expression[split_position..split_position + OPERATOR_SIZE];
let right_part = &boolean_expression[split_position + OPERATOR_SIZE..];
Ok((
left_part.to_string(),
Some(operator.to_string()),
Some(right_part.to_string()),
))
}
pub fn from_boolean_expression(boolean_expression: &str) -> Result<Self, Error> {
let boolean_expression_example = "(Department::HR || Department::RnD) && Level::level_2";
let boolean_expression = Self::sanitize_spaces(boolean_expression);
if !boolean_expression.contains("::") {
return Err(Error::InvalidBooleanExpression(format!(
"'{boolean_expression}' does not contain any attribute separator '::'. Example: \
{boolean_expression_example}"
)));
}
let first_char = boolean_expression.chars().next().unwrap_or_default();
if first_char == '(' {
let boolean_expression = &boolean_expression[1..];
let c = boolean_expression.matches(')').count();
if c == 0 {
return Err(Error::InvalidBooleanExpression(format!(
"closing parenthesis missing in {boolean_expression}"
)));
}
let matching_closing_parenthesis = Self::find_next_parenthesis(boolean_expression)?;
let (left_part, operator, right_part) =
Self::decompose_expression(boolean_expression, matching_closing_parenthesis)?;
if operator.is_none() {
return Self::from_boolean_expression(left_part.as_str());
}
let operator = operator.unwrap_or_default();
let right_part = right_part.unwrap_or_default();
let ap1 = Box::new(Self::from_boolean_expression(left_part.as_str())?);
let ap2 = Box::new(Self::from_boolean_expression(right_part.as_str())?);
let ap = match operator.as_str() {
"&&" => Ok(Self::And(ap1, ap2)),
"||" => Ok(Self::Or(ap1, ap2)),
_ => Err(Error::UnsupportedOperator(operator.to_string())),
}?;
Ok(ap)
} else {
let or_position = boolean_expression.find("||");
let and_position = boolean_expression.find("&&");
let position = if or_position.is_none() && and_position.is_none() {
0
} else if or_position.is_none() {
and_position.unwrap_or_default()
} else if and_position.is_none() {
or_position.unwrap_or_default()
} else {
std::cmp::min(
or_position.unwrap_or_default(),
and_position.unwrap_or_default(),
)
};
if position == 0 {
let attribute_vec = boolean_expression.split("::").collect::<Vec<_>>();
if attribute_vec.len() != 2
|| attribute_vec[0].is_empty()
|| attribute_vec[1].is_empty()
{
return Err(Error::InvalidBooleanExpression(format!(
"'{boolean_expression}' does not respect the format <axis::name>. \
Example: {boolean_expression_example}"
)));
}
return Ok(Self::new(attribute_vec[0], attribute_vec[1]));
}
let (left_part, operator, right_part) =
Self::decompose_expression(&boolean_expression, position)?;
if operator.is_none() {
return Self::from_boolean_expression(left_part.as_str());
}
let operator = operator.unwrap_or_default();
let right_part = right_part.unwrap_or_default();
let ap1 = Box::new(Self::from_boolean_expression(left_part.as_str())?);
let ap2 = Box::new(Self::from_boolean_expression(right_part.as_str())?);
let ap = match operator.as_str() {
"&&" => Ok(Self::And(ap1, ap2)),
"||" => Ok(Self::Or(ap1, ap2)),
_ => Err(Error::UnsupportedOperator(operator.to_string())),
}?;
Ok(ap)
}
}
pub fn attributes(&self) -> Vec<Attribute> {
let mut attributes = self._attributes();
attributes.sort();
attributes
}
fn _attributes(&self) -> Vec<Attribute> {
match self {
Self::Attr(att) => vec![att.clone()],
Self::And(a1, a2) | Self::Or(a1, a2) => {
let mut v = Self::_attributes(a1);
v.extend(Self::_attributes(a2));
v
}
Self::All => vec![],
}
}
}
impl BitAnd for AccessPolicy {
type Output = Self;
fn bitand(self, rhs: Self) -> Self::Output {
Self::And(Box::new(self), Box::new(rhs))
}
}
impl BitOr for AccessPolicy {
type Output = Self;
fn bitor(self, rhs: Self) -> Self::Output {
Self::Or(Box::new(self), Box::new(rhs))
}
}
impl From<Attribute> for AccessPolicy {
fn from(attribute: Attribute) -> Self {
Self::Attr(attribute)
}
}
pub fn ap(axis: &str, attribute_name: &str) -> AccessPolicy {
AccessPolicy::new(axis, attribute_name)
}