1+ use std:: cell:: RefCell ;
12use std:: fs:: File ;
23use std:: fs:: OpenOptions ;
34use std:: io:: { ErrorKind , Read , Write } ;
@@ -10,9 +11,11 @@ use crate::obj::objbytes::PyBytesRef;
1011use crate :: obj:: objdict:: PyDictRef ;
1112use crate :: obj:: objint;
1213use crate :: obj:: objint:: PyIntRef ;
14+ use crate :: obj:: objiter;
1315use crate :: obj:: objstr;
1416use crate :: obj:: objstr:: PyStringRef ;
15- use crate :: pyobject:: { ItemProtocol , PyObjectRef , PyResult } ;
17+ use crate :: obj:: objtype:: PyClassRef ;
18+ use crate :: pyobject:: { ItemProtocol , PyObjectRef , PyRef , PyResult , PyValue } ;
1619use crate :: vm:: VirtualMachine ;
1720
1821#[ cfg( unix) ]
@@ -190,6 +193,85 @@ fn _os_environ(vm: &VirtualMachine) -> PyDictRef {
190193 environ
191194}
192195
196+ #[ derive( Debug ) ]
197+ struct DirEntry {
198+ entry : fs:: DirEntry ,
199+ }
200+
201+ type DirEntryRef = PyRef < DirEntry > ;
202+
203+ impl PyValue for DirEntry {
204+ fn class ( vm : & VirtualMachine ) -> PyClassRef {
205+ vm. class ( "os" , "DirEntry" )
206+ }
207+ }
208+
209+ impl DirEntryRef {
210+ fn name ( self , _vm : & VirtualMachine ) -> String {
211+ self . entry . file_name ( ) . into_string ( ) . unwrap ( )
212+ }
213+
214+ fn path ( self , _vm : & VirtualMachine ) -> String {
215+ self . entry . path ( ) . to_str ( ) . unwrap ( ) . to_string ( )
216+ }
217+
218+ fn is_dir ( self , vm : & VirtualMachine ) -> PyResult < bool > {
219+ Ok ( self
220+ . entry
221+ . file_type ( )
222+ . map_err ( |s| vm. new_os_error ( s. to_string ( ) ) ) ?
223+ . is_dir ( ) )
224+ }
225+
226+ fn is_file ( self , vm : & VirtualMachine ) -> PyResult < bool > {
227+ Ok ( self
228+ . entry
229+ . file_type ( )
230+ . map_err ( |s| vm. new_os_error ( s. to_string ( ) ) ) ?
231+ . is_file ( ) )
232+ }
233+ }
234+
235+ #[ derive( Debug ) ]
236+ pub struct ScandirIterator {
237+ entries : RefCell < fs:: ReadDir > ,
238+ }
239+
240+ impl PyValue for ScandirIterator {
241+ fn class ( vm : & VirtualMachine ) -> PyClassRef {
242+ vm. class ( "os" , "ScandirIter" )
243+ }
244+ }
245+
246+ type ScandirIteratorRef = PyRef < ScandirIterator > ;
247+
248+ impl ScandirIteratorRef {
249+ fn next ( self , vm : & VirtualMachine ) -> PyResult {
250+ match self . entries . borrow_mut ( ) . next ( ) {
251+ Some ( entry) => match entry {
252+ Ok ( entry) => Ok ( DirEntry { entry } . into_ref ( vm) . into_object ( ) ) ,
253+ Err ( s) => Err ( vm. new_os_error ( s. to_string ( ) ) ) ,
254+ } ,
255+ None => Err ( objiter:: new_stop_iteration ( vm) ) ,
256+ }
257+ }
258+
259+ fn iter ( self , _vm : & VirtualMachine ) -> Self {
260+ self
261+ }
262+ }
263+
264+ fn os_scandir ( path : PyStringRef , vm : & VirtualMachine ) -> PyResult {
265+ match fs:: read_dir ( & path. value ) {
266+ Ok ( iter) => Ok ( ScandirIterator {
267+ entries : RefCell :: new ( iter) ,
268+ }
269+ . into_ref ( vm)
270+ . into_object ( ) ) ,
271+ Err ( s) => Err ( vm. new_os_error ( s. to_string ( ) ) ) ,
272+ }
273+ }
274+
193275pub fn make_module ( vm : & VirtualMachine ) -> PyObjectRef {
194276 let ctx = & vm. ctx ;
195277
@@ -201,6 +283,18 @@ pub fn make_module(vm: &VirtualMachine) -> PyObjectRef {
201283
202284 let environ = _os_environ ( vm) ;
203285
286+ let scandir_iter = py_class ! ( ctx, "ScandirIter" , ctx. object( ) , {
287+ "__iter__" => ctx. new_rustfunc( ScandirIteratorRef :: iter) ,
288+ "__next__" => ctx. new_rustfunc( ScandirIteratorRef :: next) ,
289+ } ) ;
290+
291+ let dir_entry = py_class ! ( ctx, "DirEntry" , ctx. object( ) , {
292+ "name" => ctx. new_property( DirEntryRef :: name) ,
293+ "path" => ctx. new_property( DirEntryRef :: path) ,
294+ "is_dir" => ctx. new_rustfunc( DirEntryRef :: is_dir) ,
295+ "is_file" => ctx. new_rustfunc( DirEntryRef :: is_file) ,
296+ } ) ;
297+
204298 py_module ! ( vm, "_os" , {
205299 "open" => ctx. new_rustfunc( os_open) ,
206300 "close" => ctx. new_rustfunc( os_close) ,
@@ -217,6 +311,9 @@ pub fn make_module(vm: &VirtualMachine) -> PyObjectRef {
217311 "unsetenv" => ctx. new_rustfunc( os_unsetenv) ,
218312 "environ" => environ,
219313 "name" => ctx. new_str( os_name) ,
314+ "scandir" => ctx. new_rustfunc( os_scandir) ,
315+ "ScandirIter" => scandir_iter,
316+ "DirEntry" => dir_entry,
220317 "O_RDONLY" => ctx. new_int( 0 ) ,
221318 "O_WRONLY" => ctx. new_int( 1 ) ,
222319 "O_RDWR" => ctx. new_int( 2 ) ,
0 commit comments