about summary refs log tree commit diff
path: root/src/libstd/rt
diff options
context:
space:
mode:
authorJeff Olson <olson.jeffery@gmail.com>2013-09-16 13:25:10 -0700
committerJeff Olson <olson.jeffery@gmail.com>2013-09-16 23:19:24 -0700
commitbf399d558e755b5964666892e9f95440ad6f8ef2 (patch)
tree60bba6e534cf70e65b84058dfeb8a4433579d165 /src/libstd/rt
parent25b4d8c1d7de70bab8eca8a57fdfb703e5e281c9 (diff)
downloadrust-bf399d558e755b5964666892e9f95440ad6f8ef2.tar.gz
rust-bf399d558e755b5964666892e9f95440ad6f8ef2.zip
std: bind uv_fs_readdir(), flesh out DirectoryInfo and docs/cleanup
Diffstat (limited to 'src/libstd/rt')
-rw-r--r--src/libstd/rt/io/file.rs85
-rw-r--r--src/libstd/rt/rtio.rs2
-rw-r--r--src/libstd/rt/uv/file.rs50
-rw-r--r--src/libstd/rt/uv/uvio.rs38
-rw-r--r--src/libstd/rt/uv/uvll.rs14
5 files changed, 173 insertions, 16 deletions
diff --git a/src/libstd/rt/io/file.rs b/src/libstd/rt/io/file.rs
index af202a729e3..7aac79b964f 100644
--- a/src/libstd/rt/io/file.rs
+++ b/src/libstd/rt/io/file.rs
@@ -108,6 +108,22 @@ pub fn stat<P: PathLike>(path: &P) -> Option<FileStat> {
     }
 }
 
+pub fn readdir<P: PathLike>(path: &P) -> Option<~[Path]> {
+    let readdir_result = unsafe {
+        let io: *mut IoFactoryObject = Local::unsafe_borrow();
+        (*io).fs_readdir(path, 0)
+    };
+    match readdir_result {
+        Ok(p) => {
+            Some(p)
+        },
+        Err(ioerr) => {
+            io_error::cond.raise(ioerr);
+            None
+        }
+    }
+}
+
 /// Read-only view of file
 pub struct FileReader { priv stream: FileStream }
 
@@ -272,6 +288,18 @@ pub trait FileSystemInfo {
 /// Represents passive information about a file (primarily exposed
 /// via the `stat()` method. Also provides methods for opening
 /// a file in various modes/permissions.
+///
+/// # Example
+///
+/// * Check if a file exists, reading from it if so
+///
+///     let f = &Path("/some/file/path.txt");
+///     if f.exists() {
+///         let reader = f.open_reader(Open);
+///         let mut mem = [0u8, 8*64000];
+///         reader.read(mem);
+///         // ...
+///     }
 pub trait FileInfo : FileSystemInfo {
     /// Whether the underlying implemention (be it a file path,
     /// or something else) points at a "regular file" on the FS. Will return
@@ -290,7 +318,7 @@ pub trait FileInfo : FileSystemInfo {
         match suppressed_stat(|| self.stat()) {
             Some(s) => match s.is_file {
                 true => open(self.get_path(), mode, access),
-                false => None // FIXME: raise condition, not a regular file..
+                false => None
             },
             None => open(self.get_path(), mode, access)
         }
@@ -320,13 +348,16 @@ pub trait FileInfo : FileSystemInfo {
     }
 }
 
-/// `FileSystemInfo` implementation for `Path`s 
+/// `FileSystemInfo` implementation for `Path`s
 impl FileSystemInfo for Path {
     fn get_path<'a>(&'a self) -> &'a Path { self }
 }
-/// `FileInfo` implementation for `Path`s 
+/// `FileInfo` implementation for `Path`s
 impl FileInfo for Path { }
 
+/// Passive information about a directory on the filesystem. Includes
+/// Convenience methods to iterate over a directory's contents (via `readdir`, as
+/// as `mkdir` and `rmdir` operations.
 trait DirectoryInfo : FileSystemInfo {
     /// Whether the underlying implemention (be it a file path,
     /// or something else) points at a directory file" on the FS. Will return
@@ -368,8 +399,9 @@ trait DirectoryInfo : FileSystemInfo {
                         let ioerr = IoError {
                             kind: MismatchedFileTypeForOperation,
                             desc: "Cannot do rmdir() on a non-directory",
-                            detail:
-                                Some(fmt!("%s is a non-directory; can't rmdir it", self.get_path().to_str()))
+                            detail: Some(fmt!(
+                                "%s is a non-directory; can't rmdir it",
+                                self.get_path().to_str()))
                         };
                         io_error::cond.raise(ioerr);
                     }
@@ -383,14 +415,13 @@ trait DirectoryInfo : FileSystemInfo {
                 })
         }
     }
-    fn readdir(&self) -> ~[~str] {
-        ~[]
+    fn readdir(&self) -> Option<~[Path]> {
+        readdir(self.get_path())
     }
     //fn get_subdirs(&self, filter: &str) -> ~[Path];
     //fn get_files(&self, filter: &str) -> ~[Path];
 }
 
-/// FIXME: DOCS
 impl DirectoryInfo for Path { }
 
 fn file_test_smoke_test_impl() {
@@ -663,3 +694,41 @@ fn file_test_directoryinfo_check_exists_before_and_after_mkdir() {
         assert!(!dir.exists());
     }
 }
+
+#[test]
+fn file_test_directoryinfo_readdir() {
+    use str;
+    do run_in_mt_newsched_task {
+        let dir = &Path("./tmp/di_readdir");
+        dir.mkdir();
+        let prefix = "foo";
+        for n in range(0,3) {
+            let f = dir.push(fmt!("%d.txt", n));
+            let mut w = f.open_writer(Create);
+            let msg_str = (prefix + n.to_str().to_owned()).to_owned();
+            let msg = msg_str.as_bytes();
+            w.write(msg);
+        }
+        match dir.readdir() {
+            Some(files) => {
+                let mut mem = [0u8, .. 4];
+                for f in files.iter() {
+                    {
+                        let n = f.filestem();
+                        let mut r = f.open_reader(Open);
+                        r.read(mem);
+                        let read_str = str::from_utf8(mem);
+                        let expected = match n {
+                            Some(n) => prefix+n,
+                            None => fail!("really shouldn't happen..")
+                        };
+                        assert!(expected == read_str);
+                    }
+                    f.unlink();
+                }
+            },
+            None => fail!("shouldn't happen")
+        }
+        dir.rmdir();
+    }
+}
\ No newline at end of file
diff --git a/src/libstd/rt/rtio.rs b/src/libstd/rt/rtio.rs
index 0abf81f62de..568ea3317e5 100644
--- a/src/libstd/rt/rtio.rs
+++ b/src/libstd/rt/rtio.rs
@@ -78,6 +78,8 @@ pub trait IoFactory {
     //fn fs_fstat(&mut self, fd: c_int) -> Result<FileStat, IoError>;
     fn fs_mkdir<P: PathLike>(&mut self, path: &P) -> Result<(), IoError>;
     fn fs_rmdir<P: PathLike>(&mut self, path: &P) -> Result<(), IoError>;
+    fn fs_readdir<P: PathLike>(&mut self, path: &P, flags: c_int) ->
+        Result<~[Path], IoError>;
 }
 
 pub trait RtioStream {
diff --git a/src/libstd/rt/uv/file.rs b/src/libstd/rt/uv/file.rs
index 34f87e3601e..cd0a217cc45 100644
--- a/src/libstd/rt/uv/file.rs
+++ b/src/libstd/rt/uv/file.rs
@@ -17,6 +17,7 @@ use rt::uv::uvll;
 use rt::uv::uvll::*;
 use super::super::io::support::PathLike;
 use cast::transmute;
+use libc;
 use libc::{c_int};
 use option::{None, Some, Option};
 
@@ -28,14 +29,6 @@ pub struct RequestData {
 }
 
 impl FsRequest {
-    pub fn new_REFACTOR_ME(cb: Option<FsCallback>) -> FsRequest {
-        let fs_req = unsafe { malloc_req(UV_FS) };
-        assert!(fs_req.is_not_null());
-        let fs_req: FsRequest = NativeHandle::from_native_handle(fs_req);
-        fs_req.install_req_data(cb);
-        fs_req
-    }
-
     pub fn new() -> FsRequest {
         let fs_req = unsafe { malloc_req(UV_FS) };
         assert!(fs_req.is_not_null());
@@ -180,6 +173,17 @@ impl FsRequest {
         });
     }
 
+    pub fn readdir<P: PathLike>(self, loop_: &Loop, path: &P,
+                                flags: c_int, cb: FsCallback) {
+        let complete_cb_ptr = self.req_boilerplate(Some(cb));
+        path.path_as_str(|p| {
+            p.to_c_str().with_ref(|p| unsafe {
+            uvll::fs_readdir(loop_.native_handle(),
+                          self.native_handle(), p, flags, complete_cb_ptr)
+            })
+        });
+    }
+
     // accessors/utility funcs
     fn sync_cleanup(self, result: c_int)
           -> Result<c_int, UvError> {
@@ -235,6 +239,36 @@ impl FsRequest {
         stat
     }
 
+    pub fn get_ptr(&self) -> *libc::c_void {
+        unsafe {
+            uvll::get_ptr_from_fs_req(self.native_handle())
+        }
+    }
+
+    pub fn get_paths(&mut self) -> ~[~str] {
+        use str;
+        let ptr = self.get_ptr();
+        match self.get_result() {
+            n if (n <= 0) => {
+                ~[]
+            },
+            n => {
+                let n_len = n as uint;
+                // we pass in the len that uv tells us is there
+                // for the entries and we don't continue past that..
+                // it appears that sometimes the multistring isn't
+                // correctly delimited and we stray into garbage memory?
+                // in any case, passing Some(n_len) fixes it and ensures
+                // good results
+                let raw_path_strs = unsafe {
+                    str::raw::from_c_multistring(ptr as *libc::c_char, Some(n_len)) };
+                let raw_len = raw_path_strs.len();
+                assert_eq!(raw_len, n_len);
+                raw_path_strs
+            }
+        }
+    }
+
     fn cleanup_and_delete(self) {
         unsafe {
             let data = uvll::get_data_for_req(self.native_handle());
diff --git a/src/libstd/rt/uv/uvio.rs b/src/libstd/rt/uv/uvio.rs
index d701a87004c..2d024f04e1d 100644
--- a/src/libstd/rt/uv/uvio.rs
+++ b/src/libstd/rt/uv/uvio.rs
@@ -704,6 +704,44 @@ impl IoFactory for UvIoFactory {
             };
         }
     }
+    fn fs_readdir<P: PathLike>(&mut self, path: &P, flags: c_int) ->
+        Result<~[Path], IoError> {
+        use str::StrSlice;
+        let result_cell = Cell::new_empty();
+        let result_cell_ptr: *Cell<Result<~[Path],
+                                           IoError>> = &result_cell;
+        let path_cell = Cell::new(path);
+        do task::unkillable { // FIXME(#8674)
+            let scheduler: ~Scheduler = Local::take();
+            let stat_req = file::FsRequest::new();
+            do scheduler.deschedule_running_task_and_then |_, task| {
+                let task_cell = Cell::new(task);
+                let path = path_cell.take();
+                let path_str = path.path_as_str(|p| p.to_owned());
+                do stat_req.readdir(self.uv_loop(), path, flags)
+                      |req,err| {
+                    let res = match err {
+                        None => {
+                            let rel_paths = req.get_paths();
+                            let mut paths = ~[];
+                            for r in rel_paths.iter() {
+                                paths.push(Path(path_str+"/"+*r));
+                            }
+                            Ok(paths)
+                        },
+                        Some(e) => {
+                            Err(uv_error_to_io_error(e))
+                        }
+                    };
+                    unsafe { (*result_cell_ptr).put_back(res); }
+                    let scheduler: ~Scheduler = Local::take();
+                    scheduler.resume_blocked_task_immediately(task_cell.take());
+                };
+            };
+        };
+        assert!(!result_cell.is_empty());
+        return result_cell.take();
+    }
 }
 
 pub struct UvTcpListener {
diff --git a/src/libstd/rt/uv/uvll.rs b/src/libstd/rt/uv/uvll.rs
index a2d1c48c3e1..42102a52e2e 100644
--- a/src/libstd/rt/uv/uvll.rs
+++ b/src/libstd/rt/uv/uvll.rs
@@ -811,6 +811,12 @@ pub unsafe fn fs_rmdir(loop_ptr: *uv_loop_t, req: *uv_fs_t, path: *c_char,
 
     rust_uv_fs_rmdir(loop_ptr, req, path, cb)
 }
+pub unsafe fn fs_readdir(loop_ptr: *uv_loop_t, req: *uv_fs_t, path: *c_char,
+                flags: c_int, cb: *u8) -> c_int {
+    #[fixed_stack_segment]; #[inline(never)];
+
+    rust_uv_fs_readdir(loop_ptr, req, path, flags, cb)
+}
 pub unsafe fn populate_stat(req_in: *uv_fs_t, stat_out: *uv_stat_t) {
     #[fixed_stack_segment]; #[inline(never)];
 
@@ -828,6 +834,11 @@ pub unsafe fn get_result_from_fs_req(req: *uv_fs_t) -> c_int {
 
     rust_uv_get_result_from_fs_req(req)
 }
+pub unsafe fn get_ptr_from_fs_req(req: *uv_fs_t) -> *libc::c_void {
+    #[fixed_stack_segment]; #[inline(never)];
+
+    rust_uv_get_ptr_from_fs_req(req)
+}
 pub unsafe fn get_loop_from_fs_req(req: *uv_fs_t) -> *uv_loop_t {
     #[fixed_stack_segment]; #[inline(never)];
 
@@ -1014,9 +1025,12 @@ extern {
                         mode: c_int, cb: *u8) -> c_int;
     fn rust_uv_fs_rmdir(loop_ptr: *c_void, req: *uv_fs_t, path: *c_char,
                         cb: *u8) -> c_int;
+    fn rust_uv_fs_readdir(loop_ptr: *c_void, req: *uv_fs_t, path: *c_char,
+                        flags: c_int, cb: *u8) -> c_int;
     fn rust_uv_fs_req_cleanup(req: *uv_fs_t);
     fn rust_uv_populate_uv_stat(req_in: *uv_fs_t, stat_out: *uv_stat_t);
     fn rust_uv_get_result_from_fs_req(req: *uv_fs_t) -> c_int;
+    fn rust_uv_get_ptr_from_fs_req(req: *uv_fs_t) -> *libc::c_void;
     fn rust_uv_get_loop_from_fs_req(req: *uv_fs_t) -> *uv_loop_t;
     fn rust_uv_get_loop_from_getaddrinfo_req(req: *uv_fs_t) -> *uv_loop_t;