1use serde::{Deserialize, Serialize};
14use std::path::{Path, PathBuf};
15use uuid::Uuid;
16
17pub mod access;
18pub mod chunker;
19pub mod consolidation;
20pub mod db;
21pub mod document;
22pub mod embedder;
23pub mod error;
24pub mod graph;
25pub mod item;
26pub mod mcp;
27pub mod retry;
28
29pub use chunker::{ChunkResult, ChunkingConfig, chunk_content};
30pub use db::Database;
31pub use document::ContentType;
32pub use embedder::{EMBEDDING_DIM, Embedder};
33pub use error::{Result, SedimentError};
34pub use item::{Chunk, ConflictInfo, Item, ItemFilters, SearchResult, StoreResult};
35pub use retry::{RetryConfig, with_retry};
36
37#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
39#[serde(rename_all = "lowercase")]
40pub enum StoreScope {
41 #[default]
43 Project,
44 Global,
46}
47
48impl std::str::FromStr for StoreScope {
49 type Err = String;
50
51 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
52 match s.to_lowercase().as_str() {
53 "project" => Ok(StoreScope::Project),
54 "global" => Ok(StoreScope::Global),
55 _ => Err(format!(
56 "Invalid store scope: {}. Use 'project' or 'global'",
57 s
58 )),
59 }
60 }
61}
62
63impl std::fmt::Display for StoreScope {
64 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
65 match self {
66 StoreScope::Project => write!(f, "project"),
67 StoreScope::Global => write!(f, "global"),
68 }
69 }
70}
71
72#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
74#[serde(rename_all = "lowercase")]
75pub enum ListScope {
76 Project,
78 Global,
80 #[default]
82 All,
83}
84
85impl std::str::FromStr for ListScope {
86 type Err = String;
87
88 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
89 match s.to_lowercase().as_str() {
90 "project" => Ok(ListScope::Project),
91 "global" => Ok(ListScope::Global),
92 "all" => Ok(ListScope::All),
93 _ => Err(format!(
94 "Invalid list scope: {}. Use 'project', 'global', or 'all'",
95 s
96 )),
97 }
98 }
99}
100
101impl std::fmt::Display for ListScope {
102 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
103 match self {
104 ListScope::Project => write!(f, "project"),
105 ListScope::Global => write!(f, "global"),
106 ListScope::All => write!(f, "all"),
107 }
108 }
109}
110
111pub fn central_db_path() -> PathBuf {
116 if let Ok(path) = std::env::var("SEDIMENT_DB") {
117 return PathBuf::from(path);
118 }
119
120 dirs::home_dir()
121 .unwrap_or_else(|| PathBuf::from("."))
122 .join(".sediment")
123 .join("data")
124}
125
126pub fn default_db_path() -> PathBuf {
128 central_db_path()
129}
130
131#[derive(Debug, Clone, Serialize, Deserialize)]
133pub struct ProjectConfig {
134 pub project_id: String,
136}
137
138impl Default for ProjectConfig {
139 fn default() -> Self {
140 Self {
141 project_id: Uuid::new_v4().to_string(),
142 }
143 }
144}
145
146pub fn get_or_create_project_id(project_root: &Path) -> std::io::Result<String> {
151 let config_path = project_root.join(".sediment").join("config");
152
153 if config_path.exists() {
155 let content = std::fs::read_to_string(&config_path)?;
156 if let Ok(config) = serde_json::from_str::<ProjectConfig>(&content) {
157 return Ok(config.project_id);
158 }
159 }
160
161 let config = ProjectConfig::default();
163
164 let sediment_dir = project_root.join(".sediment");
166 std::fs::create_dir_all(&sediment_dir)?;
167
168 let content =
170 serde_json::to_string_pretty(&config).map_err(|e| std::io::Error::other(e.to_string()))?;
171 std::fs::write(&config_path, content)?;
172
173 Ok(config.project_id)
174}
175
176pub fn boost_similarity(
182 base: f32,
183 mem_project: Option<&str>,
184 current_project: Option<&str>,
185) -> f32 {
186 match (mem_project, current_project) {
187 (Some(m), Some(c)) if m == c => (base * 1.15).min(1.0), (Some(_), Some(_)) => base * 0.95, _ => base, }
191}
192
193pub fn find_project_root(start: &Path) -> Option<PathBuf> {
198 let mut current = start.to_path_buf();
199
200 if current.is_file() {
202 current = current.parent()?.to_path_buf();
203 }
204
205 loop {
206 if current.join(".sediment").is_dir() {
208 return Some(current);
209 }
210
211 if current.join(".git").exists() {
213 return Some(current);
214 }
215
216 match current.parent() {
218 Some(parent) => current = parent.to_path_buf(),
219 None => return None,
220 }
221 }
222}
223
224pub fn init_project(project_root: &Path) -> std::io::Result<PathBuf> {
228 let sediment_dir = project_root.join(".sediment");
229 std::fs::create_dir_all(&sediment_dir)?;
230
231 get_or_create_project_id(project_root)?;
233
234 Ok(sediment_dir)
235}