@@ -7,27 +7,54 @@ use serde_json;
7
7
8
8
use error:: * ;
9
9
10
- /// Invoke cargo to generate the save-analysis data for the crate being documented.
10
+ /// The kinds of targets that we can document.
11
+ #[ derive( Debug , PartialEq , Eq ) ]
12
+ pub enum TargetKind {
13
+ /// A `bin` target.
14
+ Binary ,
15
+
16
+ /// A `lib` target.
17
+ Library ,
18
+ }
19
+
20
+ /// A target of documentation.
21
+ #[ derive( Debug , PartialEq , Eq ) ]
22
+ pub struct Target {
23
+ /// The kind of the target.
24
+ pub kind : TargetKind ,
25
+
26
+ /// The name of the target.
27
+ ///
28
+ /// This is *not* the name of the target's crate, which is used to retrieve the analysis data.
29
+ /// Use the [`crate_name`] method instead.
30
+ ///
31
+ /// [`crate_name`]: ./struct.Target.html#method.crate_name
32
+ pub name : String ,
33
+ }
34
+
35
+ impl Target {
36
+ /// Returns the name of the target's crate.
37
+ ///
38
+ /// This name is equivalent to the target's name, with dashes replaced by underscores.
39
+ pub fn crate_name ( & self ) -> String {
40
+ self . name . replace ( '-' , "_" )
41
+ }
42
+ }
43
+
44
+ /// Generate and parse the metadata of a cargo project.
11
45
///
12
46
/// ## Arguments
13
47
///
14
48
/// - `manifest_path`: The path containing the `Cargo.toml` of the crate
15
- pub fn generate_analysis ( manifest_path : & Path ) -> Result < ( ) > {
16
- // FIXME: Here we assume that we are documenting a library. This could be wrong, but it's the
17
- // common case, and it ensures that we are documenting the right target in the case that the
18
- // crate contains a binary and a library with the same name.
19
- //
20
- // Maybe we could use Cargo.toml's `doc = false` attribute to figure out the right target?
21
- let mut command = Command :: new ( "cargo" ) ;
22
- command
23
- . arg ( "check" )
24
- . arg ( "--lib" )
49
+ pub fn retrieve_metadata ( manifest_path : & Path ) -> Result < serde_json:: Value > {
50
+ let output = Command :: new ( "cargo" )
51
+ . arg ( "metadata" )
25
52
. arg ( "--manifest-path" )
26
53
. arg ( manifest_path. join ( "Cargo.toml" ) )
27
- . env ( "RUSTFLAGS" , "-Z save-analysis ")
28
- . env ( "CARGO_TARGET_DIR" , manifest_path . join ( "target/rls" ) ) ;
29
-
30
- let output = command . output ( ) ?;
54
+ . arg ( "--no-deps ")
55
+ . arg ( "--format-version" )
56
+ . arg ( "1" )
57
+ . output ( ) ?;
31
58
32
59
if !output. status . success ( ) {
33
60
return Err (
@@ -38,24 +65,33 @@ pub fn generate_analysis(manifest_path: &Path) -> Result<()> {
38
65
) ;
39
66
}
40
67
41
- Ok ( ( ) )
68
+ Ok ( serde_json :: from_slice ( & output . stdout ) ? )
42
69
}
43
70
44
- /// Grab the name of the binary or library from it's `Cargo.toml` file .
71
+ /// Invoke cargo to generate the save-analysis data for the crate being documented .
45
72
///
46
73
/// ## Arguments
47
74
///
48
- /// - `manifest_path`: The path to the location of `Cargo.toml` of the crate being documented
49
- pub fn crate_name_from_manifest_path ( manifest_path : & Path ) -> Result < String > {
75
+ /// - `manifest_path`: The path containing the `Cargo.toml` of the crate
76
+ /// - `target`: The target that we should generate the analysis data for
77
+ pub fn generate_analysis ( manifest_path : & Path , target : & Target ) -> Result < ( ) > {
50
78
let mut command = Command :: new ( "cargo" ) ;
51
79
52
80
command
53
- . arg ( "metadata " )
81
+ . arg ( "check " )
54
82
. arg ( "--manifest-path" )
55
83
. arg ( manifest_path. join ( "Cargo.toml" ) )
56
- . arg ( "--no-deps" )
57
- . arg ( "--format-version" )
58
- . arg ( "1" ) ;
84
+ . env ( "RUSTFLAGS" , "-Z save-analysis" )
85
+ . env ( "CARGO_TARGET_DIR" , manifest_path. join ( "target/rls" ) ) ;
86
+
87
+ match target. kind {
88
+ TargetKind :: Library => {
89
+ command. arg ( "--lib" ) ;
90
+ }
91
+ TargetKind :: Binary => {
92
+ command. args ( & [ "--bin" , & target. name ] ) ;
93
+ }
94
+ }
59
95
60
96
let output = command. output ( ) ?;
61
97
@@ -68,83 +104,181 @@ pub fn crate_name_from_manifest_path(manifest_path: &Path) -> Result<String> {
68
104
) ;
69
105
}
70
106
71
- let metadata = serde_json:: from_slice ( & output. stdout ) ?;
72
- crate_name_from_metadata ( & metadata)
107
+ Ok ( ( ) )
73
108
}
74
109
75
- /// Parse the crate name of the binary or library from crate metadata.
110
+ /// Parse the library target from the crate metadata.
76
111
///
77
112
/// ## Arguments
78
113
///
79
- /// - `metadata`: The JSON metadata of the crate.
80
- fn crate_name_from_metadata ( metadata : & serde_json:: Value ) -> Result < String > {
81
- let targets = match metadata[ "packages" ] [ 0 ] [ "targets" ] . as_array ( ) {
82
- Some ( targets) => targets,
83
- None => return Err ( ErrorKind :: Json ( "targets is not an array" ) . into ( ) ) ,
84
- } ;
85
-
86
- for target in targets {
87
- let crate_types = match target[ "crate_types" ] . as_array ( ) {
88
- Some ( crate_types) => crate_types,
89
- None => return Err ( ErrorKind :: Json ( "crate types is not an array" ) . into ( ) ) ,
90
- } ;
91
-
92
- for crate_type in crate_types {
93
- let ty = match crate_type. as_str ( ) {
94
- Some ( t) => t,
95
- None => {
96
- return Err (
97
- ErrorKind :: Json ( "crate type contents are not a string" ) . into ( ) ,
98
- )
99
- }
100
- } ;
114
+ /// - metadata: The JSON metadata of the crate.
115
+ pub fn target_from_metadata ( metadata : & serde_json:: Value ) -> Result < Target > {
116
+ // We can expect at least one package and target, otherwise the metadata generation would have
117
+ // failed.
118
+ let targets = metadata[ "packages" ] [ 0 ] [ "targets" ] . as_array ( ) . expect (
119
+ "`targets` is not an array" ,
120
+ ) ;
121
+
122
+ let mut targets = targets
123
+ . into_iter ( )
124
+ . flat_map ( |target| {
125
+ let name = target[ "name" ] . as_str ( ) . expect ( "`name` is not a string" ) ;
126
+ let kinds = target[ "kind" ] . as_array ( ) . expect ( "`kind` is not an array" ) ;
101
127
102
- if ty == "lib" {
103
- match target[ "name" ] . as_str ( ) {
104
- Some ( name) => return Ok ( name. replace ( '-' , "_" ) ) ,
105
- None => return Err ( ErrorKind :: Json ( "target name is not a string" ) . into ( ) ) ,
106
- }
128
+ if kinds. len ( ) != 1 {
129
+ return Some ( Err (
130
+ ErrorKind :: Json (
131
+ format ! ( "expected one kind for target '{}'" , name) ,
132
+ ) . into ( ) ,
133
+ ) ) ;
107
134
}
135
+
136
+ let kind = match kinds[ 0 ] . as_str ( ) . unwrap ( ) {
137
+ "lib" => TargetKind :: Library ,
138
+ "bin" => TargetKind :: Binary ,
139
+ _ => return None ,
140
+ } ;
141
+
142
+ let target = Target {
143
+ name : name. to_owned ( ) ,
144
+ kind,
145
+ } ;
146
+
147
+ Some ( Ok ( target) )
148
+ } )
149
+ . collect :: < Result < Vec < _ > > > ( ) ?;
150
+
151
+ if targets. is_empty ( ) {
152
+ bail ! ( ErrorKind :: Json (
153
+ "no targets with supported kinds (`bin`, `lib`) found"
154
+ . into( ) ,
155
+ ) ) ;
156
+ } else if targets. len ( ) == 1 {
157
+ Ok ( targets. remove ( 0 ) )
158
+ } else {
159
+ // FIXME(#105): Handle more than one target.
160
+ print ! ( "warning: Found more than one target to document. " ) ;
161
+ let ( mut libs, mut bins) : ( Vec < _ > , Vec < _ > ) =
162
+ targets. into_iter ( ) . partition ( |target| match target. kind {
163
+ TargetKind :: Library => true ,
164
+ TargetKind :: Binary => false ,
165
+ } ) ;
166
+
167
+ if !libs. is_empty ( ) {
168
+ println ! ( "Documenting the library." ) ;
169
+ Ok ( libs. remove ( 0 ) )
170
+ } else {
171
+ let target = bins. remove ( 0 ) ;
172
+ println ! ( "Documenting the first binary: {}" , target. name) ;
173
+ Ok ( target)
108
174
}
109
175
}
110
-
111
- Err (
112
- ErrorKind :: Json ( "cargo metadata contained no targets" ) . into ( ) ,
113
- )
114
176
}
115
177
116
178
#[ cfg( test) ]
117
179
mod tests {
180
+ use super :: { Target , TargetKind } ;
181
+
118
182
#[ test]
119
- fn crate_name_from_metadata ( ) {
183
+ fn target_from_metadata ( ) {
120
184
let metadata = json ! ( {
121
185
"packages" : [
122
186
{
123
187
"name" : "underscored_name" ,
124
188
"targets" : [
125
189
{
126
- "crate_types " : [ "lib" ] ,
190
+ "kind " : [ "lib" ] ,
127
191
"name" : "underscored_name" ,
128
192
} ,
129
193
] ,
130
194
} ,
131
195
] ,
132
196
} ) ;
133
- assert_eq ! ( & super :: crate_name_from_metadata( & metadata) . unwrap( ) , "underscored_name" ) ;
197
+ let target = super :: target_from_metadata ( & metadata) . unwrap ( ) ;
198
+ assert_eq ! ( target, Target { kind: TargetKind :: Library , name: "underscored_name" . into( ) } ) ;
199
+ assert_eq ! ( & target. crate_name( ) , "underscored_name" ) ;
134
200
135
201
let metadata = json ! ( {
136
202
"packages" : [
137
203
{
138
204
"name" : "dashed-name" ,
139
205
"targets" : [
140
206
{
141
- "crate_types " : [ "lib" ] ,
207
+ "kind " : [ "lib" ] ,
142
208
"name" : "dashed-name" ,
143
209
} ,
144
210
] ,
145
211
} ,
146
212
] ,
147
213
} ) ;
148
- assert_eq ! ( & super :: crate_name_from_metadata( & metadata) . unwrap( ) , "dashed_name" ) ;
214
+ let target = super :: target_from_metadata ( & metadata) . unwrap ( ) ;
215
+ assert_eq ! ( target, Target { kind: TargetKind :: Library , name: "dashed-name" . into( ) } ) ;
216
+ assert_eq ! ( & target. crate_name( ) , "dashed_name" ) ;
217
+
218
+ let metadata = json ! ( {
219
+ "packages" : [
220
+ {
221
+ "name" : "underscored_name" ,
222
+ "targets" : [
223
+ {
224
+ "kind" : [ "bin" ] ,
225
+ "name" : "underscored_name" ,
226
+ } ,
227
+ ] ,
228
+ } ,
229
+ ] ,
230
+ } ) ;
231
+ let target = super :: target_from_metadata ( & metadata) . unwrap ( ) ;
232
+ assert_eq ! ( target, Target { kind: TargetKind :: Binary , name: "underscored_name" . into( ) } ) ;
233
+ assert_eq ! ( & target. crate_name( ) , "underscored_name" ) ;
234
+
235
+ let metadata = json ! ( {
236
+ "packages" : [
237
+ {
238
+ "name" : "library" ,
239
+ "targets" : [
240
+ {
241
+ "kind" : [ "lib" ] ,
242
+ "name" : "library" ,
243
+ } ,
244
+ ] ,
245
+ } ,
246
+ ] ,
247
+ } ) ;
248
+ assert_eq ! ( super :: target_from_metadata( & metadata) . unwrap( ) . kind, TargetKind :: Library ) ;
249
+
250
+ let metadata = json ! ( {
251
+ "packages" : [
252
+ {
253
+ "name" : "binary" ,
254
+ "targets" : [
255
+ {
256
+ "kind" : [ "bin" ] ,
257
+ "name" : "binary" ,
258
+ } ,
259
+ ] ,
260
+ } ,
261
+ ] ,
262
+ } ) ;
263
+ assert_eq ! ( super :: target_from_metadata( & metadata) . unwrap( ) . kind, TargetKind :: Binary ) ;
264
+
265
+ let metadata = json ! ( {
266
+ "packages" : [
267
+ {
268
+ "name" : "library" ,
269
+ "targets" : [
270
+ {
271
+ "kind" : [ "lib" ] ,
272
+ "name" : "library" ,
273
+ } ,
274
+ {
275
+ "kind" : [ "test" ] ,
276
+ "name" : "other_kind" ,
277
+ } ,
278
+ ] ,
279
+ } ,
280
+ ] ,
281
+ } ) ;
282
+ assert_eq ! ( super :: target_from_metadata( & metadata) . unwrap( ) . kind, TargetKind :: Library ) ;
149
283
}
150
284
}
0 commit comments