Skip to content

Commit 78735c5

Browse files
authored
Implement basic metadata parsing
1 parent ec67acb commit 78735c5

File tree

2 files changed

+92
-9
lines changed

2 files changed

+92
-9
lines changed

src/distribution.rs

Lines changed: 77 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ pub struct Distribution {
2323
pub description: Option<String>,
2424
/// A list of additional keywords, separated by commas, to be used to
2525
/// assist searching for the distribution in a larger catalog.
26-
pub keywords: Vec<String>,
26+
pub keywords: Option<String>,
2727
/// A string containing the URL for the distribution’s home page.
2828
pub home_page: Option<String>,
2929
/// A string containing the URL from which this version of the distribution can be downloaded.
@@ -75,10 +75,64 @@ impl Distribution {
7575
let headers = msg.get_headers();
7676
let metadata_version = headers
7777
.get_first_value("Metadata-Version")
78-
.ok_or_else(|| Error::KeyError("Metadata-Version"))?;
78+
.ok_or_else(|| Error::FieldNotFound("Metadata-Version"))?;
79+
let name = headers
80+
.get_first_value("Name")
81+
.ok_or_else(|| Error::FieldNotFound("Name"))?;
82+
let version = headers
83+
.get_first_value("Version")
84+
.ok_or_else(|| Error::FieldNotFound("Version"))?;
85+
let platforms = headers.get_all_values("Platform");
86+
let supported_platforms = headers.get_all_values("Supported-Platform");
87+
let summary = headers.get_first_value("Summary");
88+
let body = msg.get_body()?;
89+
let description = if !body.trim().is_empty() {
90+
Some(body)
91+
} else {
92+
headers.get_first_value("Description")
93+
};
94+
let keywords = headers.get_first_value("Keywords");
95+
let home_page = headers.get_first_value("Home-Page");
96+
let download_url = headers.get_first_value("Download-URL");
97+
let author = headers.get_first_value("Author");
98+
let author_email = headers.get_first_value("Author-email");
99+
let license = headers.get_first_value("License");
100+
let classifiers = headers.get_all_values("Classifier");
101+
let requires_dist = headers.get_all_values("Requires-Dist");
102+
let provides_dist = headers.get_all_values("Provides-Dist");
103+
let obsoletes_dist = headers.get_all_values("Obsoletes-Dist");
104+
let maintainer = headers.get_first_value("Maintainer");
105+
let maintainer_email = headers.get_first_value("Maintainer-email");
106+
let requires_python = headers.get_first_value("Requires-Python");
107+
let requires_external = headers.get_all_values("Requires-External");
108+
let project_urls = headers.get_all_values("Project-URL");
109+
let provides_extras = headers.get_all_values("Provides-Extra");
110+
let description_content_type = headers.get_first_value("Description-Content-Type");
79111
Ok(Distribution {
80112
metadata_version,
81-
..Default::default()
113+
name,
114+
version,
115+
platforms,
116+
supported_platforms,
117+
summary,
118+
description,
119+
keywords,
120+
home_page,
121+
download_url,
122+
author,
123+
author_email,
124+
license,
125+
classifiers,
126+
requires_dist,
127+
provides_dist,
128+
obsoletes_dist,
129+
maintainer,
130+
maintainer_email,
131+
requires_python,
132+
requires_external,
133+
project_urls,
134+
provides_extras,
135+
description_content_type,
82136
})
83137
}
84138
}
@@ -94,11 +148,30 @@ impl FromStr for Distribution {
94148
#[cfg(test)]
95149
mod tests {
96150
use super::Distribution;
151+
use crate::Error;
97152

98153
#[test]
99154
fn test_parse_from_str() {
100155
let s = "Metadata-Version: 1.0";
101-
let dist: Distribution = s.parse().unwrap();
156+
let dist: Result<Distribution, Error> = s.parse();
157+
assert!(matches!(dist, Err(Error::FieldNotFound("Name"))));
158+
159+
let s = "Metadata-Version: 1.0\nName: asdf";
160+
let dist = Distribution::parse(s.as_bytes());
161+
assert!(matches!(dist, Err(Error::FieldNotFound("Version"))));
162+
163+
let s = "Metadata-Version: 1.0\nName: asdf\nVersion: 1.0";
164+
let dist = Distribution::parse(s.as_bytes()).unwrap();
102165
assert_eq!(dist.metadata_version, "1.0");
166+
assert_eq!(dist.name, "asdf");
167+
assert_eq!(dist.version, "1.0");
168+
169+
let s = "Metadata-Version: 1.0\nName: asdf\nVersion: 1.0\nDescription: a Python package";
170+
let dist: Distribution = s.parse().unwrap();
171+
assert_eq!(dist.description.as_deref(), Some("a Python package"));
172+
173+
let s = "Metadata-Version: 1.0\nName: asdf\nVersion: 1.0\n\na Python package";
174+
let dist: Distribution = s.parse().unwrap();
175+
assert_eq!(dist.description.as_deref(), Some("a Python package"));
103176
}
104177
}

src/error.rs

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::{error, fmt};
1+
use std::{error, fmt, io};
22

33
use mailparse::MailParseError;
44

@@ -7,15 +7,18 @@ use mailparse::MailParseError;
77
pub enum Error {
88
/// mail parse error
99
MailParse(MailParseError),
10-
/// Metadata key error
11-
KeyError(&'static str),
10+
/// Metadata field not found
11+
FieldNotFound(&'static str),
12+
/// I/O error
13+
Io(io::Error),
1214
}
1315

1416
impl fmt::Display for Error {
1517
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1618
match self {
1719
Error::MailParse(err) => err.fmt(f),
18-
Error::KeyError(key) => write!(f, "metadata key {} not found", key),
20+
Error::FieldNotFound(key) => write!(f, "metadata field {} not found", key),
21+
Error::Io(err) => err.fmt(f),
1922
}
2023
}
2124
}
@@ -24,7 +27,8 @@ impl error::Error for Error {
2427
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
2528
match self {
2629
Error::MailParse(err) => Some(err),
27-
Error::KeyError(_) => None,
30+
Error::FieldNotFound(_) => None,
31+
Error::Io(err) => Some(err),
2832
}
2933
}
3034
}
@@ -34,3 +38,9 @@ impl From<MailParseError> for Error {
3438
Self::MailParse(err)
3539
}
3640
}
41+
42+
impl From<io::Error> for Error {
43+
fn from(err: io::Error) -> Self {
44+
Self::Io(err)
45+
}
46+
}

0 commit comments

Comments
 (0)