greentic-bundle 0.5.11

Greentic bundle authoring CLI scaffold with embedded i18n and answer-document contracts.
Documentation
pub mod edit;
pub mod eval;
pub mod gmap;
pub mod parse;

use std::path::Path;

use anyhow::{Context, Result};
use serde::Serialize;

pub use edit::upsert_policy;
pub use eval::{MatchDecision, eval_policy, eval_with_overlay};
pub use gmap::{GmapMutation, GmapTarget};
pub use parse::{GmapPath, GmapRule, Policy, parse_file, parse_path, parse_rule_line, parse_str};

#[derive(Debug, Serialize)]
pub struct AccessMutationPreview {
    pub gmap_path: String,
    pub rule_path: String,
    pub policy: String,
    pub writes: Vec<String>,
}

pub fn mutate_access(
    root: &Path,
    target: &GmapTarget,
    mutation: &GmapMutation,
    dry_run: bool,
) -> Result<AccessMutationPreview> {
    let gmap_path = crate::project::gmap_path(root, target);
    let resolved_writes =
        crate::project::resolved_output_paths(root, &target.tenant, target.team.as_deref());

    let preview = AccessMutationPreview {
        gmap_path: relative_display(root, &gmap_path),
        rule_path: mutation.rule_path.clone(),
        policy: mutation.policy.to_string(),
        writes: std::iter::once(relative_display(root, &gmap_path))
            .chain(
                resolved_writes
                    .iter()
                    .map(|path| relative_display(root, path)),
            )
            .collect(),
    };

    if dry_run {
        return Ok(preview);
    }

    crate::project::ensure_layout(root)?;
    if let Some(team) = &target.team {
        crate::project::ensure_team(root, &target.tenant, team)?;
    } else {
        crate::project::ensure_tenant(root, &target.tenant)?;
    }
    upsert_policy(&gmap_path, &mutation.rule_path, mutation.policy.clone())
        .with_context(|| format!("update gmap {}", gmap_path.display()))?;
    crate::project::sync_project(root)?;
    Ok(preview)
}

fn relative_display(root: &Path, path: &Path) -> String {
    path.strip_prefix(root)
        .unwrap_or(path)
        .display()
        .to_string()
}

pub const ACCESS_LAYOUT_HINT: &str = "tenants/<tenant>/tenant.gmap";