areev 0.2.0

Rust SDK for the Areev knowledge database — gRPC and HTTP transports
Documentation
//! `Policy` resource — compliance policy attachment, resolution,
//! enforcement, simulation, and data residency.
//!
//! Mirrors the Python SDK's `client.policy.*` surface.

use serde_json::{Map, Value};

use crate::error::Result;
use crate::http::HttpClient;

/// Policy engine operations.
///
/// Access via [`crate::Areev::policy`]. Requires: admin scope for the
/// mutating methods ([`Policy::set`], [`Policy::enforce`],
/// [`Policy::set_residency`], [`Policy::delete_residency`]).
pub struct Policy<'a> {
    http: &'a HttpClient,
    memory_id: String,
}

impl<'a> Policy<'a> {
    /// Internal constructor — use [`crate::Areev::policy`].
    pub(crate) fn new(http: &'a HttpClient, memory_id: String) -> Self {
        Self { http, memory_id }
    }

    /// Current policy attachments on the memory.
    pub async fn get(&self) -> Result<Value> {
        let path = format!("/memories/{}/policy", self.memory_id);
        self.http._get(&path, None).await
    }

    /// Effective (resolved) policy after inheritance + precedence.
    pub async fn resolved(&self) -> Result<Value> {
        let path = format!("/memories/{}/policy/resolved", self.memory_id);
        self.http._get(&path, None).await
    }

    /// Add and/or remove policies on the memory.
    ///
    /// `downgrade_reason` is required by the server when removing a
    /// policy weakens protection. Requires: admin scope. Emits an audit
    /// event.
    pub async fn set(
        &self,
        add_policies: Option<&[&str]>,
        remove_policies: Option<&[&str]>,
        downgrade_reason: Option<&str>,
    ) -> Result<Value> {
        let mut body = Map::new();
        if let Some(a) = add_policies {
            body.insert(
                "add_policies".into(),
                Value::Array(a.iter().map(|p| Value::String(p.to_string())).collect()),
            );
        }
        if let Some(r) = remove_policies {
            body.insert(
                "remove_policies".into(),
                Value::Array(r.iter().map(|p| Value::String(p.to_string())).collect()),
            );
        }
        if let Some(d) = downgrade_reason {
            body.insert("downgrade_reason".into(), Value::String(d.to_string()));
        }
        let path = format!("/memories/{}/policy", self.memory_id);
        self.http._put(&path, Some(&Value::Object(body))).await
    }

    /// Whether enforcement is active and what it currently blocks.
    pub async fn enforcement_status(&self) -> Result<Value> {
        let path = format!("/memories/{}/policy/enforcement-status", self.memory_id);
        self.http._get(&path, None).await
    }

    /// Run enforcement now. Requires: admin scope. Emits an audit event.
    pub async fn enforce(&self, opts: Option<Value>) -> Result<Value> {
        let body = opts.unwrap_or_else(|| Value::Object(Map::new()));
        let path = format!("/memories/{}/policy/enforce", self.memory_id);
        self.http._post(&path, Some(&body)).await
    }

    /// Dry-run a policy change without applying it.
    pub async fn simulate(&self, opts: Option<Value>) -> Result<Value> {
        let body = opts.unwrap_or_else(|| Value::Object(Map::new()));
        let path = format!("/memories/{}/policy/simulate", self.memory_id);
        self.http._post(&path, Some(&body)).await
    }

    /// Set the data-residency policy for the memory.
    ///
    /// Requires: admin scope. Emits an audit event.
    pub async fn set_residency(&self, residency: Value) -> Result<Value> {
        let path = format!("/memories/{}/policy/residency", self.memory_id);
        self.http._put(&path, Some(&residency)).await
    }

    /// Clear the data-residency policy. Requires: admin scope. Emits an
    /// audit event.
    pub async fn delete_residency(&self) -> Result<()> {
        let path = format!("/memories/{}/policy/residency", self.memory_id);
        self.http._delete(&path).await.map(|_| ())
    }
}