@@ -20,24 +20,29 @@ use serde_json::Value;
20
20
// use this to store the crates when interacting with the crates.toml file
21
21
#[ derive( Debug , Serialize , Deserialize ) ]
22
22
struct CrateList {
23
- crates : HashMap < String , Vec < String > > ,
23
+ crates : HashMap < String , TomlCrate > ,
24
24
}
25
25
26
26
// crate data we stored in the toml, can have multiple versions per crate
27
27
// A single TomlCrate is laster mapped to several CrateSources in that case
28
+ #[ derive( Debug , Serialize , Deserialize ) ]
28
29
struct TomlCrate {
29
30
name : String ,
30
- versions : Vec < String > ,
31
+ versions : Option < Vec < String > > ,
32
+ git_url : Option < String > ,
33
+ git_hash : Option < String > ,
31
34
}
32
35
33
36
// represents an archive we download from crates.io
34
37
#[ derive( Debug , Serialize , Deserialize , Eq , Hash , PartialEq ) ]
35
- struct CrateSource {
36
- name : String ,
37
- version : String ,
38
+ enum CrateSource {
39
+ CratesIo { name : String , version : String } ,
40
+ Git { name : String , url : String , commit : String } ,
38
41
}
39
42
40
43
// represents the extracted sourcecode of a crate
44
+ // we actually don't need to special-case git repos here because it does not matter for clippy, yay!
45
+ // (clippy only needs a simple path)
41
46
#[ derive( Debug ) ]
42
47
struct Crate {
43
48
version : String ,
@@ -69,40 +74,71 @@ impl std::fmt::Display for ClippyWarning {
69
74
70
75
impl CrateSource {
71
76
fn download_and_extract ( & self ) -> Crate {
72
- let extract_dir = PathBuf :: from ( "target/lintcheck/crates" ) ;
73
- let krate_download_dir = PathBuf :: from ( "target/lintcheck/downloads" ) ;
74
-
75
- // url to download the crate from crates.io
76
- let url = format ! (
77
- "https://crates.io/api/v1/crates/{}/{}/download" ,
78
- self . name, self . version
79
- ) ;
80
- println ! ( "Downloading and extracting {} {} from {}" , self . name, self . version, url) ;
81
- let _ = std:: fs:: create_dir ( "target/lintcheck/" ) ;
82
- let _ = std:: fs:: create_dir ( & krate_download_dir) ;
83
- let _ = std:: fs:: create_dir ( & extract_dir) ;
84
-
85
- let krate_file_path = krate_download_dir. join ( format ! ( "{}-{}.crate.tar.gz" , & self . name, & self . version) ) ;
86
- // don't download/extract if we already have done so
87
- if !krate_file_path. is_file ( ) {
88
- // create a file path to download and write the crate data into
89
- let mut krate_dest = std:: fs:: File :: create ( & krate_file_path) . unwrap ( ) ;
90
- let mut krate_req = ureq:: get ( & url) . call ( ) . unwrap ( ) . into_reader ( ) ;
91
- // copy the crate into the file
92
- std:: io:: copy ( & mut krate_req, & mut krate_dest) . unwrap ( ) ;
93
-
94
- // unzip the tarball
95
- let ungz_tar = flate2:: read:: GzDecoder :: new ( std:: fs:: File :: open ( & krate_file_path) . unwrap ( ) ) ;
96
- // extract the tar archive
97
- let mut archive = tar:: Archive :: new ( ungz_tar) ;
98
- archive. unpack ( & extract_dir) . expect ( "Failed to extract!" ) ;
99
- }
100
- // crate is extracted, return a new Krate object which contains the path to the extracted
101
- // sources that clippy can check
102
- Crate {
103
- version : self . version . clone ( ) ,
104
- name : self . name . clone ( ) ,
105
- path : extract_dir. join ( format ! ( "{}-{}/" , self . name, self . version) ) ,
77
+ match self {
78
+ CrateSource :: CratesIo { name, version } => {
79
+ let extract_dir = PathBuf :: from ( "target/lintcheck/crates" ) ;
80
+ let krate_download_dir = PathBuf :: from ( "target/lintcheck/downloads" ) ;
81
+
82
+ // url to download the crate from crates.io
83
+ let url = format ! ( "https://crates.io/api/v1/crates/{}/{}/download" , name, version) ;
84
+ println ! ( "Downloading and extracting {} {} from {}" , name, version, url) ;
85
+ let _ = std:: fs:: create_dir ( "target/lintcheck/" ) ;
86
+ let _ = std:: fs:: create_dir ( & krate_download_dir) ;
87
+ let _ = std:: fs:: create_dir ( & extract_dir) ;
88
+
89
+ let krate_file_path = krate_download_dir. join ( format ! ( "{}-{}.crate.tar.gz" , name, version) ) ;
90
+ // don't download/extract if we already have done so
91
+ if !krate_file_path. is_file ( ) {
92
+ // create a file path to download and write the crate data into
93
+ let mut krate_dest = std:: fs:: File :: create ( & krate_file_path) . unwrap ( ) ;
94
+ let mut krate_req = ureq:: get ( & url) . call ( ) . unwrap ( ) . into_reader ( ) ;
95
+ // copy the crate into the file
96
+ std:: io:: copy ( & mut krate_req, & mut krate_dest) . unwrap ( ) ;
97
+
98
+ // unzip the tarball
99
+ let ungz_tar = flate2:: read:: GzDecoder :: new ( std:: fs:: File :: open ( & krate_file_path) . unwrap ( ) ) ;
100
+ // extract the tar archive
101
+ let mut archive = tar:: Archive :: new ( ungz_tar) ;
102
+ archive. unpack ( & extract_dir) . expect ( "Failed to extract!" ) ;
103
+ }
104
+ // crate is extracted, return a new Krate object which contains the path to the extracted
105
+ // sources that clippy can check
106
+ Crate {
107
+ version : version. clone ( ) ,
108
+ name : name. clone ( ) ,
109
+ path : extract_dir. join ( format ! ( "{}-{}/" , name, version) ) ,
110
+ }
111
+ } ,
112
+ CrateSource :: Git { name, url, commit } => {
113
+ let repo_path = {
114
+ let mut repo_path = PathBuf :: from ( "target/lintcheck/downloads" ) ;
115
+ // add a -git suffix in case we have the same crate from crates.io and a git repo
116
+ repo_path. push ( format ! ( "{}-git" , name) ) ;
117
+ repo_path
118
+ } ;
119
+ // clone the repo if we have not done so
120
+ if !repo_path. is_dir ( ) {
121
+ println ! ( "Cloning {} and checking out {}" , url, commit) ;
122
+ Command :: new ( "git" )
123
+ . arg ( "clone" )
124
+ . arg ( url)
125
+ . arg ( & repo_path)
126
+ . output ( )
127
+ . expect ( "Failed to clone git repo!" ) ;
128
+ }
129
+ // check out the commit/branch/whatever
130
+ Command :: new ( "git" )
131
+ . arg ( "checkout" )
132
+ . arg ( commit)
133
+ . output ( )
134
+ . expect ( "Failed to check out commit" ) ;
135
+
136
+ Crate {
137
+ version : commit. clone ( ) ,
138
+ name : name. clone ( ) ,
139
+ path : repo_path,
140
+ }
141
+ } ,
106
142
}
107
143
}
108
144
}
@@ -114,7 +150,7 @@ impl Crate {
114
150
115
151
let shared_target_dir = clippy_project_root ( ) . join ( "target/lintcheck/shared_target_dir/" ) ;
116
152
117
- let all_output = std:: process:: Command :: new ( cargo_clippy_path)
153
+ let all_output = std:: process:: Command :: new ( & cargo_clippy_path)
118
154
. env ( "CARGO_TARGET_DIR" , shared_target_dir)
119
155
// lint warnings will look like this:
120
156
// src/cargo/ops/cargo_compile.rs:127:35: warning: usage of `FromIterator::from_iter`
@@ -128,10 +164,16 @@ impl Crate {
128
164
] )
129
165
. current_dir ( & self . path )
130
166
. output ( )
131
- . unwrap ( ) ;
167
+ . unwrap_or_else ( |error| {
168
+ panic ! (
169
+ "Encountered error:\n {:?}\n cargo_clippy_path: {}\n crate path:{}\n " ,
170
+ error,
171
+ & cargo_clippy_path. display( ) ,
172
+ & self . path. display( )
173
+ ) ;
174
+ } ) ;
132
175
let stdout = String :: from_utf8_lossy ( & all_output. stdout ) ;
133
176
let output_lines = stdout. lines ( ) ;
134
- //dbg!(&output_lines);
135
177
let warnings: Vec < ClippyWarning > = output_lines
136
178
. into_iter ( )
137
179
// get all clippy warnings
@@ -160,19 +202,40 @@ fn read_crates() -> Vec<CrateSource> {
160
202
let tomlcrates: Vec < TomlCrate > = crate_list
161
203
. crates
162
204
. into_iter ( )
163
- . map ( |( name , versions ) | TomlCrate { name , versions } )
205
+ . map ( |( _cratename , tomlcrate ) | tomlcrate )
164
206
. collect ( ) ;
165
207
166
208
// flatten TomlCrates into CrateSources (one TomlCrates may represent several versions of a crate =>
167
209
// multiple Cratesources)
168
210
let mut crate_sources = Vec :: new ( ) ;
169
211
tomlcrates. into_iter ( ) . for_each ( |tk| {
170
- tk. versions . iter ( ) . for_each ( |ver| {
171
- crate_sources. push ( CrateSource {
212
+ // if we have multiple versions, save each one
213
+ if let Some ( ref versions) = tk. versions {
214
+ versions. iter ( ) . for_each ( |ver| {
215
+ crate_sources. push ( CrateSource :: CratesIo {
216
+ name : tk. name . clone ( ) ,
217
+ version : ver. to_string ( ) ,
218
+ } ) ;
219
+ } )
220
+ }
221
+ // otherwise, we should have a git source
222
+ if tk. git_url . is_some ( ) && tk. git_hash . is_some ( ) {
223
+ crate_sources. push ( CrateSource :: Git {
172
224
name : tk. name . clone ( ) ,
173
- version : ver. to_string ( ) ,
225
+ url : tk. git_url . clone ( ) . unwrap ( ) ,
226
+ commit : tk. git_hash . clone ( ) . unwrap ( ) ,
174
227
} ) ;
175
- } )
228
+ }
229
+ // if we have a version as well as a git data OR only one git data, something is funky
230
+ if tk. versions . is_some ( ) && ( tk. git_url . is_some ( ) || tk. git_hash . is_some ( ) )
231
+ || tk. git_hash . is_some ( ) != tk. git_url . is_some ( )
232
+ {
233
+ eprintln ! ( "tomlkrate: {:?}" , tk) ;
234
+ if tk. git_hash . is_some ( ) != tk. git_url . is_some ( ) {
235
+ panic ! ( "Encountered TomlCrate with only one of git_hash and git_url!" )
236
+ }
237
+ unreachable ! ( "Failed to translate TomlCrate into CrateSource!" ) ;
238
+ }
176
239
} ) ;
177
240
crate_sources
178
241
}
@@ -228,8 +291,14 @@ pub fn run(clap_config: &ArgMatches) {
228
291
let crates = read_crates ( ) ;
229
292
230
293
let clippy_warnings: Vec < ClippyWarning > = if let Some ( only_one_crate) = clap_config. value_of ( "only" ) {
231
- // if we don't have the specified crated in the .toml, throw an error
232
- if !crates. iter ( ) . any ( |krate| krate. name == only_one_crate) {
294
+ // if we don't have the specified crate in the .toml, throw an error
295
+ if !crates. iter ( ) . any ( |krate| {
296
+ let name = match krate {
297
+ CrateSource :: CratesIo { name, .. } => name,
298
+ CrateSource :: Git { name, .. } => name,
299
+ } ;
300
+ name == only_one_crate
301
+ } ) {
233
302
eprintln ! (
234
303
"ERROR: could not find crate '{}' in clippy_dev/lintcheck_crates.toml" ,
235
304
only_one_crate
0 commit comments