systemprompt_sync/local/
access_control_sync.rs1use std::path::PathBuf;
12
13use systemprompt_database::DbPool;
14use systemprompt_security::authz::{
15 AccessControlConfig, AccessControlIngestionService, IngestOptions,
16};
17
18use crate::error::{SyncError, SyncResult};
19use crate::models::{LocalSyncDirection, LocalSyncResult};
20
21#[derive(Debug)]
22pub struct AccessControlLocalSync {
23 db: DbPool,
24 yaml_path: PathBuf,
25}
26
27impl AccessControlLocalSync {
28 pub const fn new(db: DbPool, yaml_path: PathBuf) -> Self {
29 Self { db, yaml_path }
30 }
31
32 pub async fn sync_to_db(
33 &self,
34 override_existing: bool,
35 delete_orphans: bool,
36 ) -> SyncResult<LocalSyncResult> {
37 if !self.yaml_path.exists() {
38 return Err(SyncError::MissingConfig(format!(
39 "Access-control config not found at: {}",
40 self.yaml_path.display()
41 )));
42 }
43
44 let raw = std::fs::read_to_string(&self.yaml_path).map_err(|e| {
45 SyncError::internal(format!(
46 "Failed to read {}: {}",
47 self.yaml_path.display(),
48 e
49 ))
50 })?;
51 let config: AccessControlConfig = serde_yaml::from_str(&raw).map_err(|e| {
52 SyncError::invalid_input(format!(
53 "Failed to parse {} as AccessControlConfig: {}",
54 self.yaml_path.display(),
55 e
56 ))
57 })?;
58
59 let svc = AccessControlIngestionService::new(&self.db).map_err(SyncError::internal)?;
60 let report = svc
61 .ingest_config(
62 &config,
63 IngestOptions {
64 override_existing,
65 delete_orphans,
66 },
67 )
68 .await
69 .map_err(SyncError::internal)?;
70
71 Ok(LocalSyncResult {
72 items_synced: report.rules_inserted + report.rules_updated,
73 items_skipped: report.rules_skipped,
74 items_skipped_modified: 0,
75 items_deleted: report.rules_deleted,
76 errors: Vec::new(),
77 direction: LocalSyncDirection::ToDatabase,
78 })
79 }
80}