1use crate::watcher;
2use crate::TimeTracker;
3use directories::BaseDirs;
4use directories::ProjectDirs;
5use serde_derive::{Deserialize, Serialize};
6use std::fmt;
7use std::fmt::Display;
8use std::fmt::Formatter;
9use std::fs;
10use std::fs::OpenOptions;
11use std::io::Read;
12use std::io::Write;
13use std::path::Path;
14use std::path::PathBuf;
15use std::sync::mpsc::channel;
16use toml;
17
18pub struct Configuration {
19 user_config_path: PathBuf, pub track_paths: Vec<PathBuf>,
21 pub raw_data_path: PathBuf,
22 pub processed_data_path: PathBuf,
23}
24
25impl Display for Configuration {
26 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
27 write!(f,
28"TimeTrack Configuration
30 User configuration: {:?}
31 Tracking paths: {:?}
32 Raw data: {:?}
33 Processed data: {:?}",
34 self.user_config_path,
35 self.track_paths,
36 self.raw_data_path,
37 self.processed_data_path
38 )
39 }
40}
41
42impl Configuration {
43 pub fn new_mock_config(
45 track_paths: Vec<PathBuf>,
46 raw_data_path: PathBuf,
47 processed_data_path: PathBuf,
48 ) -> Self {
49 Configuration {
50 user_config_path: PathBuf::new(), track_paths,
52 raw_data_path,
53 processed_data_path,
54 }
55 }
56}
57
58#[derive(Deserialize, Serialize)]
59struct UserConfig {
60 track_paths: Vec<PathBuf>,
61}
62
63pub fn get_config() -> Configuration {
64 let project_dir = ProjectDirs::from("rust", "cargo", "timetrack")
65 .expect("Failed to read project directories");
66
67 let raw_data_path = get_data_file_path(&project_dir, ".timetrack_raw");
68 let processed_data_path = get_data_file_path(&project_dir, ".timetrack_processed");
69 let user_config_path = project_dir.config_dir().join("timetrack_config");
70 let user_config = read_user_config(&user_config_path);
71
72 Configuration {
73 user_config_path,
74 track_paths: user_config.track_paths,
76 raw_data_path,
77 processed_data_path,
78 }
79}
80
81impl<'a> TimeTracker<'a> {
82 pub fn print_config(&self) {
83 println!("{}", self.config);
84 println!("Starting self test..");
85 let (tx, _rx) = channel();
86 for track_path in &self.config.track_paths {
87 match watcher::get_watcher(track_path, tx.clone()) {
88 Ok(_) => {
89 println!(
90 "Successfully added watcher for path {}",
91 track_path.to_string_lossy()
92 );
93 }
94 Err(err) => {
95 println!(
96 "Error {} adding watcher for path {:?}",
97 err,
98 track_path.to_string_lossy()
99 );
100 }
101 };
102 }
103 println!("Completed self test.");
104 }
105}
106
107fn get_data_file_path(project_dirs: &ProjectDirs, filename: &str) -> PathBuf {
108 let data_directory = project_dirs.data_local_dir();
109 let data_file_path = data_directory.join(filename);
110
111 fs::create_dir_all(&data_directory).expect("Failed to create data directory");
112 OpenOptions::new()
113 .create(true)
114 .read(true)
115 .write(true)
116 .open(&data_file_path)
117 .expect("Failed to create data file");
118
119 data_file_path
120}
121
122fn read_user_config(user_config_path: &PathBuf) -> UserConfig {
123 if !user_config_path.exists() {
124 init_config_file(&user_config_path);
125 }
126
127 let mut f = OpenOptions::new()
128 .read(true)
129 .open(&user_config_path)
130 .expect("Failed to open config file");
131
132 let mut contents = String::new();
133 f.read_to_string(&mut contents)
134 .expect("something went wrong reading the file");
135
136 toml::from_str(&contents).expect("Failed to parse config file as TOML")
137}
138
139fn init_config_file(config_file_path: impl AsRef<Path>) {
140 let config_dir = config_file_path.as_ref().parent().unwrap();
141
142 fs::create_dir_all(&config_dir).expect("Failed to create config directory");
143 let mut f = OpenOptions::new()
144 .create(true)
145 .read(true)
146 .write(true)
147 .open(&config_file_path)
148 .expect("Failed to create config file");
149
150 let home_dir = BaseDirs::new()
151 .expect("Unable to find home directory")
152 .home_dir()
153 .to_owned();
154 let default_config = UserConfig {
155 track_paths: vec![home_dir],
156 };
157
158 write!(
159 &mut f,
160 "{}",
161 toml::to_string(&default_config).expect("Failed to convert default user config to TOML")
162 )
163 .expect("Failed to initialize configuration file");
164}