1- use pyo3:: exceptions:: { PyFileNotFoundError , PyRuntimeError } ;
1+ use pyo3:: exceptions:: { PyFileNotFoundError , PyRuntimeError } ;
22use pyo3:: prelude:: * ;
33use std:: collections:: HashMap ;
44type FileSystemContents = HashMap < String , String > ;
5- use unindent:: unindent;
6- use std:: path:: { Path , PathBuf } ;
75use std:: fs;
6+ use std:: path:: { Path , PathBuf } ;
7+ use unindent:: unindent;
8+
9+ pub trait FileSystem : Send + Sync {
10+ fn sep ( & self ) -> String ;
11+
12+ fn join ( & self , components : Vec < String > ) -> String ;
13+
14+ fn split ( & self , file_name : & str ) -> ( String , String ) ;
815
9- #[ pyclass]
16+ fn exists ( & self , file_name : & str ) -> bool ;
17+
18+ fn read ( & self , file_name : & str ) -> PyResult < String > ;
19+ }
20+
21+ #[ derive( Clone ) ]
1022pub struct RealBasicFileSystem { }
1123
12- #[ pymethods]
13- impl RealBasicFileSystem {
14- #[ new]
15- fn new ( ) -> Self {
16- RealBasicFileSystem { }
17- }
24+ #[ pyclass( name = "RealBasicFileSystem" ) ]
25+ pub struct PyRealBasicFileSystem {
26+ pub inner : RealBasicFileSystem ,
27+ }
1828
19- # [ getter ]
29+ impl FileSystem for RealBasicFileSystem {
2030 fn sep ( & self ) -> String {
2131 std:: path:: MAIN_SEPARATOR . to_string ( )
2232 }
2333
24- #[ pyo3( signature = ( * components) ) ]
2534 fn join ( & self , components : Vec < String > ) -> String {
2635 let mut path = PathBuf :: new ( ) ;
2736 for component in components {
@@ -45,30 +54,65 @@ impl RealBasicFileSystem {
4554 None => PathBuf :: new ( ) , // If there's no parent (e.g., just a filename), return empty
4655 } ;
4756
48- ( head. to_str ( ) . unwrap ( ) . to_string ( ) , tail. to_str ( ) . unwrap ( ) . to_string ( ) )
57+ (
58+ head. to_str ( ) . unwrap ( ) . to_string ( ) ,
59+ tail. to_str ( ) . unwrap ( ) . to_string ( ) ,
60+ )
4961 }
50-
62+
5163 fn exists ( & self , file_name : & str ) -> bool {
5264 Path :: new ( file_name) . is_file ( )
5365 }
5466
5567 fn read ( & self , file_name : & str ) -> PyResult < String > {
5668 // TODO: is this good enough for handling non-UTF8 encodings?
57- fs:: read_to_string ( file_name) . map_err (
58- |_| PyRuntimeError :: new_err ( format ! ( "Could not read {}" , file_name) )
59- )
69+ fs:: read_to_string ( file_name)
70+ . map_err ( |_| PyRuntimeError :: new_err ( format ! ( "Could not read {}" , file_name) ) )
6071 }
6172}
6273
63- #[ pyclass]
74+ #[ pymethods]
75+ impl PyRealBasicFileSystem {
76+ #[ new]
77+ fn new ( ) -> Self {
78+ PyRealBasicFileSystem {
79+ inner : RealBasicFileSystem { } ,
80+ }
81+ }
82+
83+ #[ getter]
84+ fn sep ( & self ) -> String {
85+ self . inner . sep ( )
86+ }
87+
88+ #[ pyo3( signature = ( * components) ) ]
89+ fn join ( & self , components : Vec < String > ) -> String {
90+ self . inner . join ( components)
91+ }
92+
93+ fn split ( & self , file_name : & str ) -> ( String , String ) {
94+ self . inner . split ( file_name)
95+ }
96+
97+ fn exists ( & self , file_name : & str ) -> bool {
98+ self . inner . exists ( file_name)
99+ }
100+
101+ fn read ( & self , file_name : & str ) -> PyResult < String > {
102+ self . inner . read ( file_name)
103+ }
104+ }
105+ #[ derive( Clone ) ]
64106pub struct FakeBasicFileSystem {
65- contents : FileSystemContents ,
107+ contents : Box < FileSystemContents > ,
108+ }
109+
110+ #[ pyclass( name = "FakeBasicFileSystem" ) ]
111+ pub struct PyFakeBasicFileSystem {
112+ pub inner : FakeBasicFileSystem ,
66113}
67114
68- #[ pymethods]
69115impl FakeBasicFileSystem {
70- #[ pyo3( signature = ( contents=None , content_map=None ) ) ]
71- #[ new]
72116 fn new ( contents : Option < & str > , content_map : Option < HashMap < String , String > > ) -> PyResult < Self > {
73117 let mut parsed_contents = match contents {
74118 Some ( contents) => parse_indented_fs_string ( contents) ,
@@ -82,16 +126,16 @@ impl FakeBasicFileSystem {
82126 parsed_contents. extend ( unindented_map) ;
83127 } ;
84128 Ok ( FakeBasicFileSystem {
85- contents : parsed_contents,
129+ contents : Box :: new ( parsed_contents) ,
86130 } )
87131 }
132+ }
88133
89- # [ getter ]
134+ impl FileSystem for FakeBasicFileSystem {
90135 fn sep ( & self ) -> String {
91136 "/" . to_string ( )
92137 }
93138
94- #[ pyo3( signature = ( * components) ) ]
95139 fn join ( & self , components : Vec < String > ) -> String {
96140 let sep = self . sep ( ) ; // Get the separator from the getter method
97141 components
@@ -131,7 +175,6 @@ impl FakeBasicFileSystem {
131175 ( head, tail. to_string ( ) )
132176 }
133177
134- /// Checks if a file or directory exists within the file system.
135178 fn exists ( & self , file_name : & str ) -> bool {
136179 self . contents . contains_key ( file_name)
137180 }
@@ -144,6 +187,40 @@ impl FakeBasicFileSystem {
144187 }
145188}
146189
190+ #[ pymethods]
191+ impl PyFakeBasicFileSystem {
192+ #[ pyo3( signature = ( contents=None , content_map=None ) ) ]
193+ #[ new]
194+ fn new ( contents : Option < & str > , content_map : Option < HashMap < String , String > > ) -> PyResult < Self > {
195+ Ok ( PyFakeBasicFileSystem {
196+ inner : FakeBasicFileSystem :: new ( contents, content_map) ?,
197+ } )
198+ }
199+
200+ #[ getter]
201+ fn sep ( & self ) -> String {
202+ self . inner . sep ( )
203+ }
204+
205+ #[ pyo3( signature = ( * components) ) ]
206+ fn join ( & self , components : Vec < String > ) -> String {
207+ self . inner . join ( components)
208+ }
209+
210+ fn split ( & self , file_name : & str ) -> ( String , String ) {
211+ self . inner . split ( file_name)
212+ }
213+
214+ /// Checks if a file or directory exists within the file system.
215+ fn exists ( & self , file_name : & str ) -> bool {
216+ self . inner . exists ( file_name)
217+ }
218+
219+ fn read ( & self , file_name : & str ) -> PyResult < String > {
220+ self . inner . read ( file_name)
221+ }
222+ }
223+
147224/// Parses an indented string representing a file system structure
148225/// into a HashMap where keys are full file paths.
149226///
0 commit comments