systemprompt_models/bootstrap/
mod.rs1use std::marker::PhantomData;
9use std::path::Path;
10
11use anyhow::{Context, Result};
12
13use crate::profile_bootstrap::ProfileBootstrap;
14use crate::secrets::SecretsBootstrap;
15use crate::{AppPaths, Config, PathsConfig};
16
17pub trait BootstrapState {}
19
20#[derive(Debug, Clone, Copy)]
22pub struct Uninitialized;
23impl BootstrapState for Uninitialized {}
24
25#[derive(Debug, Clone, Copy)]
27pub struct ProfileInitialized;
28impl BootstrapState for ProfileInitialized {}
29
30#[derive(Debug, Clone, Copy)]
32pub struct SecretsInitialized;
33impl BootstrapState for SecretsInitialized {}
34
35#[derive(Debug, Clone, Copy)]
37pub struct PathsInitialized;
38impl BootstrapState for PathsInitialized {}
39
40#[derive(Debug)]
45pub struct BootstrapSequence<S: BootstrapState> {
46 _state: PhantomData<S>,
47}
48
49impl Default for BootstrapSequence<Uninitialized> {
50 fn default() -> Self {
51 Self::new()
52 }
53}
54
55impl BootstrapSequence<Uninitialized> {
56 #[must_use]
58 pub const fn new() -> Self {
59 Self {
60 _state: PhantomData,
61 }
62 }
63
64 #[allow(clippy::unused_self)]
68 pub fn with_profile(self, path: &Path) -> Result<BootstrapSequence<ProfileInitialized>> {
69 ProfileBootstrap::init_from_path(path)
70 .with_context(|| format!("Profile initialization failed from: {}", path.display()))?;
71
72 Ok(BootstrapSequence {
73 _state: PhantomData,
74 })
75 }
76
77 #[must_use]
79 pub const fn skip_profile(self) -> Self {
80 self
81 }
82}
83
84impl BootstrapSequence<ProfileInitialized> {
85 #[allow(clippy::unused_self)]
89 pub fn with_secrets(self) -> Result<BootstrapSequence<SecretsInitialized>> {
90 SecretsBootstrap::init().context("Secrets initialization failed")?;
91
92 Ok(BootstrapSequence {
93 _state: PhantomData,
94 })
95 }
96
97 #[must_use]
101 pub const fn skip_secrets(self) -> Self {
102 self
103 }
104}
105
106impl BootstrapSequence<SecretsInitialized> {
107 #[allow(clippy::unused_self)]
111 pub fn with_paths(self) -> Result<BootstrapSequence<PathsInitialized>> {
112 let profile = ProfileBootstrap::get()?;
113 AppPaths::init(&profile.paths).context("Failed to initialize paths")?;
114 Config::try_init().context("Failed to initialize configuration")?;
115
116 Ok(BootstrapSequence {
117 _state: PhantomData,
118 })
119 }
120
121 #[allow(clippy::unused_self)]
123 pub fn with_paths_config(
124 self,
125 paths_config: &PathsConfig,
126 ) -> Result<BootstrapSequence<PathsInitialized>> {
127 AppPaths::init(paths_config).context("Failed to initialize paths")?;
128 Config::try_init().context("Failed to initialize configuration")?;
129
130 Ok(BootstrapSequence {
131 _state: PhantomData,
132 })
133 }
134
135 #[must_use]
137 pub const fn skip_paths(self) -> Self {
138 self
139 }
140}
141
142impl BootstrapSequence<PathsInitialized> {
143 #[must_use]
145 #[allow(clippy::unused_self)]
146 pub const fn complete(&self) -> BootstrapComplete {
147 BootstrapComplete { _private: () }
148 }
149}
150
151#[derive(Debug, Clone, Copy)]
153pub struct BootstrapComplete {
154 _private: (),
155}
156
157pub mod presets {
159 use std::path::Path;
160
161 use anyhow::Result;
162
163 use super::{
164 BootstrapComplete, BootstrapSequence, ProfileInitialized, SecretsInitialized, Uninitialized,
165 };
166
167 pub fn full(profile_path: &Path) -> Result<BootstrapComplete> {
169 Ok(BootstrapSequence::<Uninitialized>::new()
170 .with_profile(profile_path)?
171 .with_secrets()?
172 .with_paths()?
173 .complete())
174 }
175
176 pub fn profile_and_secrets(
178 profile_path: &Path,
179 ) -> Result<BootstrapSequence<SecretsInitialized>> {
180 BootstrapSequence::<Uninitialized>::new()
181 .with_profile(profile_path)?
182 .with_secrets()
183 }
184
185 pub fn profile_only(profile_path: &Path) -> Result<BootstrapSequence<ProfileInitialized>> {
187 BootstrapSequence::<Uninitialized>::new().with_profile(profile_path)
188 }
189}