@@ -16,6 +16,7 @@ pub enum Error {
1616 ParsingLockfile ( std:: num:: ParseIntError ) ,
1717 ReadingLockfile ( std:: io:: Error ) ,
1818 WritingLockfile ( std:: io:: Error ) ,
19+ ProjectFolderMissing ( std:: path:: PathBuf ) ,
1920}
2021
2122impl std:: fmt:: Display for Error {
@@ -31,6 +32,10 @@ impl std::fmt::Display for Error {
3132 format ! ( "Could not read lockfile: \n {e} \n (try removing it and running the command again)" )
3233 }
3334 Error :: WritingLockfile ( e) => format ! ( "Could not write lockfile: \n {e}" ) ,
35+ Error :: ProjectFolderMissing ( path) => format ! (
36+ "Could not write lockfile because the specified project folder does not exist: {}" ,
37+ path. to_string_lossy( )
38+ ) ,
3439 } ;
3540 write ! ( f, "{msg}" )
3641 }
@@ -41,35 +46,91 @@ pub enum Lock {
4146 Error ( Error ) ,
4247}
4348
44- fn exists ( to_check_pid : u32 ) -> bool {
49+ fn pid_exists ( to_check_pid : u32 ) -> bool {
4550 System :: new_all ( )
4651 . processes ( )
4752 . iter ( )
4853 . any ( |( pid, _process) | pid. as_u32 ( ) == to_check_pid)
4954}
5055
51- fn create ( lockfile_location : & Path , pid : u32 ) -> Lock {
52- // Create /lib if not exists
53- if let Some ( Err ( e) ) = lockfile_location. parent ( ) . map ( fs:: create_dir_all) {
54- return Lock :: Error ( Error :: WritingLockfile ( e) ) ;
55- } ;
56-
57- File :: create ( lockfile_location)
58- . and_then ( |mut file| file. write ( pid. to_string ( ) . as_bytes ( ) ) . map ( |_| Lock :: Aquired ( pid) ) )
59- . unwrap_or_else ( |e| Lock :: Error ( Error :: WritingLockfile ( e) ) )
60- }
61-
6256pub fn get ( folder : & str ) -> Lock {
63- let location = Path :: new ( folder) . join ( "lib" ) . join ( LOCKFILE ) ;
57+ let project_folder = Path :: new ( folder) ;
58+ if !project_folder. exists ( ) {
59+ return Lock :: Error ( Error :: ProjectFolderMissing ( project_folder. to_path_buf ( ) ) ) ;
60+ }
61+
62+ let lib_dir = project_folder. join ( "lib" ) ;
63+ let location = lib_dir. join ( LOCKFILE ) ;
6464 let pid = process:: id ( ) ;
6565
66+ // When a lockfile already exists we parse its PID: if the process is still alive we refuse to
67+ // proceed, otherwise we will overwrite the stale lock with our own PID.
6668 match fs:: read_to_string ( & location) {
67- Err ( e) if ( e. kind ( ) == std:: io:: ErrorKind :: NotFound ) => create ( & location, pid) ,
68- Err ( e) => Lock :: Error ( Error :: ReadingLockfile ( e) ) ,
69- Ok ( s) => match s. parse :: < u32 > ( ) {
70- Ok ( parsed_pid) if !exists ( parsed_pid) => create ( & location, pid) ,
71- Ok ( parsed_pid) => Lock :: Error ( Error :: Locked ( parsed_pid) ) ,
72- Err ( e) => Lock :: Error ( Error :: ParsingLockfile ( e) ) ,
69+ Ok ( contents) => match contents. parse :: < u32 > ( ) {
70+ Ok ( parsed_pid) if pid_exists ( parsed_pid) => return Lock :: Error ( Error :: Locked ( parsed_pid) ) ,
71+ Ok ( _) => ( ) ,
72+ Err ( e) => return Lock :: Error ( Error :: ParsingLockfile ( e) ) ,
73+ } ,
74+ Err ( e) if e. kind ( ) == std:: io:: ErrorKind :: NotFound => ( ) ,
75+ Err ( e) => return Lock :: Error ( Error :: ReadingLockfile ( e) ) ,
76+ }
77+
78+ if let Err ( e) = fs:: create_dir_all ( & lib_dir) {
79+ return Lock :: Error ( Error :: WritingLockfile ( e) ) ;
80+ }
81+
82+ // Rewrite the lockfile with our own PID.
83+ match File :: create ( & location) {
84+ Ok ( mut file) => match file. write ( pid. to_string ( ) . as_bytes ( ) ) {
85+ Ok ( _) => Lock :: Aquired ( pid) ,
86+ Err ( e) => Lock :: Error ( Error :: WritingLockfile ( e) ) ,
7387 } ,
88+ Err ( e) => Lock :: Error ( Error :: WritingLockfile ( e) ) ,
89+ }
90+ }
91+
92+ #[ cfg( test) ]
93+ mod tests {
94+ use super :: * ;
95+ use std:: fs;
96+ use tempfile:: TempDir ;
97+
98+ #[ test]
99+ fn returns_error_when_project_folder_missing ( ) {
100+ let temp_dir = TempDir :: new ( ) . expect ( "temp dir should be created" ) ;
101+ let missing_folder = temp_dir. path ( ) . join ( "missing_project" ) ;
102+
103+ match get ( missing_folder. to_str ( ) . expect ( "path should be valid" ) ) {
104+ Lock :: Error ( Error :: ProjectFolderMissing ( path) ) => {
105+ assert_eq ! ( path, missing_folder) ;
106+ }
107+ _ => panic ! ( "expected ProjectFolderMissing error" ) ,
108+ }
109+
110+ assert ! (
111+ !missing_folder. exists( ) ,
112+ "missing project folder should not be created"
113+ ) ;
114+ }
115+
116+ #[ test]
117+ fn creates_lock_when_project_folder_exists ( ) {
118+ let temp_dir = TempDir :: new ( ) . expect ( "temp dir should be created" ) ;
119+ let project_folder = temp_dir. path ( ) . join ( "project" ) ;
120+ fs:: create_dir ( & project_folder) . expect ( "project folder should be created" ) ;
121+
122+ match get ( project_folder. to_str ( ) . expect ( "path should be valid" ) ) {
123+ Lock :: Aquired ( _) => { }
124+ _ => panic ! ( "expected lock to be acquired" ) ,
125+ }
126+
127+ assert ! (
128+ project_folder. join( "lib" ) . exists( ) ,
129+ "lib directory should be created"
130+ ) ;
131+ assert ! (
132+ project_folder. join( "lib" ) . join( LOCKFILE ) . exists( ) ,
133+ "lockfile should be created"
134+ ) ;
74135 }
75136}
0 commit comments