use cratestack_core::CratestackContext;
use crate::{PolicyExpr, ReadPolicy, RelationQuantifier};
use super::policy_predicate::render_policy_predicate;
use super::relation::relation_from_sql;
pub(crate) fn render_read_policy_sql(
allow_policies: &[ReadPolicy],
deny_policies: &[ReadPolicy],
ctx: &CratestackContext,
bind_index: &mut usize,
) -> Option<String> {
let render_or_false = |policies: &[ReadPolicy], bind_index: &mut usize| {
if policies.is_empty() {
Some("FALSE".to_owned())
} else {
render_allow_policy_sql(policies, ctx, bind_index)
}
};
if deny_policies.is_empty() {
let allow_sql = render_or_false(allow_policies, bind_index)?;
return Some(format!("({allow_sql})"));
}
let deny_sql = render_allow_policy_sql(deny_policies, ctx, bind_index)?;
let allow_sql = render_or_false(allow_policies, bind_index)?;
Some(format!("(NOT ({deny_sql}) AND ({allow_sql}))"))
}
fn render_allow_policy_sql(
policies: &[ReadPolicy],
ctx: &CratestackContext,
bind_index: &mut usize,
) -> Option<String> {
if policies.is_empty() {
return None;
}
let mut sql = String::new();
for (policy_index, policy) in policies.iter().enumerate() {
if policy_index > 0 {
sql.push_str(" OR ");
}
render_policy_expr_sql(policy.expr, ctx, &mut sql, bind_index);
}
Some(sql)
}
pub(crate) fn render_policy_expr_sql(
expr: PolicyExpr,
ctx: &CratestackContext,
sql: &mut String,
bind_index: &mut usize,
) {
match expr {
PolicyExpr::Predicate(predicate) => {
render_policy_predicate(predicate, ctx, sql, bind_index)
}
PolicyExpr::And(exprs) => render_grouped_policy_sql(exprs, " AND ", ctx, sql, bind_index),
PolicyExpr::Or(exprs) => render_grouped_policy_sql(exprs, " OR ", ctx, sql, bind_index),
}
}
#[allow(clippy::too_many_arguments)]
pub(super) fn render_relation_policy_sql(
quantifier: RelationQuantifier,
parent_table: &'static str,
parent_column: &'static str,
related_table: &'static str,
related_column: &'static str,
expr: &'static PolicyExpr,
ctx: &CratestackContext,
sql: &mut String,
bind_index: &mut usize,
) {
let (open, negate) = match quantifier {
RelationQuantifier::ToOne | RelationQuantifier::Some => ("EXISTS (SELECT 1 ", false),
RelationQuantifier::None => ("NOT EXISTS (SELECT 1 ", false),
RelationQuantifier::Every => ("NOT EXISTS (SELECT 1 ", true),
};
sql.push_str(open);
sql.push_str(&relation_from_sql(
parent_table,
parent_column,
related_table,
related_column,
));
sql.push_str(if negate { " AND NOT (" } else { " AND " });
render_policy_expr_sql(*expr, ctx, sql, bind_index);
sql.push_str(if negate { "))" } else { ")" });
}
fn render_grouped_policy_sql(
exprs: &[PolicyExpr],
joiner: &str,
ctx: &CratestackContext,
sql: &mut String,
bind_index: &mut usize,
) {
sql.push('(');
for (index, expr) in exprs.iter().enumerate() {
if index > 0 {
sql.push_str(joiner);
}
render_policy_expr_sql(*expr, ctx, sql, bind_index);
}
sql.push(')');
}