vtcode_core/
project.rs

1//! Simple project management using markdown storage
2//!
3//! This module provides simple project management capabilities using
4//! markdown files for storage instead of complex database systems.
5
6use crate::markdown_storage::{ProjectData, ProjectStorage};
7use anyhow::{Context, Result};
8use std::path::{Path, PathBuf};
9
10/// Simple project manager
11#[derive(Clone)]
12pub struct SimpleProjectManager {
13    /// Project storage using markdown
14    storage: ProjectStorage,
15    /// Workspace root
16    workspace_root: PathBuf,
17}
18
19impl SimpleProjectManager {
20    /// Create a new simple project manager
21    pub fn new(workspace_root: PathBuf) -> Self {
22        let storage_dir = workspace_root.join(".vtcode").join("projects");
23        let storage = ProjectStorage::new(storage_dir);
24
25        Self {
26            storage,
27            workspace_root,
28        }
29    }
30
31    /// Initialize the project manager
32    pub fn init(&self) -> Result<()> {
33        self.storage.init()
34    }
35
36    /// Create a new project
37    pub fn create_project(&self, name: &str, description: Option<&str>) -> Result<()> {
38        let mut project = ProjectData::new(name);
39        project.description = description.map(|s| s.to_string());
40
41        self.storage.save_project(&project)?;
42        Ok(())
43    }
44
45    /// Load a project by name
46    pub fn load_project(&self, name: &str) -> Result<ProjectData> {
47        self.storage.load_project(name)
48    }
49
50    /// List all projects
51    pub fn list_projects(&self) -> Result<Vec<String>> {
52        self.storage.list_projects()
53    }
54
55    /// Delete a project
56    pub fn delete_project(&self, name: &str) -> Result<()> {
57        self.storage.delete_project(name)
58    }
59
60    /// Update project metadata
61    pub fn update_project(&self, project: &ProjectData) -> Result<()> {
62        self.storage.save_project(project)
63    }
64
65    /// Get project data directory
66    pub fn project_data_dir(&self, project_name: &str) -> PathBuf {
67        self.workspace_root
68            .join(".vtcode")
69            .join("projects")
70            .join(project_name)
71    }
72
73    /// Get project config directory
74    pub fn config_dir(&self, project_name: &str) -> PathBuf {
75        self.project_data_dir(project_name).join("config")
76    }
77
78    /// Get project cache directory
79    pub fn cache_dir(&self, project_name: &str) -> PathBuf {
80        self.project_data_dir(project_name).join("cache")
81    }
82
83    /// Get workspace root
84    pub fn workspace_root(&self) -> &Path {
85        &self.workspace_root
86    }
87
88    /// Check if project exists
89    pub fn project_exists(&self, name: &str) -> bool {
90        self.storage
91            .list_projects()
92            .map(|projects| projects.contains(&name.to_string()))
93            .unwrap_or(false)
94    }
95
96    /// Get project info as simple text
97    pub fn get_project_info(&self, name: &str) -> Result<String> {
98        let project = self.load_project(name)?;
99
100        let mut info = format!("Project: {}\n", project.name);
101        if let Some(desc) = &project.description {
102            info.push_str(&format!("Description: {}\n", desc));
103        }
104        info.push_str(&format!("Version: {}\n", project.version));
105        info.push_str(&format!("Tags: {}\n", project.tags.join(", ")));
106
107        if !project.metadata.is_empty() {
108            info.push_str("\nMetadata:\n");
109            for (key, value) in &project.metadata {
110                info.push_str(&format!("  {}: {}\n", key, value));
111            }
112        }
113
114        Ok(info)
115    }
116
117    /// Simple project identification from current directory
118    pub fn identify_current_project(&self) -> Result<String> {
119        // Check for .vtcode-project file
120        let project_file = self.workspace_root.join(".vtcode-project");
121        if project_file.exists() {
122            let content = std::fs::read_to_string(&project_file)?;
123            return Ok(content.trim().to_string());
124        }
125
126        // Fallback to directory name
127        self.workspace_root
128            .file_name()
129            .and_then(|name| name.to_str())
130            .map(|name| name.to_string())
131            .ok_or_else(|| anyhow::anyhow!("Could not determine project name from directory"))
132    }
133
134    /// Set current project
135    pub fn set_current_project(&self, name: &str) -> Result<()> {
136        let project_file = self.workspace_root.join(".vtcode-project");
137        std::fs::write(project_file, name)?;
138        Ok(())
139    }
140}
141
142/// Simple cache using file system
143pub struct SimpleCache {
144    cache_dir: PathBuf,
145}
146
147impl SimpleCache {
148    /// Create a new simple cache
149    pub fn new(cache_dir: PathBuf) -> Self {
150        Self { cache_dir }
151    }
152
153    /// Initialize cache directory
154    pub fn init(&self) -> Result<()> {
155        std::fs::create_dir_all(&self.cache_dir)?;
156        Ok(())
157    }
158
159    /// Store data in cache
160    pub fn store(&self, key: &str, data: &str) -> Result<()> {
161        let file_path = self.cache_dir.join(format!("{}.txt", key));
162        std::fs::write(file_path, data)?;
163        Ok(())
164    }
165
166    /// Load data from cache
167    pub fn load(&self, key: &str) -> Result<String> {
168        let file_path = self.cache_dir.join(format!("{}.txt", key));
169        std::fs::read_to_string(file_path).with_context(|| format!("Cache key '{}' not found", key))
170    }
171
172    /// Check if cache entry exists
173    pub fn exists(&self, key: &str) -> bool {
174        let file_path = self.cache_dir.join(format!("{}.txt", key));
175        file_path.exists()
176    }
177
178    /// Clear cache
179    pub fn clear(&self) -> Result<()> {
180        for entry in std::fs::read_dir(&self.cache_dir)? {
181            let entry = entry?;
182            if entry.path().is_file() {
183                std::fs::remove_file(entry.path())?;
184            }
185        }
186        Ok(())
187    }
188
189    /// List cache entries
190    pub fn list(&self) -> Result<Vec<String>> {
191        let mut entries = Vec::new();
192        for entry in std::fs::read_dir(&self.cache_dir)? {
193            let entry = entry?;
194            if let Some(file_name) = entry.path().file_stem() {
195                if let Some(name) = file_name.to_str() {
196                    entries.push(name.to_string());
197                }
198            }
199        }
200        Ok(entries)
201    }
202}