dojo_cli/commands/
init.rs1use crate::config::Config;
2use dialoguer::{Confirm, Input, Password};
3use std::path::Path;
4use std::process::Command;
5
6pub async fn handle_init_command(
7 sub_matches: &clap::ArgMatches,
8) -> Result<(), Box<dyn std::error::Error>> {
9 println!("__________.__ __ .__ ________ __ ");
10 println!("\\______ \\__|/ |_ ____ ____ |__| ____ \\______ \\ ____ |__| ____ ");
11 println!(" | | _/ \\ __\\/ ___\\/ _ \\| |/ \\ | | \\ / _ \\ | |/ _ \\ ");
12 println!(" | | \\ || | \\ \\__( <_> ) | | \\ | ` ( <_> ) | ( <_> )");
13 println!(" |______ /__||__| \\___ >____/|__|___| / /_______ /\\____/\\__| |\\____/ ");
14 println!(" \\/ \\/ \\/ \\/ \\______| \n");
15 println!("š Welcome to Bitcoin Dojo CLI Setup!");
16 println!("This will help you configure your development environment.\n");
17
18 if Config::exists() {
20 let overwrite = Confirm::new()
21 .with_prompt("Configuration already exists. Do you want to overwrite it?")
22 .default(false)
23 .interact()?;
24
25 if !overwrite {
26 println!("Setup cancelled.");
27 return Ok(());
28 }
29 }
30
31 let default_workspace = dirs::home_dir()
33 .map(|home| {
34 home.join("bitcoin_dojo_workspace")
35 .to_string_lossy()
36 .to_string()
37 })
38 .unwrap_or_else(|| "./bitcoin_dojo_workspace".to_string());
39
40 let workspace_directory: String = Input::new()
41 .with_prompt("Enter your workspace directory (or press enter to use the default directory)")
42 .default(default_workspace)
43 .interact_text()?;
44
45 let workspace_path = Path::new(&workspace_directory);
47 if !workspace_path.exists() {
48 let create_dir = Confirm::new()
49 .with_prompt(format!(
50 "Directory '{}' doesn't exist. Create it?",
51 workspace_directory
52 ))
53 .default(true)
54 .interact()?;
55
56 if create_dir {
57 std::fs::create_dir_all(workspace_path)?;
58 println!("ā
Created workspace directory: {}", workspace_directory);
59 }
60 }
61
62 let api_token = if let Some(token) = sub_matches.get_one::<String>("token") {
64 token.to_string()
65 } else {
66 println!("\nš API Token Setup");
67 println!("You can get your API token from the Bitcoin Dojo web dashboard.");
68
69 Password::new()
70 .with_prompt("Enter your API token")
71 .interact()?
72 };
73
74 if api_token.trim().is_empty() {
76 return Err("API token cannot be empty".into());
77 }
78
79 println!("\nš§ Initializing curriculum version...");
81 let curriculum_version = Config::init_curriculum_version(&api_token).await?;
82 println!("ā
Curriculum version initialized: {}", curriculum_version);
83
84 let config = Config::new(
86 workspace_directory.clone(),
87 api_token,
88 curriculum_version.clone(),
89 );
90 config.save()?;
91
92 let config_path = Config::get_config_path()?;
93
94 println!("\nā
Configuration saved successfully!");
95 println!("š Config file: {}", config_path.display());
96 println!("š Workspace: {}", workspace_directory);
97 println!("š Curriculum version: {}", curriculum_version);
98
99 let cargo_available = is_cargo_available();
101
102 if !cargo_available {
103 println!("\nā ļø Cargo is not available on your system.");
104 println!("This means you won't be able to create a Rust project automatically.");
105 println!("You can still use the CLI to download exercises, but you'll need to create the Rust project manually.");
106
107 let continue_without_cargo = Confirm::new()
108 .with_prompt("Continue without creating a Rust project?")
109 .default(true)
110 .interact()?;
111
112 if !continue_without_cargo {
113 println!("Setup cancelled. Please install Rust and Cargo first, then try again.");
114 return Ok(());
115 }
116
117 println!("ā
Configuration saved successfully!");
118 println!("š Config file: {}", config_path.display());
119 println!("š Workspace: {}", workspace_directory);
120 println!("š Curriculum version: {}", curriculum_version);
121 println!("\nšÆ You can now use 'dojo-cli download --exercise <exercise-name>' to download exercises.");
122 println!("Remember to create a Rust project manually in your workspace when you're ready.");
123 return Ok(());
124 }
125
126 println!("\nš¦ Creating Rust project...");
128 let project_path = workspace_path.join("bitcoin_dojo");
129
130 if project_path.exists() {
131 let overwrite_project = Confirm::new()
132 .with_prompt("Rust project 'bitcoin_dojo' already exists in workspace. Overwrite it?")
133 .default(false)
134 .interact()?;
135
136 if !overwrite_project {
137 println!("Skipping Rust project creation.");
138 } else {
139 std::fs::remove_dir_all(&project_path)?;
140 create_rust_project(&workspace_directory)?;
141 }
142 } else {
143 create_rust_project(&workspace_directory)?;
144 }
145
146 println!("\nšÆ You're all set! Open your editor to {}/bitcoin_dojo and run 'dojo-cli download --exercise 1.1-hashing-randomness' to download the first exercise.", &workspace_directory);
147
148 Ok(())
149}
150
151fn create_rust_project(workspace_directory: &str) -> Result<(), Box<dyn std::error::Error>> {
152 if !is_cargo_available() {
154 return Err(
155 "ā Cargo is not available. Please install Rust and Cargo first.\n\n\
156 To install Rust, visit https://rustup.rs/ or run:\n\
157 curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh\n\n\
158 After installation, restart your terminal and try again."
159 .into(),
160 );
161 }
162
163 let output = Command::new("cargo")
164 .args(["new", "--lib", "bitcoin_dojo"])
165 .current_dir(workspace_directory)
166 .output()?;
167
168 if output.status.success() {
169 println!("ā
Created Rust project: bitcoin_dojo");
170 println!("š Project location: {}/bitcoin_dojo", workspace_directory);
171 } else {
172 let error_msg = String::from_utf8_lossy(&output.stderr);
173 return Err(format!("Failed to create Rust project: {}", error_msg).into());
174 }
175
176 Ok(())
177}
178
179fn is_cargo_available() -> bool {
181 Command::new("cargo")
182 .arg("--version")
183 .output()
184 .map(|output| output.status.success())
185 .unwrap_or(false)
186}