2727from __future__ import unicode_literals
2828
2929from collections import OrderedDict
30-
3130import io
3231import toml
3332import logging
33+ import re
3434
3535import attr
3636
@@ -58,9 +58,9 @@ class RustCargoCrate(models.Package):
5858 metafiles = ('Cargo.toml' ,)
5959 default_type = 'cargo'
6060 default_primary_language = 'Rust'
61- default_web_baseurl = " https://crates.io/"
62- default_download_baseurl = " https://crates.io/api/v1/"
63- default_api_baseurl = " https://crates.io/api/v1/"
61+ default_web_baseurl = ' https://crates.io'
62+ default_download_baseurl = ' https://crates.io/api/v1'
63+ default_api_baseurl = ' https://crates.io/api/v1'
6464
6565 @classmethod
6666 def recognize (cls , location ):
@@ -94,9 +94,7 @@ def parse(location):
9494 if not is_cargo_toml (location ):
9595 return
9696
97- with io .open (location , encoding = 'utf-8' ) as loc :
98- package_data = toml .load (location , _dict = OrderedDict )
99-
97+ package_data = toml .load (location , _dict = OrderedDict )
10098 return build_package (package_data )
10199
102100
@@ -105,20 +103,80 @@ def build_package(package_data):
105103 Return a Pacakge object from a package data mapping or None.
106104 """
107105
108- name = package_data .get ('package' ).get ('name' )
109- version = package_data .get ('package' ).get ('version' )
106+ core_package_data = package_data .get ('package' , {})
107+ name = core_package_data .get ('name' )
108+ version = core_package_data .get ('version' )
109+ description = core_package_data .get ('description' )
110+ if description :
111+ description = description .strip ()
110112
111- # TODO: Remove this ordered_dict_map once cargo.py is able to handle
112- # the appropriate data (source_packages, dependencies, etc..)
113- # At the moment, this is only useful for making tests pass
114- ordered_dict_map = {}
115- for key in ("source_packages" , "dependencies" , "keywords" , "parties" ):
116- ordered_dict_map [key ] = OrderedDict ()
113+ authors = core_package_data .get ('authors' )
114+ parties = list (party_mapper (authors , party_role = 'author' ))
117115
118116 package = RustCargoCrate (
119117 name = name ,
120118 version = version ,
121- ** ordered_dict_map
119+ description = description ,
120+ parties = parties ,
122121 )
123122
124123 return package
124+
125+
126+ def party_mapper (party , party_role ):
127+ """
128+ Yields a Party object with party of `party_role`.
129+ https://doc.rust-lang.org/cargo/reference/manifest.html#the-authors-field-optional
130+ """
131+ for person in party :
132+ name , email = parse_person (person )
133+ yield models .Party (
134+ type = models .party_person ,
135+ name = name ,
136+ role = party_role ,
137+ email = email )
138+
139+
140+ def parse_person (person ):
141+ """
142+ https://doc.rust-lang.org/cargo/reference/manifest.html#the-authors-field-optional
143+ A "person" is an object with an optional "name" or "email" field.
144+
145+ A person can be in the form:
146+ "author": "Isaac Z. Schlueter <i@izs.me>"
147+
148+ For example:
149+ >>> parse_person('Barney Rubble <b@rubble.com>')
150+ (u'Barney Rubble', u'b@rubble.com')
151+ >>> parse_person('Barney Rubble')
152+ (u'Barney Rubble', None)
153+ >>> parse_person('<b@rubble.com>')
154+ (None, u'b@rubble.com')
155+ """
156+
157+ parsed = person_parser (person )
158+ if not parsed :
159+ name = None
160+ parsed = person_parser_no_name (person )
161+ else :
162+ name = parsed .group ('name' )
163+
164+ email = parsed .group ('email' )
165+
166+ if name :
167+ name = name .strip ()
168+ if email :
169+ email = email .strip ('<> ' )
170+
171+ return name , email
172+
173+
174+ person_parser = re .compile (
175+ r'^(?P<name>[^\(<]+)'
176+ r'\s?'
177+ r'(?P<email><([^>]+)>)?'
178+ ).match
179+
180+ person_parser_no_name = re .compile (
181+ r'(?P<email><([^>]+)>)?'
182+ ).match
0 commit comments