1use std::path::Path;
2
3use anyhow::Result;
4
5use crate::index::{count_bean_formats, Index};
6
7pub fn cmd_sync(beans_dir: &Path) -> Result<()> {
9 let (md_count, yaml_count) = count_bean_formats(beans_dir)?;
11
12 let index = Index::build(beans_dir)?;
13 let count = index.beans.len();
14 index.save(beans_dir)?;
15
16 println!("Index rebuilt: {} beans indexed.", count);
17
18 if md_count > 0 && yaml_count > 0 {
20 eprintln!();
21 eprintln!("Warning: Mixed bean formats detected!");
22 eprintln!(" {} .md files (current format)", md_count);
23 eprintln!(" {} .yaml files (legacy format)", yaml_count);
24 eprintln!();
25 eprintln!("This can cause confusion. Consider migrating legacy files:");
26 eprintln!(" - Remove or archive .yaml files: mkdir -p .beans/legacy && mv .beans/*.yaml .beans/legacy/");
27 eprintln!(" - Or run 'bn doctor' for more details");
28 }
29
30 Ok(())
31}
32
33#[cfg(test)]
34mod tests {
35 use super::*;
36 use crate::bean::Bean;
37 use crate::util::title_to_slug;
38 use std::fs;
39 use tempfile::TempDir;
40
41 #[test]
42 fn sync_rebuilds_index() {
43 let dir = TempDir::new().unwrap();
44 let beans_dir = dir.path().join(".beans");
45 fs::create_dir(&beans_dir).unwrap();
46
47 let bean1 = Bean::new("1", "Task one");
48 let bean2 = Bean::new("2", "Task two");
49
50 let slug1 = title_to_slug(&bean1.title);
51 let slug2 = title_to_slug(&bean2.title);
52
53 bean1
54 .to_file(beans_dir.join(format!("1-{}.md", slug1)))
55 .unwrap();
56 bean2
57 .to_file(beans_dir.join(format!("2-{}.md", slug2)))
58 .unwrap();
59
60 let result = cmd_sync(&beans_dir);
62 assert!(result.is_ok());
63
64 assert!(beans_dir.join("index.yaml").exists());
66
67 let index = Index::load(&beans_dir).unwrap();
69 assert_eq!(index.beans.len(), 2);
70 }
71
72 #[test]
73 fn sync_counts_beans() {
74 let dir = TempDir::new().unwrap();
75 let beans_dir = dir.path().join(".beans");
76 fs::create_dir(&beans_dir).unwrap();
77
78 for i in 1..=5 {
80 let bean = Bean::new(i.to_string(), format!("Task {}", i));
81 let slug = title_to_slug(&bean.title);
82 bean.to_file(beans_dir.join(format!("{}-{}.md", i, slug)))
83 .unwrap();
84 }
85
86 let result = cmd_sync(&beans_dir);
87 assert!(result.is_ok());
88
89 let index = Index::load(&beans_dir).unwrap();
90 assert_eq!(index.beans.len(), 5);
91 }
92
93 #[test]
94 fn sync_empty_project() {
95 let dir = TempDir::new().unwrap();
96 let beans_dir = dir.path().join(".beans");
97 fs::create_dir(&beans_dir).unwrap();
98
99 let result = cmd_sync(&beans_dir);
100 assert!(result.is_ok());
101
102 let index = Index::load(&beans_dir).unwrap();
103 assert_eq!(index.beans.len(), 0);
104 }
105}