Skip to main content

systemprompt_models/
profile_bootstrap.rs

1use anyhow::{Context, Result};
2use std::path::Path;
3use std::sync::OnceLock;
4
5use crate::profile::Profile;
6
7static PROFILE: OnceLock<Profile> = OnceLock::new();
8static PROFILE_PATH: OnceLock<String> = OnceLock::new();
9
10#[derive(Debug, Clone, Copy)]
11pub struct ProfileBootstrap;
12
13#[derive(Debug, thiserror::Error)]
14pub enum ProfileBootstrapError {
15    #[error("Profile not initialized. Call ProfileBootstrap::init() at application startup")]
16    NotInitialized,
17
18    #[error("Profile already initialized")]
19    AlreadyInitialized,
20
21    #[error("Profile path not set. Set SYSTEMPROMPT_PROFILE environment variable")]
22    PathNotSet,
23
24    #[error("Profile validation failed: {0}")]
25    ValidationFailed(String),
26
27    #[error("Failed to load profile: {0}")]
28    LoadFailed(String),
29}
30
31impl ProfileBootstrap {
32    pub fn init() -> Result<&'static Profile> {
33        if PROFILE.get().is_some() {
34            anyhow::bail!(ProfileBootstrapError::AlreadyInitialized);
35        }
36
37        let path_str =
38            std::env::var("SYSTEMPROMPT_PROFILE").map_err(|_| ProfileBootstrapError::PathNotSet)?;
39        let path = std::path::PathBuf::from(path_str);
40
41        let profile = Self::load_from_path_and_validate(&path)
42            .with_context(|| format!("Failed to initialize profile from: {}", path.display()))?;
43
44        PROFILE_PATH
45            .set(path.to_string_lossy().to_string())
46            .map_err(|_| ProfileBootstrapError::AlreadyInitialized)?;
47
48        PROFILE
49            .set(profile)
50            .map_err(|_| ProfileBootstrapError::AlreadyInitialized)?;
51
52        PROFILE
53            .get()
54            .ok_or_else(|| anyhow::anyhow!(ProfileBootstrapError::NotInitialized))
55    }
56
57    pub fn get() -> Result<&'static Profile, ProfileBootstrapError> {
58        PROFILE.get().ok_or(ProfileBootstrapError::NotInitialized)
59    }
60
61    pub fn get_path() -> Result<&'static str, ProfileBootstrapError> {
62        PROFILE_PATH
63            .get()
64            .map(String::as_str)
65            .ok_or(ProfileBootstrapError::NotInitialized)
66    }
67
68    pub fn is_initialized() -> bool {
69        PROFILE.get().is_some()
70    }
71
72    pub fn try_init() -> Result<&'static Profile> {
73        if let Some(profile) = PROFILE.get() {
74            return Ok(profile);
75        }
76        Self::init()
77    }
78
79    pub fn init_from_path(path: &Path) -> Result<&'static Profile> {
80        if PROFILE.get().is_some() {
81            anyhow::bail!(ProfileBootstrapError::AlreadyInitialized);
82        }
83
84        let profile = Self::load_from_path_and_validate(path)
85            .with_context(|| format!("Failed to initialize profile from: {}", path.display()))?;
86
87        PROFILE_PATH
88            .set(path.to_string_lossy().to_string())
89            .map_err(|_| ProfileBootstrapError::AlreadyInitialized)?;
90
91        PROFILE
92            .set(profile)
93            .map_err(|_| ProfileBootstrapError::AlreadyInitialized)?;
94
95        PROFILE
96            .get()
97            .ok_or_else(|| anyhow::anyhow!(ProfileBootstrapError::NotInitialized))
98    }
99
100    fn load_from_path_and_validate(path: &Path) -> Result<Profile> {
101        let content = std::fs::read_to_string(path)
102            .with_context(|| format!("Failed to read profile: {}", path.display()))?;
103
104        let profile = Profile::parse(&content, path)?;
105        profile.validate()?;
106        Ok(profile)
107    }
108}