]> git.lizzy.rs Git - rust.git/blob - src/libstd/rt/io/file.rs
534e308a1a6e8dc65e0414560617070abe56aaa7
[rust.git] / src / libstd / rt / io / file.rs
1 // Copyright 2013 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 use prelude::*;
12 use super::support::PathLike;
13 use super::{Reader, Writer, Seek};
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::*;
20
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: *mut IoFactoryObject = Local::unsafe_borrow();
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     }
40 }
41
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: *mut IoFactoryObject = Local::unsafe_borrow();
47         (*io).fs_unlink(path)
48     };
49     match unlink_result {
50         Ok(_) => (),
51         Err(ioerr) => {
52             io_error::cond.raise(ioerr);
53         }
54     }
55 }
56
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 }
73
74 impl Reader for FileStream {
75     fn read(&mut self, buf: &mut [u8]) -> Option<uint> {
76         match self.fd.read(buf) {
77             Ok(read) => {
78                 self.last_nread = read;
79                 match read {
80                     0 => None,
81                     _ => Some(read as uint)
82                 }
83             },
84             Err(ioerr) => {
85                 // EOF is indicated by returning None
86                 if ioerr.kind != EndOfFile {
87                     read_error::cond.raise(ioerr);
88                 }
89                 return None;
90             }
91         }
92     }
93
94     fn eof(&mut self) -> bool {
95         self.last_nread == 0
96     }
97 }
98
99 impl Writer for FileStream {
100     fn write(&mut self, buf: &[u8]) {
101         match self.fd.write(buf) {
102             Ok(_) => (),
103             Err(ioerr) => {
104                 io_error::cond.raise(ioerr);
105             }
106         }
107     }
108
109     fn flush(&mut self) {
110         match self.fd.flush() {
111             Ok(_) => (),
112             Err(ioerr) => {
113                 read_error::cond.raise(ioerr);
114             }
115         }
116     }
117 }
118
119 impl Seek for FileStream {
120     fn tell(&self) -> u64 {
121         let res = self.fd.tell();
122         match res {
123             Ok(cursor) => cursor,
124             Err(ioerr) => {
125                 read_error::cond.raise(ioerr);
126                 return -1;
127             }
128         }
129     }
130
131     fn seek(&mut self, pos: i64, style: SeekStyle) {
132         match self.fd.seek(pos, style) {
133             Ok(_) => {
134                 // successful seek resets EOF indicator
135                 self.last_nread = -1;
136                 ()
137             },
138             Err(ioerr) => {
139                 read_error::cond.raise(ioerr);
140             }
141         }
142     }
143 }
144
145 fn file_test_smoke_test_impl() {
146     do run_in_newsched_task {
147         let message = "it's alright. have a good time";
148         let filename = &Path("./tmp/file_rt_io_file_test.txt");
149         {
150             let mut write_stream = open(filename, Create, ReadWrite).unwrap();
151             write_stream.write(message.as_bytes());
152         }
153         {
154             use str;
155             let mut read_stream = open(filename, Open, Read).unwrap();
156             let mut read_buf = [0, .. 1028];
157             let read_str = match read_stream.read(read_buf).unwrap() {
158                 -1|0 => fail!("shouldn't happen"),
159                 n => str::from_bytes(read_buf.slice_to(n))
160             };
161             assert!(read_str == message.to_owned());
162         }
163         unlink(filename);
164     }
165 }
166
167 #[test]
168 fn file_test_io_smoke_test() {
169     file_test_smoke_test_impl();
170 }
171
172 fn file_test_invalid_path_opened_without_create_should_raise_condition_impl() {
173     do run_in_newsched_task {
174         let filename = &Path("./tmp/file_that_does_not_exist.txt");
175         let mut called = false;
176         do io_error::cond.trap(|_| {
177             called = true;
178         }).inside {
179             let result = open(filename, Open, Read);
180             assert!(result.is_none());
181         }
182         assert!(called);
183     }
184 }
185 #[test]
186 fn file_test_io_invalid_path_opened_without_create_should_raise_condition() {
187     file_test_invalid_path_opened_without_create_should_raise_condition_impl();
188 }
189
190 fn file_test_unlinking_invalid_path_should_raise_condition_impl() {
191     do run_in_newsched_task {
192         let filename = &Path("./tmp/file_another_file_that_does_not_exist.txt");
193         let mut called = false;
194         do io_error::cond.trap(|_| {
195             called = true;
196         }).inside {
197             unlink(filename);
198         }
199         assert!(called);
200     }
201 }
202 #[test]
203 fn file_test_iounlinking_invalid_path_should_raise_condition() {
204     file_test_unlinking_invalid_path_should_raise_condition_impl();
205 }
206
207 fn file_test_io_non_positional_read_impl() {
208     do run_in_newsched_task {
209         use str;
210         let message = "ten-four";
211         let mut read_mem = [0, .. 8];
212         let filename = &Path("./tmp/file_rt_io_file_test_positional.txt");
213         {
214             let mut rw_stream = open(filename, Create, ReadWrite).unwrap();
215             rw_stream.write(message.as_bytes());
216         }
217         {
218             let mut read_stream = open(filename, Open, Read).unwrap();
219             {
220                 let read_buf = read_mem.mut_slice(0, 4);
221                 read_stream.read(read_buf);
222             }
223             {
224                 let read_buf = read_mem.mut_slice(4, 8);
225                 read_stream.read(read_buf);
226             }
227         }
228         unlink(filename);
229         let read_str = str::from_bytes(read_mem);
230         assert!(read_str == message.to_owned());
231     }
232 }
233
234 #[test]
235 fn file_test_io_non_positional_read() {
236     file_test_io_non_positional_read_impl();
237 }
238
239 fn file_test_io_seeking_impl() {
240     do run_in_newsched_task {
241         use str;
242         let message = "ten-four";
243         let mut read_mem = [0, .. 4];
244         let set_cursor = 4 as u64;
245         let mut tell_pos_pre_read;
246         let mut tell_pos_post_read;
247         let filename = &Path("./tmp/file_rt_io_file_test_seeking.txt");
248         {
249             let mut rw_stream = open(filename, Create, ReadWrite).unwrap();
250             rw_stream.write(message.as_bytes());
251         }
252         {
253             let mut read_stream = open(filename, Open, Read).unwrap();
254             read_stream.seek(set_cursor as i64, SeekSet);
255             tell_pos_pre_read = read_stream.tell();
256             read_stream.read(read_mem);
257             tell_pos_post_read = read_stream.tell();
258         }
259         unlink(filename);
260         let read_str = str::from_bytes(read_mem);
261         assert!(read_str == message.slice(4, 8).to_owned());
262         assert!(tell_pos_pre_read == set_cursor);
263         assert!(tell_pos_post_read == message.len() as u64);
264     }
265 }
266 #[test]
267 fn file_test_io_seek_and_tell_smoke_test() {
268     file_test_io_seeking_impl();
269 }
270
271 fn file_test_io_seek_and_write_impl() {
272     use io;
273     do run_in_newsched_task {
274         use str;
275         let initial_msg =   "food-is-yummy";
276         let overwrite_msg =    "-the-bar!!";
277         let final_msg =     "foo-the-bar!!";
278         let seek_idx = 3;
279         let mut read_mem = [0, .. 13];
280         let filename = &Path("./tmp/file_rt_io_file_test_seek_and_write.txt");
281         {
282             let mut rw_stream = open(filename, Create, ReadWrite).unwrap();
283             rw_stream.write(initial_msg.as_bytes());
284             rw_stream.seek(seek_idx as i64, SeekSet);
285             rw_stream.write(overwrite_msg.as_bytes());
286         }
287         {
288             let mut read_stream = open(filename, Open, Read).unwrap();
289             read_stream.read(read_mem);
290         }
291         unlink(filename);
292         let read_str = str::from_bytes(read_mem);
293         io::println(fmt!("read_str: '%?' final_msg: '%?'", read_str, final_msg));
294         assert!(read_str == final_msg.to_owned());
295     }
296 }
297 #[test]
298 fn file_test_io_seek_and_write() {
299     file_test_io_seek_and_write_impl();
300 }
301
302 fn file_test_io_seek_shakedown_impl() {
303     do run_in_newsched_task {
304         use str;          // 01234567890123
305         let initial_msg =   "qwer-asdf-zxcv";
306         let chunk_one = "qwer";
307         let chunk_two = "asdf";
308         let chunk_three = "zxcv";
309         let mut read_mem = [0, .. 4];
310         let filename = &Path("./tmp/file_rt_io_file_test_seek_shakedown.txt");
311         {
312             let mut rw_stream = open(filename, Create, ReadWrite).unwrap();
313             rw_stream.write(initial_msg.as_bytes());
314         }
315         {
316             let mut read_stream = open(filename, Open, Read).unwrap();
317
318             read_stream.seek(-4, SeekEnd);
319             read_stream.read(read_mem);
320             let read_str = str::from_bytes(read_mem);
321             assert!(read_str == chunk_three.to_owned());
322
323             read_stream.seek(-9, SeekCur);
324             read_stream.read(read_mem);
325             let read_str = str::from_bytes(read_mem);
326             assert!(read_str == chunk_two.to_owned());
327
328             read_stream.seek(0, SeekSet);
329             read_stream.read(read_mem);
330             let read_str = str::from_bytes(read_mem);
331             assert!(read_str == chunk_one.to_owned());
332         }
333         unlink(filename);
334     }
335 }
336 #[test]
337 fn file_test_io_seek_shakedown() {
338     file_test_io_seek_shakedown_impl();
339 }