blueprint_manager/sdk/
utils.rs1use crate::protocols::resolver::NativeGithubMetadata;
2use gadget_sdk::{info, warn};
3use sha2::Digest;
4use std::path::Path;
5use std::string::FromUtf8Error;
6use std::sync::atomic::{AtomicBool, Ordering};
7use std::sync::Arc;
8use tangle_subxt::tangle_testnet_runtime::api::runtime_types::tangle_primitives::services::field::BoundedString;
9use tangle_subxt::tangle_testnet_runtime::api::runtime_types::tangle_primitives::services::{
10 GadgetBinary, GithubFetcher,
11};
12
13pub fn github_fetcher_to_native_github_metadata(
14 gh: &GithubFetcher,
15 blueprint_id: u64,
16) -> NativeGithubMetadata {
17 let owner = bytes_to_utf8_string(gh.owner.0 .0.clone()).expect("Should be valid");
18 let repo = bytes_to_utf8_string(gh.repo.0 .0.clone()).expect("Should be valid");
19 let tag = bytes_to_utf8_string(gh.tag.0 .0.clone()).expect("Should be valid");
20 let git = format!("https://github.com/{owner}/{repo}");
21
22 NativeGithubMetadata {
23 fetcher: gh.clone(),
24 git,
25 tag,
26 repo,
27 owner,
28 gadget_binaries: gh.binaries.0.clone(),
29 blueprint_id,
30 }
31}
32
33pub fn bounded_string_to_string(string: BoundedString) -> Result<String, FromUtf8Error> {
34 let bytes: &Vec<u8> = &string.0 .0;
35 String::from_utf8(bytes.clone())
36}
37
38pub fn hash_bytes_to_hex<T: AsRef<[u8]>>(input: T) -> String {
39 let mut hasher = sha2::Sha256::default();
40 hasher.update(input);
41 hex::encode(hasher.finalize())
42}
43
44pub async fn valid_file_exists(path: &str, expected_hash: &str) -> bool {
45 if let Ok(file) = gadget_io::tokio::fs::read(path).await {
47 let retrieved_bytes = hash_bytes_to_hex(file);
49 expected_hash == retrieved_bytes.as_str()
50 } else {
51 false
52 }
53}
54
55pub fn get_formatted_os_string() -> String {
56 let os = std::env::consts::OS;
57
58 match os {
59 "macos" => "apple-darwin".to_string(),
60 "windows" => "pc-windows-msvc".to_string(),
61 "linux" => "unknown-linux-gnu".to_string(),
62 _ => os.to_string(),
63 }
64}
65
66pub fn get_download_url(binary: &GadgetBinary, fetcher: &GithubFetcher) -> String {
67 let os = get_formatted_os_string();
68 let ext = if os == "windows" { ".exe" } else { "" };
69 let owner = String::from_utf8(fetcher.owner.0 .0.clone()).expect("Should be a valid owner");
70 let repo = String::from_utf8(fetcher.repo.0 .0.clone()).expect("Should be a valid repo");
71 let tag = String::from_utf8(fetcher.tag.0 .0.clone()).expect("Should be a valid tag");
72 let binary_name =
73 String::from_utf8(binary.name.0 .0.clone()).expect("Should be a valid binary name");
74 let os_name = format!("{:?}", binary.os).to_lowercase();
75 let arch_name = format!("{:?}", binary.arch).to_lowercase();
76 format!("https://github.com/{owner}/{repo}/releases/download/v{tag}/{binary_name}-{os_name}-{arch_name}{ext}")
78}
79
80pub fn msg_to_error<T: Into<String>>(msg: T) -> color_eyre::Report {
81 color_eyre::Report::msg(msg.into())
82}
83
84pub fn get_service_str(svc: &NativeGithubMetadata) -> String {
85 let repo = svc.git.clone();
86 let vals: Vec<&str> = repo.split(".com/").collect();
87 vals[1].to_string()
88}
89
90pub async fn chmod_x_file<P: AsRef<Path>>(path: P) -> color_eyre::Result<()> {
91 let success = gadget_io::tokio::process::Command::new("chmod")
92 .arg("+x")
93 .arg(format!("{}", path.as_ref().display()))
94 .spawn()?
95 .wait_with_output()
96 .await?
97 .status
98 .success();
99
100 if success {
101 Ok(())
102 } else {
103 Err(color_eyre::eyre::eyre!(
104 "Failed to chmod +x {}",
105 path.as_ref().display()
106 ))
107 }
108}
109
110pub fn is_windows() -> bool {
111 std::env::consts::OS == "windows"
112}
113
114pub fn generate_running_process_status_handle(
115 process: gadget_io::tokio::process::Child,
116 service_name: &str,
117) -> (Arc<AtomicBool>, gadget_io::tokio::sync::oneshot::Sender<()>) {
118 let (stop_tx, stop_rx) = gadget_io::tokio::sync::oneshot::channel::<()>();
119 let status = Arc::new(AtomicBool::new(true));
120 let status_clone = status.clone();
121 let service_name = service_name.to_string();
122
123 let task = async move {
124 info!("Starting process execution for {service_name}");
125 let output = process.wait_with_output().await;
126 warn!("Process for {service_name} exited: {output:?}");
127 status_clone.store(false, Ordering::Relaxed);
128 };
129
130 let task = async move {
131 gadget_io::tokio::select! {
132 _ = stop_rx => {},
133 _ = task => {},
134 }
135 };
136
137 gadget_io::tokio::spawn(task);
138 (status, stop_tx)
139}
140
141pub fn bytes_to_utf8_string<T: Into<Vec<u8>>>(input: T) -> color_eyre::Result<String> {
142 String::from_utf8(input.into()).map_err(|err| msg_to_error(err.to_string()))
143}
144
145pub fn slice_32_to_sha_hex_string(hash: [u8; 32]) -> String {
146 use std::fmt::Write;
147 hash.iter().fold(String::new(), |mut acc, byte| {
148 write!(&mut acc, "{:02x}", byte).expect("Should be able to write");
149 acc
150 })
151}