1use std::path::Path;
19
20use serde::{Deserialize, Serialize};
21
22#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
27#[cfg_attr(feature = "specta", derive(specta::Type))]
28#[serde(rename_all = "lowercase")]
29pub enum Aspect {
30 Obsidian,
32 Git,
34 Generic,
36}
37
38pub fn detect_aspects(path: &Path) -> Vec<Aspect> {
43 let mut aspects = vec![Aspect::Generic];
44
45 if path.join(".obsidian").is_dir() {
46 aspects.push(Aspect::Obsidian);
47 }
48
49 if path.join(".git").is_dir() {
50 aspects.push(Aspect::Git);
51 }
52
53 aspects
54}
55
56pub fn detect_obsidian_vault_id(path: &Path) -> Option<String> {
61 let config_dir = dirs::config_dir()?;
62 let obsidian_json = config_dir.join("obsidian").join("obsidian.json");
63
64 let contents = std::fs::read_to_string(&obsidian_json).ok()?;
65 let parsed: serde_json::Value = serde_json::from_str(&contents).ok()?;
66
67 let vaults = parsed.get("vaults")?.as_object()?;
68 let canonical = path.canonicalize().ok()?;
69
70 for (id, info) in vaults {
71 if let Some(vault_path_str) = info.get("path").and_then(|v| v.as_str()) {
72 if let Ok(vault_canonical) = Path::new(vault_path_str).canonicalize() {
73 if vault_canonical == canonical {
74 return Some(id.clone());
75 }
76 }
77 }
78 }
79
80 None
81}
82
83#[cfg(test)]
84mod tests {
85 use super::*;
86 use std::fs;
87
88 fn create_test_dir(name: &str) -> std::path::PathBuf {
89 let dir = std::env::temp_dir().join(format!("dir_aspect_test_{}", name));
90 let _ = fs::remove_dir_all(&dir);
91 fs::create_dir_all(&dir).expect("create test dir");
92 dir
93 }
94
95 fn cleanup(dir: &Path) {
96 let _ = fs::remove_dir_all(dir);
97 }
98
99 #[test]
100 fn plain_directory_returns_generic_only() {
101 let dir = create_test_dir("plain");
102 let aspects = detect_aspects(&dir);
103 assert_eq!(aspects, vec![Aspect::Generic]);
104 cleanup(&dir);
105 }
106
107 #[test]
108 fn obsidian_directory_detected() {
109 let dir = create_test_dir("obsidian");
110 fs::create_dir_all(dir.join(".obsidian")).expect("create .obsidian");
111 let aspects = detect_aspects(&dir);
112 assert_eq!(aspects, vec![Aspect::Generic, Aspect::Obsidian]);
113 cleanup(&dir);
114 }
115
116 #[test]
117 fn git_directory_detected() {
118 let dir = create_test_dir("git");
119 fs::create_dir_all(dir.join(".git")).expect("create .git");
120 let aspects = detect_aspects(&dir);
121 assert_eq!(aspects, vec![Aspect::Generic, Aspect::Git]);
122 cleanup(&dir);
123 }
124
125 #[test]
126 fn both_obsidian_and_git_detected() {
127 let dir = create_test_dir("both");
128 fs::create_dir_all(dir.join(".obsidian")).expect("create .obsidian");
129 fs::create_dir_all(dir.join(".git")).expect("create .git");
130 let aspects = detect_aspects(&dir);
131 assert_eq!(aspects, vec![Aspect::Generic, Aspect::Obsidian, Aspect::Git,]);
132 cleanup(&dir);
133 }
134}