@@ -5,7 +5,7 @@ use flate2::{Compression, read::GzDecoder, write::GzEncoder};
55use serde:: { Serialize , de:: DeserializeOwned } ;
66use std:: {
77 fs:: { self , File } ,
8- io:: { BufReader , BufWriter , Read , Write } ,
8+ io:: { BufReader , BufWriter , Read , Seek , SeekFrom , Write } ,
99 path:: { Component , Path , PathBuf } ,
1010} ;
1111
@@ -62,25 +62,29 @@ pub fn read_json_gzip_file<T: DeserializeOwned>(path: &Path) -> Result<T> {
6262/// Reads the entire contents of a locked shared file into a string.
6363pub fn locked_read_to_string ( path : impl AsRef < Path > ) -> Result < String > {
6464 let path = path. as_ref ( ) ;
65- let file =
65+ let mut file =
6666 fs:: OpenOptions :: new ( ) . read ( true ) . open ( path) . map_err ( |err| FsPathError :: open ( err, path) ) ?;
6767 file. lock_shared ( ) . map_err ( |err| FsPathError :: lock ( err, path) ) ?;
68- let mut contents = String :: new ( ) ;
69- ( & file) . read_to_string ( & mut contents) . map_err ( |err| FsPathError :: read ( err, path) ) ?;
68+ let contents = read_inner ( path, & mut file) ?;
7069 file. unlock ( ) . map_err ( |err| FsPathError :: unlock ( err, path) ) ?;
71- Ok ( contents)
70+ String :: from_utf8 ( contents) . map_err ( |err| FsPathError :: read ( std :: io :: Error :: other ( err ) , path ) )
7271}
7372
7473/// Reads the entire contents of a locked shared file into a bytes vector.
7574pub fn locked_read ( path : impl AsRef < Path > ) -> Result < Vec < u8 > > {
7675 let path = path. as_ref ( ) ;
77- let file =
76+ let mut file =
7877 fs:: OpenOptions :: new ( ) . read ( true ) . open ( path) . map_err ( |err| FsPathError :: open ( err, path) ) ?;
7978 file. lock_shared ( ) . map_err ( |err| FsPathError :: lock ( err, path) ) ?;
79+ let contents = read_inner ( path, & mut file) ?;
80+ file. unlock ( ) . map_err ( |err| FsPathError :: unlock ( err, path) ) ?;
81+ Ok ( contents)
82+ }
83+
84+ fn read_inner ( path : & Path , file : & mut File ) -> Result < Vec < u8 > > {
8085 let file_len = file. metadata ( ) . map_err ( |err| FsPathError :: open ( err, path) ) ?. len ( ) as usize ;
8186 let mut buffer = Vec :: with_capacity ( file_len) ;
82- ( & file) . read_to_end ( & mut buffer) . map_err ( |err| FsPathError :: read ( err, path) ) ?;
83- file. unlock ( ) . map_err ( |err| FsPathError :: unlock ( err, path) ) ?;
87+ file. read_to_end ( & mut buffer) . map_err ( |err| FsPathError :: read ( err, path) ) ?;
8488 Ok ( buffer)
8589}
8690
@@ -136,18 +140,39 @@ pub fn locked_write(path: impl AsRef<Path>, contents: impl AsRef<[u8]>) -> Resul
136140}
137141
138142/// Writes a line in an exclusive locked file.
139- pub fn locked_write_line ( path : impl AsRef < Path > , line : & String ) -> Result < ( ) > {
143+ pub fn locked_write_line ( path : impl AsRef < Path > , line : & str ) -> Result < ( ) > {
140144 let path = path. as_ref ( ) ;
145+ if cfg ! ( windows) {
146+ return locked_write_line_windows ( path, line) ;
147+ }
148+
141149 let mut file = std:: fs:: OpenOptions :: new ( )
142150 . append ( true )
143151 . create ( true )
144152 . open ( path)
145153 . map_err ( |err| FsPathError :: open ( err, path) ) ?;
154+
146155 file. lock ( ) . map_err ( |err| FsPathError :: lock ( err, path) ) ?;
147156 writeln ! ( file, "{line}" ) . map_err ( |err| FsPathError :: write ( err, path) ) ?;
148157 file. unlock ( ) . map_err ( |err| FsPathError :: unlock ( err, path) )
149158}
150159
160+ // Locking fails on Windows if the file is opened in append mode.
161+ fn locked_write_line_windows ( path : & Path , line : & str ) -> Result < ( ) > {
162+ let mut file = std:: fs:: OpenOptions :: new ( )
163+ . write ( true )
164+ . truncate ( false )
165+ . create ( true )
166+ . open ( path)
167+ . map_err ( |err| FsPathError :: open ( err, path) ) ?;
168+ file. lock ( ) . map_err ( |err| FsPathError :: lock ( err, path) ) ?;
169+
170+ file. seek ( SeekFrom :: End ( 0 ) ) . map_err ( |err| FsPathError :: write ( err, path) ) ?;
171+ writeln ! ( file, "{line}" ) . map_err ( |err| FsPathError :: write ( err, path) ) ?;
172+
173+ file. unlock ( ) . map_err ( |err| FsPathError :: unlock ( err, path) )
174+ }
175+
151176/// Wrapper for `std::fs::copy`
152177pub fn copy ( from : impl AsRef < Path > , to : impl AsRef < Path > ) -> Result < u64 > {
153178 let from = from. as_ref ( ) ;
0 commit comments