11use rustler:: { Atom , Binary , Env , Error , NifResult , Term } ;
22use std:: ffi:: CString ;
3+
34#[ cfg( unix) ]
45use std:: io;
5- // Unix-specific imports
6+
67#[ cfg( unix) ]
78use std:: ffi:: OsStr ;
89#[ cfg( unix) ]
910use std:: os:: unix:: ffi:: OsStrExt ;
1011#[ cfg( unix) ]
1112use std:: path:: Path ;
1213
13- // Windows-specific imports
1414#[ cfg( windows) ]
15- use windows:: core:: { PCWSTR , PWSTR } ;
15+ use std:: ptr;
16+ #[ cfg( windows) ]
17+ use windows:: core:: PCWSTR ;
18+ #[ cfg( windows) ]
19+ use windows:: core:: PWSTR ;
1620#[ cfg( windows) ]
1721use windows:: Win32 :: Foundation :: { GetLastError , LocalFree , HLOCAL } ;
1822#[ cfg( windows) ]
@@ -25,7 +29,6 @@ use windows::Win32::System::Diagnostics::Debug::{
2529 FORMAT_MESSAGE_IGNORE_INSERTS ,
2630} ;
2731
28- // nix imports with proper cfg to avoid unused warnings
2932#[ cfg( all( unix, target_os = "linux" ) ) ]
3033use nix:: sys:: statfs:: { statfs, Statfs } ;
3134#[ cfg( all( unix, not( target_os = "linux" ) ) ) ]
@@ -37,6 +40,7 @@ mod atoms {
3740 error,
3841 wrong_arity,
3942 invalid_path,
43+ alloc_failed,
4044 path_conversion_failed,
4145 not_directory,
4246 winapi_failed,
@@ -81,42 +85,35 @@ fn make_errno_error_tuple<'a>(env: Env<'a>, reason: Atom, err: io::Error) -> Nif
8185#[ cfg( windows) ]
8286// Helper: Create error tuple with WinAPI error details
8387fn make_winapi_error_tuple < ' a > ( env : Env < ' a > , reason : Atom , errnum : u32 ) -> NifResult < Term < ' a > > {
84- use windows:: core:: PWSTR ;
85- use windows:: Win32 :: Foundation :: GetLastError ;
86- use windows:: Win32 :: System :: Diagnostics :: Debug :: {
87- FormatMessageW , FORMAT_MESSAGE_ALLOCATE_BUFFER , FORMAT_MESSAGE_FROM_SYSTEM ,
88- FORMAT_MESSAGE_IGNORE_INSERTS ,
89- } ;
90-
91- let mut buffer_ptr: * mut u16 = std:: ptr:: null_mut ( ) ;
88+ let mut buffer_ptr: * mut u16 = ptr:: null_mut ( ) ;
9289 let flags =
9390 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS ;
91+ let lang: u32 = 0 ;
9492
9593 let len = unsafe {
9694 FormatMessageW (
9795 flags,
9896 None ,
9997 errnum,
100- 0 ,
101- PWSTR ( & mut buffer_ptr as * mut _ as * mut u16 ) ,
98+ lang ,
99+ PWSTR ( & mut buffer_ptr as * mut _ as * mut _ ) ,
102100 0 ,
103101 None ,
104102 )
105103 } ;
106104
107105 let errstr = if len == 0 {
108- format ! ( "Unknown WinAPI error: code {}" , errnum )
106+ "Unknown WinAPI error" . to_string ( )
109107 } else {
110- let slice = unsafe { std:: slice:: from_raw_parts ( buffer_ptr, len as usize ) } ;
111- widestring:: U16Str :: from_slice ( slice)
112- . to_string_lossy ( )
113- . trim_end ( )
114- . to_string ( )
108+ let message_slice = unsafe { std:: slice:: from_raw_parts ( buffer_ptr, len as usize ) } ;
109+
110+ let wide_str = widestring:: U16Str :: from_slice ( message_slice) ;
111+ wide_str. to_string_lossy ( ) . trim_end ( ) . to_string ( )
115112 } ;
116113
117114 if !buffer_ptr. is_null ( ) {
118115 unsafe {
119- windows :: Win32 :: Foundation :: LocalFree ( buffer_ptr as isize ) ;
116+ let _ = LocalFree ( HLOCAL ( buffer_ptr as isize ) ) ;
120117 }
121118 }
122119
@@ -126,7 +123,7 @@ fn make_winapi_error_tuple<'a>(env: Env<'a>, reason: Atom, errnum: u32) -> NifRe
126123 make_error_tuple3 ( env, reason, detail)
127124}
128125
129- // Helper: Convert Elixir term to a path (as bytes). For Windows we later turn UTF-8 -> UTF-16.
126+ // Helper: Convert Elixir term to a path
130127fn get_path_from_term < ' a > ( _env : Env < ' a > , term : Term < ' a > ) -> NifResult < CString > {
131128 // Try binary first
132129 let binary = match term. decode :: < Binary > ( ) {
@@ -143,11 +140,9 @@ fn get_path_from_term<'a>(_env: Env<'a>, term: Term<'a>) -> NifResult<CString> {
143140 }
144141 }
145142 } ;
146-
147143 if binary. is_empty ( ) {
148144 return Err ( Error :: BadArg ) ;
149145 }
150-
151146 match CString :: new ( binary. as_slice ( ) ) {
152147 Ok ( cstr) => Ok ( cstr) ,
153148 Err ( _) => Err ( Error :: BadArg ) ,
@@ -163,37 +158,30 @@ fn stat_fs<'a>(env: Env<'a>, path_term: Term<'a>) -> NifResult<Term<'a>> {
163158
164159 #[ cfg( windows) ]
165160 {
166- // The NIF expects UTF-8 input; Windows APIs require UTF-16.
167161 let path_str = match path_cstr. to_str ( ) {
168162 Ok ( s) => s,
169163 Err ( _) => return make_error_tuple ( env, atoms:: path_conversion_failed ( ) ) ,
170164 } ;
171165
172- // Long-path handling. Preserve existing \?\ prefix; convert UNC to \\?\UNC\...
173166 let is_unc = path_str. starts_with ( "\\ \\ " ) && !path_str. starts_with ( "\\ \\ ?\\ " ) ;
174- let mut long_path_str = if is_unc {
167+ let long_path_str = if is_unc {
175168 format ! ( "\\ \\ ?\\ UNC{}" , & path_str[ 2 ..] )
176169 } else if !path_str. starts_with ( "\\ \\ ?\\ " ) {
177170 format ! ( "\\ \\ ?\\ {}" , path_str)
178171 } else {
179172 path_str. to_string ( )
180173 } ;
181174
182- // Normalize bare drive roots like "\\?\C:" -> "\\?\C:\" for more predictable API behavior.
183- if long_path_str. len ( ) == 6 && long_path_str. ends_with ( ':' ) {
184- long_path_str. push ( '\\' ) ;
185- }
186-
187175 let wide_str = match widestring:: WideCString :: from_str ( & long_path_str) {
188176 Ok ( ws) => ws,
189177 Err ( _) => return make_error_tuple ( env, atoms:: path_conversion_failed ( ) ) ,
190178 } ;
191- let wpath = PCWSTR ( wide_str. as_ptr ( ) ) ;
179+ let long_wpath = PCWSTR :: from_raw ( wide_str. as_ptr ( ) ) ;
192180
193- let attr = unsafe { GetFileAttributesW ( wpath ) } ;
181+ let attr = unsafe { GetFileAttributesW ( long_wpath ) } ;
194182 if attr == INVALID_FILE_ATTRIBUTES {
195183 let err = unsafe { GetLastError ( ) } ;
196- return make_winapi_error_tuple ( env, atoms:: winapi_failed ( ) , err. 0 ) ;
184+ return make_winapi_error_tuple ( env, atoms:: not_directory ( ) , err. 0 ) ;
197185 }
198186 if ( attr & FILE_ATTRIBUTE_DIRECTORY . 0 ) == 0 {
199187 return make_error_tuple ( env, atoms:: not_directory ( ) ) ;
@@ -202,11 +190,15 @@ fn stat_fs<'a>(env: Env<'a>, path_term: Term<'a>) -> NifResult<Term<'a>> {
202190 let mut avail: u64 = 0 ;
203191 let mut total: u64 = 0 ;
204192 let mut free: u64 = 0 ;
205-
206- let ok = unsafe {
207- GetDiskFreeSpaceExW ( wpath, Some ( & mut avail) , Some ( & mut total) , Some ( & mut free) )
193+ let success = unsafe {
194+ GetDiskFreeSpaceExW (
195+ long_wpath,
196+ Some ( & mut avail) ,
197+ Some ( & mut total) ,
198+ Some ( & mut free) ,
199+ )
208200 } ;
209- if let Err ( e) = ok {
201+ if let Err ( e) = success {
210202 return make_winapi_error_tuple ( env, atoms:: winapi_failed ( ) , e. code ( ) . 0 as u32 ) ;
211203 }
212204
@@ -216,10 +208,10 @@ fn stat_fs<'a>(env: Env<'a>, path_term: Term<'a>) -> NifResult<Term<'a>> {
216208 . map_put ( atoms:: free ( ) . to_term ( env) , free) ?
217209 . map_put ( atoms:: total ( ) . to_term ( env) , total) ?
218210 . map_put ( atoms:: used ( ) . to_term ( env) , used) ?;
219- return Ok ( rustler:: types:: tuple:: make_tuple (
211+ Ok ( rustler:: types:: tuple:: make_tuple (
220212 env,
221213 & [ atoms:: ok ( ) . to_term ( env) , map] ,
222- ) ) ;
214+ ) )
223215 }
224216
225217 #[ cfg( unix) ]
@@ -238,7 +230,7 @@ fn stat_fs<'a>(env: Env<'a>, path_term: Term<'a>) -> NifResult<Term<'a>> {
238230 let statfs_buf: Statfs = match statfs ( os_path) {
239231 Ok ( buf) => buf,
240232 Err ( err) => {
241- let io_err = io:: Error :: from_raw_os_error ( err as i32 ) ; // nix Errno layout matches C errno
233+ let io_err = io:: Error :: from_raw_os_error ( err as i32 ) ;
242234 return make_errno_error_tuple ( env, atoms:: statfs_failed ( ) , io_err) ;
243235 }
244236 } ;
@@ -252,18 +244,18 @@ fn stat_fs<'a>(env: Env<'a>, path_term: Term<'a>) -> NifResult<Term<'a>> {
252244 . map_put ( atoms:: free ( ) . to_term ( env) , free) ?
253245 . map_put ( atoms:: total ( ) . to_term ( env) , total) ?
254246 . map_put ( atoms:: used ( ) . to_term ( env) , used) ?;
255- return Ok ( rustler:: types:: tuple:: make_tuple (
247+ Ok ( rustler:: types:: tuple:: make_tuple (
256248 env,
257249 & [ atoms:: ok ( ) . to_term ( env) , map] ,
258- ) ) ;
250+ ) )
259251 }
260252
261253 #[ cfg( not( target_os = "linux" ) ) ]
262254 {
263255 let statvfs_buf: Statvfs = match statvfs ( os_path) {
264256 Ok ( buf) => buf,
265257 Err ( err) => {
266- let io_err = io:: Error :: from_raw_os_error ( err as i32 ) ; // nix Errno layout matches C errno
258+ let io_err = io:: Error :: from_raw_os_error ( err as i32 ) ;
267259 return make_errno_error_tuple ( env, atoms:: statvfs_failed ( ) , io_err) ;
268260 }
269261 } ;
@@ -277,10 +269,10 @@ fn stat_fs<'a>(env: Env<'a>, path_term: Term<'a>) -> NifResult<Term<'a>> {
277269 . map_put ( atoms:: free ( ) . to_term ( env) , free) ?
278270 . map_put ( atoms:: total ( ) . to_term ( env) , total) ?
279271 . map_put ( atoms:: used ( ) . to_term ( env) , used) ?;
280- return Ok ( rustler:: types:: tuple:: make_tuple (
272+ Ok ( rustler:: types:: tuple:: make_tuple (
281273 env,
282274 & [ atoms:: ok ( ) . to_term ( env) , map] ,
283- ) ) ;
275+ ) )
284276 }
285277 }
286278}
0 commit comments