1use crate::db::{create_pool, run_migrations};
2use crate::error::{IntentError, Result};
3use sqlx::SqlitePool;
4use std::path::PathBuf;
5
6const INTENT_DIR: &str = ".intent-engine";
7const DB_FILE: &str = "project.db";
8
9pub struct ProjectContext {
10 pub root: PathBuf,
11 pub db_path: PathBuf,
12 pub pool: SqlitePool,
13}
14
15impl ProjectContext {
16 pub fn find_project_root() -> Option<PathBuf> {
18 let mut current = std::env::current_dir().ok()?;
19
20 loop {
21 let intent_dir = current.join(INTENT_DIR);
22 if intent_dir.exists() && intent_dir.is_dir() {
23 return Some(current);
24 }
25
26 if !current.pop() {
27 break;
28 }
29 }
30
31 None
32 }
33
34 pub async fn initialize_project() -> Result<Self> {
36 let root = std::env::current_dir()?;
37 let intent_dir = root.join(INTENT_DIR);
38 let db_path = intent_dir.join(DB_FILE);
39
40 if !intent_dir.exists() {
42 std::fs::create_dir_all(&intent_dir)?;
43 }
44
45 let pool = create_pool(&db_path).await?;
47
48 run_migrations(&pool).await?;
50
51 Ok(ProjectContext {
52 root,
53 db_path,
54 pool,
55 })
56 }
57
58 pub async fn load() -> Result<Self> {
60 let root = Self::find_project_root().ok_or(IntentError::NotAProject)?;
61 let db_path = root.join(INTENT_DIR).join(DB_FILE);
62
63 let pool = create_pool(&db_path).await?;
64
65 Ok(ProjectContext {
66 root,
67 db_path,
68 pool,
69 })
70 }
71
72 pub async fn load_or_init() -> Result<Self> {
74 match Self::load().await {
75 Ok(ctx) => Ok(ctx),
76 Err(IntentError::NotAProject) => Self::initialize_project().await,
77 Err(e) => Err(e),
78 }
79 }
80}