1+ use itertools:: Itertools ;
12use pyo3:: exceptions:: { PyFileNotFoundError , PyUnicodeDecodeError } ;
23use pyo3:: prelude:: * ;
34use regex:: Regex ;
45use std:: collections:: HashMap ;
56use std:: ffi:: OsStr ;
67use std:: fs;
78use std:: path:: { Path , PathBuf } ;
9+ use std:: sync:: LazyLock ;
810use unindent:: unindent;
9- use lazy_static:: lazy_static;
1011
1112
12- lazy_static ! {
13- static ref ENCODING_RE : Regex = Regex :: new( r"^[ \t\f]*#.*?coding[:=][ \t]*([-_.a-zA-Z0-9]+)" ) . unwrap( ) ;
14- }
13+ static ENCODING_RE : LazyLock < Regex > = LazyLock :: new ( || {
14+ Regex :: new ( r"^[ \t\f]*#.*?coding[:=][ \t]*([-_.a-zA-Z0-9]+)" ) . unwrap ( )
15+ } ) ;
1516
1617pub trait FileSystem : Send + Sync {
17- fn sep ( & self ) -> String ;
18+ fn sep ( & self ) -> & str ;
1819
1920 fn join ( & self , components : Vec < String > ) -> String ;
2021
@@ -37,16 +38,16 @@ pub struct PyRealBasicFileSystem {
3738}
3839
3940impl FileSystem for RealBasicFileSystem {
40- fn sep ( & self ) -> String {
41- std:: path:: MAIN_SEPARATOR . to_string ( )
41+ fn sep ( & self ) -> & str {
42+ std:: path:: MAIN_SEPARATOR_STR
4243 }
4344
4445 fn join ( & self , components : Vec < String > ) -> String {
4546 let mut path = PathBuf :: new ( ) ;
4647 for component in components {
4748 path. push ( component) ;
4849 }
49- path. to_str ( ) . unwrap ( ) . to_string ( )
50+ path. to_str ( ) . expect ( "Path components should be valid unicode" ) . to_string ( )
5051 }
5152
5253 fn split ( & self , file_name : & str ) -> ( String , String ) {
@@ -65,8 +66,8 @@ impl FileSystem for RealBasicFileSystem {
6566 } ;
6667
6768 (
68- head. to_str ( ) . unwrap ( ) . to_string ( ) ,
69- tail. to_str ( ) . unwrap ( ) . to_string ( ) ,
69+ head. to_str ( ) . expect ( "Path components should be valid unicode" ) . to_string ( ) ,
70+ tail. to_str ( ) . expect ( "Path components should be valid unicode" ) . to_string ( ) ,
7071 )
7172 }
7273
@@ -136,7 +137,7 @@ impl PyRealBasicFileSystem {
136137 }
137138
138139 #[ getter]
139- fn sep ( & self ) -> String {
140+ fn sep ( & self ) -> & str {
140141 self . inner . sep ( )
141142 }
142143
@@ -191,17 +192,16 @@ impl FakeBasicFileSystem {
191192}
192193
193194impl FileSystem for FakeBasicFileSystem {
194- fn sep ( & self ) -> String {
195- "/" . to_string ( )
195+ fn sep ( & self ) -> & str {
196+ "/"
196197 }
197198
198199 fn join ( & self , components : Vec < String > ) -> String {
199200 let sep = self . sep ( ) ;
200201 components
201202 . into_iter ( )
202- . map ( |c| c. trim_end_matches ( & sep) . to_string ( ) )
203- . collect :: < Vec < String > > ( )
204- . join ( & sep)
203+ . map ( |c| c. trim_end_matches ( sep) . to_string ( ) )
204+ . join ( sep)
205205 }
206206
207207 fn split ( & self , file_name : & str ) -> ( String , String ) {
@@ -217,8 +217,8 @@ impl FileSystem for FakeBasicFileSystem {
217217 tail = path. file_name ( ) . unwrap_or ( OsStr :: new ( "" ) ) ;
218218 }
219219 (
220- head. to_str ( ) . unwrap ( ) . to_string ( ) ,
221- tail. to_str ( ) . unwrap ( ) . to_string ( ) ,
220+ head. to_str ( ) . expect ( "Path components should be valid unicode" ) . to_string ( ) ,
221+ tail. to_str ( ) . expect ( "Path components should be valid unicode" ) . to_string ( ) ,
222222 )
223223 }
224224
@@ -230,7 +230,7 @@ impl FileSystem for FakeBasicFileSystem {
230230 fn read ( & self , file_name : & str ) -> PyResult < String > {
231231 match self . contents . get ( file_name) {
232232 Some ( file_name) => Ok ( file_name. clone ( ) ) ,
233- None => Err ( PyFileNotFoundError :: new_err ( "" ) ) ,
233+ None => Err ( PyFileNotFoundError :: new_err ( format ! ( "No such file: {file_name}" ) ) ) ,
234234 }
235235 }
236236}
@@ -246,7 +246,7 @@ impl PyFakeBasicFileSystem {
246246 }
247247
248248 #[ getter]
249- fn sep ( & self ) -> String {
249+ fn sep ( & self ) -> & str {
250250 self . inner . sep ( )
251251 }
252252
@@ -288,7 +288,8 @@ pub fn parse_indented_file_system_string(file_system_string: &str) -> HashMap<St
288288 let buffer = file_system_string. replace ( "\r \n " , "\n " ) ;
289289 let lines: Vec < & str > = buffer. split ( '\n' ) . collect ( ) ;
290290
291- for line_raw in lines. clone ( ) {
291+ let first_line_starts_with_slash = lines[ 0 ] . trim ( ) . starts_with ( '/' ) ;
292+ for line_raw in lines {
292293 let line = line_raw. trim_end ( ) ; // Remove trailing whitespace
293294 if line. is_empty ( ) {
294295 continue ; // Skip empty lines
@@ -331,7 +332,7 @@ pub fn parse_indented_file_system_string(file_system_string: &str) -> HashMap<St
331332 let mut joined = path_stack. join ( "/" ) ;
332333 // If the original root started with a slash, ensure the final path does too.
333334 // But be careful not to double-slash if a component is e.g. "/root"
334- if lines [ 0 ] . trim ( ) . starts_with ( '/' ) && !joined. starts_with ( '/' ) {
335+ if first_line_starts_with_slash && !joined. starts_with ( '/' ) {
335336 joined = format ! ( "/{joined}" ) ;
336337 }
337338 joined
@@ -353,7 +354,7 @@ pub fn parse_indented_file_system_string(file_system_string: &str) -> HashMap<St
353354 // Edge case: If the very first line was a file and it ended up on the stack, it needs to be processed.
354355 // This handles single-file inputs like "myfile.txt"
355356 if !path_stack. is_empty ( )
356- && !path_stack. last ( ) . unwrap ( ) . ends_with ( '/' )
357+ && !path_stack. last ( ) . expect ( "path_stack should be non-empty" ) . ends_with ( '/' )
357358 && !file_paths_map. contains_key ( & path_stack. join ( "/" ) )
358359 {
359360 file_paths_map. insert ( path_stack. join ( "/" ) , String :: new ( ) ) ;
0 commit comments