1#![forbid(unsafe_code)]
21
22#![deny(
23 non_camel_case_types,
24 non_snake_case,
25 path_statements,
26 trivial_numeric_casts,
27 unstable_features,
28 unused_allocation,
29 unused_import_braces,
30 unused_imports,
31 unused_must_use,
32 unused_mut,
33 unused_qualifications,
34 while_true,
35)]
36
37extern crate clap;
38#[macro_use] extern crate failure;
39
40#[cfg(test)]
41extern crate toml;
42
43#[macro_use] extern crate libimagrt;
44extern crate libimagerror;
45
46mod ui;
47
48use std::fs::OpenOptions;
49use std::io::Write;
50use std::path::PathBuf;
51use std::path::Path;
52use std::process::Command;
53
54use failure::Fallible as Result;
55use failure::ResultExt;
56use failure::Error;
57use failure::err_msg;
58
59use libimagrt::runtime::Runtime;
60use libimagrt::application::ImagApplication;
61
62use clap::App;
63
64const CONFIGURATION_STR : &str = include_str!("../imagrc.toml");
65
66const GITIGNORE_STR : &str = r#"
67# We ignore the imagrc.toml file by default
68#
69# That is because we expect the user to put
70# this dotfile into his dotfile repository
71# and symlink it here.
72# If you do not do this, feel free to remove
73# this line from the gitignore and add the
74# configuration to this git repository.
75
76imagrc.toml
77"#;
78
79pub enum ImagInit {}
84impl ImagApplication for ImagInit {
85 fn run(_rt: Runtime) -> Result<()> {
86 panic!("imag-init needs to be run as a seperate binary, or we'll need to figure something out here!");
87 }
88
89 fn build_cli<'a>(app: App<'a, 'a>) -> App<'a, 'a> {
90 ui::build_ui(app)
91 }
92
93 fn name() -> &'static str {
94 env!("CARGO_PKG_NAME")
95 }
96
97 fn description() -> &'static str {
98 "Intialize the imag store"
99 }
100
101 fn version() -> &'static str {
102 env!("CARGO_PKG_VERSION")
103 }
104}
105
106pub fn imag_init() -> Result<()> {
107 let version = make_imag_version!();
108 let app = ui::build_ui(Runtime::get_default_cli_builder(
109 "imag-init",
110 version.as_str(),
111 "Intializes the imag store, optionally with git"));
112 let matches = app.get_matches();
113 let mut out = ::std::io::stdout();
114
115 let path = if let Some(p) = matches.value_of("path") {
116 PathBuf::from(String::from(p))
117 } else {
118 ::std::env::var("HOME")
119 .map_err(Error::from)
120 .map(PathBuf::from)
121 .map(|mut p| { p.push(".imag"); p })
122 .and_then(|path| if path.exists() {
123 Err(format_err!("Cannot continue: Path '{}' already exists", path.display()))
124 } else {
125 Ok(path)
126 })
127 .map_err(|_| err_msg("Failed to retrieve/build path for imag directory."))?
128 };
129
130 {
131 let mut store_path = path.clone();
132 store_path.push("store");
133 println!("Creating {}", store_path.display());
134
135 ::std::fs::create_dir_all(store_path).context("Failed to create directory")?;
136 }
137
138 let config_path = {
139 let mut config_path = path.clone();
140 config_path.push("imagrc.toml");
141 config_path
142 };
143
144 OpenOptions::new()
145 .write(true)
146 .create(true)
147 .open(config_path)
148 .map_err(Error::from)
149 .and_then(|mut f| {
150 let content = if matches.is_present("devel") {
151 get_config_devel()
152 } else {
153 get_config()
154 };
155
156 f.write_all(content.as_bytes())
157 .context("Failed to write complete config to file")
158 .map_err(Error::from)
159 })
160 .context("Failed to open new configuration file")?;
161
162 if find_command("git").is_some() && !matches.is_present("nogit") {
163 writeln!(out, "Going to initialize a git repository in the imag directory...")?;
165
166 let gitignore_path = {
167 let mut gitignore_path = path.clone();
168 gitignore_path.push(".gitignore");
169 gitignore_path.to_str().map(String::from)
170 }.ok_or_else(|| err_msg("Cannot convert path to string"))?;
171
172 OpenOptions::new()
173 .write(true)
174 .create(true)
175 .open(gitignore_path.clone())
176 .map_err(Error::from)
177 .and_then(|mut f| {
178 f.write_all(GITIGNORE_STR.as_bytes())
179 .context("Failed to write complete gitignore to file")
180 .map_err(Error::from)
181 })
182 .context("Failed to open new configuration file")?;
183
184 let path_str = path.to_str().map(String::from).ok_or_else(|| err_msg("Cannot convert path to string"))?;
185 let worktree = format!("--work-tree={}", path_str);
186 let gitdir = format!("--git-dir={}/.git", path_str);
187
188 {
189 let output = Command::new("git")
190 .args(&[&worktree, &gitdir, "--no-pager", "init"])
191 .output()
192 .context("Calling 'git init' failed")?;
193
194 if output.status.success() {
195 writeln!(out, "{}", String::from_utf8(output.stdout).expect("No UTF-8 output"))?;
196 writeln!(out, "'git {} {} --no-pager init' succeeded", worktree, gitdir)?;
197 } else {
198 writeln!(out, "{}", String::from_utf8(output.stderr).expect("No UTF-8 output"))?;
199 if !output.status.success() {
200 return Err(err_msg("Failed to execute git command"));
201 }
202 }
203 }
204
205 {
206 let output = Command::new("git")
207 .args(&[&worktree, &gitdir, "--no-pager", "add", &gitignore_path])
208 .output()
209 .context("Calling 'git add' failed")?;
210
211 if output.status.success() {
212 writeln!(out, "{}", String::from_utf8(output.stdout).expect("No UTF-8 output"))?;
213 writeln!(out, "'git {} {} --no-pager add {}' succeeded", worktree, gitdir, gitignore_path)?;
214 } else {
215 writeln!(out, "{}", String::from_utf8(output.stderr).expect("No UTF-8 output"))?;
216 if !output.status.success() {
217 return Err(err_msg("Failed to execute git command"));
218 }
219 }
220 }
221
222 {
223 let output = Command::new("git")
224 .args(&[&worktree, &gitdir, "--no-pager", "commit", &gitignore_path, "-m", "'Initial import'"])
225 .output()
226 .context("Calling 'git commit' failed")?;
227 if output.status.success() {
228 writeln!(out, "{}", String::from_utf8(output.stdout).expect("No UTF-8 output"))?;
229 writeln!(out, "'git {} {} --no-pager commit {} -m 'Initial import'' succeeded", worktree, gitdir, gitignore_path)?;
230 } else {
231 writeln!(out, "{}", String::from_utf8(output.stderr).expect("No UTF-8 output"))?;
232 if !output.status.success() {
233 return Err(err_msg("Failed to execute git command"));
234 }
235 }
236 }
237
238 writeln!(out, "git stuff finished!")?;
239 } else {
240 writeln!(out, "No git repository will be initialized")?;
241 }
242
243 writeln!(out, "Ready. Have fun with imag!").map_err(Error::from)
244}
245
246fn get_config() -> String {
247 get_config_devel()
248 .replace(
249 r#"level = "debug""#,
250 r#"level = "info""#
251 )
252}
253
254fn get_config_devel() -> String {
255 String::from(CONFIGURATION_STR)
256}
257
258fn find_command<P: AsRef<Path>>(exe_name: P) -> Option<PathBuf> {
259 ::std::env::var_os("PATH")
260 .and_then(|paths| {
261 ::std::env::split_paths(&paths)
262 .filter_map(|dir| {
263 let full_path = dir.join(&exe_name);
264 if full_path.is_file() {
265 Some(full_path)
266 } else {
267 None
268 }
269 })
270 .next()
271 })
272}
273
274#[cfg(test)]
275mod tests {
276 use toml::from_str;
277 use toml::Value;
278 use super::get_config;
279 use super::get_config_devel;
280
281 #[test]
282 fn test_config() {
283 let val = from_str::<Value>(&get_config()[..]);
284 assert!(val.is_ok(), "Config parsing errored: {:?}", val);
285
286 let val = from_str::<Value>(&get_config_devel()[..]);
287 assert!(val.is_ok(), "Config parsing errored: {:?}", val);
288 }
289
290}