Created
October 22, 2023 12:00
-
-
Save sagoez/f2030bd6d53520039637631559b9f8c0 to your computer and use it in GitHub Desktop.
Revisions
-
sagoez created this gist
Oct 22, 2023 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,114 @@ // Exercise: https://google.github.io/comprehensive-rust/exercises/day-3/safe-ffi-wrapper.html mod ffi { use std::os::raw::{c_char, c_int}; #[cfg(not(target_os = "macos"))] use std::os::raw::{c_long, c_uchar, c_ulong, c_ushort}; // Opaque type. See https://doc.rust-lang.org/nomicon/ffi.html. #[repr(C)] pub struct DIR { _data: [u8; 0], _marker: core::marker::PhantomData<(*mut u8, core::marker::PhantomPinned)>, } // Layout according to the Linux man page for readdir(3), where ino_t and // off_t are resolved according to the definitions in // /usr/include/x86_64-linux-gnu/{sys/types.h, bits/typesizes.h}. #[cfg(not(target_os = "macos"))] #[repr(C)] pub struct dirent { pub d_ino: c_ulong, pub d_off: c_long, pub d_reclen: c_ushort, pub d_type: c_uchar, pub d_name: [c_char; 256], } // Layout according to the macOS man page for dir(5). #[cfg(target_os = "macos")] #[repr(C)] pub struct dirent { pub d_fileno: u64, pub d_seekoff: u64, pub d_reclen: u16, pub d_namlen: u16, pub d_type: u8, pub d_name: [c_char; 1024], } extern "C" { pub fn opendir(s: *const c_char) -> *mut DIR; #[cfg(not(all(target_os = "macos", target_arch = "x86_64")))] pub fn readdir(s: *mut DIR) -> *const dirent; // See https://github.com/rust-lang/libc/issues/414 and the section on // _DARWIN_FEATURE_64_BIT_INODE in the macOS man page for stat(2). // // "Platforms that existed before these updates were available" refers // to macOS (as opposed to iOS / wearOS / etc.) on Intel and PowerPC. #[cfg(all(target_os = "macos", target_arch = "x86_64"))] #[link_name = "readdir$INODE64"] pub fn readdir(s: *mut DIR) -> *const dirent; pub fn closedir(s: *mut DIR) -> c_int; } } use std::ffi::{CStr, CString, OsStr, OsString}; use std::os::unix::ffi::OsStrExt; #[derive(Debug)] struct DirectoryIterator { path: CString, dir: *mut ffi::DIR, } impl DirectoryIterator { fn new(path: &str) -> Result<DirectoryIterator, String> { // Call opendir and return a Ok value if that worked, // otherwise return Err with a message. // Hint: you will need to convert the path to a CString. let path = CString::new(path).map_err(|err| format!("path is not valid: {}", err))?; let dir = unsafe { ffi::opendir(path.as_ptr()) }; if dir.is_null() { return Err("opendir failed".to_string()); } Ok(DirectoryIterator { path, dir }) } } impl Iterator for DirectoryIterator { type Item = OsString; fn next(&mut self) -> Option<OsString> { // Keep calling readdir until we get a NULL pointer back. // If we get a NULL pointer, return None. // Otherwise, return Some with the name of the file. let dirent: *const ffi::dirent = unsafe { ffi::readdir(self.dir) }; if dirent.is_null() { return None; } // SAFETY: We just checked that dirent is not null so it is safe to dereference. let dir_name: &CStr = unsafe { CStr::from_ptr((*dirent).d_name.as_ptr()) }; Some(OsStr::from_bytes(dir_name.to_bytes()).into()) } } impl Drop for DirectoryIterator { fn drop(&mut self) { // Call closedir as needed. if !self.dir.is_null() && unsafe { ffi::closedir(self.dir) } != 0 { panic!("closedir failed"); } } } fn main() -> Result<(), String> { let iter = DirectoryIterator::new(".")?; println!("files: {:#?}", iter.collect::<Vec<_>>()); Ok(()) }