mithril_client_cli/utils/
cardano_db.rs1use anyhow::anyhow;
2use futures::Future;
3use indicatif::{MultiProgress, ProgressBar};
4use std::path::Path;
5use std::time::Duration;
6
7use super::CardanoDbDownloadCheckerError;
8use mithril_client::{MithrilError, MithrilResult};
9
10pub struct CardanoDbUtils;
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub enum LedgerFormat {
16 Legacy,
18 InMemory,
20 Lmdb,
22}
23
24impl CardanoDbUtils {
25 pub fn check_disk_space_error(error: MithrilError) -> MithrilResult<String> {
27 match error.downcast_ref::<CardanoDbDownloadCheckerError>() {
28 Some(CardanoDbDownloadCheckerError::NotEnoughSpaceForArchive { .. })
29 | Some(CardanoDbDownloadCheckerError::NotEnoughSpaceForUncompressedData { .. }) => {
30 Ok(format!("Warning: {error}"))
31 }
32 _ => Err(error),
33 }
34 }
35
36 pub async fn wait_spinner<T, E>(
38 progress_bar: &MultiProgress,
39 future: impl Future<Output = Result<T, E>>,
40 ) -> MithrilResult<T>
41 where
42 MithrilError: From<E>,
43 {
44 let pb = progress_bar.add(ProgressBar::new_spinner());
45 let spinner = async move {
46 loop {
47 pb.tick();
48 tokio::time::sleep(Duration::from_millis(50)).await;
49 }
50 };
51
52 tokio::select! {
53 _ = spinner => Err(anyhow!("timeout")),
54 res = future => res.map_err(Into::into),
55 }
56 }
57
58 pub fn format_bytes_to_gigabytes(bytes: u64) -> String {
59 let size_in_giga = bytes as f64 / (1024.0 * 1024.0 * 1024.0);
60
61 format!("{size_in_giga:.2} GiB")
62 }
63
64 pub fn get_docker_run_command<P: AsRef<Path>>(
69 canonical_db_filepath: P,
70 cardano_network: &str,
71 cardano_node_version: &str,
72 ledger_format: LedgerFormat,
73 ) -> String {
74 let db_path = canonical_db_filepath.as_ref();
75 let cardano_node_version = if matches!(ledger_format, LedgerFormat::Legacy) {
76 "10.3.1"
77 } else {
78 cardano_node_version
79 };
80 let cardano_node_config = match ledger_format {
81 LedgerFormat::Lmdb => {
82 r#" -e CARDANO_CONFIG_JSON_MERGE='{"LedgerDB": { "Backend": "V1LMDB" }}'"#
83 }
84 _ => "",
85 };
86
87 let docker_cmd = format!(
88 "docker run -v cardano-node-ipc:/ipc -v cardano-node-data:/data --mount type=bind,source=\"{db_path}\",target=/data/db/ -e NETWORK={cardano_network}{cardano_node_config} ghcr.io/intersectmbo/cardano-node:{cardano_node_version}",
89 db_path = db_path.display(),
90 );
91
92 docker_cmd
93 }
94}
95
96#[cfg(test)]
97mod test {
98 use super::*;
99 use std::path::PathBuf;
100
101 #[test]
102 fn check_disk_space_error_should_return_warning_message_if_error_is_not_enough_space_for_archive()
103 {
104 let not_enough_space_error = CardanoDbDownloadCheckerError::NotEnoughSpaceForArchive {
105 left_space: 1_f64,
106 pathdir: PathBuf::new(),
107 archive_size: 2_f64,
108 };
109 let expected = format!("Warning: {not_enough_space_error}");
110
111 let result = CardanoDbUtils::check_disk_space_error(anyhow!(not_enough_space_error))
112 .expect("check_disk_space_error should not error");
113
114 assert!(result.contains(&expected));
115 }
116
117 #[test]
118 fn check_disk_space_error_should_return_warning_message_if_error_is_not_enough_space_for_uncompressed_data()
119 {
120 let not_enough_space_error =
121 CardanoDbDownloadCheckerError::NotEnoughSpaceForUncompressedData {
122 left_space: 1_f64,
123 pathdir: PathBuf::new(),
124 db_size: 2_f64,
125 };
126 let expected = format!("Warning: {not_enough_space_error}");
127
128 let result = CardanoDbUtils::check_disk_space_error(anyhow!(not_enough_space_error))
129 .expect("check_disk_space_error should not error");
130
131 assert!(result.contains(&expected));
132 }
133
134 #[test]
135 fn check_disk_space_error_should_return_error_if_error_is_not_error_not_enough_space() {
136 let error = CardanoDbDownloadCheckerError::UnpackDirectoryNotEmpty(PathBuf::new());
137
138 let error = CardanoDbUtils::check_disk_space_error(anyhow!(error))
139 .expect_err("check_disk_space_error should fail");
140
141 assert!(
142 matches!(
143 error.downcast_ref::<CardanoDbDownloadCheckerError>(),
144 Some(CardanoDbDownloadCheckerError::UnpackDirectoryNotEmpty(_))
145 ),
146 "Unexpected error: {error:?}"
147 );
148 }
149
150 #[test]
151 fn format_bytes_to_gigabytes_zero() {
152 let one_gigabyte = 1024 * 1024 * 1024;
153
154 assert_eq!(CardanoDbUtils::format_bytes_to_gigabytes(0), "0.00 GiB");
155
156 assert_eq!(
157 CardanoDbUtils::format_bytes_to_gigabytes(one_gigabyte),
158 "1.00 GiB"
159 );
160
161 assert_eq!(
162 CardanoDbUtils::format_bytes_to_gigabytes(one_gigabyte / 2),
163 "0.50 GiB"
164 );
165
166 assert_eq!(
167 CardanoDbUtils::format_bytes_to_gigabytes(one_gigabyte * 10),
168 "10.00 GiB"
169 );
170 }
171
172 #[test]
173 fn get_docker_run_command_for_legacy_ledger() {
174 let run_command = CardanoDbUtils::get_docker_run_command(
175 Path::new("/path/to/db"),
176 "mainnet",
177 "whatever",
178 LedgerFormat::Legacy,
179 );
180
181 assert_eq!(
182 run_command,
183 r#"docker run -v cardano-node-ipc:/ipc -v cardano-node-data:/data --mount type=bind,source="/path/to/db",target=/data/db/ -e NETWORK=mainnet ghcr.io/intersectmbo/cardano-node:10.3.1"#
184 )
185 }
186
187 #[test]
188 fn get_docker_run_command_for_in_memory_ledger() {
189 let run_command = CardanoDbUtils::get_docker_run_command(
190 Path::new("/path/to/db"),
191 "mainnet",
192 "10.5.4",
193 LedgerFormat::InMemory,
194 );
195
196 assert_eq!(
197 run_command,
198 r#"docker run -v cardano-node-ipc:/ipc -v cardano-node-data:/data --mount type=bind,source="/path/to/db",target=/data/db/ -e NETWORK=mainnet ghcr.io/intersectmbo/cardano-node:10.5.4"#
199 )
200 }
201
202 #[test]
203 fn get_docker_run_command_for_lmdb_ledger() {
204 let run_command = CardanoDbUtils::get_docker_run_command(
205 Path::new("/path/to/db"),
206 "mainnet",
207 "10.6.2",
208 LedgerFormat::Lmdb,
209 );
210
211 assert_eq!(
212 run_command,
213 r#"docker run -v cardano-node-ipc:/ipc -v cardano-node-data:/data --mount type=bind,source="/path/to/db",target=/data/db/ -e NETWORK=mainnet -e CARDANO_CONFIG_JSON_MERGE='{"LedgerDB": { "Backend": "V1LMDB" }}' ghcr.io/intersectmbo/cardano-node:10.6.2"#
214 )
215 }
216}