2 use serde::{Serialize, Serializer};
3 use std::collections::BTreeMap;
4 use std::path::{Path, PathBuf};
7 #[serde(rename_all = "kebab-case")]
8 pub(crate) struct Manifest {
9 pub(crate) manifest_version: String,
10 pub(crate) date: String,
11 pub(crate) pkg: BTreeMap<String, Package>,
12 pub(crate) artifacts: BTreeMap<String, Artifact>,
13 pub(crate) renames: BTreeMap<String, Rename>,
14 pub(crate) profiles: BTreeMap<String, Vec<String>>,
18 pub(crate) fn add_artifact(&mut self, name: &str, f: impl FnOnce(&mut Artifact)) {
19 let mut artifact = Artifact { target: BTreeMap::new() };
21 self.artifacts.insert(name.to_string(), artifact);
26 pub(crate) struct Package {
27 pub(crate) version: String,
28 pub(crate) git_commit_hash: Option<String>,
29 pub(crate) target: BTreeMap<String, Target>,
33 pub(crate) struct Rename {
34 pub(crate) to: String,
38 pub(crate) struct Artifact {
39 pub(crate) target: BTreeMap<String, Vec<ArtifactFile>>,
43 pub(crate) fn add_file(&mut self, builder: &mut Builder, target: &str, path: &str) {
44 if let Some(path) = record_shipped_file(builder, builder.input.join(path)) {
45 self.target.entry(target.into()).or_insert_with(Vec::new).push(ArtifactFile {
46 url: builder.url(&path),
47 hash_sha256: FileHash::Missing(path),
52 pub(crate) fn add_tarball(&mut self, builder: &mut Builder, target: &str, base_path: &str) {
53 let files = self.target.entry(target.into()).or_insert_with(Vec::new);
54 let base_path = builder.input.join(base_path);
55 for compression in &["gz", "xz"] {
56 if let Some(tarball) = tarball_variant(builder, &base_path, compression) {
57 files.push(ArtifactFile {
58 url: builder.url(&tarball),
59 hash_sha256: FileHash::Missing(tarball),
67 #[serde(rename_all = "kebab-case")]
68 pub(crate) struct ArtifactFile {
69 pub(crate) url: String,
70 pub(crate) hash_sha256: FileHash,
73 #[derive(Serialize, Default)]
74 pub(crate) struct Target {
75 pub(crate) available: bool,
76 pub(crate) url: Option<String>,
77 pub(crate) hash: Option<FileHash>,
78 pub(crate) xz_url: Option<String>,
79 pub(crate) xz_hash: Option<FileHash>,
80 pub(crate) components: Option<Vec<Component>>,
81 pub(crate) extensions: Option<Vec<Component>>,
85 pub(crate) fn from_compressed_tar(builder: &mut Builder, base_path: &str) -> Self {
86 let base_path = builder.input.join(base_path);
87 let gz = tarball_variant(builder, &base_path, "gz");
88 let xz = tarball_variant(builder, &base_path, "xz");
91 return Self::unavailable();
99 url: gz.as_ref().map(|path| builder.url(path)),
100 hash: gz.map(FileHash::Missing),
102 xz_url: xz.as_ref().map(|path| builder.url(path)),
103 xz_hash: xz.map(FileHash::Missing),
107 pub(crate) fn unavailable() -> Self {
113 pub(crate) struct Component {
114 pub(crate) pkg: String,
115 pub(crate) target: String,
119 pub(crate) fn from_str(pkg: &str, target: &str) -> Self {
120 Self { pkg: pkg.to_string(), target: target.to_string() }
125 pub(crate) enum FileHash {
130 impl Serialize for FileHash {
131 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
133 FileHash::Missing(path) => Err(serde::ser::Error::custom(format!(
134 "can't serialize a missing hash for file {}",
137 FileHash::Present(inner) => inner.serialize(serializer),
142 fn tarball_variant(builder: &mut Builder, base: &Path, ext: &str) -> Option<PathBuf> {
143 let mut path = base.to_path_buf();
144 path.set_extension(ext);
145 record_shipped_file(builder, path)
148 fn record_shipped_file(builder: &mut Builder, path: PathBuf) -> Option<PathBuf> {
150 builder.shipped_files.insert(
152 .expect("missing filename")
154 .expect("non-utf-8 filename")
163 pub(crate) fn visit_file_hashes(manifest: &mut Manifest, mut f: impl FnMut(&mut FileHash)) {
164 for pkg in manifest.pkg.values_mut() {
165 for target in pkg.target.values_mut() {
166 if let Some(hash) = &mut target.hash {
169 if let Some(hash) = &mut target.xz_hash {
175 for artifact in manifest.artifacts.values_mut() {
176 for target in artifact.target.values_mut() {
178 f(&mut file.hash_sha256);