fapt/parse/
src.rs

1use std::collections::HashMap;
2
3use failure::bail;
4use failure::ensure;
5use failure::err_msg;
6use failure::Error;
7use insideout::InsideOut;
8
9use super::deps::parse_dep;
10use super::deps::Dependency;
11use super::ident::Identity;
12use super::pkg;
13use super::vcs;
14use crate::rfc822;
15use crate::rfc822::RfcMapExt;
16use std::collections::HashSet;
17
18/// Source package specific fields.
19#[derive(Clone, Debug, PartialEq, Eq)]
20pub struct Source {
21    pub format: SourceFormat,
22
23    pub binaries: Vec<SourceBinary>,
24    pub files: Vec<SourceArchive>,
25    pub vcs: Vec<vcs::Vcs>,
26
27    pub directory: String,
28    pub standards_version: String,
29
30    pub build_dep: Vec<Dependency>,
31    pub build_dep_arch: Vec<Dependency>,
32    pub build_dep_indep: Vec<Dependency>,
33    pub build_conflict: Vec<Dependency>,
34    pub build_conflict_arch: Vec<Dependency>,
35    pub build_conflict_indep: Vec<Dependency>,
36
37    pub uploaders: Vec<Identity>,
38}
39
40/// The `Files` making up a source package
41// TODO: This is *very* similar to a ReleaseContent
42#[derive(Clone, Debug, PartialEq, Eq)]
43pub struct SourceArchive {
44    pub name: String,
45    pub size: u64,
46    pub md5: crate::checksum::MD5,
47    pub sha256: Option<crate::checksum::SHA256>,
48}
49
50/// Information on the binary packages built from a source package.
51#[derive(Clone, Debug, PartialEq, Eq)]
52pub struct SourceBinary {
53    pub name: String,
54    pub style: String,
55    pub section: String,
56
57    pub priority: pkg::Priority,
58    pub extras: Vec<String>,
59}
60
61/// The Debian "format" name for the source layout
62#[derive(Copy, Clone, Debug, PartialEq, Eq)]
63pub enum SourceFormat {
64    Original,
65    Quilt3dot0,
66    Native3dot0,
67    Git3dot0,
68}
69
70pub(super) fn parse_src(map: &mut rfc822::Map) -> Result<Source, Error> {
71    Ok(Source {
72        format: parse_format(map.remove_value("Format").one_line_req()?)?,
73        binaries: take_package_list(map)?,
74        files: take_files(map)?,
75        directory: map.remove_value("Directory").one_line_req()?.to_string(),
76        vcs: super::vcs::extract(map)?,
77        // TODO: Option<> instead of empty string?
78        standards_version: map
79            .remove_value("Standards-Version")
80            .one_line()?
81            .unwrap_or("")
82            .to_string(),
83        build_dep: parse_dep(&map.remove("Build-Depends").unwrap_or_else(Vec::new))?,
84        build_dep_arch: parse_dep(&map.remove("Build-Depends-Arch").unwrap_or_else(Vec::new))?,
85        build_dep_indep: parse_dep(&map.remove("Build-Depends-Indep").unwrap_or_else(Vec::new))?,
86        build_conflict: parse_dep(&map.remove("Build-Conflicts").unwrap_or_else(Vec::new))?,
87        build_conflict_arch: parse_dep(
88            &map.remove("Build-Conflicts-Arch").unwrap_or_else(Vec::new),
89        )?,
90        build_conflict_indep: parse_dep(
91            &map.remove("Build-Conflicts-Indep").unwrap_or_else(Vec::new),
92        )?,
93        uploaders: map
94            .remove_value("Uploaders")
95            .one_line()?
96            .map(|line| super::ident::read(line))
97            .inside_out()?
98            .unwrap_or_else(Vec::new),
99    })
100}
101
102pub(super) fn parse_format(string: &str) -> Result<SourceFormat, Error> {
103    Ok(match string {
104        "3.0 (quilt)" => SourceFormat::Quilt3dot0,
105        "1.0" => SourceFormat::Original,
106        "3.0 (git)" => SourceFormat::Git3dot0,
107        "3.0 (native)" => SourceFormat::Native3dot0,
108        other => bail!("unsupported source format: '{}'", other),
109    })
110}
111
112pub(super) fn take_package_list(map: &mut rfc822::Map) -> Result<Vec<SourceBinary>, Error> {
113    let package_list = match map.remove("Package-List") {
114        Some(list) => list,
115        None => {
116            // sigh legacy
117            return Ok(map
118                .remove_value("Binary")
119                .split_comma()?
120                .into_iter()
121                // TODO: optional, instead of empty string?
122                // TODO: or fallback to the values on the parent package?
123                .map(|v| SourceBinary {
124                    name: v.to_string(),
125                    style: String::new(),
126                    section: String::new(),
127                    priority: super::Priority::Unknown,
128                    extras: Vec::new(),
129                })
130                .collect());
131        }
132    };
133
134    let mut binary_names: HashSet<_> = map
135        .remove_value("Binary")
136        .split_comma()?
137        .into_iter()
138        .collect();
139
140    let mut binaries = Vec::with_capacity(package_list.len());
141
142    for line in package_list {
143        let parts: Vec<_> = line.split_whitespace().collect();
144        ensure!(parts.len() >= 4, "package list line too short: {:?}", line);
145        let name = parts[0];
146        ensure!(
147            binary_names.remove(name),
148            "{:?} in package list, but not in Binary",
149            name
150        );
151        binaries.push(SourceBinary {
152            name: name.to_string(),
153            style: parts[1].to_string(),
154            section: parts[2].to_string(),
155            priority: super::pkg::parse_priority(parts[3])?,
156            extras: parts[4..].into_iter().map(|s| s.to_string()).collect(),
157        });
158    }
159
160    ensure!(
161        binary_names.is_empty(),
162        "some binary names were not in Package-List: {:?}",
163        binary_names
164    );
165
166    Ok(binaries)
167}
168
169pub(super) fn take_files(map: &mut rfc822::Map) -> Result<Vec<SourceArchive>, Error> {
170    use crate::checksum::parse_md5;
171    use crate::checksum::parse_sha256;
172    use crate::release::take_checksums;
173    let file_and_size_to_md5 =
174        take_checksums(map, "Files")?.ok_or_else(|| err_msg("Files required"))?;
175    let mut file_and_size_to_sha256 =
176        take_checksums(map, "Checksums-Sha256")?.unwrap_or_else(HashMap::new);
177
178    let mut archives = Vec::with_capacity(file_and_size_to_md5.len());
179    for ((name, size), md5) in file_and_size_to_md5 {
180        let sha256 = file_and_size_to_sha256.remove(&(name, size));
181        archives.push(SourceArchive {
182            name: name.to_string(),
183            size,
184            md5: parse_md5(md5)?,
185            sha256: sha256.map(|v| parse_sha256(v)).inside_out()?,
186        })
187    }
188
189    ensure!(
190        file_and_size_to_sha256.is_empty(),
191        "sha256sum for a file which didn't exist: {:?}",
192        file_and_size_to_sha256
193    );
194
195    Ok(archives)
196}