use std::sync::{Arc, RwLock, atomic::{AtomicBool, AtomicU8}};
use stynx_code_config::PermissionSettings;
use stynx_code_errors::AppResult;
use stynx_code_types::{PermissionChecker, PermissionDecision, PermissionMode};
use serde_json::Value;
use crate::application::pattern_matcher::{parse_rule, rule_matches};
use crate::infrastructure::InteractivePermissionChecker;
use crate::infrastructure::prompt_bridge::PromptBridge;
pub struct ConfigAwarePermissionChecker {
settings: PermissionSettings,
interactive: InteractivePermissionChecker,
mode: Arc<AtomicU8>,
skill_allow_rules: RwLock<Vec<String>>,
}
impl ConfigAwarePermissionChecker {
pub fn new(settings: PermissionSettings, mode: Arc<AtomicU8>) -> Self {
Self { settings, interactive: InteractivePermissionChecker::new(), mode, skill_allow_rules: RwLock::new(Vec::new()) }
}
pub fn new_with_pause(settings: PermissionSettings, mode: Arc<AtomicU8>, paused: Arc<AtomicBool>) -> Self {
Self { settings, interactive: InteractivePermissionChecker::with_flag(paused), mode, skill_allow_rules: RwLock::new(Vec::new()) }
}
pub fn pause_flag(&self) -> Arc<AtomicBool> {
self.interactive.pause_flag()
}
pub fn install_prompt_bridge(&self, bridge: PromptBridge) {
self.interactive.bridge_handle().set(bridge);
}
pub fn set_skill_allow_rules(&self, rules: Vec<String>) {
*self.skill_allow_rules.write().unwrap() = rules;
}
pub fn clear_skill_allow_rules(&self) {
self.skill_allow_rules.write().unwrap().clear();
}
}
#[async_trait::async_trait]
impl PermissionChecker for ConfigAwarePermissionChecker {
async fn check(&self, tool_name: &str, input: &Value) -> AppResult<PermissionDecision> {
let mode = PermissionMode::load(&self.mode);
for rule_str in &self.settings.deny {
if rule_matches(&parse_rule(rule_str), tool_name, input) {
return Ok(PermissionDecision::Deny(format!("denied by config rule: {rule_str}")));
}
}
if mode == PermissionMode::AutoAccept || mode == PermissionMode::Bypass {
return Ok(PermissionDecision::Allow);
}
if mode == PermissionMode::Plan {
return Ok(PermissionDecision::Deny("plan mode: only read-only tools allowed".into()));
}
for rule_str in &self.settings.allow {
if rule_matches(&parse_rule(rule_str), tool_name, input) {
return Ok(PermissionDecision::Allow);
}
}
{
let skill_rules = self.skill_allow_rules.read().unwrap();
for rule_str in skill_rules.iter() {
if rule_matches(&parse_rule(rule_str), tool_name, input) {
return Ok(PermissionDecision::Allow);
}
}
}
self.interactive.check(tool_name, input).await
}
}