@@ -14,7 +14,7 @@ use std::{
1414 env:: VarError ,
1515 path:: { Path , PathBuf } ,
1616} ;
17- use tracing:: { debug, info} ;
17+ use tracing:: { debug, info, warn } ;
1818
1919/// Parses a user-specified path with support for environment variables and common shorthands (e.g.
2020/// ~ for the user's home directory).
@@ -89,3 +89,147 @@ where
8989
9090 Ok ( block)
9191}
92+
93+ /// Check available disk space for the given path using sysinfo.
94+ ///
95+ /// Returns the available space in MB, or None if the check fails.
96+ pub fn get_available_disk_space_mb ( path : & Path ) -> Option < u64 > {
97+ use sysinfo:: Disks ;
98+
99+ // Find the disk that contains the given path
100+ let path_canonical = match std:: fs:: canonicalize ( path) {
101+ Ok ( p) => p,
102+ Err ( _) => return None ,
103+ } ;
104+
105+ let disks = Disks :: new_with_refreshed_list ( ) ;
106+
107+ // Find the disk that contains the given path
108+ for disk in disks. iter ( ) {
109+ let mount_point = disk. mount_point ( ) ;
110+ if path_canonical. starts_with ( mount_point) {
111+ // Get available space in bytes, convert to MB
112+ let available_bytes = disk. available_space ( ) ;
113+ return Some ( available_bytes / ( 1024 * 1024 ) ) ;
114+ }
115+ }
116+
117+ None
118+ }
119+
120+ /// Check if disk space is below the minimum threshold.
121+ ///
122+ /// Returns true if the available disk space is below the minimum threshold (in MB).
123+ pub fn is_disk_space_low ( path : & Path , min_free_disk_mb : u64 ) -> bool {
124+ if min_free_disk_mb == 0 {
125+ return false ; // Feature disabled
126+ }
127+
128+ match get_available_disk_space_mb ( path) {
129+ Some ( available_mb) => {
130+ if available_mb <= min_free_disk_mb {
131+ warn ! (
132+ target: "reth::cli" ,
133+ ?path,
134+ available_mb,
135+ min_free_disk_mb,
136+ "Disk space below minimum threshold"
137+ ) ;
138+ return true ;
139+ }
140+ false
141+ }
142+ None => {
143+ warn ! (
144+ target: "reth::cli" ,
145+ ?path,
146+ "Failed to check disk space, continuing anyway"
147+ ) ;
148+ false
149+ }
150+ }
151+ }
152+
153+ #[ cfg( test) ]
154+ mod tests {
155+ use super :: * ;
156+ use std:: path:: Path ;
157+
158+ #[ test]
159+ fn test_is_disk_space_low_disabled ( ) {
160+ // When min_free_disk is 0, feature is disabled
161+ let temp_dir = std:: env:: temp_dir ( ) ;
162+ assert ! ( !is_disk_space_low( & temp_dir, 0 ) , "Should return false when disabled" ) ;
163+ }
164+
165+ #[ test]
166+ fn test_is_disk_space_low_with_valid_path ( ) {
167+ // Test with a valid path (should not panic)
168+ // We can't easily mock sysinfo, so we just test that it doesn't panic
169+ // and returns a boolean value
170+ let temp_dir = std:: env:: temp_dir ( ) ;
171+ let result = is_disk_space_low ( & temp_dir, 1_000_000_000 ) ; // Very large threshold
172+ // Should return either true or false, but not panic
173+ assert ! ( result == true || result == false ) ;
174+ }
175+
176+ #[ test]
177+ fn test_is_disk_space_low_with_invalid_path ( ) {
178+ // Test with a non-existent path
179+ let invalid_path = Path :: new ( "/nonexistent/path/that/does/not/exist" ) ;
180+ // Should not panic, but may return false (feature disabled) or handle gracefully
181+ let result = is_disk_space_low ( invalid_path, 1000 ) ;
182+ // Should not panic, result depends on sysinfo behavior
183+ assert ! ( result == true || result == false ) ;
184+ }
185+
186+ #[ test]
187+ fn test_get_available_disk_space_mb_with_valid_path ( ) {
188+ // Test with a valid path
189+ let temp_dir = std:: env:: temp_dir ( ) ;
190+ let result = get_available_disk_space_mb ( & temp_dir) ;
191+ // Should return Some(u64) if successful, or None if it fails
192+ match result {
193+ Some ( mb) => {
194+ // If successful, should be a reasonable value (not 0 for temp dir)
195+ assert ! ( mb > 0 || mb == 0 , "Available space should be a valid number" ) ;
196+ }
197+ None => {
198+ // It's okay if it fails, sysinfo might not work in all test environments
199+ }
200+ }
201+ }
202+
203+ #[ test]
204+ fn test_get_available_disk_space_mb_with_invalid_path ( ) {
205+ // Test with a non-existent path
206+ let invalid_path = Path :: new ( "/nonexistent/path/that/does/not/exist" ) ;
207+ let result = get_available_disk_space_mb ( invalid_path) ;
208+ // Should return None for invalid paths
209+ assert_eq ! ( result, None ) ;
210+ }
211+
212+ #[ test]
213+ fn test_is_disk_space_low_threshold_comparison ( ) {
214+ // Test that the function correctly compares available space with threshold
215+ let temp_dir = std:: env:: temp_dir ( ) ;
216+
217+ if let Some ( available_mb) = get_available_disk_space_mb ( & temp_dir) {
218+ // Test with a threshold smaller than available space (should pass - space is sufficient)
219+ if available_mb > 0 {
220+ let result_small = is_disk_space_low ( & temp_dir, available_mb. saturating_sub ( 1 ) ) ;
221+ assert ! ( !result_small, "Should pass (return false) when threshold is less than available space" ) ;
222+ }
223+
224+ // Test with a threshold larger than available space (should fail - space is insufficient)
225+ let result_large = is_disk_space_low ( & temp_dir, available_mb. saturating_add ( 1 ) ) ;
226+ assert ! ( result_large, "Should fail (return true) when threshold is greater than available space" ) ;
227+
228+ // Test with threshold equal to available space (edge case)
229+ // When available == threshold, should trigger shutdown (return true) because "once reached" includes equality
230+ let result_equal = is_disk_space_low ( & temp_dir, available_mb) ;
231+ assert ! ( result_equal, "Should fail (return true) when threshold equals available space, as 'once reached' includes equality" ) ;
232+ }
233+ // If we can't get disk space, that's okay - test environment might not support it
234+ }
235+ }
0 commit comments