1extern crate dirs;
2#[macro_use]
3extern crate log;
4extern crate core;
5
6use std::io;
7use std::io::{Error, ErrorKind};
8
9use crate::config_manager::ConfigManagement;
10use crate::config_manager::ConfigType::Repo;
11use crate::git::GitManagement;
12use crate::printer::{Print, PrintColor};
13use crate::program_access::ProgramOpener;
14use crate::reader::ReadInput;
15use std::path::Path;
16
17pub mod config_manager;
18pub mod git;
19pub mod printer;
20pub mod program_access;
21pub mod reader;
22
23pub struct Eureka<
24 CM: ConfigManagement,
25 W: Print + PrintColor,
26 R: ReadInput,
27 G: GitManagement,
28 PO: ProgramOpener,
29> {
30 cm: CM,
31 printer: W,
32 reader: R,
33 git: G,
34 program_opener: PO,
35}
36
37#[derive(Debug)]
38pub struct EurekaOptions {
39 pub clear_config: bool,
41
42 pub view: bool,
44}
45
46impl<CM, W, R, G, PO> Eureka<CM, W, R, G, PO>
47where
48 CM: ConfigManagement,
49 W: Print + PrintColor,
50 R: ReadInput,
51 G: GitManagement,
52 PO: ProgramOpener,
53{
54 pub fn new(cm: CM, printer: W, reader: R, git: G, program_opener: PO) -> Self {
55 Eureka {
56 cm,
57 printer,
58 reader,
59 git,
60 program_opener,
61 }
62 }
63
64 pub fn run(&mut self, opts: EurekaOptions) -> io::Result<()> {
65 debug!("Running with options: {:?}", &opts);
66
67 if opts.clear_config {
68 self.clear_config()?;
69 debug!("Cleared config");
70 return Ok(());
71 }
72
73 if opts.view {
74 self.open_idea_file()?;
75 return Ok(());
76 }
77
78 if self.is_config_missing() {
79 debug!("Config is missing");
80
81 if !self.cm.config_dir_exists() {
83 self.cm.config_dir_create()?;
84 debug!("Created config dir");
85 }
86
87 self.printer.fts_banner()?;
88
89 if self.cm.config_read(Repo).is_err() {
91 self.setup_repo_path()?;
92 debug!("Setup repo path successfully");
93 }
94
95 self.printer
96 .println("First time setup complete. Happy ideation!")?;
97 Ok(())
98 } else {
99 self.ask_for_idea()
100 }
101 }
102
103 fn ask_for_idea(&mut self) -> io::Result<()> {
104 let mut idea_summary = String::new();
105
106 while idea_summary.is_empty() {
107 self.printer.input_header(">> Idea summary")?;
108 idea_summary = self.reader.read_input()?;
109 }
110
111 let repo_path = self.cm.config_read(Repo)?;
112 self.git
114 .init(&repo_path)
115 .map_err(|git_err| Error::new(ErrorKind::InvalidInput, git_err))?;
116
117 self.program_opener
118 .open_editor(&format!("{}/README.md", &repo_path))
119 .and(self.git_add_commit_push(idea_summary))
120 }
121
122 fn clear_config(&self) -> io::Result<()> {
123 self.cm.config_rm()
124 }
125
126 fn open_idea_file(&self) -> io::Result<()> {
127 self.program_opener
128 .open_pager(&format!("{}/README.md", self.cm.config_read(Repo)?))
129 }
130
131 fn git_add_commit_push(&mut self, commit_subject: String) -> io::Result<()> {
132 let branch_name = "main";
133 self.printer.println(&format!(
134 "Adding and committing your new idea to {}..",
135 &branch_name
136 ))?;
137 self.git
138 .checkout_branch(branch_name)
139 .and_then(|_| self.git.add())
140 .and_then(|_| self.git.commit(commit_subject.as_str()))
141 .map_err(|err| io::Error::new(ErrorKind::Other, err))?;
142 self.printer.println("Added and committed!")?;
143
144 self.printer.println("Pushing your new idea..")?;
145 self.git
146 .push(branch_name)
147 .map_err(|err| io::Error::new(ErrorKind::Other, err))?;
148 self.printer.println("Pushed!")?;
149
150 Ok(())
151 }
152
153 fn setup_repo_path(&mut self) -> io::Result<()> {
154 loop {
155 self.printer
156 .input_header("Absolute path to your idea repo")?;
157 let user_input = &self.reader.read_input()?;
158
159 if user_input.is_empty() {
160 continue;
161 }
162
163 let path = Path::new(user_input);
164
165 if path.is_absolute() {
166 break self.cm.config_write(Repo, path.display().to_string());
167 } else {
168 self.printer.error("Path must be absolute")?;
169 }
170 }
171 }
172
173 fn is_config_missing(&self) -> bool {
174 self.cm.config_read(Repo).is_err()
175 }
176}