Skip to main content

joy_core/
context.rs

1// Copyright (c) 2026 Joydev GmbH (joydev.com)
2// SPDX-License-Identifier: MIT
3
4//! Command context: identity, guard, project root.
5//!
6//! Loaded once per command, provides all the infrastructure that
7//! write operations need: who is acting, what are they allowed to do,
8//! and where is the project.
9
10use std::path::PathBuf;
11
12use crate::error::JoyError;
13use crate::guard::{Action, Guard};
14use crate::identity::{self, Identity};
15use crate::store;
16
17/// Shared context for CLI commands.
18pub struct Context {
19    pub root: PathBuf,
20    pub identity: Identity,
21    guard: Guard,
22}
23
24impl Context {
25    /// Load context from the current directory.
26    /// Finds the project root, resolves identity from session or git email,
27    /// and loads Guard with gate config.
28    pub fn load() -> Result<Self, JoyError> {
29        let cwd =
30            std::env::current_dir().map_err(|e| JoyError::Other(format!("current dir: {e}")))?;
31        let root = store::find_project_root(&cwd).ok_or(JoyError::NotInitialized)?;
32        let identity = identity::resolve_identity(&root).unwrap_or(Identity {
33            member: "unknown".into(),
34            delegated_by: None,
35            authenticated: false,
36        });
37        let guard = Guard::load(&root)?;
38        Ok(Self {
39            root,
40            identity,
41            guard,
42        })
43    }
44
45    /// Check and enforce a guard action. Logs events on deny/warn.
46    pub fn enforce(&self, action: &Action, target: &str) -> Result<(), JoyError> {
47        self.guard
48            .check(action, &self.identity)
49            .enforce(&self.root, target, &self.identity)
50    }
51
52    /// Get the identity's log_user string for event logging.
53    pub fn log_user(&self) -> String {
54        self.identity.log_user()
55    }
56}