ricecoder_storage/
manager.rs1use crate::error::{StorageError, StorageResult};
4use crate::types::{ResourceType, StorageMode};
5use std::path::{Path, PathBuf};
6
7pub trait StorageManager: Send + Sync {
9 fn global_path(&self) -> &PathBuf;
11
12 fn project_path(&self) -> Option<&PathBuf>;
14
15 fn mode(&self) -> StorageMode;
17
18 fn global_resource_path(&self, resource_type: ResourceType) -> PathBuf;
20
21 fn project_resource_path(&self, resource_type: ResourceType) -> Option<PathBuf>;
23
24 fn is_first_run(&self) -> bool;
26}
27
28pub struct PathResolver;
30
31impl PathResolver {
32 pub fn resolve_global_path() -> StorageResult<PathBuf> {
39 if let Ok(home_override) = std::env::var("RICECODER_HOME") {
41 let path = PathBuf::from(home_override);
42 return Ok(path);
43 }
44
45 if let Some(docs_dir) = dirs::document_dir() {
47 let ricecoder_path = docs_dir.join(".ricecoder");
48 return Ok(ricecoder_path);
49 }
50
51 if let Some(home_dir) = dirs::home_dir() {
53 let ricecoder_path = home_dir.join(".ricecoder");
54 return Ok(ricecoder_path);
55 }
56
57 Err(StorageError::path_resolution_error(
58 "Could not determine home directory",
59 ))
60 }
61
62 pub fn resolve_project_path() -> PathBuf {
64 PathBuf::from(".agent")
65 }
66
67 pub fn expand_home(path: &Path) -> StorageResult<PathBuf> {
69 let path_str = path
70 .to_str()
71 .ok_or_else(|| StorageError::path_resolution_error("Invalid path encoding"))?;
72
73 if path_str.starts_with("~") {
74 if let Some(home_dir) = dirs::home_dir() {
75 let expanded = if path_str == "~" {
76 home_dir
77 } else {
78 home_dir.join(&path_str[2..])
79 };
80 return Ok(expanded);
81 }
82 }
83
84 Ok(path.to_path_buf())
85 }
86}
87
88#[cfg(test)]
89mod tests {
90 use super::*;
91
92 #[test]
93 fn test_resolve_global_path_with_env_override() {
94 std::env::set_var("RICECODER_HOME", "/tmp/ricecoder-test");
96 let path = PathResolver::resolve_global_path().expect("Should resolve path");
97 assert_eq!(path, PathBuf::from("/tmp/ricecoder-test"));
98 std::env::remove_var("RICECODER_HOME");
99 }
100
101 #[test]
102 fn test_resolve_global_path_without_env() {
103 std::env::remove_var("RICECODER_HOME");
105 let path = PathResolver::resolve_global_path().expect("Should resolve path");
106 assert!(path.to_string_lossy().contains(".ricecoder"));
108 }
109
110 #[test]
111 fn test_resolve_project_path() {
112 let path = PathResolver::resolve_project_path();
113 assert_eq!(path, PathBuf::from(".agent"));
114 }
115
116 #[test]
117 fn test_expand_home_with_tilde() {
118 let path = PathBuf::from("~/.ricecoder");
119 let expanded = PathResolver::expand_home(&path).expect("Should expand");
120 assert!(!expanded.to_string_lossy().contains("~"));
121 }
122
123 #[test]
124 fn test_expand_home_without_tilde() {
125 let path = PathBuf::from("/tmp/ricecoder");
126 let expanded = PathResolver::expand_home(&path).expect("Should expand");
127 assert_eq!(expanded, path);
128 }
129}