godwit_daemon/config/
mod.rs1mod internal;
2
3use crate::errors::{PatchError, PurgeBaseError, RevisionError};
4use chrono::prelude::*;
5use getter_derive::Getter;
6use glob::glob;
7use log::info;
8use serde::{Deserialize, Serialize};
9use serde_json::Value;
10use std::fmt::Display;
11use std::fs::{self, File};
12use std::io::{self, prelude::*};
13use std::path::{Path, PathBuf};
14
15#[derive(Debug, Deserialize, Serialize, Getter)]
17#[serde(rename_all = "snake_case")]
18pub struct Config {
19 pub daemon_directory: PathBuf,
20 pub max_threads: usize,
21 pub daemon_url: String,
22 pub client_url: String,
23}
24
25impl Config {
26 pub fn init(
28 daemon_directory: Option<PathBuf>,
29 max_threads: Option<usize>,
30 tcp_port: Option<String>,
31 ) -> Self {
32 Config {
33 daemon_directory: daemon_directory
34 .unwrap_or_else(|| Config::default().daemon_directory),
35 max_threads: max_threads.unwrap_or_else(|| Config::default().max_threads),
36 daemon_url: tcp_port
37 .clone()
38 .unwrap_or_else(|| Config::default().daemon_url),
39 client_url: tcp_port
40 .clone()
41 .unwrap_or_else(|| Config::default().client_url),
42 }
43 }
44
45 pub fn get_base_path(&self, application: &str, file_name: &str) -> Option<PathBuf> {
46 let base_path = self
47 .daemon_directory
48 .as_path()
49 .join(application)
50 .join(format!("{}.base", &file_name));
51
52 return if base_path.exists() {
53 Some(base_path)
54 } else {
55 None
56 };
57 }
58
59 pub fn get_patched_rev(
61 &self,
62 application: &str,
63 file_name: &str,
64 ) -> Result<Value, RevisionError> {
65 let base_path = self
66 .daemon_directory
67 .as_path()
68 .join(application)
69 .join(format!("{}.base", &file_name));
70
71 if !base_path.exists() {
72 return Err(RevisionError::BaseNotFound {
73 base_path: file_name.to_string(),
74 });
75 }
76
77 let base = fs::read_to_string(&base_path)?;
78
79 let patched = glob(&format!(
80 "{}/*.patch",
81 &self
82 .daemon_directory
83 .as_path()
84 .join(application.to_string())
85 .join("patches")
86 .to_string_lossy(),
87 ))?
88 .into_iter()
89 .try_fold(base, |acc, patch_path| {
90 let patch_file = fs::read_to_string(patch_path?)?;
91
92 internal::patch(acc, patch_file)
93 })?;
94
95 Ok(serde_json::from_str(&patched)?)
96 }
97
98 pub fn save_patch_file<T>(
100 &self,
101 application: &str,
102 file_name: &str,
103 patch_content: T,
104 ) -> Result<(), PatchError>
105 where
106 T: Display + Serialize,
107 {
108 if self.get_base_path(application, file_name).is_none() {
109 let patch_path = self
110 .daemon_directory
111 .as_path()
112 .join(application.to_string())
113 .join(format!("{}.base", &file_name));
114
115 fs::create_dir_all(patch_path.parent().unwrap())?;
116 let patch_file = File::create(patch_path)?;
117
118 serde_json::to_writer_pretty(patch_file, &patch_content)?;
119 } else {
120 let patch_path = self
121 .daemon_directory
122 .as_path()
123 .join(application.to_string())
124 .join("patches")
125 .join(&file_name)
126 .join(format!("{}-{}.patch", &file_name, Utc::now()));
127
128 fs::create_dir_all(patch_path.parent().unwrap())?;
129 let mut patch_file = File::create(patch_path)?;
130
131 patch_file.write_all(patch_content.to_string().as_bytes())?;
132 };
133 Ok(())
134 }
135}
136
137impl Default for Config {
138 fn default() -> Self {
139 Config {
140 daemon_directory: dirs::home_dir()
141 .expect("Home couldn't be located in current $PATH variables.")
142 .join(".godwit")
143 .join("daemon"),
144 max_threads: 23,
145 daemon_url: "tcp://*:5555".to_string(),
146 client_url: "tcp://127.0.0.1:5555".to_string(),
147 }
148 }
149}
150
151pub fn get_config() -> Result<Config, io::Error> {
153 let config_rc_path: PathBuf = dirs::home_dir()
154 .expect("Home couldn't be located in current $PATH variables.")
155 .join(".gdrc")
156 .iter()
157 .collect();
158
159 let config_path: PathBuf = dirs::home_dir()
160 .expect("Home couldn't be located in current $PATH variables.")
161 .join(".godwit")
162 .join("daemon.gwcore")
163 .iter()
164 .collect();
165
166 let config = if config_rc_path.exists() {
167 info!("Daemon config found at {}", config_rc_path.display());
168 File::open(config_rc_path).and_then(|config_file| {
169 let config: Config = serde_json::from_reader(config_file)?;
170 Ok(config)
171 })?
172 } else if config_path.exists() {
173 info!("Daemon config found at {}", config_path.display());
174 File::open(config_path).and_then(|config_file| {
175 let config: Config = serde_json::from_reader(config_file)?;
176 Ok(config)
177 })?
178 } else {
179 Config::default()
180 };
181
182 Ok(config)
183}
184
185pub fn update_patches(
186 application: &str,
187 file_path: &Path,
188 curr_instance: Value,
189) -> Result<(), RevisionError> {
190 let config = get_config()?;
191
192 let file_name = file_path
193 .file_name()
194 .unwrap()
195 .to_string_lossy()
196 .into_owned();
197
198 if config.get_base_path(application, &file_name).is_none() {
199 return config
200 .save_patch_file(application, &file_name, curr_instance)
201 .map_err(Into::into);
202 }
203
204 let prev_instance = config.get_patched_rev(application, &file_name)?;
205
206 let new_patch = internal::diff(prev_instance, curr_instance)?;
207
208 config.save_patch_file(application, &file_name, new_patch)?;
209 Ok(())
210}
211
212pub fn purge_base_file(application: &str, file_path: &Path) -> Result<(), PurgeBaseError> {
213 let file_name = file_path
214 .file_name()
215 .unwrap_or_default()
216 .to_string_lossy()
217 .into_owned();
218
219 if let Some(base_path) = get_config()?.get_base_path(application, &file_name) {
220 fs::remove_dir_all(base_path)?;
221 }
222 Ok(())
223}