7
7
# See https://aboutcode.org for more information about nexB OSS projects.
8
8
#
9
9
10
+ import os
10
11
import re
11
12
12
13
import saneyaml
20
21
"""
21
22
22
23
23
- class CargoTomlHandler (models .DatafileHandler ):
24
+ class CargoBaseHandler (models .DatafileHandler ):
25
+ @classmethod
26
+ def assemble (cls , package_data , resource , codebase , package_adder ):
27
+ """
28
+ Assemble Cargo.toml and possible Cargo.lock datafiles. Also
29
+ support cargo workspaces where we have multiple packages from
30
+ a repository and some shared information present at top-level.
31
+ """
32
+ workspace = package_data .extra_data .get ("workspace" , {})
33
+ workspace_members = workspace .get ("members" , [])
34
+ workspace_package_data = workspace .get ("package" , {})
35
+ attributes_to_copy = [
36
+ "license_detections" ,
37
+ "declared_license_expression" ,
38
+ "declared_license_expression_spdx"
39
+ ]
40
+ if "license" in workspace_package_data :
41
+ for attribute in attributes_to_copy :
42
+ workspace_package_data [attribute ] = getattr (package_data , attribute )
43
+
44
+ workspace_root_path = resource .parent (codebase ).path
45
+ if workspace_package_data and workspace_members :
46
+ for workspace_member_path in workspace_members :
47
+ workspace_directory_path = os .path .join (workspace_root_path , workspace_member_path )
48
+ workspace_directory = codebase .get_resource (path = workspace_directory_path )
49
+ if not workspace_directory :
50
+ continue
51
+
52
+ # Update the package data for all members with the
53
+ # workspace package data
54
+ for resource in workspace_directory .children (codebase ):
55
+ if cls .is_datafile (location = resource .location ):
56
+ if not resource .package_data :
57
+ continue
58
+
59
+ updated_package_data = cls .update_resource_package_data (
60
+ package_data = workspace_package_data ,
61
+ old_package_data = resource .package_data .pop (),
62
+ mapping = CARGO_ATTRIBUTE_MAPPING ,
63
+ )
64
+ resource .package_data .append (updated_package_data )
65
+ resource .save (codebase )
66
+
67
+ yield from cls .assemble_from_many_datafiles (
68
+ datafile_name_patterns = ('Cargo.toml' , 'cargo.toml' , 'Cargo.lock' , 'cargo.lock' ),
69
+ directory = workspace_directory ,
70
+ codebase = codebase ,
71
+ package_adder = package_adder ,
72
+ )
73
+ else :
74
+ yield from cls .assemble_from_many_datafiles (
75
+ datafile_name_patterns = ('Cargo.toml' , 'cargo.toml' , 'Cargo.lock' , 'cargo.lock' ),
76
+ directory = resource .parent (codebase ),
77
+ codebase = codebase ,
78
+ package_adder = package_adder ,
79
+ )
80
+
81
+ @classmethod
82
+ def update_resource_package_data (cls , package_data , old_package_data , mapping = None ):
83
+
84
+ for attribute in old_package_data .keys ():
85
+ if attribute in mapping :
86
+ replace_by_attribute = mapping .get (attribute )
87
+ old_package_data [attribute ] = package_data .get (replace_by_attribute )
88
+ elif attribute == "parties" :
89
+ old_package_data [attribute ] = list (get_parties (
90
+ person_names = package_data .get ("authors" ),
91
+ party_role = 'author' ,
92
+ ))
93
+
94
+ return old_package_data
95
+
96
+
97
+
98
+ class CargoTomlHandler (CargoBaseHandler ):
24
99
datasource_id = 'cargo_toml'
25
100
path_patterns = ('*/Cargo.toml' , '*/cargo.toml' ,)
26
101
default_package_type = 'cargo'
@@ -31,11 +106,16 @@ class CargoTomlHandler(models.DatafileHandler):
31
106
@classmethod
32
107
def parse (cls , location ):
33
108
package_data = toml .load (location , _dict = dict )
34
-
35
109
core_package_data = package_data .get ('package' , {})
110
+ workspace = package_data .get ('workspace' , {})
111
+ extra_data = {}
36
112
37
113
name = core_package_data .get ('name' )
38
114
version = core_package_data .get ('version' )
115
+ if isinstance (version , dict ) and "workspace" in version :
116
+ version = None
117
+ extra_data ["version" ] = "workspace"
118
+
39
119
description = core_package_data .get ('description' ) or ''
40
120
description = description .strip ()
41
121
@@ -66,6 +146,8 @@ def parse(cls, location):
66
146
repository_homepage_url = name and f'https://crates.io/crates/{ name } '
67
147
repository_download_url = name and version and f'https://crates.io/api/v1/crates/{ name } /{ version } /download'
68
148
api_data_url = name and f'https://crates.io/api/v1/crates/{ name } '
149
+ if workspace :
150
+ extra_data ["workspace" ] = workspace
69
151
70
152
yield models .PackageData (
71
153
datasource_id = cls .datasource_id ,
@@ -82,22 +164,24 @@ def parse(cls, location):
82
164
repository_download_url = repository_download_url ,
83
165
api_data_url = api_data_url ,
84
166
dependencies = dependencies ,
167
+ extra_data = extra_data ,
85
168
)
86
169
87
- @classmethod
88
- def assemble (cls , package_data , resource , codebase , package_adder ):
89
- """
90
- Assemble Cargo.toml and possible Cargo.lock datafiles
91
- """
92
- yield from cls .assemble_from_many_datafiles (
93
- datafile_name_patterns = ('Cargo.toml' , 'cargo.toml' , 'Cargo.lock' , 'cargo.lock' ),
94
- directory = resource .parent (codebase ),
95
- codebase = codebase ,
96
- package_adder = package_adder ,
97
- )
170
+
171
+ CARGO_ATTRIBUTE_MAPPING = {
172
+ # Fields in PackageData model: Fields in cargo
173
+ "homepage_url" : "homepage" ,
174
+ "vcs_url" : "repository" ,
175
+ "keywords" : "categories" ,
176
+ "extracted_license_statement" : "license" ,
177
+ # These are fields carried over to avoid re-detection of licenses
178
+ "license_detections" : "license_detections" ,
179
+ "declared_license_expression" : "declared_license_expression" ,
180
+ "declared_license_expression_spdx" : "declared_license_expression_spdx" ,
181
+ }
98
182
99
183
100
- class CargoLockHandler (models . DatafileHandler ):
184
+ class CargoLockHandler (CargoBaseHandler ):
101
185
datasource_id = 'cargo_lock'
102
186
path_patterns = ('*/Cargo.lock' , '*/cargo.lock' ,)
103
187
default_package_type = 'cargo'
@@ -144,18 +228,6 @@ def parse(cls, location):
144
228
dependencies = dependencies ,
145
229
)
146
230
147
- @classmethod
148
- def assemble (cls , package_data , resource , codebase , package_adder ):
149
- """
150
- Assemble Cargo.toml and possible Cargo.lock datafiles
151
- """
152
- yield from cls .assemble_from_many_datafiles (
153
- datafile_name_patterns = ('Cargo.toml' , 'Cargo.lock' ,),
154
- directory = resource .parent (codebase ),
155
- codebase = codebase ,
156
- package_adder = package_adder ,
157
- )
158
-
159
231
160
232
def dependency_mapper (dependencies , scope = 'dependencies' ):
161
233
"""
@@ -197,7 +269,7 @@ def get_parties(person_names, party_role):
197
269
name = name ,
198
270
role = party_role ,
199
271
email = email ,
200
- )
272
+ ). to_dict ()
201
273
202
274
203
275
person_parser = re .compile (
0 commit comments