1use crate::{app_config::AppConfig, filter::PathFilter};
2use std::{path::PathBuf, sync::mpsc::Receiver};
3
4use anyhow::{Context, Result};
5use log::{debug, warn};
6
7use crate::{repo::GitwatchRepo, watcher::FileWatcher};
8
9pub struct App {
10 commit_on_start: bool,
11 path_filter: PathFilter,
12 repo: GitwatchRepo,
13 repo_path: PathBuf,
14 watch: bool,
15 watcher: FileWatcher,
16}
17
18impl App {
19 pub fn new(config: AppConfig) -> Result<Self> {
20 let repo_path = &config.repository;
21 let repo = GitwatchRepo::new(
22 repo_path,
23 config.commit_message,
24 config.commit_message_script,
25 config.ignore_regex.clone(),
26 config.dry_run,
27 config.remote,
28 )?;
29 let watcher = FileWatcher::new(config.debounce_seconds, config.retries);
30 let path_filter = PathFilter::new(repo_path, config.ignore_regex)?;
31
32 Ok(Self {
33 commit_on_start: config.commit_on_start,
34 path_filter,
35 repo,
36 repo_path: config.repository,
37 watch: config.watch,
38 watcher,
39 })
40 }
41
42 pub fn run(&self, shutdown_rx: Option<Receiver<()>>) -> Result<()> {
43 if self.commit_on_start {
44 self.repo
45 .process_changes()
46 .context("Failed to commit changes")?;
47 }
48
49 if !self.watch {
50 warn!("Watch is disabled");
51 return Ok(());
52 }
53
54 self.watcher.watch(
55 &self.repo_path,
56 |paths| {
57 self.log_changed_paths(paths);
58 self.repo.process_changes()
59 },
60 |path| self.path_filter.is_path_ignored(path),
61 shutdown_rx,
62 )
63 }
64
65 fn log_changed_paths(&self, paths: &[PathBuf]) {
66 let formatted_paths = paths
67 .iter()
68 .map(|p| p.strip_prefix(&self.repo_path).unwrap_or(p))
69 .map(|p| format!(" {}", p.display()))
70 .collect::<Vec<_>>()
71 .join("\n");
72
73 debug!("Detected changes:\n{formatted_paths}");
74 }
75}
76
77#[cfg(test)]
78mod tests {
79 use std::fs;
80
81 use git2::Repository;
82
83 use super::*;
84
85 #[test]
86 fn test_watch_false() -> Result<()> {
87 let temp_dir = tempfile::tempdir()?;
88 let repository = temp_dir.path().to_path_buf();
89 let git_repo = Repository::init(&repository)?;
90
91 fs::write(repository.join("foo.txt"), "bar")?;
93
94 let config = AppConfig {
95 repository,
96 commit_message: Some("test message".to_string()),
97 commit_on_start: false,
98 watch: false,
99 ..AppConfig::default()
100 };
101 let app = App::new(config)?;
102 app.run(None)?;
103
104 assert!(!git_repo.statuses(None)?.is_empty());
105 Ok(())
106 }
107
108 #[test]
109 fn test_commit_on_start() -> Result<()> {
110 let temp_dir = tempfile::tempdir()?;
111 let repository = temp_dir.path().to_path_buf();
112 let git_repo = Repository::init(&repository)?;
113
114 fs::write(repository.join("foo.txt"), "bar")?;
116
117 let config = AppConfig {
118 repository,
119 commit_message: Some("test message".to_string()),
120 commit_on_start: true,
121 watch: false,
122 ..AppConfig::default()
123 };
124 let app = App::new(config)?;
125 let result = app.run(None);
126 assert!(result.is_err());
127 assert!(result
128 .unwrap_err()
129 .to_string()
130 .eq("Failed to commit changes"));
131
132 assert!(!git_repo.statuses(None)?.is_empty());
133 Ok(())
134 }
135}