1use indexmap::IndexMap;
2use serde::de::IgnoredAny;
3use serde::Deserialize;
4use toml;
5
6use crate::debian::control::PkgTestRestriction;
7use crate::errors::Result;
8
9use std::borrow::Cow;
10use std::collections::HashMap;
11use std::ffi::OsStr;
12use std::fs::File;
13use std::io::Read;
14use std::path::{Path, PathBuf};
15
16pub const RUST_MAINT: &str =
17 "Debian Rust Maintainers <pkg-rust-maintainers@alioth-lists.debian.net>";
18
19pub const DEFAULT_REPACK_SUFFIX: &str = "ds";
20
21#[derive(Deserialize, Debug, Clone)]
22#[serde(default)]
23pub struct Config {
24 pub bin: Option<bool>,
25 pub bin_name: String,
26 pub lib: Option<bool>,
27 pub semver_suffix: bool,
28 pub overlay: Option<PathBuf>,
29 pub excludes: Option<Vec<String>>,
30 pub whitelist: Option<Vec<String>>,
31 pub repack_suffix: Option<String>,
32 pub allow_prerelease_deps: bool,
33 pub crate_src_path: Option<PathBuf>,
34 pub summary: Option<String>,
35 pub description: Option<String>,
36 pub maintainer: String,
37 pub uploaders: Option<Vec<String>>,
38 pub collapse_features: bool,
39 pub remove_features: Option<Vec<String>>,
40 pub remove_target_types: Option<Vec<TargetType>>,
41 pub filter_targets: Option<Vec<FilterTarget>>,
42 pub requires_root: Option<String>,
43
44 pub source: Option<SourceOverride>,
45 pub packages: IndexMap<String, PackageOverride>,
46
47 #[serde(flatten)]
48 pub unknown_fields: HashMap<String, IgnoredAny>,
49}
50
51#[derive(Deserialize, Debug, Clone, Default)]
52pub struct SourceOverride {
53 section: Option<String>,
54 policy: Option<String>,
55 homepage: Option<String>,
56 vcs_git: Option<String>,
57 vcs_browser: Option<String>,
58 build_depends: Option<Vec<String>>,
59 build_depends_arch: Option<Vec<String>>,
60 build_depends_indep: Option<Vec<String>>,
61 build_depends_excludes: Option<Vec<String>>,
62 build_features: Option<Vec<String>>,
63 skip_nocheck: Option<bool>,
64
65 #[serde(flatten)]
66 pub unknown_fields: HashMap<String, IgnoredAny>,
67}
68
69impl SourceOverride {
70 #[expect(clippy::too_many_arguments)]
71 pub fn new(
72 section: Option<String>,
73 policy: Option<String>,
74 homepage: Option<String>,
75 vcs_git: Option<String>,
76 vcs_browser: Option<String>,
77 build_depends: Option<Vec<String>>,
78 build_depends_arch: Option<Vec<String>>,
79 build_depends_indep: Option<Vec<String>>,
80 build_depends_excludes: Option<Vec<String>>,
81 build_features: Option<Vec<String>>,
82 skip_nocheck: Option<bool>,
83 ) -> Self {
84 Self {
85 section,
86 policy,
87 homepage,
88 vcs_git,
89 vcs_browser,
90 build_depends,
91 build_depends_arch,
92 build_depends_indep,
93 build_depends_excludes,
94 build_features,
95 skip_nocheck,
96 unknown_fields: HashMap::new(),
97 }
98 }
99}
100#[derive(Deserialize, Debug, Clone, Default)]
101pub struct PackageOverride {
102 section: Option<String>,
103 summary: Option<String>,
104 description: Option<String>,
105 architecture: Option<Vec<String>>,
106 multi_arch: Option<String>,
107 depends: Option<Vec<String>>,
108 recommends: Option<Vec<String>>,
109 suggests: Option<Vec<String>>,
110 provides: Option<Vec<String>>,
111 breaks: Option<Vec<String>>,
112 replaces: Option<Vec<String>>,
113 conflicts: Option<Vec<String>>,
114 extra_lines: Option<Vec<String>>,
115 test_is_broken: Option<bool>,
116 test_architecture: Option<Vec<String>>,
117 test_depends: Option<Vec<String>>,
118 test_restrictions: Option<Vec<PkgTestRestriction>>,
119 test_command: Option<String>,
120
121 #[serde(flatten)]
122 pub unknown_fields: HashMap<String, IgnoredAny>,
123}
124
125#[derive(Deserialize, Debug, Clone)]
126#[serde(rename_all = "lowercase")]
127pub enum FilterTarget {
129 Debian,
131 Wasm,
133}
134impl FilterTarget {
135 pub(crate) fn target_list(&self) -> &[&str] {
136 match self {
137 Self::Debian => &[
138 "aarch64-unknown-linux-gnu",
140 "armv7-unknown-linux-gnueabihf",
141 "i686-unknown-linux-gnu",
142 "loongarch64-unknown-linux-gnu",
143 "powerpc64le-unknown-linux-gnu",
144 "riscv64gc-unknown-linux-gnu",
145 "s390x-unknown-linux-gnu",
146 "x86_64-unknown-linux-gnu",
147 "i686-unknown-hurd-gnu",
149 "m68k-unknown-linux-gnu", "powerpc-unknown-linux-gnu",
151 "powerpc64-unknown-linux-gnu",
152 "sparc64-unknown-linux-gnu",
153 "x86_64-unknown-hurd-gnu",
154 "x86_64-unknown-linux-gnux32",
155 ],
156 Self::Wasm => &[
157 "wasm32-unknown-unknown",
158 "wasm32-wasip1",
159 "wasm32-wasip1-threads",
160 "wasm32-wasip2",
161 "wasm32-wasip3",
162 ],
163 }
164 }
165}
166
167#[derive(Deserialize, Debug, Clone)]
168#[serde(rename_all = "lowercase")]
169pub enum TargetType {
171 Bench,
173 Example,
175 Test,
177}
178
179impl std::fmt::Display for TargetType {
180 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
181 match self {
182 TargetType::Bench => f.write_str("bench"),
183 TargetType::Example => f.write_str("example"),
184 TargetType::Test => f.write_str("test"),
185 }
186 }
187}
188
189impl Default for Config {
190 fn default() -> Self {
191 Config {
192 bin: None,
193 bin_name: "<default>".to_string(),
194 lib: None,
195 semver_suffix: false,
196 overlay: None,
197 excludes: None,
198 repack_suffix: None,
199 whitelist: None,
200 allow_prerelease_deps: false,
201 crate_src_path: None,
202 summary: None,
203 description: None,
204 maintainer: RUST_MAINT.to_string(),
205 uploaders: None,
206 collapse_features: true,
207 remove_features: None,
208 remove_target_types: None,
209 filter_targets: None,
210 source: None,
211 packages: IndexMap::new(),
212 requires_root: None,
213 unknown_fields: HashMap::new(),
214 }
215 }
216}
217
218impl Config {
219 pub fn parse(src: &Path) -> Result<Config> {
220 let mut config_file = File::open(src)?;
221 let mut content = String::new();
222 config_file.read_to_string(&mut content)?;
223
224 let config: Config = toml::from_str(&content)?;
225
226 let mut unknown_fields = Vec::new();
227
228 for field in config.unknown_fields.keys() {
229 unknown_fields.push(field.clone());
230 }
231
232 if let Some(ref source) = config.source {
233 for field in source.unknown_fields.keys() {
234 unknown_fields.push(format!("source.{field}"));
235 }
236 }
237
238 for field in config.packages.keys() {
239 if PackageKey::from_key(field).is_none() {
240 unknown_fields.push(format!("packages.{field}"));
241 }
242 }
243
244 for (name, package) in &config.packages {
245 for field in package.unknown_fields.keys() {
246 unknown_fields.push(format!("packages.{name}.{field}"));
247 }
248 }
249
250 if !unknown_fields.is_empty() {
251 debcargo_warn!(
252 "Warning: Unknown fields in {}: {:?}",
253 src.display(),
254 unknown_fields
255 );
256 debcargo_warn!(" These fields will be ignored. Please check for typos.");
257 }
258
259 Ok(config)
260 }
261
262 pub fn build_bin_package(&self) -> bool {
263 self.bin.unwrap_or(!self.semver_suffix)
264 }
265
266 pub fn build_lib_package(&self) -> bool {
267 self.lib.unwrap_or(true)
268 }
269
270 pub fn build_extra_package(&self) -> bool {
271 self.configured_packages()
272 .any(|p| matches!(p, PackageKey::Extra(_)))
273 }
274
275 pub fn overlay_dir(&self, config_path: Option<&Path>) -> Option<PathBuf> {
276 Some(config_path?.parent()?.join(self.overlay.as_ref()?))
277 }
278
279 pub fn crate_src_path(&self, config_path: Option<&Path>) -> Option<PathBuf> {
280 Some(config_path?.parent()?.join(self.crate_src_path.as_ref()?))
281 }
282
283 pub fn orig_tar_excludes(&self) -> Option<&Vec<String>> {
284 self.excludes.as_ref()
285 }
286
287 pub fn orig_tar_whitelist(&self) -> Option<&Vec<String>> {
288 self.whitelist.as_ref()
289 }
290
291 pub fn repack_suffix(&self) -> Option<&str> {
292 self.repack_suffix
293 .as_deref()
294 .or_else(|| self.orig_tar_excludes().and(Some(DEFAULT_REPACK_SUFFIX)))
295 }
296
297 pub fn maintainer(&self) -> &str {
298 self.maintainer.as_str()
299 }
300
301 pub fn uploaders(&self) -> Option<&Vec<String>> {
302 self.uploaders.as_ref()
303 }
304
305 pub fn requires_root(&self) -> Option<&String> {
306 self.requires_root.as_ref()
307 }
308
309 pub fn section(&self) -> Option<&str> {
312 Some(self.source.as_ref()?.section.as_ref()?)
313 }
314
315 pub fn policy_version(&self) -> Option<&str> {
316 Some(self.source.as_ref()?.policy.as_ref()?)
317 }
318
319 pub fn homepage(&self) -> Option<&str> {
320 Some(self.source.as_ref()?.homepage.as_ref()?)
321 }
322
323 pub fn vcs_git(&self) -> Option<&str> {
324 Some(self.source.as_ref()?.vcs_git.as_ref()?)
325 }
326
327 pub fn vcs_browser(&self) -> Option<&str> {
328 Some(self.source.as_ref()?.vcs_browser.as_ref()?)
329 }
330
331 pub fn build_depends(&self) -> Option<&Vec<String>> {
332 self.source.as_ref()?.build_depends.as_ref()
333 }
334
335 pub fn build_depends_arch(&self) -> Option<&Vec<String>> {
336 self.source.as_ref()?.build_depends_arch.as_ref()
337 }
338
339 pub fn build_depends_indep(&self) -> Option<&Vec<String>> {
340 self.source.as_ref()?.build_depends_indep.as_ref()
341 }
342
343 pub fn build_depends_excludes(&self) -> Option<&Vec<String>> {
344 self.source.as_ref()?.build_depends_excludes.as_ref()
345 }
346
347 pub fn build_features(&self) -> Option<&Vec<String>> {
348 self.source.as_ref()?.build_features.as_ref()
349 }
350
351 pub fn skip_nocheck(&self) -> Option<bool> {
352 self.source.as_ref()?.skip_nocheck
353 }
354
355 pub fn configured_packages(&'_ self) -> impl Iterator<Item = PackageKey<'_>> {
358 self.packages.keys().filter_map(|k| PackageKey::from_key(k))
359 }
360
361 fn with_package<'a, T, F: FnOnce(&'a PackageOverride) -> Option<T>>(
362 &'a self,
363 key: PackageKey,
364 f: F,
365 ) -> Option<T> {
366 self.packages.get(&key.key_string()[..]).and_then(f)
367 }
368
369 pub fn package_section(&self, key: PackageKey) -> Option<&str> {
370 self.with_package(key, |pkg| pkg.section.as_deref())
371 }
372
373 pub fn package_summary(&self, key: PackageKey) -> Option<&str> {
374 self.with_package(key, |pkg| pkg.summary.as_deref())
375 }
376
377 pub fn package_description(&self, key: PackageKey) -> Option<&str> {
378 self.with_package(key, |pkg| pkg.description.as_deref())
379 }
380
381 pub fn package_architecture(&self, key: PackageKey) -> Option<&Vec<String>> {
382 self.with_package(key, |pkg| pkg.architecture.as_ref())
383 }
384
385 pub fn package_multi_arch(&self, key: PackageKey) -> Option<&str> {
386 self.with_package(key, |pkg| pkg.multi_arch.as_deref())
387 }
388
389 pub fn package_depends(&self, key: PackageKey) -> Option<&Vec<String>> {
390 self.with_package(key, |pkg| pkg.depends.as_ref())
391 }
392
393 pub fn package_recommends(&self, key: PackageKey) -> Option<&Vec<String>> {
394 self.with_package(key, |pkg| pkg.recommends.as_ref())
395 }
396
397 pub fn package_suggests(&self, key: PackageKey) -> Option<&Vec<String>> {
398 self.with_package(key, |pkg| pkg.suggests.as_ref())
399 }
400
401 pub fn package_provides(&self, key: PackageKey) -> Option<&Vec<String>> {
402 self.with_package(key, |pkg| pkg.provides.as_ref())
403 }
404
405 pub fn package_breaks(&self, key: PackageKey) -> Option<&Vec<String>> {
406 self.with_package(key, |pkg| pkg.breaks.as_ref())
407 }
408
409 pub fn package_replaces(&self, key: PackageKey) -> Option<&Vec<String>> {
410 self.with_package(key, |pkg| pkg.replaces.as_ref())
411 }
412
413 pub fn package_conflicts(&self, key: PackageKey) -> Option<&Vec<String>> {
414 self.with_package(key, |pkg| pkg.conflicts.as_ref())
415 }
416
417 pub fn package_extra_lines(&self, key: PackageKey) -> Option<&Vec<String>> {
418 self.with_package(key, |pkg| pkg.extra_lines.as_ref())
419 }
420
421 pub fn package_test_is_broken(&self, key: PackageKey) -> Option<bool> {
422 self.with_package(key, |pkg| pkg.test_is_broken)
423 }
424
425 pub fn package_test_restrictions(&self, key: PackageKey) -> Option<&Vec<PkgTestRestriction>> {
426 self.with_package(key, |pkg| pkg.test_restrictions.as_ref())
427 }
428
429 pub fn package_test_architecture(&self, key: PackageKey) -> Option<&Vec<String>> {
430 self.with_package(key, |pkg| pkg.test_architecture.as_ref())
431 }
432
433 pub fn package_test_depends(&self, key: PackageKey) -> Option<&Vec<String>> {
434 self.with_package(key, |pkg| pkg.test_depends.as_ref())
435 }
436
437 pub fn package_test_command(&self, key: PackageKey) -> Option<&String> {
438 self.with_package(key, |pkg| pkg.test_command.as_ref())
439 }
440}
441
442pub fn package_field_for_feature<'a, 'b, F: Fn(PackageKey) -> Option<&'a Vec<String>>>(
443 get_field: F,
444 feature: PackageKey<'b>,
445 f_provides: &'b [&'b str],
446) -> impl Iterator<Item = String> + use<'a, 'b, F> {
447 Some(feature)
448 .into_iter()
449 .chain(f_provides.iter().map(|s| PackageKey::feature(s)))
450 .flat_map(move |f| get_field(f).into_iter().flatten())
451 .map(ToString::to_string)
452}
453
454#[derive(Clone, Copy)]
455pub enum PackageKey<'a> {
456 Bin,
457 BareLib,
458 FeatureLib(&'a str),
459 Extra(&'a str),
460}
461
462impl<'a> PackageKey<'a> {
463 pub fn feature(f: &'a str) -> PackageKey<'a> {
464 use self::PackageKey::{BareLib, FeatureLib};
465 if f.is_empty() {
466 BareLib
467 } else {
468 FeatureLib(f)
469 }
470 }
471
472 pub fn from_key(k: &'a str) -> Option<PackageKey<'a>> {
473 use self::PackageKey::{BareLib, Bin, Extra, FeatureLib};
474 Some(match k {
475 "bin" => Bin,
476 "lib" => BareLib,
477 _ => {
478 if let Some(feature) = k.strip_prefix("lib+") {
479 FeatureLib(feature)
480 } else {
481 let package = k.strip_prefix("extra+")?;
482 Extra(package)
483 }
484 }
485 })
486 }
487
488 fn key_string(&self) -> Cow<'static, str> {
489 use self::PackageKey::{BareLib, Bin, Extra, FeatureLib};
490 match self {
491 Bin => "bin".into(),
492 BareLib => "lib".into(),
493 FeatureLib(feature) => format!("lib+{feature}").into(),
494 Extra(package) => format!("extra+{package}").into(),
495 }
496 }
497}
498
499pub fn testing_ignore_debpolv() -> bool {
500 std::env::var_os("DEBCARGO_TESTING_IGNORE_DEBIAN_POLICY_VIOLATION").as_deref()
501 == Some(OsStr::new("1"))
502}
503
504pub fn testing_ruzt() -> bool {
505 std::env::var_os("DEBCARGO_TESTING_RUZT").as_deref() == Some(OsStr::new("1"))
506}