|
11 | 11 | use prelude::*;
|
12 | 12 | use super::support::PathLike;
|
13 | 13 | use super::{Reader, Writer, Seek};
|
14 |
| -use super::SeekStyle; |
| 14 | +use super::{SeekSet, SeekCur, SeekEnd, SeekStyle}; |
| 15 | +use rt::rtio::{RtioFileStream, IoFactory, IoFactoryObject}; |
| 16 | +use rt::io::{io_error, read_error, EndOfFile, |
| 17 | + FileMode, FileAccess, Open, Read, Create, ReadWrite}; |
| 18 | +use rt::local::Local; |
| 19 | +use rt::test::*; |
15 | 20 |
|
16 |
| -/// # FIXME #7785 |
17 |
| -/// * Ugh, this is ridiculous. What is the best way to represent these options? |
18 |
| -enum FileMode { |
19 |
| - /// Opens an existing file. IoError if file does not exist. |
20 |
| - Open, |
21 |
| - /// Creates a file. IoError if file exists. |
22 |
| - Create, |
23 |
| - /// Opens an existing file or creates a new one. |
24 |
| - OpenOrCreate, |
25 |
| - /// Opens an existing file or creates a new one, positioned at EOF. |
26 |
| - Append, |
27 |
| - /// Opens an existing file, truncating it to 0 bytes. |
28 |
| - Truncate, |
29 |
| - /// Opens an existing file or creates a new one, truncating it to 0 bytes. |
30 |
| - CreateOrTruncate, |
| 21 | +/// Open a file for reading/writing, as indicated by `path`. |
| 22 | +pub fn open<P: PathLike>(path: &P, |
| 23 | + mode: FileMode, |
| 24 | + access: FileAccess |
| 25 | + ) -> Option<FileStream> { |
| 26 | + let open_result = unsafe { |
| 27 | + let io = Local::unsafe_borrow::<IoFactoryObject>(); |
| 28 | + (*io).fs_open(path, mode, access) |
| 29 | + }; |
| 30 | + match open_result { |
| 31 | + Ok(fd) => Some(FileStream { |
| 32 | + fd: fd, |
| 33 | + last_nread: -1 |
| 34 | + }), |
| 35 | + Err(ioerr) => { |
| 36 | + io_error::cond.raise(ioerr); |
| 37 | + None |
| 38 | + } |
| 39 | + } |
31 | 40 | }
|
32 | 41 |
|
33 |
| -enum FileAccess { |
34 |
| - Read, |
35 |
| - Write, |
36 |
| - ReadWrite |
| 42 | +/// Unlink (remove) a file from the filesystem, as indicated |
| 43 | +/// by `path`. |
| 44 | +pub fn unlink<P: PathLike>(path: &P) { |
| 45 | + let unlink_result = unsafe { |
| 46 | + let io = Local::unsafe_borrow::<IoFactoryObject>(); |
| 47 | + (*io).fs_unlink(path) |
| 48 | + }; |
| 49 | + match unlink_result { |
| 50 | + Ok(_) => (), |
| 51 | + Err(ioerr) => { |
| 52 | + io_error::cond.raise(ioerr); |
| 53 | + } |
| 54 | + } |
37 | 55 | }
|
38 | 56 |
|
39 |
| -pub struct FileStream; |
| 57 | +/// Abstraction representing *positional* access to a file. In this case, |
| 58 | +/// *positional* refers to it keeping an encounter *cursor* of where in the |
| 59 | +/// file a subsequent `read` or `write` will begin from. Users of a `FileStream` |
| 60 | +/// can `seek` to move the cursor to a given location *within the bounds of the |
| 61 | +/// file* and can ask to have the `FileStream` `tell` them the location, in |
| 62 | +/// bytes, of the cursor. |
| 63 | +/// |
| 64 | +/// This abstraction is roughly modeled on the access workflow as represented |
| 65 | +/// by `open(2)`, `read(2)`, `write(2)` and friends. |
| 66 | +/// |
| 67 | +/// The `open` and `unlink` static methods are provided to manage creation/removal |
| 68 | +/// of files. All other methods operatin on an instance of `FileStream`. |
| 69 | +pub struct FileStream { |
| 70 | + fd: ~RtioFileStream, |
| 71 | + last_nread: int, |
| 72 | +} |
40 | 73 |
|
41 | 74 | impl FileStream {
|
42 |
| - pub fn open<P: PathLike>(_path: &P, |
43 |
| - _mode: FileMode, |
44 |
| - _access: FileAccess |
45 |
| - ) -> Option<FileStream> { |
46 |
| - fail!() |
47 |
| - } |
48 | 75 | }
|
49 | 76 |
|
50 | 77 | impl Reader for FileStream {
|
51 |
| - fn read(&mut self, _buf: &mut [u8]) -> Option<uint> { |
52 |
| - fail!() |
| 78 | + fn read(&mut self, buf: &mut [u8]) -> Option<uint> { |
| 79 | + match self.fd.read(buf) { |
| 80 | + Ok(read) => { |
| 81 | + self.last_nread = read; |
| 82 | + match read { |
| 83 | + 0 => None, |
| 84 | + _ => Some(read as uint) |
| 85 | + } |
| 86 | + }, |
| 87 | + Err(ioerr) => { |
| 88 | + // EOF is indicated by returning None |
| 89 | + if ioerr.kind != EndOfFile { |
| 90 | + read_error::cond.raise(ioerr); |
| 91 | + } |
| 92 | + return None; |
| 93 | + } |
| 94 | + } |
53 | 95 | }
|
54 | 96 |
|
55 | 97 | fn eof(&mut self) -> bool {
|
56 |
| - fail!() |
| 98 | + self.last_nread == 0 |
57 | 99 | }
|
58 | 100 | }
|
59 | 101 |
|
60 | 102 | impl Writer for FileStream {
|
61 |
| - fn write(&mut self, _v: &[u8]) { fail!() } |
| 103 | + fn write(&mut self, buf: &[u8]) { |
| 104 | + match self.fd.write(buf) { |
| 105 | + Ok(_) => (), |
| 106 | + Err(ioerr) => { |
| 107 | + io_error::cond.raise(ioerr); |
| 108 | + } |
| 109 | + } |
| 110 | + } |
62 | 111 |
|
63 |
| - fn flush(&mut self) { fail!() } |
| 112 | + fn flush(&mut self) { |
| 113 | + match self.fd.flush() { |
| 114 | + Ok(_) => (), |
| 115 | + Err(ioerr) => { |
| 116 | + read_error::cond.raise(ioerr); |
| 117 | + } |
| 118 | + } |
| 119 | + } |
64 | 120 | }
|
65 | 121 |
|
66 | 122 | impl Seek for FileStream {
|
67 |
| - fn tell(&self) -> u64 { fail!() } |
| 123 | + fn tell(&self) -> u64 { |
| 124 | + let res = self.fd.tell(); |
| 125 | + match res { |
| 126 | + Ok(cursor) => cursor, |
| 127 | + Err(ioerr) => { |
| 128 | + read_error::cond.raise(ioerr); |
| 129 | + return -1; |
| 130 | + } |
| 131 | + } |
| 132 | + } |
| 133 | + |
| 134 | + fn seek(&mut self, pos: i64, style: SeekStyle) { |
| 135 | + match self.fd.seek(pos, style) { |
| 136 | + Ok(_) => { |
| 137 | + // successful seek resets EOF indicator |
| 138 | + self.last_nread = -1; |
| 139 | + () |
| 140 | + }, |
| 141 | + Err(ioerr) => { |
| 142 | + read_error::cond.raise(ioerr); |
| 143 | + } |
| 144 | + } |
| 145 | + } |
| 146 | +} |
68 | 147 |
|
69 |
| - fn seek(&mut self, _pos: i64, _style: SeekStyle) { fail!() } |
| 148 | +fn file_test_smoke_test_impl() { |
| 149 | + do run_in_newsched_task { |
| 150 | + let message = "it's alright. have a good time"; |
| 151 | + let filename = &Path("./tmp/file_rt_io_file_test.txt"); |
| 152 | + { |
| 153 | + let mut write_stream = open(filename, Create, ReadWrite).unwrap(); |
| 154 | + write_stream.write(message.as_bytes()); |
| 155 | + } |
| 156 | + { |
| 157 | + use str; |
| 158 | + let mut read_stream = open(filename, Open, Read).unwrap(); |
| 159 | + let mut read_buf = [0, .. 1028]; |
| 160 | + let read_str = match read_stream.read(read_buf).unwrap() { |
| 161 | + -1|0 => fail!("shouldn't happen"), |
| 162 | + n => str::from_bytes(read_buf.slice_to(n)) |
| 163 | + }; |
| 164 | + assert!(read_str == message.to_owned()); |
| 165 | + } |
| 166 | + unlink(filename); |
| 167 | + } |
70 | 168 | }
|
71 | 169 |
|
72 | 170 | #[test]
|
73 |
| -#[ignore] |
74 |
| -fn super_simple_smoke_test_lets_go_read_some_files_and_have_a_good_time() { |
75 |
| - let message = "it's alright. have a good time"; |
76 |
| - let filename = &Path("test.txt"); |
77 |
| - let mut outstream = FileStream::open(filename, Create, Read).unwrap(); |
78 |
| - outstream.write(message.as_bytes()); |
| 171 | +fn file_test_io_smoke_test() { |
| 172 | + file_test_smoke_test_impl(); |
| 173 | +} |
| 174 | + |
| 175 | +fn file_test_invalid_path_opened_without_create_should_raise_condition_impl() { |
| 176 | + do run_in_newsched_task { |
| 177 | + let filename = &Path("./tmp/file_that_does_not_exist.txt"); |
| 178 | + let mut called = false; |
| 179 | + do io_error::cond.trap(|_| { |
| 180 | + called = true; |
| 181 | + }).inside { |
| 182 | + let result = open(filename, Open, Read); |
| 183 | + assert!(result.is_none()); |
| 184 | + } |
| 185 | + assert!(called); |
| 186 | + } |
| 187 | +} |
| 188 | +#[test] |
| 189 | +fn file_test_io_invalid_path_opened_without_create_should_raise_condition() { |
| 190 | + file_test_invalid_path_opened_without_create_should_raise_condition_impl(); |
| 191 | +} |
| 192 | + |
| 193 | +fn file_test_unlinking_invalid_path_should_raise_condition_impl() { |
| 194 | + do run_in_newsched_task { |
| 195 | + let filename = &Path("./tmp/file_another_file_that_does_not_exist.txt"); |
| 196 | + let mut called = false; |
| 197 | + do io_error::cond.trap(|_| { |
| 198 | + called = true; |
| 199 | + }).inside { |
| 200 | + unlink(filename); |
| 201 | + } |
| 202 | + assert!(called); |
| 203 | + } |
| 204 | +} |
| 205 | +#[test] |
| 206 | +fn file_test_iounlinking_invalid_path_should_raise_condition() { |
| 207 | + file_test_unlinking_invalid_path_should_raise_condition_impl(); |
| 208 | +} |
| 209 | + |
| 210 | +fn file_test_io_non_positional_read_impl() { |
| 211 | + do run_in_newsched_task { |
| 212 | + use str; |
| 213 | + let message = "ten-four"; |
| 214 | + let mut read_mem = [0, .. 8]; |
| 215 | + let filename = &Path("./tmp/file_rt_io_file_test_positional.txt"); |
| 216 | + { |
| 217 | + let mut rw_stream = open(filename, Create, ReadWrite).unwrap(); |
| 218 | + rw_stream.write(message.as_bytes()); |
| 219 | + } |
| 220 | + { |
| 221 | + let mut read_stream = open(filename, Open, Read).unwrap(); |
| 222 | + { |
| 223 | + let read_buf = read_mem.mut_slice(0, 4); |
| 224 | + read_stream.read(read_buf); |
| 225 | + } |
| 226 | + { |
| 227 | + let read_buf = read_mem.mut_slice(4, 8); |
| 228 | + read_stream.read(read_buf); |
| 229 | + } |
| 230 | + } |
| 231 | + unlink(filename); |
| 232 | + let read_str = str::from_bytes(read_mem); |
| 233 | + assert!(read_str == message.to_owned()); |
| 234 | + } |
| 235 | +} |
| 236 | + |
| 237 | +#[test] |
| 238 | +fn file_test_io_non_positional_read() { |
| 239 | + file_test_io_non_positional_read_impl(); |
| 240 | +} |
| 241 | + |
| 242 | +fn file_test_io_seeking_impl() { |
| 243 | + do run_in_newsched_task { |
| 244 | + use str; |
| 245 | + let message = "ten-four"; |
| 246 | + let mut read_mem = [0, .. 4]; |
| 247 | + let set_cursor = 4 as u64; |
| 248 | + let mut tell_pos_pre_read; |
| 249 | + let mut tell_pos_post_read; |
| 250 | + let filename = &Path("./tmp/file_rt_io_file_test_seeking.txt"); |
| 251 | + { |
| 252 | + let mut rw_stream = open(filename, Create, ReadWrite).unwrap(); |
| 253 | + rw_stream.write(message.as_bytes()); |
| 254 | + } |
| 255 | + { |
| 256 | + let mut read_stream = open(filename, Open, Read).unwrap(); |
| 257 | + read_stream.seek(set_cursor as i64, SeekSet); |
| 258 | + tell_pos_pre_read = read_stream.tell(); |
| 259 | + read_stream.read(read_mem); |
| 260 | + tell_pos_post_read = read_stream.tell(); |
| 261 | + } |
| 262 | + unlink(filename); |
| 263 | + let read_str = str::from_bytes(read_mem); |
| 264 | + assert!(read_str == message.slice(4, 8).to_owned()); |
| 265 | + assert!(tell_pos_pre_read == set_cursor); |
| 266 | + assert!(tell_pos_post_read == message.len() as u64); |
| 267 | + } |
| 268 | +} |
| 269 | +#[test] |
| 270 | +fn file_test_io_seek_and_tell_smoke_test() { |
| 271 | + file_test_io_seeking_impl(); |
| 272 | +} |
| 273 | + |
| 274 | +fn file_test_io_seek_and_write_impl() { |
| 275 | + use io; |
| 276 | + do run_in_newsched_task { |
| 277 | + use str; |
| 278 | + let initial_msg = "food-is-yummy"; |
| 279 | + let overwrite_msg = "-the-bar!!"; |
| 280 | + let final_msg = "foo-the-bar!!"; |
| 281 | + let seek_idx = 3; |
| 282 | + let mut read_mem = [0, .. 13]; |
| 283 | + let filename = &Path("./tmp/file_rt_io_file_test_seek_and_write.txt"); |
| 284 | + { |
| 285 | + let mut rw_stream = open(filename, Create, ReadWrite).unwrap(); |
| 286 | + rw_stream.write(initial_msg.as_bytes()); |
| 287 | + rw_stream.seek(seek_idx as i64, SeekSet); |
| 288 | + rw_stream.write(overwrite_msg.as_bytes()); |
| 289 | + } |
| 290 | + { |
| 291 | + let mut read_stream = open(filename, Open, Read).unwrap(); |
| 292 | + read_stream.read(read_mem); |
| 293 | + } |
| 294 | + unlink(filename); |
| 295 | + let read_str = str::from_bytes(read_mem); |
| 296 | + io::println(fmt!("read_str: '%?' final_msg: '%?'", read_str, final_msg)); |
| 297 | + assert!(read_str == final_msg.to_owned()); |
| 298 | + } |
| 299 | +} |
| 300 | +#[test] |
| 301 | +fn file_test_io_seek_and_write() { |
| 302 | + file_test_io_seek_and_write_impl(); |
| 303 | +} |
| 304 | + |
| 305 | +fn file_test_io_seek_shakedown_impl() { |
| 306 | + do run_in_newsched_task { |
| 307 | + use str; // 01234567890123 |
| 308 | + let initial_msg = "qwer-asdf-zxcv"; |
| 309 | + let chunk_one = "qwer"; |
| 310 | + let chunk_two = "asdf"; |
| 311 | + let chunk_three = "zxcv"; |
| 312 | + let mut read_mem = [0, .. 4]; |
| 313 | + let filename = &Path("./tmp/file_rt_io_file_test_seek_shakedown.txt"); |
| 314 | + { |
| 315 | + let mut rw_stream = open(filename, Create, ReadWrite).unwrap(); |
| 316 | + rw_stream.write(initial_msg.as_bytes()); |
| 317 | + } |
| 318 | + { |
| 319 | + let mut read_stream = open(filename, Open, Read).unwrap(); |
| 320 | + |
| 321 | + read_stream.seek(-4, SeekEnd); |
| 322 | + read_stream.read(read_mem); |
| 323 | + let read_str = str::from_bytes(read_mem); |
| 324 | + assert!(read_str == chunk_three.to_owned()); |
| 325 | + |
| 326 | + read_stream.seek(-9, SeekCur); |
| 327 | + read_stream.read(read_mem); |
| 328 | + let read_str = str::from_bytes(read_mem); |
| 329 | + assert!(read_str == chunk_two.to_owned()); |
| 330 | + |
| 331 | + read_stream.seek(0, SeekSet); |
| 332 | + read_stream.read(read_mem); |
| 333 | + let read_str = str::from_bytes(read_mem); |
| 334 | + assert!(read_str == chunk_one.to_owned()); |
| 335 | + } |
| 336 | + unlink(filename); |
| 337 | + } |
| 338 | +} |
| 339 | +#[test] |
| 340 | +fn file_test_io_seek_shakedown() { |
| 341 | + file_test_io_seek_shakedown_impl(); |
79 | 342 | }
|
0 commit comments