sindri_cli/commands/
clone.rs1use std::{fs, io::Cursor, path::Path};
2
3use flate2::read::GzDecoder;
4use regex::Regex;
5use tar::Archive;
6
7use sindri::client::SindriClient;
8
9use crate::handle_operation_error;
10
11pub fn clone(client: &SindriClient, circuit: String, directory: Option<String>) {
12 println!("{}", console::style("Cloning...").bold());
13
14 let circuit_regex =
15 Regex::new(r"^(?:([-a-zA-Z0-9_]+)\/)?([-a-zA-Z0-9_]+)(?::([-a-zA-Z0-9_.]+))?$").unwrap();
16 let circuit_name = if let Some(captures) = circuit_regex.captures(&circuit) {
17 captures
18 .get(2)
19 .map(|m| m.as_str().to_string())
20 .unwrap_or_else(|| handle_operation_error("Clone", "Invalid circuit identifier"))
21 } else {
22 handle_operation_error("Clone", "Invalid circuit identifier")
23 };
24 let output_directory = directory.unwrap_or(circuit_name.clone());
25 println!(
26 "{}",
27 console::style(format!(" ✓ Valid circuit identifier: {}", circuit)).cyan()
28 );
29
30 let download_path = {
31 let p = Path::new(&output_directory);
32 if p.is_dir() {
33 handle_operation_error("Clone", "Output directory already exists");
34 }
35 match fs::create_dir_all(p) {
36 Ok(_) => p.join("circuit.tar.gz"),
37 Err(e) => handle_operation_error("Clone", &e.to_string()),
38 }
39 };
40
41 match client.clone_circuit_blocking(&circuit, download_path.to_string_lossy().to_string()) {
42 Ok(_) => println!(
43 "{}",
44 console::style(" ✓ Successfully downloaded circuit").cyan()
45 ),
46 Err(e) => {
47 if e.to_string().contains("404") {
48 handle_operation_error(
49 "Clone",
50 "Circuit does not exist or you lack permission to access it.",
51 );
52 } else {
53 handle_operation_error("Clone", &e.to_string());
54 }
55 }
56 }
57
58 println!("{}", console::style(" ✓ Unpacking circuit...").cyan());
59 let downloaded = fs::read(&download_path).unwrap();
61 let cursor = Cursor::new(downloaded);
62 let gz_decoder = GzDecoder::new(cursor);
63 let mut archive = Archive::new(gz_decoder);
64
65 (|| -> Result<(), Box<dyn std::error::Error>> {
67 for entry in archive.entries()? {
68 let mut entry = entry?;
69 let path = entry.path()?;
70 if let Some(stripped) = path.iter().skip(1).collect::<std::path::PathBuf>().to_str() {
71 let output_path = Path::new(&output_directory).join(stripped);
72 if let Some(parent) = output_path.parent() {
73 fs::create_dir_all(parent)?;
74 }
75 entry.unpack(&output_path)?;
76 }
77 }
78 Ok(())
79 })()
80 .unwrap_or_else(|e| {
81 handle_operation_error("Clone", &format!("Issue unpacking circuit: {}", e))
82 });
83
84 std::fs::remove_file(&download_path).unwrap();
86
87 println!(
88 "{}",
89 console::style(" ✓ Circuit cloned successfully!").cyan()
90 );
91 println!(
92 "\n{}",
93 console::style(format!("Circuit downloaded to: {}", output_directory)).bold()
94 );
95}