@@ -16,6 +16,7 @@ pub enum Error {
16
16
ParsingLockfile ( std:: num:: ParseIntError ) ,
17
17
ReadingLockfile ( std:: io:: Error ) ,
18
18
WritingLockfile ( std:: io:: Error ) ,
19
+ ProjectFolderMissing ( std:: path:: PathBuf ) ,
19
20
}
20
21
21
22
impl std:: fmt:: Display for Error {
@@ -31,6 +32,10 @@ impl std::fmt::Display for Error {
31
32
format ! ( "Could not read lockfile: \n {e} \n (try removing it and running the command again)" )
32
33
}
33
34
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
+ ) ,
34
39
} ;
35
40
write ! ( f, "{msg}" )
36
41
}
@@ -41,35 +46,91 @@ pub enum Lock {
41
46
Error ( Error ) ,
42
47
}
43
48
44
- fn exists ( to_check_pid : u32 ) -> bool {
49
+ fn pid_exists ( to_check_pid : u32 ) -> bool {
45
50
System :: new_all ( )
46
51
. processes ( )
47
52
. iter ( )
48
53
. any ( |( pid, _process) | pid. as_u32 ( ) == to_check_pid)
49
54
}
50
55
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
-
62
56
pub 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 ) ;
64
64
let pid = process:: id ( ) ;
65
65
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.
66
68
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) ) ,
73
87
} ,
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
+ ) ;
74
135
}
75
136
}
0 commit comments