Skip to main content

buildlog_consultant/problems/
debian.rs

1use crate::Problem;
2use debversion::Version;
3
4/// Problem representing a generic dpkg error.
5///
6/// This struct is used for errors reported by the dpkg package manager
7/// that don't fit into more specific categories.
8#[derive(Debug)]
9pub struct DpkgError(pub String);
10
11impl Problem for DpkgError {
12    fn kind(&self) -> std::borrow::Cow<'_, str> {
13        "dpkg-error".into()
14    }
15
16    fn json(&self) -> serde_json::Value {
17        serde_json::json!({
18            "msg": self.0,
19        })
20    }
21
22    fn as_any(&self) -> &dyn std::any::Any {
23        self
24    }
25}
26
27inventory::submit! {
28    crate::ProblemKindInfo {
29        kind: "dpkg-error",
30        detail_fields: &["msg"],
31    }
32}
33
34impl std::fmt::Display for DpkgError {
35    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
36        write!(f, "dpkg error: {}", self.0)
37    }
38}
39
40/// Problem representing an error during apt-get update.
41///
42/// This struct is used when the apt package database update process
43/// fails for any reason.
44#[derive(Debug, Clone)]
45pub struct AptUpdateError;
46
47impl Problem for AptUpdateError {
48    fn kind(&self) -> std::borrow::Cow<'_, str> {
49        "apt-update-error".into()
50    }
51
52    fn json(&self) -> serde_json::Value {
53        serde_json::json!({})
54    }
55
56    fn as_any(&self) -> &dyn std::any::Any {
57        self
58    }
59}
60
61inventory::submit! {
62    crate::ProblemKindInfo {
63        kind: "apt-update-error",
64        detail_fields: &[],
65    }
66}
67
68impl std::fmt::Display for AptUpdateError {
69    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
70        write!(f, "apt update error")
71    }
72}
73
74/// Problem representing a failure to fetch a package or repository data.
75///
76/// This struct is used when apt cannot download a package or repository
77/// data from the specified URL.
78#[derive(Debug, Clone)]
79pub struct AptFetchFailure {
80    /// The URL that apt was trying to fetch from, if available.
81    pub url: Option<String>,
82    /// The error message from the fetch failure.
83    pub error: String,
84}
85
86impl Problem for AptFetchFailure {
87    fn kind(&self) -> std::borrow::Cow<'_, str> {
88        "apt-file-fetch-failure".into()
89    }
90
91    fn json(&self) -> serde_json::Value {
92        serde_json::json!({
93            "url": self.url,
94            "error": self.error,
95        })
96    }
97
98    fn as_any(&self) -> &dyn std::any::Any {
99        self
100    }
101}
102
103inventory::submit! {
104    crate::ProblemKindInfo {
105        kind: "apt-file-fetch-failure",
106        detail_fields: &["url", "error"],
107    }
108}
109
110impl std::fmt::Display for AptFetchFailure {
111    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
112        if let Some(url) = &self.url {
113            write!(f, "apt fetch failure: {} ({})", url, self.error)
114        } else {
115            write!(f, "apt fetch failure: {}", self.error)
116        }
117    }
118}
119
120/// Problem representing a missing Release file for a repository.
121///
122/// This struct is used when apt cannot find the Release file for a repository,
123/// which typically indicates a misconfigured or unavailable repository.
124#[derive(Debug, Clone)]
125pub struct AptMissingReleaseFile(pub String);
126
127impl Problem for AptMissingReleaseFile {
128    fn kind(&self) -> std::borrow::Cow<'_, str> {
129        "missing-release-file".into()
130    }
131
132    fn json(&self) -> serde_json::Value {
133        serde_json::json!({
134            "url": self.0,
135        })
136    }
137
138    fn as_any(&self) -> &dyn std::any::Any {
139        self
140    }
141}
142
143inventory::submit! {
144    crate::ProblemKindInfo {
145        kind: "missing-release-file",
146        detail_fields: &["url"],
147    }
148}
149
150impl std::fmt::Display for AptMissingReleaseFile {
151    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
152        write!(f, "apt missing release file: {}", self.0)
153    }
154}
155
156/// Problem representing a package that apt cannot find.
157///
158/// This struct is used when apt cannot find a requested package in any
159/// of the configured repositories.
160#[derive(Debug, Clone)]
161pub struct AptPackageUnknown(pub String);
162
163impl Problem for AptPackageUnknown {
164    fn kind(&self) -> std::borrow::Cow<'_, str> {
165        "apt-package-unknown".into()
166    }
167
168    fn json(&self) -> serde_json::Value {
169        serde_json::json!({
170            "package": self.0,
171        })
172    }
173
174    fn as_any(&self) -> &dyn std::any::Any {
175        self
176    }
177}
178
179inventory::submit! {
180    crate::ProblemKindInfo {
181        kind: "apt-package-unknown",
182        detail_fields: &["package"],
183    }
184}
185
186crate::register_problem_de_fn!("apt-package-unknown", |v| {
187    let pkg = v
188        .get("package")
189        .and_then(|m| m.as_str())
190        .or_else(|| v.as_str())
191        .ok_or_else(|| serde::de::Error::missing_field("package"))?
192        .to_string();
193    Ok(Box::new(AptPackageUnknown(pkg)) as Box<dyn crate::Problem>)
194});
195
196impl std::fmt::Display for AptPackageUnknown {
197    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
198        write!(f, "apt package unknown: {}", self.0)
199    }
200}
201
202/// Problem representing broken package dependencies.
203///
204/// This struct is used when apt reports broken packages in the dependency
205/// resolution process, which can occur when packages have incompatible dependencies.
206#[derive(Debug, Clone)]
207pub struct AptBrokenPackages {
208    /// A description of the broken package situation.
209    pub description: String,
210    /// List of packages that are broken, if available.
211    pub broken: Option<Vec<String>>,
212}
213
214impl Problem for AptBrokenPackages {
215    fn kind(&self) -> std::borrow::Cow<'_, str> {
216        "apt-broken-packages".into()
217    }
218
219    fn json(&self) -> serde_json::Value {
220        serde_json::json!({
221            "description": self.description,
222            "broken": self.broken,
223        })
224    }
225
226    fn as_any(&self) -> &dyn std::any::Any {
227        self
228    }
229}
230
231inventory::submit! {
232    crate::ProblemKindInfo {
233        kind: "apt-broken-packages",
234        detail_fields: &["description", "broken"],
235    }
236}
237
238impl std::fmt::Display for AptBrokenPackages {
239    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
240        write!(f, "apt broken packages: {}", self.description)
241    }
242}
243
244/// Problem representing a missing upstream source tarball.
245///
246/// This struct is used when the build process cannot find the upstream
247/// source tarball for a package, which is required for the build.
248#[derive(Debug, Clone)]
249pub struct UnableToFindUpstreamTarball {
250    /// The name of the package.
251    pub package: String,
252    /// The version of the package for which the tarball is missing.
253    pub version: Version,
254}
255
256impl Problem for UnableToFindUpstreamTarball {
257    fn kind(&self) -> std::borrow::Cow<'_, str> {
258        "unable-to-find-upstream-tarball".into()
259    }
260
261    fn json(&self) -> serde_json::Value {
262        serde_json::json!({
263            "package": self.package,
264            "version": self.version.to_string(),
265        })
266    }
267
268    fn as_any(&self) -> &dyn std::any::Any {
269        self
270    }
271}
272
273inventory::submit! {
274    crate::ProblemKindInfo {
275        kind: "unable-to-find-upstream-tarball",
276        detail_fields: &["package", "version"],
277    }
278}
279
280impl std::fmt::Display for UnableToFindUpstreamTarball {
281    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
282        write!(
283            f,
284            "Unable to find upstream tarball for {} {}",
285            self.package, self.version
286        )
287    }
288}
289
290/// Problem representing a source format that cannot be built.
291///
292/// This struct is used when the source package format specified in
293/// debian/source/format cannot be built for some reason.
294#[derive(Debug, Clone)]
295pub struct SourceFormatUnbuildable {
296    /// The source format that can't be built (e.g., "3.0 (quilt)").
297    pub source_format: String,
298    /// The reason why the source format cannot be built.
299    pub reason: String,
300}
301
302impl Problem for SourceFormatUnbuildable {
303    fn kind(&self) -> std::borrow::Cow<'_, str> {
304        "source-format-unbuildable".into()
305    }
306
307    fn json(&self) -> serde_json::Value {
308        serde_json::json!({
309            "source_format": self.source_format,
310            "reason": self.reason,
311        })
312    }
313
314    fn as_any(&self) -> &dyn std::any::Any {
315        self
316    }
317}
318
319inventory::submit! {
320    crate::ProblemKindInfo {
321        kind: "source-format-unbuildable",
322        detail_fields: &["source_format", "reason"],
323    }
324}
325
326impl std::fmt::Display for SourceFormatUnbuildable {
327    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
328        write!(
329            f,
330            "Source format {} is unbuildable: {}",
331            self.source_format, self.reason
332        )
333    }
334}
335
336/// Problem representing a source format that is not supported.
337///
338/// This struct is used when the source package format specified in
339/// debian/source/format is not supported by the build environment.
340#[derive(Debug, Clone)]
341pub struct SourceFormatUnsupported(pub String);
342
343impl Problem for SourceFormatUnsupported {
344    fn kind(&self) -> std::borrow::Cow<'_, str> {
345        "source-format-unsupported".into()
346    }
347
348    fn json(&self) -> serde_json::Value {
349        serde_json::json!({
350            "source_format": self.0,
351        })
352    }
353
354    fn as_any(&self) -> &dyn std::any::Any {
355        self
356    }
357}
358
359inventory::submit! {
360    crate::ProblemKindInfo {
361        kind: "source-format-unsupported",
362        detail_fields: &["source_format"],
363    }
364}
365
366impl std::fmt::Display for SourceFormatUnsupported {
367    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
368        write!(f, "Source format {} is unsupported", self.0)
369    }
370}
371
372/// Problem representing a missing patch file.
373///
374/// This struct is used when a build requires a patch file that is
375/// referenced in the debian/patches directory but is not found.
376#[derive(Debug, Clone)]
377pub struct PatchFileMissing(pub std::path::PathBuf);
378
379impl Problem for PatchFileMissing {
380    fn kind(&self) -> std::borrow::Cow<'_, str> {
381        "patch-file-missing".into()
382    }
383
384    fn json(&self) -> serde_json::Value {
385        serde_json::json!({
386            "path": self.0.display().to_string(),
387        })
388    }
389
390    fn as_any(&self) -> &dyn std::any::Any {
391        self
392    }
393}
394
395inventory::submit! {
396    crate::ProblemKindInfo {
397        kind: "patch-file-missing",
398        detail_fields: &["path"],
399    }
400}
401
402impl std::fmt::Display for PatchFileMissing {
403    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
404        write!(f, "Patch file missing: {}", self.0.display())
405    }
406}
407
408/// Problem representing unexpected local changes in the source package.
409///
410/// This struct is used when dpkg-source detects unexpected local changes
411/// to upstream source code, which should be represented as patches instead.
412#[derive(Debug, Clone)]
413pub struct DpkgSourceLocalChanges {
414    /// Path to the diff file showing the changes, if available.
415    pub diff_file: Option<String>,
416    /// List of files that have been changed locally, if available.
417    pub files: Option<Vec<String>>,
418}
419
420impl Problem for DpkgSourceLocalChanges {
421    fn kind(&self) -> std::borrow::Cow<'_, str> {
422        "unexpected-local-upstream-changes".into()
423    }
424
425    fn json(&self) -> serde_json::Value {
426        serde_json::json!({
427            "diff_file": self.diff_file,
428            "files": self.files,
429        })
430    }
431
432    fn as_any(&self) -> &dyn std::any::Any {
433        self
434    }
435}
436
437inventory::submit! {
438    crate::ProblemKindInfo {
439        kind: "unexpected-local-upstream-changes",
440        detail_fields: &["diff_file", "files"],
441    }
442}
443
444impl std::fmt::Display for DpkgSourceLocalChanges {
445    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
446        if let Some(files) = self.files.as_ref() {
447            if files.len() < 5 {
448                write!(f, "Tree has local changes: {:?}", files)?;
449                return Ok(());
450            }
451
452            write!(f, "Tree has local changes: {} files", files.len())?;
453        } else {
454            write!(f, "Tree has local changes")?;
455        }
456        Ok(())
457    }
458}
459
460/// Problem representing changes that cannot be represented in the source package.
461///
462/// This struct is used when dpkg-source detects changes that cannot be
463/// represented in the chosen source format, such as mode changes in some formats.
464#[derive(Debug, Clone)]
465pub struct DpkgSourceUnrepresentableChanges;
466
467impl Problem for DpkgSourceUnrepresentableChanges {
468    fn kind(&self) -> std::borrow::Cow<'_, str> {
469        "unrepresentable-local-changes".into()
470    }
471
472    fn json(&self) -> serde_json::Value {
473        serde_json::Value::Null
474    }
475
476    fn as_any(&self) -> &dyn std::any::Any {
477        self
478    }
479}
480
481inventory::submit! {
482    crate::ProblemKindInfo {
483        kind: "unrepresentable-local-changes",
484        detail_fields: &[],
485    }
486}
487
488impl std::fmt::Display for DpkgSourceUnrepresentableChanges {
489    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
490        write!(f, "Tree has unrepresentable changes")
491    }
492}
493
494/// Problem representing unwanted binary files in the source package.
495///
496/// This struct is used when dpkg-source detects binary files in the source
497/// package that are not allowed, which can happen when the source is dirty.
498#[derive(Debug, Clone)]
499pub struct DpkgUnwantedBinaryFiles;
500
501impl Problem for DpkgUnwantedBinaryFiles {
502    fn kind(&self) -> std::borrow::Cow<'_, str> {
503        "unwanted-binary-files".into()
504    }
505
506    fn json(&self) -> serde_json::Value {
507        serde_json::Value::Null
508    }
509
510    fn as_any(&self) -> &dyn std::any::Any {
511        self
512    }
513}
514
515inventory::submit! {
516    crate::ProblemKindInfo {
517        kind: "unwanted-binary-files",
518        detail_fields: &[],
519    }
520}
521
522impl std::fmt::Display for DpkgUnwantedBinaryFiles {
523    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
524        write!(f, "Tree has unwanted binary files")
525    }
526}
527
528/// Problem representing changes to binary files in the source package.
529///
530/// This struct is used when dpkg-source detects that binary files have been
531/// changed, which cannot be properly represented in source package formats.
532#[derive(Debug, Clone)]
533pub struct DpkgBinaryFileChanged(pub Vec<String>);
534
535impl Problem for DpkgBinaryFileChanged {
536    fn kind(&self) -> std::borrow::Cow<'_, str> {
537        "binary-file-changed".into()
538    }
539
540    fn json(&self) -> serde_json::Value {
541        serde_json::json!({
542            "files": self.0,
543        })
544    }
545
546    fn as_any(&self) -> &dyn std::any::Any {
547        self
548    }
549}
550
551inventory::submit! {
552    crate::ProblemKindInfo {
553        kind: "binary-file-changed",
554        detail_fields: &["files"],
555    }
556}
557
558impl std::fmt::Display for DpkgBinaryFileChanged {
559    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
560        write!(f, "Binary file changed")
561    }
562}
563
564/// Problem representing a missing debian control file.
565///
566/// This struct is used when the debian/control file, which is required for
567/// any Debian package, is missing from the source package.
568#[derive(Debug, Clone)]
569pub struct MissingControlFile(pub std::path::PathBuf);
570
571impl Problem for MissingControlFile {
572    fn kind(&self) -> std::borrow::Cow<'_, str> {
573        "missing-control-file".into()
574    }
575
576    fn json(&self) -> serde_json::Value {
577        serde_json::Value::Null
578    }
579
580    fn as_any(&self) -> &dyn std::any::Any {
581        self
582    }
583}
584
585inventory::submit! {
586    crate::ProblemKindInfo {
587        kind: "missing-control-file",
588        detail_fields: &[],
589    }
590}
591
592impl std::fmt::Display for MissingControlFile {
593    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
594        write!(f, "Missing control file: {}", self.0.display())
595    }
596}
597
598/// Problem representing unknown Mercurial extra fields.
599///
600/// This struct is used when the build process encounters unknown extra fields
601/// in Mercurial version control metadata.
602#[derive(Debug, Clone)]
603pub struct UnknownMercurialExtraFields(pub String);
604
605impl Problem for UnknownMercurialExtraFields {
606    fn kind(&self) -> std::borrow::Cow<'_, str> {
607        "unknown-mercurial-extra-fields".into()
608    }
609
610    fn json(&self) -> serde_json::Value {
611        serde_json::json!({
612            "field": self.0,
613        })
614    }
615
616    fn as_any(&self) -> &dyn std::any::Any {
617        self
618    }
619}
620
621inventory::submit! {
622    crate::ProblemKindInfo {
623        kind: "unknown-mercurial-extra-fields",
624        detail_fields: &["field"],
625    }
626}
627
628impl std::fmt::Display for UnknownMercurialExtraFields {
629    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
630        write!(f, "Unknown Mercurial extra field: {}", self.0)
631    }
632}
633
634/// Problem representing a failure to verify an upstream PGP signature.
635///
636/// This struct is used when the build process cannot verify the PGP signature
637/// of an upstream source tarball, which may indicate a security issue.
638#[derive(Debug, Clone)]
639pub struct UpstreamPGPSignatureVerificationFailed;
640
641impl Problem for UpstreamPGPSignatureVerificationFailed {
642    fn kind(&self) -> std::borrow::Cow<'_, str> {
643        "upstream-pgp-signature-verification-failed".into()
644    }
645
646    fn json(&self) -> serde_json::Value {
647        serde_json::Value::Null
648    }
649
650    fn as_any(&self) -> &dyn std::any::Any {
651        self
652    }
653}
654
655inventory::submit! {
656    crate::ProblemKindInfo {
657        kind: "upstream-pgp-signature-verification-failed",
658        detail_fields: &[],
659    }
660}
661
662impl std::fmt::Display for UpstreamPGPSignatureVerificationFailed {
663    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
664        write!(f, "Upstream PGP signature verification failed")
665    }
666}
667
668/// Problem representing a missing requested version in uscan.
669///
670/// This struct is used when the uscan tool (which checks for upstream versions)
671/// cannot find a specifically requested version.
672#[derive(Debug, Clone)]
673pub struct UScanRequestVersionMissing(pub String);
674
675impl Problem for UScanRequestVersionMissing {
676    fn kind(&self) -> std::borrow::Cow<'_, str> {
677        "uscan-request-version-missing".into()
678    }
679
680    fn json(&self) -> serde_json::Value {
681        serde_json::json!({
682            "version": self.0,
683        })
684    }
685
686    fn as_any(&self) -> &dyn std::any::Any {
687        self
688    }
689}
690
691inventory::submit! {
692    crate::ProblemKindInfo {
693        kind: "uscan-request-version-missing",
694        detail_fields: &["version"],
695    }
696}
697
698impl std::fmt::Display for UScanRequestVersionMissing {
699    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
700        write!(f, "UScan request version missing: {}", self.0)
701    }
702}
703
704/// Problem representing a failure in the debcargo tool.
705///
706/// This struct is used when the debcargo tool, which is used to package
707/// Rust crates as Debian packages, encounters an error.
708#[derive(Debug, Clone)]
709pub struct DebcargoFailure(pub String);
710
711impl Problem for DebcargoFailure {
712    fn kind(&self) -> std::borrow::Cow<'_, str> {
713        "debcargo-failure".into()
714    }
715
716    fn json(&self) -> serde_json::Value {
717        serde_json::json!({
718            "reason": self.0,
719        })
720    }
721
722    fn as_any(&self) -> &dyn std::any::Any {
723        self
724    }
725}
726
727inventory::submit! {
728    crate::ProblemKindInfo {
729        kind: "debcargo-failure",
730        detail_fields: &["reason"],
731    }
732}
733
734impl std::fmt::Display for DebcargoFailure {
735    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
736        write!(f, "Debcargo failure: {}", self.0)
737    }
738}
739
740/// Problem representing an error parsing a debian/changelog file.
741///
742/// This struct is used when the build process encounters a syntax error
743/// or other issue when parsing the debian/changelog file.
744#[derive(Debug, Clone)]
745pub struct ChangelogParseError(pub String);
746
747impl Problem for ChangelogParseError {
748    fn kind(&self) -> std::borrow::Cow<'_, str> {
749        "changelog-parse-error".into()
750    }
751
752    fn json(&self) -> serde_json::Value {
753        serde_json::json!({
754            "reason": self.0,
755        })
756    }
757
758    fn as_any(&self) -> &dyn std::any::Any {
759        self
760    }
761}
762
763inventory::submit! {
764    crate::ProblemKindInfo {
765        kind: "changelog-parse-error",
766        detail_fields: &["reason"],
767    }
768}
769
770impl std::fmt::Display for ChangelogParseError {
771    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
772        write!(f, "Changelog parse error: {}", self.0)
773    }
774}
775
776/// Problem representing a generic error in the uscan tool.
777///
778/// This struct is used when the uscan tool, which checks for upstream versions,
779/// encounters an error that doesn't fit into more specific categories.
780#[derive(Debug, Clone)]
781pub struct UScanError(pub String);
782
783impl Problem for UScanError {
784    fn kind(&self) -> std::borrow::Cow<'_, str> {
785        "uscan-error".into()
786    }
787
788    fn json(&self) -> serde_json::Value {
789        serde_json::json!({
790            "reason": self.0,
791        })
792    }
793
794    fn as_any(&self) -> &dyn std::any::Any {
795        self
796    }
797}
798
799inventory::submit! {
800    crate::ProblemKindInfo {
801        kind: "uscan-error",
802        detail_fields: &["reason"],
803    }
804}
805
806impl std::fmt::Display for UScanError {
807    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
808        write!(f, "UScan error: {}", self.0)
809    }
810}
811
812/// Problem representing a failure in the uscan tool.
813///
814/// This struct is used when the uscan tool fails to find or process
815/// upstream versions from a specific URL.
816#[derive(Debug, Clone)]
817pub struct UScanFailed {
818    /// The URL that uscan was trying to process.
819    pub url: String,
820    /// The reason for the failure.
821    pub reason: String,
822}
823
824impl Problem for UScanFailed {
825    fn kind(&self) -> std::borrow::Cow<'_, str> {
826        "uscan-failed".into()
827    }
828
829    fn json(&self) -> serde_json::Value {
830        serde_json::json!({
831            "url": self.url,
832            "reason": self.reason,
833        })
834    }
835
836    fn as_any(&self) -> &dyn std::any::Any {
837        self
838    }
839}
840
841inventory::submit! {
842    crate::ProblemKindInfo {
843        kind: "uscan-failed",
844        detail_fields: &["url", "reason"],
845    }
846}
847
848impl std::fmt::Display for UScanFailed {
849    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
850        write!(f, "UScan failed: {}", self.reason)
851    }
852}
853
854/// Problem representing inconsistency between source format and version.
855///
856/// This struct is used when there's an inconsistency between the source format
857/// specified in debian/source/format and the version numbering scheme.
858#[derive(Debug, Clone)]
859pub struct InconsistentSourceFormat {
860    /// Whether the version is inconsistent with the source format.
861    pub version: bool,
862    /// Whether the source format is inconsistent with the version.
863    pub source_format: bool,
864}
865
866impl Problem for InconsistentSourceFormat {
867    fn kind(&self) -> std::borrow::Cow<'_, str> {
868        "inconsistent-source-format".into()
869    }
870
871    fn json(&self) -> serde_json::Value {
872        serde_json::json!({
873            "version": self.version,
874            "source_format": self.source_format,
875        })
876    }
877
878    fn as_any(&self) -> &dyn std::any::Any {
879        self
880    }
881}
882
883inventory::submit! {
884    crate::ProblemKindInfo {
885        kind: "inconsistent-source-format",
886        detail_fields: &["version", "source_format"],
887    }
888}
889
890impl std::fmt::Display for InconsistentSourceFormat {
891    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
892        write!(
893            f,
894            "Inconsistent source format between version and source format"
895        )
896    }
897}
898
899/// Problem representing an error parsing the debian/upstream/metadata file.
900///
901/// This struct is used when the build process cannot parse the
902/// debian/upstream/metadata file, which contains information about the upstream project.
903#[derive(Debug, Clone)]
904pub struct UpstreamMetadataFileParseError {
905    /// The path to the metadata file.
906    pub path: std::path::PathBuf,
907    /// The reason for the parsing failure.
908    pub reason: String,
909}
910
911impl Problem for UpstreamMetadataFileParseError {
912    fn kind(&self) -> std::borrow::Cow<'_, str> {
913        "debian-upstream-metadata-invalid".into()
914    }
915
916    fn json(&self) -> serde_json::Value {
917        serde_json::json!({
918            "path": self.path.display().to_string(),
919            "reason": self.reason,
920        })
921    }
922
923    fn as_any(&self) -> &dyn std::any::Any {
924        self
925    }
926}
927
928inventory::submit! {
929    crate::ProblemKindInfo {
930        kind: "debian-upstream-metadata-invalid",
931        detail_fields: &["path", "reason"],
932    }
933}
934
935impl std::fmt::Display for UpstreamMetadataFileParseError {
936    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
937        write!(f, "Upstream metadata file parse error: {}", self.reason)
938    }
939}
940
941/// Problem representing a failure in dpkg-source when packaging source files.
942///
943/// This struct is used when dpkg-source cannot package the source files
944/// into a source package for various reasons.
945#[derive(Debug, Clone)]
946pub struct DpkgSourcePackFailed(pub String);
947
948impl Problem for DpkgSourcePackFailed {
949    fn kind(&self) -> std::borrow::Cow<'_, str> {
950        "dpkg-source-pack-failed".into()
951    }
952
953    fn json(&self) -> serde_json::Value {
954        serde_json::json!({
955            "reason": self.0,
956        })
957    }
958
959    fn as_any(&self) -> &dyn std::any::Any {
960        self
961    }
962}
963
964inventory::submit! {
965    crate::ProblemKindInfo {
966        kind: "dpkg-source-pack-failed",
967        detail_fields: &["reason"],
968    }
969}
970
971impl std::fmt::Display for DpkgSourcePackFailed {
972    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
973        write!(f, "Dpkg source pack failed: {}", self.0)
974    }
975}
976
977/// Problem representing an invalid version string in a package.
978///
979/// This struct is used when dpkg encounters a version string that
980/// doesn't follow the Debian version format rules.
981#[derive(Debug, Clone)]
982pub struct DpkgBadVersion {
983    /// The invalid version string.
984    pub version: String,
985    /// The reason why the version is invalid, if available.
986    pub reason: Option<String>,
987}
988
989impl Problem for DpkgBadVersion {
990    fn kind(&self) -> std::borrow::Cow<'_, str> {
991        "dpkg-bad-version".into()
992    }
993
994    fn json(&self) -> serde_json::Value {
995        serde_json::json!({
996            "version": self.version,
997            "reason": self.reason,
998        })
999    }
1000
1001    fn as_any(&self) -> &dyn std::any::Any {
1002        self
1003    }
1004}
1005
1006inventory::submit! {
1007    crate::ProblemKindInfo {
1008        kind: "dpkg-bad-version",
1009        detail_fields: &["version", "reason"],
1010    }
1011}
1012
1013impl std::fmt::Display for DpkgBadVersion {
1014    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1015        if let Some(reason) = &self.reason {
1016            write!(f, "Version {} is invalid: {}", self.version, reason)
1017        } else {
1018            write!(f, "Version {} is invalid", self.version)
1019        }
1020    }
1021}
1022
1023/// Problem representing a missing Rust crate in debcargo.
1024///
1025/// This struct is used when debcargo cannot find a Rust crate
1026/// that is required for the build.
1027#[derive(Debug, Clone)]
1028pub struct MissingDebcargoCrate {
1029    /// The name of the missing Rust crate.
1030    pub cratename: String,
1031    /// The version of the crate that is required, if specified.
1032    pub version: Option<String>,
1033}
1034
1035impl Problem for MissingDebcargoCrate {
1036    fn kind(&self) -> std::borrow::Cow<'_, str> {
1037        "debcargo-missing-crate".into()
1038    }
1039
1040    fn json(&self) -> serde_json::Value {
1041        serde_json::json!({
1042            "crate": self.cratename,
1043            "version": self.version,
1044        })
1045    }
1046
1047    fn as_any(&self) -> &dyn std::any::Any {
1048        self
1049    }
1050}
1051
1052inventory::submit! {
1053    crate::ProblemKindInfo {
1054        kind: "debcargo-missing-crate",
1055        detail_fields: &["crate", "version"],
1056    }
1057}
1058
1059impl std::fmt::Display for MissingDebcargoCrate {
1060    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1061        if let Some(version) = &self.version {
1062            write!(
1063                f,
1064                "debcargo can't find crate {} (version: {})",
1065                self.cratename, version
1066            )
1067        } else {
1068            write!(f, "debcargo can't find crate {}", self.cratename)
1069        }
1070    }
1071}
1072
1073impl MissingDebcargoCrate {
1074    /// Creates a MissingDebcargoCrate instance from a string.
1075    ///
1076    /// Parses a string in the format "cratename=version" or just "cratename"
1077    /// to create a new instance.
1078    ///
1079    /// # Arguments
1080    /// * `text` - The string to parse
1081    ///
1082    /// # Returns
1083    /// A new MissingDebcargoCrate instance
1084    pub fn from_string(text: &str) -> Self {
1085        let text = text.trim();
1086        if let Some((cratename, version)) = text.split_once('=') {
1087            Self {
1088                cratename: cratename.trim().to_string(),
1089                version: Some(version.trim().to_string()),
1090            }
1091        } else {
1092            Self {
1093                cratename: text.to_string(),
1094                version: None,
1095            }
1096        }
1097    }
1098}
1099
1100/// Problem representing a missing pristine-tar tree reference.
1101///
1102/// This struct is used when a pristine-tar operation cannot find
1103/// a referenced tree in the git repository.
1104#[derive(Debug, Clone)]
1105pub struct PristineTarTreeMissing(pub String);
1106
1107impl Problem for PristineTarTreeMissing {
1108    fn kind(&self) -> std::borrow::Cow<'_, str> {
1109        "pristine-tar-missing-tree".into()
1110    }
1111
1112    fn json(&self) -> serde_json::Value {
1113        serde_json::json!({
1114            "treeish": self.0,
1115        })
1116    }
1117
1118    fn as_any(&self) -> &dyn std::any::Any {
1119        self
1120    }
1121}
1122
1123inventory::submit! {
1124    crate::ProblemKindInfo {
1125        kind: "pristine-tar-missing-tree",
1126        detail_fields: &["treeish"],
1127    }
1128}
1129
1130impl std::fmt::Display for PristineTarTreeMissing {
1131    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1132        write!(f, "Pristine-tar tree missing: {}", self.0)
1133    }
1134}
1135
1136/// Problem representing a missing revision in version control.
1137///
1138/// This struct is used when a build process references a revision
1139/// in version control that does not exist.
1140#[derive(Debug, Clone)]
1141pub struct MissingRevision(pub Vec<u8>);
1142
1143impl Problem for MissingRevision {
1144    fn kind(&self) -> std::borrow::Cow<'_, str> {
1145        "missing-revision".into()
1146    }
1147
1148    fn json(&self) -> serde_json::Value {
1149        serde_json::json!({
1150            "revision": String::from_utf8_lossy(&self.0),
1151        })
1152    }
1153
1154    fn as_any(&self) -> &dyn std::any::Any {
1155        self
1156    }
1157}
1158
1159inventory::submit! {
1160    crate::ProblemKindInfo {
1161        kind: "missing-revision",
1162        detail_fields: &["revision"],
1163    }
1164}
1165
1166impl std::fmt::Display for MissingRevision {
1167    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1168        write!(f, "Missing revision: {}", String::from_utf8_lossy(&self.0))
1169    }
1170}
1171
1172/// Problem representing a Rust crate dependency predicate that debcargo cannot handle.
1173///
1174/// This struct is used when debcargo cannot represent a predicate in a Rust
1175/// crate dependency, such as certain prerelease version constraints.
1176#[derive(Debug)]
1177pub struct DebcargoUnacceptablePredicate {
1178    /// The name of the crate with the unacceptable predicate.
1179    pub cratename: String,
1180    /// The predicate that cannot be represented.
1181    pub predicate: String,
1182}
1183
1184impl Problem for DebcargoUnacceptablePredicate {
1185    fn kind(&self) -> std::borrow::Cow<'_, str> {
1186        "debcargo-unacceptable-predicate".into()
1187    }
1188
1189    fn json(&self) -> serde_json::Value {
1190        serde_json::json!({
1191            "crate": self.cratename,
1192            "predicate": self.predicate,
1193        })
1194    }
1195
1196    fn as_any(&self) -> &dyn std::any::Any {
1197        self
1198    }
1199}
1200
1201inventory::submit! {
1202    crate::ProblemKindInfo {
1203        kind: "debcargo-unacceptable-predicate",
1204        detail_fields: &["crate", "predicate"],
1205    }
1206}
1207
1208impl std::fmt::Display for DebcargoUnacceptablePredicate {
1209    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1210        write!(
1211            f,
1212            "Cannot represent prerelease part of dependency: {}",
1213            self.predicate
1214        )
1215    }
1216}
1217
1218/// Problem representing a Rust crate dependency comparator that debcargo cannot handle.
1219///
1220/// This struct is used when debcargo cannot represent a version comparison operator
1221/// in a Rust crate dependency, such as certain complex version constraints.
1222#[derive(Debug)]
1223pub struct DebcargoUnacceptableComparator {
1224    /// The name of the crate with the unacceptable comparator.
1225    pub cratename: String,
1226    /// The comparator that cannot be represented.
1227    pub comparator: String,
1228}
1229
1230impl Problem for DebcargoUnacceptableComparator {
1231    fn kind(&self) -> std::borrow::Cow<'_, str> {
1232        "debcargo-unacceptable-comparator".into()
1233    }
1234
1235    fn json(&self) -> serde_json::Value {
1236        serde_json::json!({
1237            "crate": self.cratename,
1238            "comparator": self.comparator,
1239        })
1240    }
1241
1242    fn as_any(&self) -> &dyn std::any::Any {
1243        self
1244    }
1245}
1246
1247inventory::submit! {
1248    crate::ProblemKindInfo {
1249        kind: "debcargo-unacceptable-comparator",
1250        detail_fields: &["crate", "comparator"],
1251    }
1252}
1253
1254impl std::fmt::Display for DebcargoUnacceptableComparator {
1255    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1256        write!(
1257            f,
1258            "Cannot represent prerelease part of dependency: {}",
1259            self.comparator
1260        )
1261    }
1262}
1263
1264/// Problem representing a "too many requests" error from uscan.
1265///
1266/// This struct is used when uscan receives a rate limiting response
1267/// from a server it is checking for upstream versions.
1268#[derive(Debug)]
1269pub struct UScanTooManyRequests(pub String);
1270
1271impl Problem for UScanTooManyRequests {
1272    fn kind(&self) -> std::borrow::Cow<'_, str> {
1273        "uscan-too-many-requests".into()
1274    }
1275
1276    fn json(&self) -> serde_json::Value {
1277        serde_json::json!({
1278            "reason": self.0,
1279        })
1280    }
1281
1282    fn as_any(&self) -> &dyn std::any::Any {
1283        self
1284    }
1285}
1286
1287inventory::submit! {
1288    crate::ProblemKindInfo {
1289        kind: "uscan-too-many-requests",
1290        detail_fields: &["reason"],
1291    }
1292}
1293
1294impl std::fmt::Display for UScanTooManyRequests {
1295    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1296        write!(f, "UScan too many requests: {}", self.0)
1297    }
1298}
1299
1300/// Problem representing unsatisfied conflicts in apt dependencies.
1301///
1302/// This struct is used when apt cannot resolve package conflicts
1303/// during the dependency resolution process.
1304#[derive(Debug)]
1305pub struct UnsatisfiedAptConflicts(pub String);
1306
1307impl Problem for UnsatisfiedAptConflicts {
1308    fn kind(&self) -> std::borrow::Cow<'_, str> {
1309        "unsatisfied-apt-conflicts".into()
1310    }
1311
1312    fn json(&self) -> serde_json::Value {
1313        serde_json::json!({
1314            "relations": self.0,
1315        })
1316    }
1317
1318    fn as_any(&self) -> &dyn std::any::Any {
1319        self
1320    }
1321}
1322
1323inventory::submit! {
1324    crate::ProblemKindInfo {
1325        kind: "unsatisfied-apt-conflicts",
1326        detail_fields: &["relations"],
1327    }
1328}
1329
1330impl std::fmt::Display for UnsatisfiedAptConflicts {
1331    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
1332        write!(f, "unsatisfied apt conflicts: {}", self.0)
1333    }
1334}
1335
1336impl std::error::Error for UnsatisfiedAptConflicts {}
1337
1338/// Problem representing an architecture not in the supported architecture list.
1339///
1340/// This struct is used when a build is attempted for an architecture that
1341/// is not in the list of architectures supported by the package.
1342#[derive(Debug, Clone)]
1343pub struct ArchitectureNotInList {
1344    /// The architecture being built for.
1345    pub arch: String,
1346    /// The list of supported architectures.
1347    pub arch_list: Vec<String>,
1348}
1349
1350impl Problem for ArchitectureNotInList {
1351    fn kind(&self) -> std::borrow::Cow<'_, str> {
1352        "arch-not-in-list".into()
1353    }
1354
1355    fn json(&self) -> serde_json::Value {
1356        serde_json::json!({
1357            "arch": self.arch,
1358            "arch_list": self.arch_list,
1359        })
1360    }
1361
1362    fn as_any(&self) -> &dyn std::any::Any {
1363        self
1364    }
1365}
1366
1367inventory::submit! {
1368    crate::ProblemKindInfo {
1369        kind: "arch-not-in-list",
1370        detail_fields: &["arch", "arch_list"],
1371    }
1372}
1373
1374impl std::fmt::Display for ArchitectureNotInList {
1375    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1376        write!(f, "Architecture {} not a build arch", self.arch)
1377    }
1378}
1379
1380/// Problem representing unsatisfied dependencies in apt.
1381///
1382/// This struct is used when apt cannot satisfy the dependencies
1383/// required for a package installation.
1384#[derive(Debug)]
1385pub struct UnsatisfiedAptDependencies(pub String);
1386
1387impl Problem for UnsatisfiedAptDependencies {
1388    fn kind(&self) -> std::borrow::Cow<'_, str> {
1389        "unsatisfied-apt-dependencies".into()
1390    }
1391
1392    fn json(&self) -> serde_json::Value {
1393        serde_json::json!({
1394            "relations": self.0
1395        })
1396    }
1397
1398    fn as_any(&self) -> &dyn std::any::Any {
1399        self
1400    }
1401}
1402
1403inventory::submit! {
1404    crate::ProblemKindInfo {
1405        kind: "unsatisfied-apt-dependencies",
1406        detail_fields: &["relations"],
1407    }
1408}
1409
1410crate::register_problem_de_fn!("unsatisfied-apt-dependencies", |v| {
1411    let relations = v
1412        .get("relations")
1413        .and_then(|m| m.as_str())
1414        .ok_or_else(|| serde::de::Error::missing_field("relations"))?
1415        .to_string();
1416    Ok(Box::new(UnsatisfiedAptDependencies(relations)) as Box<dyn crate::Problem>)
1417});
1418
1419impl std::fmt::Display for UnsatisfiedAptDependencies {
1420    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
1421        write!(f, "unsatisfied apt dependencies: {}", self.0)
1422    }
1423}
1424
1425/// Problem representing insufficient disk space for a build.
1426///
1427/// This struct is used when a build process determines that there is
1428/// not enough disk space available to complete the build.
1429#[derive(Debug)]
1430pub struct InsufficientDiskSpace {
1431    /// The amount of disk space needed for the build in KiB.
1432    pub needed: i64,
1433    /// The amount of free disk space available in KiB.
1434    pub free: i64,
1435}
1436
1437impl Problem for InsufficientDiskSpace {
1438    fn kind(&self) -> std::borrow::Cow<'_, str> {
1439        "insufficient-disk-space".into()
1440    }
1441
1442    fn json(&self) -> serde_json::Value {
1443        serde_json::json!({
1444            "needed": self.needed,
1445            "free": self.free,
1446        })
1447    }
1448
1449    fn as_any(&self) -> &dyn std::any::Any {
1450        self
1451    }
1452}
1453
1454inventory::submit! {
1455    crate::ProblemKindInfo {
1456        kind: "insufficient-disk-space",
1457        detail_fields: &["needed", "free"],
1458    }
1459}
1460
1461impl std::fmt::Display for InsufficientDiskSpace {
1462    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1463        write!(
1464            f,
1465            "Insufficient disk space for build. Need: {} KiB, free: {} KiB",
1466            self.needed, self.free
1467        )
1468    }
1469}
1470
1471#[cfg(test)]
1472mod tests {
1473    use super::*;
1474    use crate::Problem;
1475
1476    #[test]
1477    fn test_dpkg_source_local_changes_trait() {
1478        let problem = DpkgSourceLocalChanges {
1479            diff_file: Some("/tmp/diff.patch".to_string()),
1480            files: Some(vec!["file1.txt".to_string(), "file2.txt".to_string()]),
1481        };
1482        let json = problem.json();
1483        assert_eq!(json["diff_file"], "/tmp/diff.patch");
1484        assert_eq!(json["files"], serde_json::json!(["file1.txt", "file2.txt"]));
1485    }
1486
1487    #[test]
1488    fn test_uscan_too_many_requests_trait() {
1489        let problem = UScanTooManyRequests("rate limit exceeded".to_string());
1490        let json = problem.json();
1491        assert_eq!(json["reason"], "rate limit exceeded");
1492    }
1493}