1use ansi_term::Color::{Blue, Green, Red};
2use config::Context;
3use dto::Request;
4use dto::Response;
5use model::{Command, Section, Value};
6use serde_yaml;
7use std::{
8 env,
9 fs::{create_dir_all, File, Permissions},
10 io::{Error, ErrorKind, prelude::Write, stdin},
11 os::unix::fs::PermissionsExt,
12 path::PathBuf,
13 process,
14 result::Result,
15 str::FromStr,
16};
17use workflow::Instruction;
18use workflow::Work;
19use model::default_behaviour;
20
21fn default_config() -> Vec<Section> {
22 let hello_world = Command {
23 name: s!("hello"),
24 description: s!("Prints hello world"),
25 alias: Some(s!("hw")),
26 value: Some(Value::Shell(s!("echo hello world"))),
27 internal: default_behaviour(),
28 usage: None,
29 min_args: None,
30 dependencies: None,
31 };
32
33 vec![Section {
34 heading: s!("Commands"),
35 commands: vec![hello_world],
36 core: false
37 }]
38}
39
40#[derive(Debug)]
41enum Answer {
42 Yes,
43 No,
44}
45
46impl FromStr for Answer {
47 type Err = ();
48
49 fn from_str(s: &str) -> Result<Self, <Self as FromStr>::Err> {
50 match s {
51 "y" => Ok(Answer::Yes),
52 "n" => Ok(Answer::No),
53 _ => Err(())
54 }
55 }
56}
57
58fn ask(question: &String) -> String {
59 let mut input = String::new();
60 loop {
61 println!("{}", question);
62 if let Ok(v) = stdin().read_line(&mut input).map(|_| input.trim()) {
63 if v.len() > 0 {
64 return v.to_owned();
65 }
66 }
67 input.clear();
68 }
69}
70
71fn confirm(question: &str) -> Answer {
72 let formatted_question = format!("{} (y/n)", question);
73 loop {
74 if let Ok(a) = ask(&formatted_question).parse::<Answer>() {
75 return a;
76 }
77 }
78}
79
80fn create_dir_if_not_exists(path: &PathBuf) -> Result<(), Error> {
81 if path.is_dir() {
82 Ok(())
83 } else {
84 create_dir_all(path)
85 }
86}
87
88pub fn run_setup(request: Request, _: &Context) -> Work {
89 let no_output = Work::instruction(Instruction::ExitCode(Response::Ok));
90
91 let directory = request.next().current
92 .map(PathBuf::from)
93 .unwrap_or(env::current_dir().unwrap());
94
95 println!("{}", Blue.paint("sdoc init"));
96
97 if let Answer::No = confirm(&format!("Setup a new CLI in {:?}?", directory)) {
98 println!("Goodbye");
99 return no_output;
100 }
101
102 let cli_name = ask(&s!("Enter your CLI name:"));
103 let content = format!("\
104#! /bin/bash -ue
105dir=$(cd $( dirname $( realpath \"{}\" ) ) && cd .. && pwd )
106COMMANDS_DIRECTORY=\"$dir\" CLI_NAME='{}' sdoc \"$@\"", "${BASH_SOURCE[0]}", cli_name);
107
108 let bin_directory = directory.join("bin");
109 let bin = &bin_directory.join(&cli_name);
110 let commands_directory = &directory.join(&cli_name);
111 let commands_yaml = &commands_directory.join("commands.yaml");
112
113 match create_dir_if_not_exists(&bin_directory)
114 .and_then(|_| File::create(&bin))
115 .and_then(|mut bin| {
116 bin.write_all(content.as_bytes())
117 .and_then(|_| bin.set_permissions(Permissions::from_mode(0o755)))
118 })
119 .and_then(|_| create_dir_if_not_exists(&commands_directory))
120 .and_then(|_| File::create(&commands_yaml))
121 .and_then(|mut y|
122 serde_yaml::to_string(&default_config())
123 .map_err(|e| Error::new(ErrorKind::Other, e))
124 .and_then(|yaml| y.write_all(yaml.as_bytes()))
125 ) {
126 Ok(_) => {
127 println!("{}", Green.paint("Setup Complete"));
128 println!("Execute ./bin/{} to begin. Even better, add '$(pwd)/bin' to your $PATH", cli_name);
129 }
130 Err(e) => {
131 println!("{}: {:?}", Red.paint("Setup Failed"), e);
132 process::exit(1);
133 }
134 };
135
136 no_output
137}