Skip to main content

buildlog_consultant/problems/
common.rs

1use crate::Problem;
2use pep508_rs::pep440_rs;
3use serde::{Deserialize, Serialize};
4use std::borrow::Cow;
5use std::fmt::{self, Debug, Display};
6use std::path::PathBuf;
7
8/// Problem representing a file that was expected but not found.
9///
10/// This struct is used to report situations where a required file is missing,
11/// which may cause build or execution failures.
12#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
13pub struct MissingFile {
14    /// The path to the missing file.
15    pub path: PathBuf,
16}
17
18impl MissingFile {
19    /// Creates a new MissingFile instance.
20    ///
21    /// # Arguments
22    /// * `path` - Path to the missing file
23    pub fn new(path: PathBuf) -> Self {
24        Self { path }
25    }
26}
27
28impl Problem for MissingFile {
29    fn kind(&self) -> Cow<'_, str> {
30        "missing-file".into()
31    }
32
33    fn json(&self) -> serde_json::Value {
34        serde_json::json!({
35            "path": self.path.to_string_lossy(),
36        })
37    }
38
39    fn as_any(&self) -> &dyn std::any::Any {
40        self
41    }
42}
43
44inventory::submit! {
45    crate::ProblemKindInfo {
46        kind: "missing-file",
47        detail_fields: &["path"],
48    }
49}
50
51crate::register_problem_de_fn!("missing-file", |v| {
52    let path: String = v
53        .get("path")
54        .and_then(|p| p.as_str())
55        .ok_or_else(|| serde::de::Error::missing_field("path"))?
56        .to_string();
57    Ok(Box::new(MissingFile { path: path.into() }) as Box<dyn crate::Problem>)
58});
59
60impl Display for MissingFile {
61    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
62        write!(f, "Missing file: {}", self.path.display())
63    }
64}
65
66/// Problem representing a missing build system file.
67///
68/// This struct is used to report when a file required by the build system
69/// (such as a Makefile, CMakeLists.txt, etc.) is missing.
70#[derive(Clone, Debug, PartialEq, Eq, Serialize)]
71pub struct MissingBuildFile {
72    /// The name of the missing build file.
73    pub filename: String,
74}
75
76impl MissingBuildFile {
77    /// Creates a new MissingBuildFile instance.
78    ///
79    /// # Arguments
80    /// * `filename` - Name of the missing build file
81    pub fn new(filename: String) -> Self {
82        Self { filename }
83    }
84}
85
86impl Problem for MissingBuildFile {
87    fn kind(&self) -> Cow<'_, str> {
88        "missing-build-file".into()
89    }
90
91    fn json(&self) -> serde_json::Value {
92        serde_json::json!({
93            "filename": self.filename,
94        })
95    }
96
97    fn as_any(&self) -> &dyn std::any::Any {
98        self
99    }
100}
101
102inventory::submit! {
103    crate::ProblemKindInfo {
104        kind: "missing-build-file",
105        detail_fields: &["filename"],
106    }
107}
108
109impl Display for MissingBuildFile {
110    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
111        write!(f, "Missing build file: {}", self.filename)
112    }
113}
114
115/// Problem representing something that could be either a missing command or build file.
116///
117/// This struct is used when it's not clear whether a missing entity is a
118/// command (executable) or a build file.
119#[derive(Clone, Debug, PartialEq, Eq)]
120pub struct MissingCommandOrBuildFile {
121    /// The name of the missing command or build file.
122    pub filename: String,
123}
124
125impl Problem for MissingCommandOrBuildFile {
126    fn kind(&self) -> Cow<'_, str> {
127        "missing-command-or-build-file".into()
128    }
129
130    fn json(&self) -> serde_json::Value {
131        serde_json::json!({
132            "filename": self.filename,
133        })
134    }
135
136    fn as_any(&self) -> &dyn std::any::Any {
137        self
138    }
139}
140
141inventory::submit! {
142    crate::ProblemKindInfo {
143        kind: "missing-command-or-build-file",
144        detail_fields: &["filename"],
145    }
146}
147
148impl Display for MissingCommandOrBuildFile {
149    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
150        write!(f, "Missing command or build file: {}", self.filename)
151    }
152}
153
154impl MissingCommandOrBuildFile {
155    /// Returns the command name, which is the same as the filename.
156    ///
157    /// # Returns
158    /// The filename/command name as a String
159    pub fn command(&self) -> String {
160        self.filename.clone()
161    }
162}
163
164/// Problem representing a need for a version control system directory.
165///
166/// This struct is used when a build process requires a version control
167/// system directory (like .git, .bzr, .svn) to be present.
168#[derive(Clone, Debug, PartialEq, Eq)]
169pub struct VcsControlDirectoryNeeded {
170    /// List of version control systems that could provide the needed directory.
171    pub vcs: Vec<String>,
172}
173
174impl Problem for VcsControlDirectoryNeeded {
175    fn kind(&self) -> Cow<'_, str> {
176        "vcs-control-directory-needed".into()
177    }
178
179    fn json(&self) -> serde_json::Value {
180        serde_json::json!({
181            "vcs": self.vcs,
182        })
183    }
184
185    fn as_any(&self) -> &dyn std::any::Any {
186        self
187    }
188}
189
190inventory::submit! {
191    crate::ProblemKindInfo {
192        kind: "vcs-control-directory-needed",
193        detail_fields: &["vcs"],
194    }
195}
196
197crate::register_problem_de_fn!("vcs-control-directory-needed", |v| {
198    let vcs: Vec<String> = v
199        .get("vcs")
200        .and_then(|x| x.as_array())
201        .map(|arr| {
202            arr.iter()
203                .filter_map(|s| s.as_str().map(String::from))
204                .collect()
205        })
206        .ok_or_else(|| serde::de::Error::missing_field("vcs"))?;
207    Ok(Box::new(VcsControlDirectoryNeeded { vcs }) as Box<dyn crate::Problem>)
208});
209
210impl VcsControlDirectoryNeeded {
211    /// Creates a new VcsControlDirectoryNeeded instance.
212    ///
213    /// # Arguments
214    /// * `vcs` - List of version control system names
215    pub fn new(vcs: Vec<&str>) -> Self {
216        Self {
217            vcs: vcs.iter().map(|s| s.to_string()).collect(),
218        }
219    }
220}
221
222/// Problem representing a missing Python module.
223///
224/// This struct is used when a required Python module is not available,
225/// which may include version constraints.
226#[derive(Debug, Clone)]
227pub struct MissingPythonModule {
228    /// The name of the missing Python module.
229    pub module: String,
230    /// The Python major version (e.g., 2 or 3) if specific.
231    pub python_version: Option<i32>,
232    /// The minimum required version of the module if specified.
233    pub minimum_version: Option<String>,
234}
235
236impl Display for MissingPythonModule {
237    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
238        if let Some(python_version) = self.python_version {
239            write!(
240                f,
241                "Missing {} Python module: {}",
242                python_version, self.module
243            )?;
244        } else {
245            write!(f, "Missing Python module: {}", self.module)?;
246        }
247        if let Some(minimum_version) = &self.minimum_version {
248            write!(f, " (>= {})", minimum_version)?;
249        }
250        Ok(())
251    }
252}
253
254impl MissingPythonModule {
255    /// Creates a simple MissingPythonModule instance without version constraints.
256    ///
257    /// # Arguments
258    /// * `module` - Name of the missing Python module
259    ///
260    /// # Returns
261    /// A new MissingPythonModule with no version requirements
262    pub fn simple(module: String) -> MissingPythonModule {
263        MissingPythonModule {
264            module,
265            python_version: None,
266            minimum_version: None,
267        }
268    }
269}
270
271impl Problem for MissingPythonModule {
272    fn kind(&self) -> Cow<'_, str> {
273        "missing-python-module".into()
274    }
275
276    fn json(&self) -> serde_json::Value {
277        serde_json::json!({
278            "module": self.module,
279            "python_version": self.python_version,
280            "minimum_version": self.minimum_version,
281        })
282    }
283
284    fn as_any(&self) -> &dyn std::any::Any {
285        self
286    }
287}
288
289inventory::submit! {
290    crate::ProblemKindInfo {
291        kind: "missing-python-module",
292        detail_fields: &["module", "python_version", "minimum_version"],
293    }
294}
295
296crate::register_problem_de_fn!("missing-python-module", |v| {
297    let module = v
298        .get("module")
299        .and_then(|m| m.as_str())
300        .ok_or_else(|| serde::de::Error::missing_field("module"))?
301        .to_string();
302    let python_version = v
303        .get("python_version")
304        .and_then(|x| x.as_i64())
305        .map(|n| n as i32);
306    let minimum_version = v
307        .get("minimum_version")
308        .and_then(|x| x.as_str())
309        .map(String::from);
310    Ok(Box::new(MissingPythonModule {
311        module,
312        python_version,
313        minimum_version,
314    }) as Box<dyn crate::Problem>)
315});
316
317/// Problem representing a missing command-line executable.
318///
319/// This struct is used when a required command is not available in the PATH.
320#[derive(Debug, Clone)]
321pub struct MissingCommand(pub String);
322
323impl Display for MissingCommand {
324    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
325        write!(f, "Missing command: {}", self.0)
326    }
327}
328
329impl Problem for MissingCommand {
330    fn kind(&self) -> Cow<'_, str> {
331        "command-missing".into()
332    }
333
334    fn json(&self) -> serde_json::Value {
335        serde_json::json!({
336            "command": self.0,
337        })
338    }
339
340    fn as_any(&self) -> &dyn std::any::Any {
341        self
342    }
343}
344
345inventory::submit! {
346    crate::ProblemKindInfo {
347        kind: "command-missing",
348        detail_fields: &["command"],
349    }
350}
351
352crate::register_problem_de_fn!("command-missing", |v| {
353    let cmd = v
354        .get("command")
355        .and_then(|c| c.as_str())
356        .ok_or_else(|| serde::de::Error::missing_field("command"))?
357        .to_string();
358    Ok(Box::new(MissingCommand(cmd)) as Box<dyn crate::Problem>)
359});
360
361/// Problem representing a missing Python package distribution.
362///
363/// This struct is used when a required Python package is not available,
364/// which may include version constraints.
365#[derive(Debug, Clone)]
366pub struct MissingPythonDistribution {
367    /// The name of the missing Python distribution.
368    pub distribution: String,
369    /// The Python major version (e.g., 2 or 3) if specific.
370    pub python_version: Option<i32>,
371    /// The minimum required version of the distribution if specified.
372    pub minimum_version: Option<String>,
373}
374
375impl Display for MissingPythonDistribution {
376    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
377        if let Some(python_version) = self.python_version {
378            write!(
379                f,
380                "Missing {} Python distribution: {}",
381                python_version, self.distribution
382            )?;
383        } else {
384            write!(f, "Missing Python distribution: {}", self.distribution)?;
385        }
386        if let Some(minimum_version) = &self.minimum_version {
387            write!(f, " (>= {})", minimum_version)?;
388        }
389        Ok(())
390    }
391}
392
393impl Problem for MissingPythonDistribution {
394    fn kind(&self) -> Cow<'_, str> {
395        "missing-python-distribution".into()
396    }
397
398    fn json(&self) -> serde_json::Value {
399        serde_json::json!({
400            "distribution": self.distribution,
401            "python_version": self.python_version,
402            "minimum_version": self.minimum_version,
403        })
404    }
405
406    fn as_any(&self) -> &dyn std::any::Any {
407        self
408    }
409}
410
411inventory::submit! {
412    crate::ProblemKindInfo {
413        kind: "missing-python-distribution",
414        detail_fields: &["distribution", "python_version", "minimum_version"],
415    }
416}
417
418crate::register_problem_de_fn!("missing-python-distribution", |v| {
419    let distribution = v
420        .get("distribution")
421        .and_then(|m| m.as_str())
422        .ok_or_else(|| serde::de::Error::missing_field("distribution"))?
423        .to_string();
424    let python_version = v
425        .get("python_version")
426        .and_then(|x| x.as_i64())
427        .map(|n| n as i32);
428    let minimum_version = v
429        .get("minimum_version")
430        .and_then(|x| x.as_str())
431        .map(String::from);
432    Ok(Box::new(MissingPythonDistribution {
433        distribution,
434        python_version,
435        minimum_version,
436    }) as Box<dyn crate::Problem>)
437});
438
439fn find_python_version(marker: Vec<Vec<pep508_rs::MarkerExpression>>) -> Option<i32> {
440    let mut major_version = None;
441    for expr in marker.iter().flat_map(|x| x.iter()) {
442        if let pep508_rs::MarkerExpression::Version {
443            key: pep508_rs::MarkerValueVersion::PythonVersion,
444            specifier,
445        } = expr
446        {
447            let version = specifier.version();
448            major_version = Some(version.release()[0] as i32);
449        }
450    }
451
452    major_version
453}
454
455impl MissingPythonDistribution {
456    /// Creates a MissingPythonDistribution from a PEP508 requirement string.
457    ///
458    /// Parses a Python package requirement string (in PEP508 format) to extract
459    /// the package name and version constraints.
460    ///
461    /// # Arguments
462    /// * `text` - The requirement string in PEP508 format
463    /// * `python_version` - Optional Python version to override detected version
464    ///
465    /// # Returns
466    /// A new MissingPythonDistribution instance
467    pub fn from_requirement_str(
468        text: &str,
469        python_version: Option<i32>,
470    ) -> MissingPythonDistribution {
471        use pep440_rs::Operator;
472        use pep508_rs::{Requirement, VersionOrUrl};
473        use std::str::FromStr;
474
475        let depspec: Requirement = Requirement::from_str(text).unwrap();
476
477        let distribution = depspec.name.to_string();
478
479        let python_version =
480            python_version.or_else(|| find_python_version(depspec.marker.to_dnf()));
481        let minimum_version =
482            if let Some(VersionOrUrl::VersionSpecifier(vs)) = depspec.version_or_url {
483                if vs.len() == 1 && *vs[0].operator() == Operator::GreaterThanEqual {
484                    Some(vs[0].version().to_string())
485                } else {
486                    None
487                }
488            } else {
489                None
490            };
491
492        MissingPythonDistribution {
493            distribution,
494            python_version,
495            minimum_version,
496        }
497    }
498
499    /// Creates a simple MissingPythonDistribution without version constraints.
500    ///
501    /// # Arguments
502    /// * `distribution` - Name of the missing Python distribution
503    ///
504    /// # Returns
505    /// A new MissingPythonDistribution with no version requirements
506    pub fn simple(distribution: &str) -> MissingPythonDistribution {
507        MissingPythonDistribution {
508            distribution: distribution.to_string(),
509            python_version: None,
510            minimum_version: None,
511        }
512    }
513}
514
515impl Display for VcsControlDirectoryNeeded {
516    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
517        write!(f, "VCS control directory needed: {}", self.vcs.join(", "))
518    }
519}
520
521/// Problem representing a missing Haskell module.
522///
523/// This struct is used when a required Haskell module is not available.
524#[derive(Debug, Clone)]
525pub struct MissingHaskellModule {
526    /// The name of the missing Haskell module.
527    pub module: String,
528}
529
530impl MissingHaskellModule {
531    /// Creates a new MissingHaskellModule instance.
532    ///
533    /// # Arguments
534    /// * `module` - Name of the missing Haskell module
535    ///
536    /// # Returns
537    /// A new MissingHaskellModule instance
538    pub fn new(module: String) -> MissingHaskellModule {
539        MissingHaskellModule { module }
540    }
541}
542
543impl Display for MissingHaskellModule {
544    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
545        write!(f, "Missing Haskell module: {}", self.module)
546    }
547}
548
549impl Problem for MissingHaskellModule {
550    fn kind(&self) -> Cow<'_, str> {
551        "missing-haskell-module".into()
552    }
553
554    fn json(&self) -> serde_json::Value {
555        serde_json::json!({
556            "module": self.module,
557        })
558    }
559
560    fn as_any(&self) -> &dyn std::any::Any {
561        self
562    }
563}
564
565inventory::submit! {
566    crate::ProblemKindInfo {
567        kind: "missing-haskell-module",
568        detail_fields: &["module"],
569    }
570}
571
572crate::register_problem_de_fn!("missing-haskell-module", |v| {
573    let module = v
574        .get("module")
575        .and_then(|m| m.as_str())
576        .ok_or_else(|| serde::de::Error::missing_field("module"))?
577        .to_string();
578    Ok(Box::new(MissingHaskellModule::new(module)) as Box<dyn crate::Problem>)
579});
580
581/// Problem representing a missing system library.
582///
583/// This struct is used when a required shared library (.so/.dll/.dylib) is not available.
584#[derive(Debug, Clone)]
585pub struct MissingLibrary(pub String);
586
587impl Display for MissingLibrary {
588    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
589        write!(f, "Missing library: {}", self.0)
590    }
591}
592
593impl Problem for MissingLibrary {
594    fn kind(&self) -> Cow<'_, str> {
595        "missing-library".into()
596    }
597
598    fn json(&self) -> serde_json::Value {
599        serde_json::json!({
600            "library": self.0,
601        })
602    }
603
604    fn as_any(&self) -> &dyn std::any::Any {
605        self
606    }
607}
608
609inventory::submit! {
610    crate::ProblemKindInfo {
611        kind: "missing-library",
612        detail_fields: &["library"],
613    }
614}
615
616crate::register_problem_de_fn!("missing-library", |v| {
617    let l = v
618        .get("library")
619        .and_then(|m| m.as_str())
620        .ok_or_else(|| serde::de::Error::missing_field("library"))?
621        .to_string();
622    Ok(Box::new(MissingLibrary(l)) as Box<dyn crate::Problem>)
623});
624
625/// Problem representing a missing GObject Introspection typelib.
626///
627/// This struct is used when a required GObject Introspection typelib file is not available.
628#[derive(Debug, Clone)]
629pub struct MissingIntrospectionTypelib(pub String);
630
631impl Display for MissingIntrospectionTypelib {
632    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
633        write!(f, "Missing introspection typelib: {}", self.0)
634    }
635}
636
637impl Problem for MissingIntrospectionTypelib {
638    fn kind(&self) -> Cow<'_, str> {
639        "missing-introspection-typelib".into()
640    }
641
642    fn json(&self) -> serde_json::Value {
643        serde_json::json!({
644            "library": self.0,
645        })
646    }
647
648    fn as_any(&self) -> &dyn std::any::Any {
649        self
650    }
651}
652
653inventory::submit! {
654    crate::ProblemKindInfo {
655        kind: "missing-introspection-typelib",
656        detail_fields: &["library"],
657    }
658}
659
660/// Problem representing a missing pytest fixture.
661///
662/// This struct is used when a pytest test requires a fixture that is not available.
663#[derive(Debug, Clone)]
664pub struct MissingPytestFixture(pub String);
665
666impl Display for MissingPytestFixture {
667    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
668        write!(f, "Missing pytest fixture: {}", self.0)
669    }
670}
671
672impl Problem for MissingPytestFixture {
673    fn kind(&self) -> Cow<'_, str> {
674        "missing-pytest-fixture".into()
675    }
676
677    fn json(&self) -> serde_json::Value {
678        serde_json::json!({
679            "fixture": self.0,
680        })
681    }
682
683    fn as_any(&self) -> &dyn std::any::Any {
684        self
685    }
686}
687
688inventory::submit! {
689    crate::ProblemKindInfo {
690        kind: "missing-pytest-fixture",
691        detail_fields: &["fixture"],
692    }
693}
694
695/// Problem representing an unsupported pytest configuration option.
696///
697/// This struct is used when a pytest configuration specifies an option
698/// that is not supported in the current environment.
699#[derive(Debug, Clone)]
700pub struct UnsupportedPytestConfigOption(pub String);
701
702impl Display for UnsupportedPytestConfigOption {
703    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
704        write!(f, "Unsupported pytest config option: {}", self.0)
705    }
706}
707
708impl Problem for UnsupportedPytestConfigOption {
709    fn kind(&self) -> Cow<'_, str> {
710        "unsupported-pytest-config-option".into()
711    }
712
713    fn json(&self) -> serde_json::Value {
714        serde_json::json!({
715            "name": self.0,
716        })
717    }
718
719    fn as_any(&self) -> &dyn std::any::Any {
720        self
721    }
722}
723
724inventory::submit! {
725    crate::ProblemKindInfo {
726        kind: "unsupported-pytest-config-option",
727        detail_fields: &["name"],
728    }
729}
730
731/// Problem representing unsupported pytest command-line arguments.
732///
733/// This struct is used when pytest is invoked with command-line arguments
734/// that are not supported in the current environment.
735#[derive(Debug, Clone)]
736pub struct UnsupportedPytestArguments(pub Vec<String>);
737
738impl Display for UnsupportedPytestArguments {
739    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
740        write!(f, "Unsupported pytest arguments: {:?}", self.0)
741    }
742}
743
744impl Problem for UnsupportedPytestArguments {
745    fn kind(&self) -> Cow<'_, str> {
746        "unsupported-pytest-arguments".into()
747    }
748
749    fn json(&self) -> serde_json::Value {
750        serde_json::json!({
751            "args": self.0,
752        })
753    }
754
755    fn as_any(&self) -> &dyn std::any::Any {
756        self
757    }
758}
759
760inventory::submit! {
761    crate::ProblemKindInfo {
762        kind: "unsupported-pytest-arguments",
763        detail_fields: &["args"],
764    }
765}
766
767/// Problem representing a missing R package.
768///
769/// This struct is used when a required R package is not installed
770/// or not available in the environment.
771#[derive(Debug, Clone)]
772pub struct MissingRPackage {
773    /// The name of the missing R package.
774    pub package: String,
775    /// The minimum required version of the package, if specified.
776    pub minimum_version: Option<String>,
777}
778
779impl MissingRPackage {
780    /// Creates a simple MissingRPackage instance without version constraints.
781    ///
782    /// # Arguments
783    /// * `package` - Name of the missing R package
784    ///
785    /// # Returns
786    /// A new MissingRPackage with no version requirements
787    pub fn simple(package: &str) -> Self {
788        Self {
789            package: package.to_string(),
790            minimum_version: None,
791        }
792    }
793}
794
795impl Display for MissingRPackage {
796    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
797        write!(f, "Missing R package: {}", self.package)?;
798        if let Some(minimum_version) = &self.minimum_version {
799            write!(f, " (>= {})", minimum_version)?;
800        }
801        Ok(())
802    }
803}
804
805impl Problem for MissingRPackage {
806    fn kind(&self) -> Cow<'_, str> {
807        "missing-r-package".into()
808    }
809
810    fn json(&self) -> serde_json::Value {
811        serde_json::json!({
812            "package": self.package,
813            "minimum_version": self.minimum_version,
814        })
815    }
816
817    fn as_any(&self) -> &dyn std::any::Any {
818        self
819    }
820}
821
822inventory::submit! {
823    crate::ProblemKindInfo {
824        kind: "missing-r-package",
825        detail_fields: &["package", "minimum_version"],
826    }
827}
828
829/// Problem representing a missing Go package.
830///
831/// This struct is used when a required Go package is not installed
832/// or not available in the environment.
833#[derive(Debug, Clone)]
834pub struct MissingGoPackage {
835    /// The import path of the missing Go package.
836    pub package: String,
837}
838
839impl Problem for MissingGoPackage {
840    fn kind(&self) -> Cow<'_, str> {
841        "missing-go-package".into()
842    }
843
844    fn json(&self) -> serde_json::Value {
845        serde_json::json!({
846            "package": self.package,
847        })
848    }
849
850    fn as_any(&self) -> &dyn std::any::Any {
851        self
852    }
853}
854
855inventory::submit! {
856    crate::ProblemKindInfo {
857        kind: "missing-go-package",
858        detail_fields: &["package"],
859    }
860}
861
862crate::register_problem_de_fn!("missing-go-package", |v| {
863    let package = v
864        .get("package")
865        .and_then(|m| m.as_str())
866        .ok_or_else(|| serde::de::Error::missing_field("package"))?
867        .to_string();
868    Ok(Box::new(MissingGoPackage { package }) as Box<dyn crate::Problem>)
869});
870
871impl Display for MissingGoPackage {
872    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
873        write!(f, "Missing Go package: {}", self.package)
874    }
875}
876
877/// Problem representing a missing C header file.
878///
879/// This struct is used when a required C header file (.h) is not available
880/// during compilation.
881#[derive(Debug, Clone)]
882pub struct MissingCHeader {
883    /// The name of the missing C header file.
884    pub header: String,
885}
886
887impl Problem for MissingCHeader {
888    fn kind(&self) -> Cow<'_, str> {
889        "missing-c-header".into()
890    }
891
892    fn json(&self) -> serde_json::Value {
893        serde_json::json!({
894            "header": self.header,
895        })
896    }
897
898    fn as_any(&self) -> &dyn std::any::Any {
899        self
900    }
901}
902
903inventory::submit! {
904    crate::ProblemKindInfo {
905        kind: "missing-c-header",
906        detail_fields: &["header"],
907    }
908}
909
910crate::register_problem_de_fn!("missing-c-header", |v| {
911    let header = v
912        .get("header")
913        .and_then(|m| m.as_str())
914        .ok_or_else(|| serde::de::Error::missing_field("header"))?
915        .to_string();
916    Ok(Box::new(MissingCHeader::new(header)) as Box<dyn crate::Problem>)
917});
918
919impl Display for MissingCHeader {
920    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
921        write!(f, "Missing C header: {}", self.header)
922    }
923}
924
925impl MissingCHeader {
926    /// Creates a new MissingCHeader instance.
927    ///
928    /// # Arguments
929    /// * `header` - Name of the missing C header file
930    ///
931    /// # Returns
932    /// A new MissingCHeader instance
933    pub fn new(header: String) -> Self {
934        Self { header }
935    }
936}
937
938/// Problem representing a missing Node.js module.
939///
940/// This struct is used when a required Node.js module is not installed
941/// or cannot be imported.
942#[derive(Debug, Clone)]
943pub struct MissingNodeModule(pub String);
944
945impl Problem for MissingNodeModule {
946    fn kind(&self) -> Cow<'_, str> {
947        "missing-node-module".into()
948    }
949
950    fn json(&self) -> serde_json::Value {
951        serde_json::json!({
952            "module": self.0,
953        })
954    }
955
956    fn as_any(&self) -> &dyn std::any::Any {
957        self
958    }
959}
960
961inventory::submit! {
962    crate::ProblemKindInfo {
963        kind: "missing-node-module",
964        detail_fields: &["module"],
965    }
966}
967
968crate::register_problem_de_fn!("missing-node-module", |v| {
969    let m = v
970        .get("module")
971        .and_then(|m| m.as_str())
972        .ok_or_else(|| serde::de::Error::missing_field("module"))?
973        .to_string();
974    Ok(Box::new(MissingNodeModule(m)) as Box<dyn crate::Problem>)
975});
976
977impl Display for MissingNodeModule {
978    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
979        write!(f, "Missing Node module: {}", self.0)
980    }
981}
982
983/// Problem representing a missing Node.js package.
984///
985/// This struct is used when a required Node.js package is not installed
986/// via npm/yarn/pnpm or cannot be found in node_modules.
987#[derive(Debug, Clone)]
988pub struct MissingNodePackage(pub String);
989
990impl Problem for MissingNodePackage {
991    fn kind(&self) -> Cow<'_, str> {
992        "missing-node-package".into()
993    }
994
995    fn json(&self) -> serde_json::Value {
996        serde_json::json!({
997            "package": self.0,
998        })
999    }
1000
1001    fn as_any(&self) -> &dyn std::any::Any {
1002        self
1003    }
1004}
1005
1006inventory::submit! {
1007    crate::ProblemKindInfo {
1008        kind: "missing-node-package",
1009        detail_fields: &["package"],
1010    }
1011}
1012
1013crate::register_problem_de_fn!("missing-node-package", |v| {
1014    let p = v
1015        .get("package")
1016        .and_then(|m| m.as_str())
1017        .ok_or_else(|| serde::de::Error::missing_field("package"))?
1018        .to_string();
1019    Ok(Box::new(MissingNodePackage(p)) as Box<dyn crate::Problem>)
1020});
1021
1022impl Display for MissingNodePackage {
1023    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
1024        write!(f, "Missing Node package: {}", self.0)
1025    }
1026}
1027
1028/// Problem representing a missing configure script.
1029///
1030/// This struct is used when a build expects to find a configure script
1031/// (typically from autotools) but it doesn't exist.
1032#[derive(Debug, Clone)]
1033pub struct MissingConfigure;
1034
1035impl Problem for MissingConfigure {
1036    fn kind(&self) -> Cow<'_, str> {
1037        "missing-configure".into()
1038    }
1039
1040    fn json(&self) -> serde_json::Value {
1041        serde_json::json!({})
1042    }
1043
1044    fn as_any(&self) -> &dyn std::any::Any {
1045        self
1046    }
1047}
1048
1049inventory::submit! {
1050    crate::ProblemKindInfo {
1051        kind: "missing-configure",
1052        detail_fields: &[],
1053    }
1054}
1055
1056impl Display for MissingConfigure {
1057    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
1058        write!(f, "Missing ./configure")
1059    }
1060}
1061
1062/// Problem representing a vague or unspecified dependency.
1063///
1064/// This struct is used when a build requires a dependency that
1065/// cannot be clearly categorized as a specific type of dependency.
1066#[derive(Debug, Clone)]
1067pub struct MissingVagueDependency {
1068    /// The name of the missing dependency.
1069    pub name: String,
1070    /// An optional URL where the dependency might be found.
1071    pub url: Option<String>,
1072    /// The minimum required version of the dependency, if specified.
1073    pub minimum_version: Option<String>,
1074    /// The current version of the dependency, if known.
1075    pub current_version: Option<String>,
1076}
1077
1078impl MissingVagueDependency {
1079    /// Creates a simple MissingVagueDependency instance with just a name.
1080    ///
1081    /// # Arguments
1082    /// * `name` - Name of the missing dependency
1083    ///
1084    /// # Returns
1085    /// A new MissingVagueDependency with no additional information
1086    pub fn simple(name: &str) -> Self {
1087        Self {
1088            name: name.to_string(),
1089            url: None,
1090            minimum_version: None,
1091            current_version: None,
1092        }
1093    }
1094}
1095
1096impl Problem for MissingVagueDependency {
1097    fn kind(&self) -> Cow<'_, str> {
1098        "missing-vague-dependency".into()
1099    }
1100
1101    fn json(&self) -> serde_json::Value {
1102        serde_json::json!({
1103            "name": self.name,
1104            "url": self.url,
1105            "minimum_version": self.minimum_version,
1106            "current_version": self.current_version,
1107        })
1108    }
1109
1110    fn as_any(&self) -> &dyn std::any::Any {
1111        self
1112    }
1113}
1114
1115inventory::submit! {
1116    crate::ProblemKindInfo {
1117        kind: "missing-vague-dependency",
1118        detail_fields: &["name", "url", "minimum_version", "current_version"],
1119    }
1120}
1121
1122impl Display for MissingVagueDependency {
1123    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
1124        write!(f, "Missing dependency: {}", self.name)
1125    }
1126}
1127
1128/// Problem representing missing Qt framework.
1129///
1130/// This struct is used when a build requires the Qt framework
1131/// but it is not installed or cannot be found.
1132#[derive(Debug, Clone)]
1133pub struct MissingQt;
1134
1135impl Problem for MissingQt {
1136    fn kind(&self) -> Cow<'_, str> {
1137        "missing-qt".into()
1138    }
1139
1140    fn json(&self) -> serde_json::Value {
1141        serde_json::json!({})
1142    }
1143
1144    fn as_any(&self) -> &dyn std::any::Any {
1145        self
1146    }
1147}
1148
1149inventory::submit! {
1150    crate::ProblemKindInfo {
1151        kind: "missing-qt",
1152        detail_fields: &[],
1153    }
1154}
1155
1156impl Display for MissingQt {
1157    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
1158        write!(f, "Missing Qt")
1159    }
1160}
1161
1162/// Problem representing missing X11 libraries or headers.
1163///
1164/// This struct is used when a build requires X11 (X Window System)
1165/// components but they are not installed or cannot be found.
1166#[derive(Debug, Clone)]
1167pub struct MissingX11;
1168
1169impl Problem for MissingX11 {
1170    fn kind(&self) -> Cow<'_, str> {
1171        "missing-x11".into()
1172    }
1173
1174    fn json(&self) -> serde_json::Value {
1175        serde_json::json!({})
1176    }
1177
1178    fn as_any(&self) -> &dyn std::any::Any {
1179        self
1180    }
1181}
1182
1183inventory::submit! {
1184    crate::ProblemKindInfo {
1185        kind: "missing-x11",
1186        detail_fields: &[],
1187    }
1188}
1189
1190impl Display for MissingX11 {
1191    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
1192        write!(f, "Missing X11")
1193    }
1194}
1195
1196/// Problem representing a missing autoconf macro.
1197///
1198/// This struct is used when a build using autoconf requires a macro
1199/// that is not available in the build environment.
1200#[derive(Debug, Clone)]
1201pub struct MissingAutoconfMacro {
1202    /// The name of the missing autoconf macro.
1203    pub r#macro: String,
1204    /// Whether the build system needs to be rebuilt after adding the macro.
1205    pub need_rebuild: bool,
1206}
1207
1208impl MissingAutoconfMacro {
1209    /// Creates a new MissingAutoconfMacro instance.
1210    ///
1211    /// # Arguments
1212    /// * `macro` - Name of the missing autoconf macro
1213    ///
1214    /// # Returns
1215    /// A new MissingAutoconfMacro instance with need_rebuild set to false
1216    pub fn new(r#macro: String) -> Self {
1217        Self {
1218            r#macro,
1219            need_rebuild: false,
1220        }
1221    }
1222}
1223
1224impl Problem for MissingAutoconfMacro {
1225    fn kind(&self) -> Cow<'_, str> {
1226        "missing-autoconf-macro".into()
1227    }
1228
1229    fn json(&self) -> serde_json::Value {
1230        serde_json::json!({
1231            "macro": self.r#macro,
1232            "need_rebuild": self.need_rebuild,
1233        })
1234    }
1235
1236    fn as_any(&self) -> &dyn std::any::Any {
1237        self
1238    }
1239}
1240
1241inventory::submit! {
1242    crate::ProblemKindInfo {
1243        kind: "missing-autoconf-macro",
1244        detail_fields: &["macro", "need_rebuild"],
1245    }
1246}
1247
1248crate::register_problem_de_fn!("missing-autoconf-macro", |v| {
1249    let m = v
1250        .get("macro")
1251        .and_then(|m| m.as_str())
1252        .ok_or_else(|| serde::de::Error::missing_field("macro"))?
1253        .to_string();
1254    let need_rebuild = v
1255        .get("need_rebuild")
1256        .and_then(|x| x.as_bool())
1257        .unwrap_or(false);
1258    let mut p = MissingAutoconfMacro::new(m);
1259    p.need_rebuild = need_rebuild;
1260    Ok(Box::new(p) as Box<dyn crate::Problem>)
1261});
1262
1263impl Display for MissingAutoconfMacro {
1264    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
1265        write!(f, "Missing autoconf macro: {}", self.r#macro)
1266    }
1267}
1268
1269/// Problem representing a directory that does not exist.
1270///
1271/// This struct is used when a build process expects a directory to exist
1272/// but it cannot be found in the filesystem.
1273#[derive(Debug, Clone)]
1274pub struct DirectoryNonExistant(pub String);
1275
1276impl Problem for DirectoryNonExistant {
1277    fn kind(&self) -> Cow<'_, str> {
1278        "local-directory-not-existing".into()
1279    }
1280
1281    fn json(&self) -> serde_json::Value {
1282        serde_json::json!({
1283            "path": self.0,
1284        })
1285    }
1286
1287    fn as_any(&self) -> &dyn std::any::Any {
1288        self
1289    }
1290}
1291
1292inventory::submit! {
1293    crate::ProblemKindInfo {
1294        kind: "local-directory-not-existing",
1295        detail_fields: &["path"],
1296    }
1297}
1298
1299impl Display for DirectoryNonExistant {
1300    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
1301        write!(f, "Directory does not exist: {}", self.0)
1302    }
1303}
1304
1305/// Problem representing a missing Vala package.
1306///
1307/// This struct is used when a build requires a Vala package
1308/// that is not installed or cannot be found.
1309#[derive(Debug, Clone)]
1310pub struct MissingValaPackage(pub String);
1311
1312impl Problem for MissingValaPackage {
1313    fn kind(&self) -> Cow<'_, str> {
1314        "missing-vala-package".into()
1315    }
1316
1317    fn json(&self) -> serde_json::Value {
1318        serde_json::json!({
1319            "package": self.0,
1320        })
1321    }
1322
1323    fn as_any(&self) -> &dyn std::any::Any {
1324        self
1325    }
1326}
1327
1328inventory::submit! {
1329    crate::ProblemKindInfo {
1330        kind: "missing-vala-package",
1331        detail_fields: &["package"],
1332    }
1333}
1334
1335crate::register_problem_de_fn!("missing-vala-package", |v| {
1336    let p = v
1337        .get("package")
1338        .and_then(|m| m.as_str())
1339        .ok_or_else(|| serde::de::Error::missing_field("package"))?
1340        .to_string();
1341    Ok(Box::new(MissingValaPackage(p)) as Box<dyn crate::Problem>)
1342});
1343
1344impl Display for MissingValaPackage {
1345    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
1346        write!(f, "Missing Vala package: {}", self.0)
1347    }
1348}
1349
1350/// Problem representing the presence of an upstart configuration file.
1351///
1352/// This struct is used to indicate that a package includes an upstart file,
1353/// which may be problematic in environments that have moved to systemd.
1354#[derive(Debug, Clone)]
1355pub struct UpstartFilePresent(pub String);
1356
1357impl Problem for UpstartFilePresent {
1358    fn kind(&self) -> Cow<'_, str> {
1359        "upstart-file-present".into()
1360    }
1361
1362    fn json(&self) -> serde_json::Value {
1363        serde_json::json!({
1364            "filename": self.0,
1365        })
1366    }
1367
1368    fn as_any(&self) -> &dyn std::any::Any {
1369        self
1370    }
1371}
1372
1373inventory::submit! {
1374    crate::ProblemKindInfo {
1375        kind: "upstart-file-present",
1376        detail_fields: &["filename"],
1377    }
1378}
1379
1380impl Display for UpstartFilePresent {
1381    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
1382        write!(f, "Upstart file present: {}", self.0)
1383    }
1384}
1385
1386/// Problem representing a missing PostgreSQL extension.
1387///
1388/// This struct is used when a build or runtime requires a PostgreSQL extension
1389/// that is not installed or cannot be found.
1390#[derive(Debug, Clone)]
1391pub struct MissingPostgresExtension(pub String);
1392
1393impl Problem for MissingPostgresExtension {
1394    fn kind(&self) -> Cow<'_, str> {
1395        "missing-postgresql-extension".into()
1396    }
1397
1398    fn json(&self) -> serde_json::Value {
1399        serde_json::json!({
1400            "extension": self.0,
1401        })
1402    }
1403
1404    fn as_any(&self) -> &dyn std::any::Any {
1405        self
1406    }
1407}
1408
1409inventory::submit! {
1410    crate::ProblemKindInfo {
1411        kind: "missing-postgresql-extension",
1412        detail_fields: &["extension"],
1413    }
1414}
1415
1416impl Display for MissingPostgresExtension {
1417    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
1418        write!(f, "Missing PostgreSQL extension: {}", self.0)
1419    }
1420}
1421
1422/// Problem representing a missing pkg-config module.
1423///
1424/// This struct is used when a build requires a package found via pkg-config
1425/// that is not installed or cannot be found.
1426#[derive(Debug, Clone)]
1427pub struct MissingPkgConfig {
1428    /// The name of the missing pkg-config module.
1429    pub module: String,
1430    /// The minimum required version of the module, if specified.
1431    pub minimum_version: Option<String>,
1432}
1433
1434impl Problem for MissingPkgConfig {
1435    fn kind(&self) -> Cow<'_, str> {
1436        "missing-pkg-config-package".into()
1437    }
1438
1439    fn json(&self) -> serde_json::Value {
1440        serde_json::json!({
1441            "module": self.module,
1442            "minimum_version": self.minimum_version,
1443        })
1444    }
1445
1446    fn as_any(&self) -> &dyn std::any::Any {
1447        self
1448    }
1449}
1450
1451inventory::submit! {
1452    crate::ProblemKindInfo {
1453        kind: "missing-pkg-config-package",
1454        detail_fields: &["module", "minimum_version"],
1455    }
1456}
1457
1458crate::register_problem_de_fn!("missing-pkg-config-package", |v| {
1459    let module = v
1460        .get("module")
1461        .and_then(|m| m.as_str())
1462        .ok_or_else(|| serde::de::Error::missing_field("module"))?
1463        .to_string();
1464    let minimum_version = v
1465        .get("minimum_version")
1466        .and_then(|x| x.as_str())
1467        .map(String::from);
1468    Ok(Box::new(MissingPkgConfig {
1469        module,
1470        minimum_version,
1471    }) as Box<dyn crate::Problem>)
1472});
1473
1474impl Display for MissingPkgConfig {
1475    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
1476        if let Some(minimum_version) = &self.minimum_version {
1477            write!(
1478                f,
1479                "Missing pkg-config module: {} >= {}",
1480                self.module, minimum_version
1481            )
1482        } else {
1483            write!(f, "Missing pkg-config module: {}", self.module)
1484        }
1485    }
1486}
1487
1488impl MissingPkgConfig {
1489    /// Creates a new MissingPkgConfig instance with optional version constraint.
1490    ///
1491    /// # Arguments
1492    /// * `module` - Name of the missing pkg-config module
1493    /// * `minimum_version` - Optional minimum version requirement
1494    ///
1495    /// # Returns
1496    /// A new MissingPkgConfig instance
1497    pub fn new(module: String, minimum_version: Option<String>) -> Self {
1498        Self {
1499            module,
1500            minimum_version,
1501        }
1502    }
1503
1504    /// Creates a simple MissingPkgConfig instance without version constraint.
1505    ///
1506    /// # Arguments
1507    /// * `module` - Name of the missing pkg-config module
1508    ///
1509    /// # Returns
1510    /// A new MissingPkgConfig with no version requirements
1511    pub fn simple(module: String) -> Self {
1512        Self {
1513            module,
1514            minimum_version: None,
1515        }
1516    }
1517}
1518
1519/// Problem representing multiple missing Haskell dependencies.
1520///
1521/// This struct is used when a build requires multiple Haskell packages
1522/// that are not installed or cannot be found.
1523#[derive(Debug, Clone)]
1524pub struct MissingHaskellDependencies(pub Vec<String>);
1525
1526impl Problem for MissingHaskellDependencies {
1527    fn kind(&self) -> Cow<'_, str> {
1528        "missing-haskell-dependencies".into()
1529    }
1530
1531    fn json(&self) -> serde_json::Value {
1532        serde_json::json!({
1533            "deps": self.0,
1534        })
1535    }
1536
1537    fn as_any(&self) -> &dyn std::any::Any {
1538        self
1539    }
1540}
1541
1542inventory::submit! {
1543    crate::ProblemKindInfo {
1544        kind: "missing-haskell-dependencies",
1545        detail_fields: &["deps"],
1546    }
1547}
1548
1549crate::register_problem_de_fn!("missing-haskell-dependencies", |v| {
1550    let deps: Vec<String> = v
1551        .as_array()
1552        .map(|arr| {
1553            arr.iter()
1554                .filter_map(|s| s.as_str().map(String::from))
1555                .collect()
1556        })
1557        .or_else(|| {
1558            // Some json() impls wrap in {"deps": [...]}. Accept that
1559            // shape too so we round-trip whichever flavour the
1560            // upstream emitted.
1561            v.get("deps").and_then(|d| d.as_array()).map(|arr| {
1562                arr.iter()
1563                    .filter_map(|s| s.as_str().map(String::from))
1564                    .collect()
1565            })
1566        })
1567        .ok_or_else(|| serde::de::Error::missing_field("deps"))?;
1568    Ok(Box::new(MissingHaskellDependencies(deps)) as Box<dyn crate::Problem>)
1569});
1570
1571impl Display for MissingHaskellDependencies {
1572    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
1573        write!(f, "Missing Haskell dependencies: {:?}", self.0)
1574    }
1575}
1576
1577/// Problem representing lack of disk space.
1578///
1579/// This struct is used when a build fails because there is no space
1580/// left on the device/filesystem where the build is running.
1581#[derive(Debug, Clone)]
1582pub struct NoSpaceOnDevice;
1583
1584impl Problem for NoSpaceOnDevice {
1585    fn kind(&self) -> Cow<'_, str> {
1586        "no-space-on-device".into()
1587    }
1588
1589    fn json(&self) -> serde_json::Value {
1590        serde_json::json!({})
1591    }
1592
1593    fn as_any(&self) -> &dyn std::any::Any {
1594        self
1595    }
1596
1597    /// Indicates that this problem is universal across all build steps.
1598    ///
1599    /// No space on device is considered a universal problem because it can
1600    /// affect any stage of the build process and is not specific to particular
1601    /// build steps.
1602    fn is_universal(&self) -> bool {
1603        true
1604    }
1605}
1606
1607inventory::submit! {
1608    crate::ProblemKindInfo {
1609        kind: "no-space-on-device",
1610        detail_fields: &[],
1611    }
1612}
1613
1614impl Display for NoSpaceOnDevice {
1615    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
1616        write!(f, "No space left on device")
1617    }
1618}
1619
1620/// Problem representing a missing Java Runtime Environment.
1621///
1622/// This struct is used when a build or runtime requires a Java Runtime
1623/// Environment (JRE) that is not installed or cannot be found.
1624#[derive(Debug, Clone)]
1625pub struct MissingJRE;
1626
1627impl Problem for MissingJRE {
1628    fn kind(&self) -> Cow<'_, str> {
1629        "missing-jre".into()
1630    }
1631
1632    fn json(&self) -> serde_json::Value {
1633        serde_json::json!({})
1634    }
1635
1636    fn as_any(&self) -> &dyn std::any::Any {
1637        self
1638    }
1639}
1640
1641inventory::submit! {
1642    crate::ProblemKindInfo {
1643        kind: "missing-jre",
1644        detail_fields: &[],
1645    }
1646}
1647
1648impl Display for MissingJRE {
1649    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
1650        write!(f, "Missing JRE")
1651    }
1652}
1653
1654/// Problem representing a missing Java Development Kit.
1655///
1656/// This struct is used when a build requires a Java Development Kit (JDK)
1657/// at a specific path but it cannot be found.
1658#[derive(Debug, Clone)]
1659pub struct MissingJDK {
1660    /// The path where the JDK was expected to be found.
1661    pub jdk_path: String,
1662}
1663
1664impl MissingJDK {
1665    /// Creates a new MissingJDK instance.
1666    ///
1667    /// # Arguments
1668    /// * `jdk_path` - Path where the JDK was expected to be found
1669    ///
1670    /// # Returns
1671    /// A new MissingJDK instance
1672    pub fn new(jdk_path: String) -> Self {
1673        Self { jdk_path }
1674    }
1675}
1676
1677impl Problem for MissingJDK {
1678    fn kind(&self) -> Cow<'_, str> {
1679        "missing-jdk".into()
1680    }
1681
1682    fn json(&self) -> serde_json::Value {
1683        serde_json::json!({
1684            "jdk_path": self.jdk_path
1685        })
1686    }
1687
1688    fn as_any(&self) -> &dyn std::any::Any {
1689        self
1690    }
1691}
1692
1693inventory::submit! {
1694    crate::ProblemKindInfo {
1695        kind: "missing-jdk",
1696        detail_fields: &["jdk_path"],
1697    }
1698}
1699
1700impl Display for MissingJDK {
1701    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
1702        write!(f, "Missing JDK at {}", self.jdk_path)
1703    }
1704}
1705
1706/// Problem representing a missing file in the Java Development Kit.
1707///
1708/// This struct is used when a build requires a specific file from the JDK
1709/// but it cannot be found in the JDK directory.
1710#[derive(Debug, Clone)]
1711pub struct MissingJDKFile {
1712    /// The path to the JDK directory.
1713    pub jdk_path: String,
1714    /// The name of the file that is missing from the JDK.
1715    pub filename: String,
1716}
1717
1718impl MissingJDKFile {
1719    /// Creates a new MissingJDKFile instance.
1720    ///
1721    /// # Arguments
1722    /// * `jdk_path` - Path to the JDK directory
1723    /// * `filename` - Name of the file that is missing from the JDK
1724    ///
1725    /// # Returns
1726    /// A new MissingJDKFile instance
1727    pub fn new(jdk_path: String, filename: String) -> Self {
1728        Self { jdk_path, filename }
1729    }
1730}
1731
1732impl Problem for MissingJDKFile {
1733    fn kind(&self) -> Cow<'_, str> {
1734        "missing-jdk-file".into()
1735    }
1736
1737    fn json(&self) -> serde_json::Value {
1738        serde_json::json!({
1739            "jdk_path": self.jdk_path,
1740            "filename": self.filename
1741        })
1742    }
1743
1744    fn as_any(&self) -> &dyn std::any::Any {
1745        self
1746    }
1747}
1748
1749inventory::submit! {
1750    crate::ProblemKindInfo {
1751        kind: "missing-jdk-file",
1752        detail_fields: &["jdk_path", "filename"],
1753    }
1754}
1755
1756impl Display for MissingJDKFile {
1757    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
1758        write!(f, "Missing JDK file {} at {}", self.filename, self.jdk_path)
1759    }
1760}
1761
1762/// Problem representing a missing Perl file.
1763///
1764/// This struct is used when a Perl script attempts to load a file
1765/// but it cannot be found in any of the include paths.
1766#[derive(Debug, Clone)]
1767pub struct MissingPerlFile {
1768    /// The name of the missing Perl file.
1769    pub filename: String,
1770    /// The include paths that were searched, if available.
1771    pub inc: Option<Vec<String>>,
1772}
1773
1774impl MissingPerlFile {
1775    /// Creates a new MissingPerlFile instance.
1776    ///
1777    /// # Arguments
1778    /// * `filename` - Name of the missing Perl file
1779    /// * `inc` - List of include paths that were searched, if known
1780    ///
1781    /// # Returns
1782    /// A new MissingPerlFile instance
1783    pub fn new(filename: String, inc: Option<Vec<String>>) -> Self {
1784        Self { filename, inc }
1785    }
1786}
1787
1788impl Problem for MissingPerlFile {
1789    fn kind(&self) -> Cow<'_, str> {
1790        "missing-perl-file".into()
1791    }
1792
1793    fn json(&self) -> serde_json::Value {
1794        serde_json::json!({
1795            "filename": self.filename,
1796            "inc": self.inc
1797        })
1798    }
1799
1800    fn as_any(&self) -> &dyn std::any::Any {
1801        self
1802    }
1803}
1804
1805inventory::submit! {
1806    crate::ProblemKindInfo {
1807        kind: "missing-perl-file",
1808        detail_fields: &["filename", "inc"],
1809    }
1810}
1811
1812impl Display for MissingPerlFile {
1813    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
1814        if let Some(inc) = self.inc.as_ref() {
1815            write!(
1816                f,
1817                "Missing Perl file {} (INC: {})",
1818                self.filename,
1819                inc.join(":")
1820            )
1821        } else {
1822            write!(f, "Missing Perl file {}", self.filename)
1823        }
1824    }
1825}
1826
1827/// Problem representing a missing Perl module.
1828///
1829/// This struct is used when a Perl script requires a module
1830/// that is not installed or cannot be found in the include paths.
1831#[derive(Debug, Clone)]
1832pub struct MissingPerlModule {
1833    /// The name of the file where the module is required, if known.
1834    pub filename: Option<String>,
1835    /// The name of the missing Perl module.
1836    pub module: String,
1837    /// The include paths that were searched, if available.
1838    pub inc: Option<Vec<String>>,
1839    /// The minimum version of the module required, if specified.
1840    pub minimum_version: Option<String>,
1841}
1842
1843impl Problem for MissingPerlModule {
1844    fn kind(&self) -> Cow<'_, str> {
1845        "missing-perl-module".into()
1846    }
1847
1848    fn json(&self) -> serde_json::Value {
1849        serde_json::json!({
1850            "filename": self.filename,
1851            "module": self.module,
1852            "inc": self.inc,
1853            "minimum_version": self.minimum_version,
1854        })
1855    }
1856
1857    fn as_any(&self) -> &dyn std::any::Any {
1858        self
1859    }
1860}
1861
1862inventory::submit! {
1863    crate::ProblemKindInfo {
1864        kind: "missing-perl-module",
1865        detail_fields: &["filename", "module", "inc", "minimum_version"],
1866    }
1867}
1868
1869impl Display for MissingPerlModule {
1870    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
1871        if let Some(filename) = &self.filename {
1872            write!(
1873                f,
1874                "Missing Perl module: {} (from {})",
1875                self.module, filename
1876            )?;
1877        } else {
1878            write!(f, "Missing Perl module: {}", self.module)?;
1879        }
1880        if let Some(minimum_version) = &self.minimum_version {
1881            write!(f, " >= {}", minimum_version)?;
1882        }
1883        if let Some(inc) = &self.inc {
1884            write!(f, " (INC: {})", inc.join(", "))?;
1885        }
1886        Ok(())
1887    }
1888}
1889
1890impl MissingPerlModule {
1891    /// Creates a simple MissingPerlModule instance with just a module name.
1892    ///
1893    /// # Arguments
1894    /// * `module` - Name of the missing Perl module
1895    ///
1896    /// # Returns
1897    /// A new MissingPerlModule with no additional information
1898    pub fn simple(module: &str) -> Self {
1899        Self {
1900            filename: None,
1901            module: module.to_string(),
1902            inc: None,
1903            minimum_version: None,
1904        }
1905    }
1906}
1907
1908/// Problem representing a missing command in a Python setup.py script.
1909///
1910/// This struct is used when a Python setup.py script is called with a command
1911/// that it does not support or recognize.
1912#[derive(Debug, Clone)]
1913pub struct MissingSetupPyCommand(pub String);
1914
1915impl Problem for MissingSetupPyCommand {
1916    fn kind(&self) -> Cow<'_, str> {
1917        "missing-setup.py-command".into()
1918    }
1919
1920    fn json(&self) -> serde_json::Value {
1921        serde_json::json!({
1922            "command": self.0,
1923        })
1924    }
1925
1926    fn as_any(&self) -> &dyn std::any::Any {
1927        self
1928    }
1929}
1930
1931impl Display for MissingSetupPyCommand {
1932    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
1933        write!(f, "Missing setup.py command: {}", self.0)
1934    }
1935}
1936
1937/// Problem representing a missing C# compiler.
1938///
1939/// This struct is used when a build requires a C# compiler
1940/// but none is available in the build environment.
1941#[derive(Debug, Clone)]
1942pub struct MissingCSharpCompiler;
1943
1944impl Problem for MissingCSharpCompiler {
1945    fn kind(&self) -> Cow<'_, str> {
1946        "missing-c#-compiler".into()
1947    }
1948
1949    fn json(&self) -> serde_json::Value {
1950        serde_json::json!({})
1951    }
1952
1953    fn as_any(&self) -> &dyn std::any::Any {
1954        self
1955    }
1956}
1957
1958impl Display for MissingCSharpCompiler {
1959    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
1960        write!(f, "Missing C# compiler")
1961    }
1962}
1963
1964/// Problem representing a missing Rust compiler.
1965///
1966/// This struct is used when a build requires a Rust compiler (rustc)
1967/// but none is available in the build environment.
1968#[derive(Debug, Clone)]
1969pub struct MissingRustCompiler;
1970
1971impl Problem for MissingRustCompiler {
1972    fn kind(&self) -> Cow<'_, str> {
1973        "missing-rust-compiler".into()
1974    }
1975
1976    fn json(&self) -> serde_json::Value {
1977        serde_json::json!({})
1978    }
1979
1980    fn as_any(&self) -> &dyn std::any::Any {
1981        self
1982    }
1983}
1984
1985inventory::submit! {
1986    crate::ProblemKindInfo {
1987        kind: "missing-rust-compiler",
1988        detail_fields: &[],
1989    }
1990}
1991
1992impl Display for MissingRustCompiler {
1993    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
1994        write!(f, "Missing Rust compiler")
1995    }
1996}
1997
1998/// Problem representing a missing assembler.
1999///
2000/// This struct is used when a build requires an assembler (like as or nasm)
2001/// but none is available in the build environment.
2002#[derive(Debug, Clone)]
2003pub struct MissingAssembler;
2004
2005impl Problem for MissingAssembler {
2006    fn kind(&self) -> Cow<'_, str> {
2007        "missing-assembler".into()
2008    }
2009
2010    fn json(&self) -> serde_json::Value {
2011        serde_json::json!({})
2012    }
2013
2014    fn as_any(&self) -> &dyn std::any::Any {
2015        self
2016    }
2017}
2018
2019inventory::submit! {
2020    crate::ProblemKindInfo {
2021        kind: "missing-assembler",
2022        detail_fields: &[],
2023    }
2024}
2025
2026impl Display for MissingAssembler {
2027    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
2028        write!(f, "Missing assembler")
2029    }
2030}
2031
2032/// Problem representing a missing Rust crate for Cargo.
2033///
2034/// This struct is used when a Cargo build requires a Rust crate
2035/// that is not available or cannot be found.
2036#[derive(Debug, Clone)]
2037pub struct MissingCargoCrate {
2038    /// The name of the missing Rust crate.
2039    pub crate_name: String,
2040    /// The requirement or dependency that needs this crate, if known.
2041    pub requirement: Option<String>,
2042}
2043
2044impl Problem for MissingCargoCrate {
2045    fn kind(&self) -> Cow<'_, str> {
2046        "missing-cargo-crate".into()
2047    }
2048
2049    fn json(&self) -> serde_json::Value {
2050        serde_json::json!({
2051            "crate": self.crate_name,
2052            "requirement": self.requirement
2053        })
2054    }
2055
2056    fn as_any(&self) -> &dyn std::any::Any {
2057        self
2058    }
2059}
2060
2061inventory::submit! {
2062    crate::ProblemKindInfo {
2063        kind: "missing-cargo-crate",
2064        detail_fields: &["crate", "requirement"],
2065    }
2066}
2067
2068impl MissingCargoCrate {
2069    /// Creates a simple MissingCargoCrate instance with just a crate name.
2070    ///
2071    /// # Arguments
2072    /// * `crate_name` - Name of the missing Rust crate
2073    ///
2074    /// # Returns
2075    /// A new MissingCargoCrate with no requirement information
2076    pub fn simple(crate_name: String) -> Self {
2077        Self {
2078            crate_name,
2079            requirement: None,
2080        }
2081    }
2082}
2083
2084impl Display for MissingCargoCrate {
2085    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
2086        if let Some(requirement) = self.requirement.as_ref() {
2087            write!(
2088                f,
2089                "Missing Cargo crate {} (required by {})",
2090                self.crate_name, requirement
2091            )
2092        } else {
2093            write!(f, "Missing Cargo crate {}", self.crate_name)
2094        }
2095    }
2096}
2097
2098/// Problem representing incorrect debhelper (dh) command argument order.
2099///
2100/// This struct is used when the debhelper command is used with arguments
2101/// in an incorrect order, which can cause build issues in Debian packaging.
2102#[derive(Debug, Clone)]
2103pub struct DhWithOrderIncorrect;
2104
2105impl Problem for DhWithOrderIncorrect {
2106    fn kind(&self) -> Cow<'_, str> {
2107        "debhelper-argument-order".into()
2108    }
2109
2110    fn json(&self) -> serde_json::Value {
2111        serde_json::json!({})
2112    }
2113
2114    fn as_any(&self) -> &dyn std::any::Any {
2115        self
2116    }
2117}
2118
2119inventory::submit! {
2120    crate::ProblemKindInfo {
2121        kind: "debhelper-argument-order",
2122        detail_fields: &[],
2123    }
2124}
2125
2126impl Display for DhWithOrderIncorrect {
2127    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
2128        write!(f, "dh argument order is incorrect")
2129    }
2130}
2131
2132/// Problem representing an unsupported debhelper compatibility level.
2133///
2134/// This struct is used when a Debian package build specifies a debhelper
2135/// compatibility level that is lower than the minimum supported level
2136/// in the current environment.
2137#[derive(Debug, Clone)]
2138pub struct UnsupportedDebhelperCompatLevel {
2139    /// The oldest (minimum) compatibility level supported by the current debhelper.
2140    pub oldest_supported: u32,
2141    /// The compatibility level requested by the package.
2142    pub requested: u32,
2143}
2144
2145impl UnsupportedDebhelperCompatLevel {
2146    /// Creates a new UnsupportedDebhelperCompatLevel instance.
2147    ///
2148    /// # Arguments
2149    /// * `oldest_supported` - The oldest (minimum) compatibility level supported
2150    /// * `requested` - The compatibility level requested by the package
2151    ///
2152    /// # Returns
2153    /// A new UnsupportedDebhelperCompatLevel instance
2154    pub fn new(oldest_supported: u32, requested: u32) -> Self {
2155        Self {
2156            oldest_supported,
2157            requested,
2158        }
2159    }
2160}
2161
2162impl Problem for UnsupportedDebhelperCompatLevel {
2163    fn kind(&self) -> Cow<'_, str> {
2164        "unsupported-debhelper-compat-level".into()
2165    }
2166
2167    fn json(&self) -> serde_json::Value {
2168        serde_json::json!({
2169            "oldest_supported": self.oldest_supported,
2170            "requested": self.requested
2171        })
2172    }
2173
2174    fn as_any(&self) -> &dyn std::any::Any {
2175        self
2176    }
2177}
2178
2179inventory::submit! {
2180    crate::ProblemKindInfo {
2181        kind: "unsupported-debhelper-compat-level",
2182        detail_fields: &["oldest_supported", "requested"],
2183    }
2184}
2185
2186impl Display for UnsupportedDebhelperCompatLevel {
2187    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
2188        write!(
2189            f,
2190            "Request debhelper compat level {} lower than supported {}",
2191            self.requested, self.oldest_supported
2192        )
2193    }
2194}
2195
2196/// Problem representing an issue with setuptools_scm version detection.
2197///
2198/// This struct is used when the setuptools_scm Python package is unable
2199/// to automatically determine the package version from version control
2200/// metadata, which typically happens when building from a source archive
2201/// rather than a git repository.
2202#[derive(Debug, Clone)]
2203pub struct SetuptoolScmVersionIssue;
2204
2205impl Problem for SetuptoolScmVersionIssue {
2206    fn kind(&self) -> Cow<'_, str> {
2207        "setuptools-scm-version-issue".into()
2208    }
2209
2210    fn json(&self) -> serde_json::Value {
2211        serde_json::json!({})
2212    }
2213
2214    fn as_any(&self) -> &dyn std::any::Any {
2215        self
2216    }
2217}
2218
2219inventory::submit! {
2220    crate::ProblemKindInfo {
2221        kind: "setuptools-scm-version-issue",
2222        detail_fields: &[],
2223    }
2224}
2225
2226impl Display for SetuptoolScmVersionIssue {
2227    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
2228        write!(f, "setuptools_scm was unable to find version")
2229    }
2230}
2231
2232/// Problem representing missing Maven artifacts.
2233///
2234/// This struct is used when a Java build process that uses Maven
2235/// is missing required artifacts from Maven repositories.
2236#[derive(Debug, Clone)]
2237pub struct MissingMavenArtifacts(pub Vec<String>);
2238
2239impl Problem for MissingMavenArtifacts {
2240    fn kind(&self) -> Cow<'_, str> {
2241        "missing-maven-artifacts".into()
2242    }
2243
2244    fn json(&self) -> serde_json::Value {
2245        serde_json::json!({
2246            "artifacts": self.0
2247        })
2248    }
2249
2250    fn as_any(&self) -> &dyn std::any::Any {
2251        self
2252    }
2253}
2254
2255inventory::submit! {
2256    crate::ProblemKindInfo {
2257        kind: "missing-maven-artifacts",
2258        detail_fields: &["artifacts"],
2259    }
2260}
2261
2262impl Display for MissingMavenArtifacts {
2263    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
2264        write!(f, "Missing Maven artifacts: {}", self.0.join(", "))
2265    }
2266}
2267
2268/// Problem representing a file that is not executable.
2269///
2270/// This struct is used when a command or script file that needs to be
2271/// executed does not have the executable permission bit set.
2272#[derive(Debug, Clone)]
2273pub struct NotExecutableFile(pub String);
2274
2275impl NotExecutableFile {
2276    /// Creates a new NotExecutableFile instance.
2277    ///
2278    /// # Arguments
2279    /// * `path` - Path to the non-executable file
2280    ///
2281    /// # Returns
2282    /// A new NotExecutableFile instance
2283    pub fn new(path: String) -> Self {
2284        Self(path)
2285    }
2286}
2287
2288impl Problem for NotExecutableFile {
2289    fn kind(&self) -> Cow<'_, str> {
2290        "not-executable-file".into()
2291    }
2292
2293    fn json(&self) -> serde_json::Value {
2294        serde_json::json!({
2295            "path": self.0
2296        })
2297    }
2298
2299    fn as_any(&self) -> &dyn std::any::Any {
2300        self
2301    }
2302}
2303
2304inventory::submit! {
2305    crate::ProblemKindInfo {
2306        kind: "not-executable-file",
2307        detail_fields: &["path"],
2308    }
2309}
2310
2311impl Display for NotExecutableFile {
2312    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
2313        write!(f, "Command not executable: {}", self.0)
2314    }
2315}
2316
2317/// Problem representing a debhelper script attempting to access an uninstalled file.
2318///
2319/// This struct is used when debhelper tries to access a file that has been
2320/// removed or was never installed in the build environment.
2321#[derive(Debug, Clone)]
2322pub struct DhMissingUninstalled(pub String);
2323
2324impl DhMissingUninstalled {
2325    /// Creates a new DhMissingUninstalled instance.
2326    ///
2327    /// # Arguments
2328    /// * `missing_file` - Path to the missing file
2329    ///
2330    /// # Returns
2331    /// A new DhMissingUninstalled instance
2332    pub fn new(missing_file: String) -> Self {
2333        Self(missing_file)
2334    }
2335}
2336
2337impl Problem for DhMissingUninstalled {
2338    fn kind(&self) -> Cow<'_, str> {
2339        "dh-missing-uninstalled".into()
2340    }
2341
2342    fn json(&self) -> serde_json::Value {
2343        serde_json::json!({
2344            "missing_file": self.0
2345        })
2346    }
2347
2348    fn as_any(&self) -> &dyn std::any::Any {
2349        self
2350    }
2351}
2352
2353inventory::submit! {
2354    crate::ProblemKindInfo {
2355        kind: "dh-missing-uninstalled",
2356        detail_fields: &["missing_file"],
2357    }
2358}
2359
2360impl Display for DhMissingUninstalled {
2361    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
2362        write!(f, "dh_missing file not installed: {}", self.0)
2363    }
2364}
2365
2366/// Problem representing a debhelper link whose destination is a directory.
2367///
2368/// This struct is used when debhelper's dh_link attempts to create a symlink
2369/// to a path that is a directory, which can cause issues in package builds.
2370#[derive(Debug, Clone)]
2371pub struct DhLinkDestinationIsDirectory(pub String);
2372
2373impl DhLinkDestinationIsDirectory {
2374    /// Creates a new DhLinkDestinationIsDirectory instance.
2375    ///
2376    /// # Arguments
2377    /// * `path` - Path to the directory that was incorrectly specified as a link destination
2378    ///
2379    /// # Returns
2380    /// A new DhLinkDestinationIsDirectory instance
2381    pub fn new(path: String) -> Self {
2382        Self(path)
2383    }
2384}
2385
2386impl Problem for DhLinkDestinationIsDirectory {
2387    fn kind(&self) -> Cow<'_, str> {
2388        "dh-link-destination-is-directory".into()
2389    }
2390
2391    fn json(&self) -> serde_json::Value {
2392        serde_json::json!({
2393            "path": self.0
2394        })
2395    }
2396
2397    fn as_any(&self) -> &dyn std::any::Any {
2398        self
2399    }
2400}
2401
2402inventory::submit! {
2403    crate::ProblemKindInfo {
2404        kind: "dh-link-destination-is-directory",
2405        detail_fields: &["path"],
2406    }
2407}
2408
2409impl Display for DhLinkDestinationIsDirectory {
2410    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
2411        write!(f, "Link destination {} is directory", self.0)
2412    }
2413}
2414
2415/// Problem representing a missing XML entity.
2416///
2417/// This struct is used when an XML parser attempts to resolve an external
2418/// entity reference but the referenced entity cannot be found at the given URL.
2419#[derive(Debug, Clone)]
2420pub struct MissingXmlEntity {
2421    /// The URL where the XML entity was expected to be found.
2422    pub url: String,
2423}
2424
2425impl MissingXmlEntity {
2426    /// Creates a new MissingXmlEntity instance.
2427    ///
2428    /// # Arguments
2429    /// * `url` - URL where the XML entity was expected to be found
2430    ///
2431    /// # Returns
2432    /// A new MissingXmlEntity instance
2433    pub fn new(url: String) -> Self {
2434        Self { url }
2435    }
2436}
2437
2438impl Problem for MissingXmlEntity {
2439    fn kind(&self) -> Cow<'_, str> {
2440        "missing-xml-entity".into()
2441    }
2442
2443    fn json(&self) -> serde_json::Value {
2444        serde_json::json!({
2445            "url": self.url
2446        })
2447    }
2448
2449    fn as_any(&self) -> &dyn std::any::Any {
2450        self
2451    }
2452}
2453
2454inventory::submit! {
2455    crate::ProblemKindInfo {
2456        kind: "missing-xml-entity",
2457        detail_fields: &["url"],
2458    }
2459}
2460
2461impl Display for MissingXmlEntity {
2462    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
2463        write!(f, "Missing XML entity: {}", self.url)
2464    }
2465}
2466
2467/// Problem representing an error from the ccache compiler cache.
2468///
2469/// This struct is used when the ccache tool, which accelerates repeated compilations,
2470/// encounters an error during its operation.
2471#[derive(Debug, Clone)]
2472pub struct CcacheError(pub String);
2473
2474impl CcacheError {
2475    /// Creates a new CcacheError instance.
2476    ///
2477    /// # Arguments
2478    /// * `error` - The error message from ccache
2479    ///
2480    /// # Returns
2481    /// A new CcacheError instance
2482    pub fn new(error: String) -> Self {
2483        Self(error)
2484    }
2485}
2486
2487impl Problem for CcacheError {
2488    fn kind(&self) -> Cow<'_, str> {
2489        "ccache-error".into()
2490    }
2491
2492    fn json(&self) -> serde_json::Value {
2493        serde_json::json!({
2494            "error": self.0
2495        })
2496    }
2497
2498    fn as_any(&self) -> &dyn std::any::Any {
2499        self
2500    }
2501}
2502
2503inventory::submit! {
2504    crate::ProblemKindInfo {
2505        kind: "ccache-error",
2506        detail_fields: &["error"],
2507    }
2508}
2509
2510impl Display for CcacheError {
2511    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
2512        write!(f, "ccache error: {}", self.0)
2513    }
2514}
2515
2516/// Problem representing a rejected Debian package version string.
2517///
2518/// This struct is used when a version string for a Debian package is rejected
2519/// by Debian tools as invalid or incompatible with policy requirements.
2520#[derive(Debug, Clone)]
2521pub struct DebianVersionRejected {
2522    /// The version string that was rejected.
2523    pub version: String,
2524}
2525
2526impl DebianVersionRejected {
2527    /// Creates a new DebianVersionRejected instance.
2528    ///
2529    /// # Arguments
2530    /// * `version` - The version string that was rejected
2531    ///
2532    /// # Returns
2533    /// A new DebianVersionRejected instance
2534    pub fn new(version: String) -> Self {
2535        Self { version }
2536    }
2537}
2538
2539impl Problem for DebianVersionRejected {
2540    fn kind(&self) -> Cow<'_, str> {
2541        "debian-version-rejected".into()
2542    }
2543
2544    fn json(&self) -> serde_json::Value {
2545        serde_json::json!({
2546            "version": self.version
2547        })
2548    }
2549
2550    fn as_any(&self) -> &dyn std::any::Any {
2551        self
2552    }
2553}
2554
2555inventory::submit! {
2556    crate::ProblemKindInfo {
2557        kind: "debian-version-rejected",
2558        detail_fields: &["version"],
2559    }
2560}
2561
2562impl Display for DebianVersionRejected {
2563    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
2564        write!(f, "Debian Version Rejected; {}", self.version)
2565    }
2566}
2567
2568/// Problem representing a failure to apply a patch.
2569///
2570/// This struct is used when a build process fails because a patch
2571/// cannot be successfully applied to the source code.
2572#[derive(Debug, Clone)]
2573pub struct PatchApplicationFailed {
2574    /// The name of the patch file that could not be applied.
2575    pub patchname: String,
2576}
2577
2578impl PatchApplicationFailed {
2579    /// Creates a new PatchApplicationFailed instance.
2580    ///
2581    /// # Arguments
2582    /// * `patchname` - Name of the patch file that failed to apply
2583    ///
2584    /// # Returns
2585    /// A new PatchApplicationFailed instance
2586    pub fn new(patchname: String) -> Self {
2587        Self { patchname }
2588    }
2589}
2590
2591impl Problem for PatchApplicationFailed {
2592    fn kind(&self) -> Cow<'_, str> {
2593        "patch-application-failed".into()
2594    }
2595
2596    fn json(&self) -> serde_json::Value {
2597        serde_json::json!({
2598            "patchname": self.patchname
2599        })
2600    }
2601
2602    fn as_any(&self) -> &dyn std::any::Any {
2603        self
2604    }
2605}
2606
2607inventory::submit! {
2608    crate::ProblemKindInfo {
2609        kind: "patch-application-failed",
2610        detail_fields: &["patchname"],
2611    }
2612}
2613
2614impl Display for PatchApplicationFailed {
2615    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
2616        write!(f, "Patch application failed: {}", self.patchname)
2617    }
2618}
2619
2620/// Problem representing a need to update PostgreSQL build extension control files.
2621///
2622/// This struct is used when PostgreSQL extension build files need to be updated
2623/// using the pg_buildext updatecontrol command to generate control files from templates.
2624#[derive(Debug, Clone)]
2625pub struct NeedPgBuildExtUpdateControl {
2626    /// The path to the generated control file.
2627    pub generated_path: String,
2628    /// The path to the template file to use for generation.
2629    pub template_path: String,
2630}
2631
2632impl NeedPgBuildExtUpdateControl {
2633    /// Creates a new NeedPgBuildExtUpdateControl instance.
2634    ///
2635    /// # Arguments
2636    /// * `generated_path` - Path to the generated control file
2637    /// * `template_path` - Path to the template file
2638    ///
2639    /// # Returns
2640    /// A new NeedPgBuildExtUpdateControl instance
2641    pub fn new(generated_path: String, template_path: String) -> Self {
2642        Self {
2643            generated_path,
2644            template_path,
2645        }
2646    }
2647}
2648
2649impl Problem for NeedPgBuildExtUpdateControl {
2650    fn kind(&self) -> Cow<'_, str> {
2651        "need-pg-buildext-updatecontrol".into()
2652    }
2653
2654    fn json(&self) -> serde_json::Value {
2655        serde_json::json!({
2656            "generated_path": self.generated_path,
2657            "template_path": self.template_path
2658        })
2659    }
2660
2661    fn as_any(&self) -> &dyn std::any::Any {
2662        self
2663    }
2664}
2665
2666inventory::submit! {
2667    crate::ProblemKindInfo {
2668        kind: "need-pg-buildext-updatecontrol",
2669        detail_fields: &["generated_path", "template_path"],
2670    }
2671}
2672
2673impl Display for NeedPgBuildExtUpdateControl {
2674    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
2675        write!(
2676            f,
2677            "Need to run 'pg_buildext updatecontrol' to update {}",
2678            self.generated_path
2679        )
2680    }
2681}
2682
2683/// Problem representing a failure to load a debhelper addon.
2684///
2685/// This struct is used when debhelper fails to load an addon module,
2686/// which typically provides additional functionality to the debhelper tools.
2687#[derive(Debug, Clone)]
2688pub struct DhAddonLoadFailure {
2689    /// The name of the addon that failed to load.
2690    pub name: String,
2691    /// The path where the addon was expected to be found.
2692    pub path: String,
2693}
2694
2695impl DhAddonLoadFailure {
2696    /// Creates a new DhAddonLoadFailure instance.
2697    ///
2698    /// # Arguments
2699    /// * `name` - Name of the addon that failed to load
2700    /// * `path` - Path where the addon was expected to be found
2701    ///
2702    /// # Returns
2703    /// A new DhAddonLoadFailure instance
2704    pub fn new(name: String, path: String) -> Self {
2705        Self { name, path }
2706    }
2707}
2708
2709impl Problem for DhAddonLoadFailure {
2710    fn kind(&self) -> Cow<'_, str> {
2711        "dh-addon-load-failure".into()
2712    }
2713
2714    fn json(&self) -> serde_json::Value {
2715        serde_json::json!({
2716            "name": self.name,
2717            "path": self.path
2718        })
2719    }
2720
2721    fn as_any(&self) -> &dyn std::any::Any {
2722        self
2723    }
2724}
2725
2726inventory::submit! {
2727    crate::ProblemKindInfo {
2728        kind: "dh-addon-load-failure",
2729        detail_fields: &["name", "path"],
2730    }
2731}
2732
2733impl Display for DhAddonLoadFailure {
2734    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
2735        write!(f, "dh addon loading failed: {}", self.name)
2736    }
2737}
2738
2739/// Problem representing an unsupported usage of the --until flag in debhelper.
2740///
2741/// This struct is used when the --until flag is used with debhelper (dh)
2742/// but the version of debhelper in use does not support this option.
2743#[derive(Debug, Clone)]
2744pub struct DhUntilUnsupported;
2745
2746impl Default for DhUntilUnsupported {
2747    /// Provides a default instance of DhUntilUnsupported.
2748    ///
2749    /// # Returns
2750    /// A new DhUntilUnsupported instance
2751    fn default() -> Self {
2752        Self::new()
2753    }
2754}
2755
2756impl DhUntilUnsupported {
2757    /// Creates a new DhUntilUnsupported instance.
2758    ///
2759    /// # Returns
2760    /// A new DhUntilUnsupported instance
2761    pub fn new() -> Self {
2762        Self
2763    }
2764}
2765
2766impl Problem for DhUntilUnsupported {
2767    fn kind(&self) -> Cow<'_, str> {
2768        "dh-until-unsupported".into()
2769    }
2770
2771    fn json(&self) -> serde_json::Value {
2772        serde_json::json!({})
2773    }
2774
2775    fn as_any(&self) -> &dyn std::any::Any {
2776        self
2777    }
2778}
2779
2780inventory::submit! {
2781    crate::ProblemKindInfo {
2782        kind: "dh-until-unsupported",
2783        detail_fields: &[],
2784    }
2785}
2786
2787impl Display for DhUntilUnsupported {
2788    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
2789        write!(f, "dh --until is no longer supported")
2790    }
2791}
2792
2793/// Problem representing a debhelper file pattern that was not found.
2794///
2795/// This struct is used when a debhelper tool is looking for files matching
2796/// a specific pattern but cannot find any matches in the searched directories.
2797#[derive(Debug, Clone)]
2798pub struct DebhelperPatternNotFound {
2799    /// The file pattern that was being searched for.
2800    pub pattern: String,
2801    /// The name of the debhelper tool that was performing the search.
2802    pub tool: String,
2803    /// The list of directories that were searched.
2804    pub directories: Vec<String>,
2805}
2806
2807impl DebhelperPatternNotFound {
2808    /// Creates a new DebhelperPatternNotFound instance.
2809    ///
2810    /// # Arguments
2811    /// * `pattern` - The file pattern that was being searched for
2812    /// * `tool` - The name of the debhelper tool
2813    /// * `directories` - The list of directories that were searched
2814    ///
2815    /// # Returns
2816    /// A new DebhelperPatternNotFound instance
2817    pub fn new(pattern: String, tool: String, directories: Vec<String>) -> Self {
2818        Self {
2819            pattern,
2820            tool,
2821            directories,
2822        }
2823    }
2824}
2825
2826impl Problem for DebhelperPatternNotFound {
2827    fn kind(&self) -> Cow<'_, str> {
2828        "debhelper-pattern-not-found".into()
2829    }
2830
2831    fn json(&self) -> serde_json::Value {
2832        serde_json::json!({
2833            "pattern": self.pattern,
2834            "tool": self.tool,
2835            "directories": self.directories
2836        })
2837    }
2838
2839    fn as_any(&self) -> &dyn std::any::Any {
2840        self
2841    }
2842}
2843
2844inventory::submit! {
2845    crate::ProblemKindInfo {
2846        kind: "debhelper-pattern-not-found",
2847        detail_fields: &["pattern", "tool", "directories"],
2848    }
2849}
2850
2851impl Display for DebhelperPatternNotFound {
2852    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
2853        write!(
2854            f,
2855            "debhelper ({}) expansion failed for {:?} (directories: {:?})",
2856            self.tool, self.pattern, self.directories
2857        )
2858    }
2859}
2860
2861/// Problem representing a missing Perl MANIFEST file.
2862///
2863/// This struct is used when a Perl module build expects to find a MANIFEST
2864/// file listing all files in the distribution, but it doesn't exist.
2865#[derive(Debug, Clone)]
2866pub struct MissingPerlManifest;
2867
2868impl Default for MissingPerlManifest {
2869    /// Provides a default instance of MissingPerlManifest.
2870    ///
2871    /// # Returns
2872    /// A new MissingPerlManifest instance
2873    fn default() -> Self {
2874        Self::new()
2875    }
2876}
2877
2878impl MissingPerlManifest {
2879    /// Creates a new MissingPerlManifest instance.
2880    ///
2881    /// # Returns
2882    /// A new MissingPerlManifest instance
2883    pub fn new() -> Self {
2884        Self
2885    }
2886}
2887
2888impl Problem for MissingPerlManifest {
2889    fn kind(&self) -> Cow<'_, str> {
2890        "missing-perl-manifest".into()
2891    }
2892
2893    fn json(&self) -> serde_json::Value {
2894        serde_json::json!({})
2895    }
2896
2897    fn as_any(&self) -> &dyn std::any::Any {
2898        self
2899    }
2900}
2901
2902inventory::submit! {
2903    crate::ProblemKindInfo {
2904        kind: "missing-perl-manifest",
2905        detail_fields: &[],
2906    }
2907}
2908
2909impl Display for MissingPerlManifest {
2910    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
2911        write!(f, "missing Perl MANIFEST")
2912    }
2913}
2914
2915/// Problem representing a missing ImageMagick delegate.
2916///
2917/// This struct is used when ImageMagick requires a delegate library
2918/// to handle a specific file format or operation, but the delegate is not available.
2919#[derive(Debug, Clone)]
2920pub struct ImageMagickDelegateMissing {
2921    /// The name of the missing ImageMagick delegate.
2922    pub delegate: String,
2923}
2924
2925impl ImageMagickDelegateMissing {
2926    /// Creates a new ImageMagickDelegateMissing instance.
2927    ///
2928    /// # Arguments
2929    /// * `delegate` - Name of the missing ImageMagick delegate
2930    ///
2931    /// # Returns
2932    /// A new ImageMagickDelegateMissing instance
2933    pub fn new(delegate: String) -> Self {
2934        Self { delegate }
2935    }
2936}
2937
2938impl Problem for ImageMagickDelegateMissing {
2939    fn kind(&self) -> Cow<'_, str> {
2940        "imagemagick-delegate-missing".into()
2941    }
2942
2943    fn json(&self) -> serde_json::Value {
2944        serde_json::json!({
2945            "delegate": self.delegate
2946        })
2947    }
2948
2949    fn as_any(&self) -> &dyn std::any::Any {
2950        self
2951    }
2952}
2953
2954inventory::submit! {
2955    crate::ProblemKindInfo {
2956        kind: "imagemagick-delegate-missing",
2957        detail_fields: &["delegate"],
2958    }
2959}
2960
2961impl Display for ImageMagickDelegateMissing {
2962    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
2963        write!(f, "Imagemagick missing delegate: {}", self.delegate)
2964    }
2965}
2966
2967/// Problem representing a cancelled build or operation.
2968///
2969/// This struct is used when a build process or operation was cancelled
2970/// before completion, typically by user intervention or a timeout.
2971#[derive(Debug, Clone)]
2972pub struct Cancelled;
2973
2974impl Default for Cancelled {
2975    /// Provides a default instance of Cancelled.
2976    ///
2977    /// # Returns
2978    /// A new Cancelled instance
2979    fn default() -> Self {
2980        Self::new()
2981    }
2982}
2983
2984impl Cancelled {
2985    /// Creates a new Cancelled instance.
2986    ///
2987    /// # Returns
2988    /// A new Cancelled instance
2989    pub fn new() -> Self {
2990        Self
2991    }
2992}
2993
2994impl Problem for Cancelled {
2995    fn kind(&self) -> Cow<'_, str> {
2996        "cancelled".into()
2997    }
2998
2999    fn json(&self) -> serde_json::Value {
3000        serde_json::json!({})
3001    }
3002
3003    fn as_any(&self) -> &dyn std::any::Any {
3004        self
3005    }
3006}
3007
3008inventory::submit! {
3009    crate::ProblemKindInfo {
3010        kind: "cancelled",
3011        detail_fields: &[],
3012    }
3013}
3014
3015impl Display for Cancelled {
3016    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
3017        write!(f, "Cancelled by runner or job manager")
3018    }
3019}
3020
3021/// Problem representing symbols that have disappeared from a library.
3022///
3023/// This struct is used when symbols (functions or variables) that were previously
3024/// exported by a library are no longer present, which can break API compatibility.
3025#[derive(Debug, Clone)]
3026pub struct DisappearedSymbols;
3027
3028impl Default for DisappearedSymbols {
3029    /// Provides a default instance of DisappearedSymbols.
3030    ///
3031    /// # Returns
3032    /// A new DisappearedSymbols instance
3033    fn default() -> Self {
3034        Self::new()
3035    }
3036}
3037
3038impl DisappearedSymbols {
3039    /// Creates a new DisappearedSymbols instance.
3040    ///
3041    /// # Returns
3042    /// A new DisappearedSymbols instance
3043    pub fn new() -> Self {
3044        Self
3045    }
3046}
3047
3048impl Problem for DisappearedSymbols {
3049    fn kind(&self) -> Cow<'_, str> {
3050        "disappeared-symbols".into()
3051    }
3052
3053    fn json(&self) -> serde_json::Value {
3054        serde_json::json!({})
3055    }
3056
3057    fn as_any(&self) -> &dyn std::any::Any {
3058        self
3059    }
3060}
3061
3062inventory::submit! {
3063    crate::ProblemKindInfo {
3064        kind: "disappeared-symbols",
3065        detail_fields: &[],
3066    }
3067}
3068
3069impl Display for DisappearedSymbols {
3070    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
3071        write!(f, "Disappeared symbols")
3072    }
3073}
3074
3075/// Problem representing duplicate debhelper compatibility level specifications.
3076///
3077/// This struct is used when the debhelper compatibility level is specified
3078/// multiple times in different places, which can lead to conflicts.
3079#[derive(Debug, Clone)]
3080pub struct DuplicateDHCompatLevel {
3081    /// The command or file where the duplicate compatibility level was found.
3082    pub command: String,
3083}
3084
3085impl DuplicateDHCompatLevel {
3086    /// Creates a new DuplicateDHCompatLevel instance.
3087    ///
3088    /// # Arguments
3089    /// * `command` - The command or file with the duplicate compatibility level
3090    ///
3091    /// # Returns
3092    /// A new DuplicateDHCompatLevel instance
3093    pub fn new(command: String) -> Self {
3094        Self { command }
3095    }
3096}
3097
3098impl Problem for DuplicateDHCompatLevel {
3099    fn kind(&self) -> Cow<'_, str> {
3100        "duplicate-dh-compat-level".into()
3101    }
3102
3103    fn json(&self) -> serde_json::Value {
3104        serde_json::json!({
3105            "command": self.command
3106        })
3107    }
3108
3109    fn as_any(&self) -> &dyn std::any::Any {
3110        self
3111    }
3112}
3113
3114inventory::submit! {
3115    crate::ProblemKindInfo {
3116        kind: "duplicate-dh-compat-level",
3117        detail_fields: &["command"],
3118    }
3119}
3120
3121impl Display for DuplicateDHCompatLevel {
3122    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
3123        write!(
3124            f,
3125            "DH Compat Level specified twice (command: {})",
3126            self.command
3127        )
3128    }
3129}
3130
3131/// Problem representing a missing debhelper compatibility level specification.
3132///
3133/// This struct is used when debhelper requires a compatibility level to be
3134/// specified, but none was found in the expected locations.
3135#[derive(Debug, Clone)]
3136pub struct MissingDHCompatLevel {
3137    /// The command that reported the missing compatibility level.
3138    pub command: String,
3139}
3140
3141impl MissingDHCompatLevel {
3142    /// Creates a new MissingDHCompatLevel instance.
3143    ///
3144    /// # Arguments
3145    /// * `command` - The command that reported the missing compatibility level
3146    ///
3147    /// # Returns
3148    /// A new MissingDHCompatLevel instance
3149    pub fn new(command: String) -> Self {
3150        Self { command }
3151    }
3152}
3153
3154impl Problem for MissingDHCompatLevel {
3155    fn kind(&self) -> Cow<'_, str> {
3156        "missing-dh-compat-level".into()
3157    }
3158
3159    fn json(&self) -> serde_json::Value {
3160        serde_json::json!({
3161            "command": self.command
3162        })
3163    }
3164
3165    fn as_any(&self) -> &dyn std::any::Any {
3166        self
3167    }
3168}
3169
3170inventory::submit! {
3171    crate::ProblemKindInfo {
3172        kind: "missing-dh-compat-level",
3173        detail_fields: &["command"],
3174    }
3175}
3176
3177impl Display for MissingDHCompatLevel {
3178    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
3179        write!(f, "Missing DH Compat Level (command: {})", self.command)
3180    }
3181}
3182
3183/// Problem representing a missing Java Virtual Machine (JVM).
3184///
3185/// This struct is used when a build process requires a Java Virtual Machine
3186/// but cannot find one installed or properly configured in the system.
3187#[derive(Debug, Clone)]
3188pub struct MissingJVM;
3189
3190impl Default for MissingJVM {
3191    /// Provides a default instance of MissingJVM.
3192    ///
3193    /// # Returns
3194    /// A new MissingJVM instance
3195    fn default() -> Self {
3196        Self::new()
3197    }
3198}
3199
3200impl MissingJVM {
3201    /// Creates a new MissingJVM instance.
3202    ///
3203    /// # Returns
3204    /// A new MissingJVM instance
3205    pub fn new() -> Self {
3206        Self
3207    }
3208}
3209
3210impl Problem for MissingJVM {
3211    fn kind(&self) -> Cow<'_, str> {
3212        "missing-jvm".into()
3213    }
3214
3215    fn json(&self) -> serde_json::Value {
3216        serde_json::json!({})
3217    }
3218
3219    fn as_any(&self) -> &dyn std::any::Any {
3220        self
3221    }
3222}
3223
3224inventory::submit! {
3225    crate::ProblemKindInfo {
3226        kind: "missing-jvm",
3227        detail_fields: &[],
3228    }
3229}
3230
3231impl Display for MissingJVM {
3232    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
3233        write!(f, "missing JVM")
3234    }
3235}
3236
3237/// Problem representing a missing Ruby gem.
3238///
3239/// This struct is used when a build process requires a Ruby gem
3240/// that is not installed or available in the current environment.
3241#[derive(Debug, Clone)]
3242pub struct MissingRubyGem {
3243    /// The name of the missing Ruby gem.
3244    pub gem: String,
3245    /// The required version of the gem, if specified.
3246    pub version: Option<String>,
3247}
3248
3249impl MissingRubyGem {
3250    /// Creates a new MissingRubyGem instance.
3251    ///
3252    /// # Arguments
3253    /// * `gem` - Name of the missing Ruby gem
3254    /// * `version` - Optional version requirement for the gem
3255    ///
3256    /// # Returns
3257    /// A new MissingRubyGem instance
3258    pub fn new(gem: String, version: Option<String>) -> Self {
3259        Self { gem, version }
3260    }
3261
3262    /// Creates a simple MissingRubyGem instance without version requirements.
3263    ///
3264    /// # Arguments
3265    /// * `gem` - Name of the missing Ruby gem
3266    ///
3267    /// # Returns
3268    /// A new MissingRubyGem instance with no version requirements
3269    pub fn simple(gem: String) -> Self {
3270        Self::new(gem, None)
3271    }
3272}
3273
3274impl Problem for MissingRubyGem {
3275    fn kind(&self) -> Cow<'_, str> {
3276        "missing-ruby-gem".into()
3277    }
3278
3279    fn json(&self) -> serde_json::Value {
3280        serde_json::json!({
3281            "gem": self.gem,
3282            "version": self.version
3283        })
3284    }
3285
3286    fn as_any(&self) -> &dyn std::any::Any {
3287        self
3288    }
3289}
3290
3291inventory::submit! {
3292    crate::ProblemKindInfo {
3293        kind: "missing-ruby-gem",
3294        detail_fields: &["gem", "version"],
3295    }
3296}
3297
3298impl Display for MissingRubyGem {
3299    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
3300        if let Some(version) = &self.version {
3301            write!(f, "missing ruby gem: {} (>= {})", self.gem, version)
3302        } else {
3303            write!(f, "missing ruby gem: {}", self.gem)
3304        }
3305    }
3306}
3307
3308/// Problem representing a missing JavaScript runtime environment.
3309///
3310/// This struct is used when a build process requires a JavaScript runtime
3311/// (like Node.js, Deno, or a browser JavaScript engine) but none is available.
3312#[derive(Debug, Clone)]
3313pub struct MissingJavaScriptRuntime;
3314
3315impl Default for MissingJavaScriptRuntime {
3316    /// Provides a default instance of MissingJavaScriptRuntime.
3317    ///
3318    /// # Returns
3319    /// A new MissingJavaScriptRuntime instance
3320    fn default() -> Self {
3321        Self::new()
3322    }
3323}
3324
3325impl MissingJavaScriptRuntime {
3326    /// Creates a new MissingJavaScriptRuntime instance.
3327    ///
3328    /// # Returns
3329    /// A new MissingJavaScriptRuntime instance
3330    pub fn new() -> Self {
3331        Self
3332    }
3333}
3334
3335impl Problem for MissingJavaScriptRuntime {
3336    fn kind(&self) -> Cow<'_, str> {
3337        "javascript-runtime-missing".into()
3338    }
3339
3340    fn json(&self) -> serde_json::Value {
3341        serde_json::json!({})
3342    }
3343
3344    fn as_any(&self) -> &dyn std::any::Any {
3345        self
3346    }
3347}
3348
3349inventory::submit! {
3350    crate::ProblemKindInfo {
3351        kind: "javascript-runtime-missing",
3352        detail_fields: &[],
3353    }
3354}
3355
3356impl Display for MissingJavaScriptRuntime {
3357    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
3358        write!(f, "Missing JavaScript Runtime")
3359    }
3360}
3361
3362/// Problem representing a missing Ruby source file.
3363///
3364/// This struct is used when a Ruby application or library tries to
3365/// load or require a Ruby file that does not exist or cannot be found.
3366#[derive(Debug, Clone)]
3367pub struct MissingRubyFile {
3368    /// The name or path of the missing Ruby file.
3369    pub filename: String,
3370}
3371
3372impl MissingRubyFile {
3373    /// Creates a new MissingRubyFile instance.
3374    ///
3375    /// # Arguments
3376    /// * `filename` - Name or path of the missing Ruby file
3377    ///
3378    /// # Returns
3379    /// A new MissingRubyFile instance
3380    pub fn new(filename: String) -> Self {
3381        Self { filename }
3382    }
3383}
3384
3385impl Problem for MissingRubyFile {
3386    fn kind(&self) -> Cow<'_, str> {
3387        "missing-ruby-file".into()
3388    }
3389
3390    fn json(&self) -> serde_json::Value {
3391        serde_json::json!({
3392            "filename": self.filename
3393        })
3394    }
3395
3396    fn as_any(&self) -> &dyn std::any::Any {
3397        self
3398    }
3399}
3400
3401inventory::submit! {
3402    crate::ProblemKindInfo {
3403        kind: "missing-ruby-file",
3404        detail_fields: &["filename"],
3405    }
3406}
3407
3408impl Display for MissingRubyFile {
3409    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
3410        write!(f, "missing ruby file: {}", self.filename)
3411    }
3412}
3413
3414/// Problem representing a missing PHP class.
3415///
3416/// This struct is used when a PHP application tries to use a class
3417/// that has not been defined or cannot be autoloaded.
3418#[derive(Debug, Clone)]
3419pub struct MissingPhpClass {
3420    /// The name of the missing PHP class.
3421    pub php_class: String,
3422}
3423
3424impl MissingPhpClass {
3425    /// Creates a new MissingPhpClass instance.
3426    ///
3427    /// # Arguments
3428    /// * `php_class` - Name of the missing PHP class
3429    ///
3430    /// # Returns
3431    /// A new MissingPhpClass instance
3432    pub fn new(php_class: String) -> Self {
3433        Self { php_class }
3434    }
3435
3436    /// Creates a simple MissingPhpClass instance.
3437    ///
3438    /// This is an alias for new() for API consistency with other similar types.
3439    ///
3440    /// # Arguments
3441    /// * `php_class` - Name of the missing PHP class
3442    ///
3443    /// # Returns
3444    /// A new MissingPhpClass instance
3445    pub fn simple(php_class: String) -> Self {
3446        Self::new(php_class)
3447    }
3448}
3449
3450impl Problem for MissingPhpClass {
3451    fn kind(&self) -> Cow<'_, str> {
3452        "missing-php-class".into()
3453    }
3454
3455    fn json(&self) -> serde_json::Value {
3456        serde_json::json!({
3457            "php_class": self.php_class
3458        })
3459    }
3460
3461    fn as_any(&self) -> &dyn std::any::Any {
3462        self
3463    }
3464}
3465
3466inventory::submit! {
3467    crate::ProblemKindInfo {
3468        kind: "missing-php-class",
3469        detail_fields: &["php_class"],
3470    }
3471}
3472
3473impl Display for MissingPhpClass {
3474    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
3475        write!(f, "missing PHP class: {}", self.php_class)
3476    }
3477}
3478
3479#[derive(Debug, Clone)]
3480/// Problem representing a missing Java class.
3481///
3482/// This struct is used when a Java application or build process
3483/// requires a Java class that cannot be found in the classpath.
3484pub struct MissingJavaClass {
3485    /// The name of the missing Java class.
3486    pub classname: String,
3487}
3488
3489impl MissingJavaClass {
3490    /// Creates a new MissingJavaClass instance.
3491    ///
3492    /// # Arguments
3493    /// * `classname` - Name of the missing Java class
3494    ///
3495    /// # Returns
3496    /// A new MissingJavaClass instance
3497    pub fn new(classname: String) -> Self {
3498        Self { classname }
3499    }
3500
3501    /// Creates a simple MissingJavaClass instance.
3502    ///
3503    /// This is an alias for new() for API consistency with other similar types.
3504    ///
3505    /// # Arguments
3506    /// * `classname` - Name of the missing Java class
3507    ///
3508    /// # Returns
3509    /// A new MissingJavaClass instance
3510    pub fn simple(classname: String) -> Self {
3511        Self::new(classname)
3512    }
3513}
3514
3515impl Problem for MissingJavaClass {
3516    fn kind(&self) -> Cow<'_, str> {
3517        "missing-java-class".into()
3518    }
3519
3520    fn json(&self) -> serde_json::Value {
3521        serde_json::json!({
3522            "classname": self.classname
3523        })
3524    }
3525
3526    fn as_any(&self) -> &dyn std::any::Any {
3527        self
3528    }
3529}
3530
3531inventory::submit! {
3532    crate::ProblemKindInfo {
3533        kind: "missing-java-class",
3534        detail_fields: &["classname"],
3535    }
3536}
3537
3538impl Display for MissingJavaClass {
3539    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
3540        write!(f, "missing Java class: {}", self.classname)
3541    }
3542}
3543
3544#[derive(Debug, Clone)]
3545/// Problem representing a missing Sprockets asset file.
3546///
3547/// This struct is used when a Ruby on Rails application using the Sprockets
3548/// asset pipeline is missing a required asset file.
3549pub struct MissingSprocketsFile {
3550    /// The name of the missing Sprockets asset file.
3551    pub name: String,
3552    /// The content type of the missing asset file.
3553    pub content_type: String,
3554}
3555
3556impl MissingSprocketsFile {
3557    /// Creates a new MissingSprocketsFile instance.
3558    ///
3559    /// # Arguments
3560    /// * `name` - Name of the missing Sprockets asset file
3561    /// * `content_type` - Content type of the missing asset file
3562    ///
3563    /// # Returns
3564    /// A new MissingSprocketsFile instance
3565    pub fn new(name: String, content_type: String) -> Self {
3566        Self { name, content_type }
3567    }
3568}
3569
3570impl Problem for MissingSprocketsFile {
3571    fn kind(&self) -> Cow<'_, str> {
3572        "missing-sprockets-file".into()
3573    }
3574
3575    fn json(&self) -> serde_json::Value {
3576        serde_json::json!({
3577            "name": self.name,
3578            "content_type": self.content_type
3579        })
3580    }
3581
3582    fn as_any(&self) -> &dyn std::any::Any {
3583        self
3584    }
3585}
3586
3587inventory::submit! {
3588    crate::ProblemKindInfo {
3589        kind: "missing-sprockets-file",
3590        detail_fields: &["name", "content_type"],
3591    }
3592}
3593
3594impl Display for MissingSprocketsFile {
3595    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
3596        write!(
3597            f,
3598            "missing sprockets file: {} (type: {})",
3599            self.name, self.content_type
3600        )
3601    }
3602}
3603
3604#[derive(Debug, Clone)]
3605/// Problem representing a missing Xfce desktop environment dependency.
3606///
3607/// This struct is used when a package build requires an Xfce-specific
3608/// dependency package that is not available.
3609pub struct MissingXfceDependency {
3610    /// The name of the missing Xfce dependency package.
3611    pub package: String,
3612}
3613
3614impl MissingXfceDependency {
3615    /// Creates a new MissingXfceDependency instance.
3616    ///
3617    /// # Arguments
3618    /// * `package` - Name of the missing Xfce dependency package
3619    ///
3620    /// # Returns
3621    /// A new MissingXfceDependency instance
3622    pub fn new(package: String) -> Self {
3623        Self { package }
3624    }
3625}
3626
3627impl Problem for MissingXfceDependency {
3628    fn kind(&self) -> Cow<'_, str> {
3629        "missing-xfce-dependency".into()
3630    }
3631
3632    fn json(&self) -> serde_json::Value {
3633        serde_json::json!({
3634            "package": self.package
3635        })
3636    }
3637
3638    fn as_any(&self) -> &dyn std::any::Any {
3639        self
3640    }
3641}
3642
3643inventory::submit! {
3644    crate::ProblemKindInfo {
3645        kind: "missing-xfce-dependency",
3646        detail_fields: &["package"],
3647    }
3648}
3649
3650impl Display for MissingXfceDependency {
3651    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
3652        write!(f, "missing XFCE build dependency: {}", self.package)
3653    }
3654}
3655
3656#[derive(Debug, Clone)]
3657/// Problem representing missing GNOME common build tools and macros.
3658///
3659/// This struct is used when a GNOME-related package build requires the
3660/// gnome-common package, which provides common build tools and macros for GNOME projects.
3661pub struct GnomeCommonMissing;
3662
3663impl Problem for GnomeCommonMissing {
3664    fn kind(&self) -> Cow<'_, str> {
3665        "missing-gnome-common".into()
3666    }
3667
3668    fn json(&self) -> serde_json::Value {
3669        serde_json::json!({})
3670    }
3671
3672    fn as_any(&self) -> &dyn std::any::Any {
3673        self
3674    }
3675}
3676
3677inventory::submit! {
3678    crate::ProblemKindInfo {
3679        kind: "missing-gnome-common",
3680        detail_fields: &[],
3681    }
3682}
3683
3684impl Display for GnomeCommonMissing {
3685    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
3686        write!(f, "gnome-common is not installed")
3687    }
3688}
3689
3690#[derive(Debug, Clone)]
3691/// Problem representing a missing input file for GNU config.status.
3692///
3693/// This struct is used when the GNU autotools config.status script
3694/// is missing one of its required input files.
3695pub struct MissingConfigStatusInput {
3696    /// The path to the missing input file.
3697    pub path: String,
3698}
3699
3700impl MissingConfigStatusInput {
3701    /// Creates a new MissingConfigStatusInput instance.
3702    ///
3703    /// # Arguments
3704    /// * `path` - Path to the missing config.status input file
3705    ///
3706    /// # Returns
3707    /// A new MissingConfigStatusInput instance
3708    pub fn new(path: String) -> Self {
3709        Self { path }
3710    }
3711}
3712
3713impl Problem for MissingConfigStatusInput {
3714    fn kind(&self) -> Cow<'_, str> {
3715        "missing-config.status-input".into()
3716    }
3717
3718    fn json(&self) -> serde_json::Value {
3719        serde_json::json!({
3720            "path": self.path
3721        })
3722    }
3723
3724    fn as_any(&self) -> &dyn std::any::Any {
3725        self
3726    }
3727}
3728
3729impl Display for MissingConfigStatusInput {
3730    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
3731        write!(f, "missing config.status input {}", self.path)
3732    }
3733}
3734
3735#[derive(Debug, Clone)]
3736/// Problem representing a missing GNOME common dependency.
3737///
3738/// This struct is used when a GNOME-related package build requires a dependency
3739/// that is typically provided by or related to the gnome-common infrastructure.
3740pub struct MissingGnomeCommonDependency {
3741    /// The name of the missing GNOME common dependency package.
3742    pub package: String,
3743    /// The minimum required version of the dependency, if specified.
3744    pub minimum_version: Option<String>,
3745}
3746
3747impl MissingGnomeCommonDependency {
3748    /// Creates a new MissingGnomeCommonDependency instance.
3749    ///
3750    /// # Arguments
3751    /// * `package` - Name of the missing GNOME common dependency
3752    /// * `minimum_version` - Optional minimum version requirement
3753    ///
3754    /// # Returns
3755    /// A new MissingGnomeCommonDependency instance
3756    pub fn new(package: String, minimum_version: Option<String>) -> Self {
3757        Self {
3758            package,
3759            minimum_version,
3760        }
3761    }
3762
3763    /// Creates a simple MissingGnomeCommonDependency instance without version constraints.
3764    ///
3765    /// # Arguments
3766    /// * `package` - Name of the missing GNOME common dependency
3767    ///
3768    /// # Returns
3769    /// A new MissingGnomeCommonDependency instance with no version requirements
3770    pub fn simple(package: String) -> Self {
3771        Self::new(package, None)
3772    }
3773}
3774
3775impl Problem for MissingGnomeCommonDependency {
3776    fn kind(&self) -> Cow<'_, str> {
3777        "missing-gnome-common-dependency".into()
3778    }
3779
3780    fn json(&self) -> serde_json::Value {
3781        serde_json::json!({
3782            "package": self.package,
3783            "minimum_version": self.minimum_version
3784        })
3785    }
3786
3787    fn as_any(&self) -> &dyn std::any::Any {
3788        self
3789    }
3790}
3791
3792inventory::submit! {
3793    crate::ProblemKindInfo {
3794        kind: "missing-gnome-common-dependency",
3795        detail_fields: &["package", "minimum_version"],
3796    }
3797}
3798
3799impl Display for MissingGnomeCommonDependency {
3800    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
3801        write!(
3802            f,
3803            "missing gnome-common dependency: {}: (>= {})",
3804            self.package,
3805            self.minimum_version.as_deref().unwrap_or("any")
3806        )
3807    }
3808}
3809
3810#[derive(Debug, Clone)]
3811/// Problem representing a missing input file for GNU Automake.
3812///
3813/// This struct is used when GNU Automake cannot find a required input
3814/// file that it needs to generate build files.
3815pub struct MissingAutomakeInput {
3816    /// The path to the missing input file.
3817    pub path: String,
3818}
3819
3820impl MissingAutomakeInput {
3821    /// Creates a new MissingAutomakeInput instance.
3822    ///
3823    /// # Arguments
3824    /// * `path` - Path to the missing Automake input file
3825    ///
3826    /// # Returns
3827    /// A new MissingAutomakeInput instance
3828    pub fn new(path: String) -> Self {
3829        Self { path }
3830    }
3831}
3832
3833impl Problem for MissingAutomakeInput {
3834    fn kind(&self) -> Cow<'_, str> {
3835        "missing-automake-input".into()
3836    }
3837
3838    fn json(&self) -> serde_json::Value {
3839        serde_json::json!({
3840            "path": self.path
3841        })
3842    }
3843
3844    fn as_any(&self) -> &dyn std::any::Any {
3845        self
3846    }
3847}
3848
3849inventory::submit! {
3850    crate::ProblemKindInfo {
3851        kind: "missing-automake-input",
3852        detail_fields: &["path"],
3853    }
3854}
3855
3856impl Display for MissingAutomakeInput {
3857    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
3858        write!(f, "automake input file {} missing", self.path)
3859    }
3860}
3861
3862#[derive(Debug, Clone)]
3863/// Problem representing a chroot environment that could not be found.
3864///
3865/// This struct is used when a build process tries to use a chroot environment
3866/// (a root directory that appears as the system root to enclosed processes),
3867/// but the specified chroot does not exist.
3868pub struct ChrootNotFound {
3869    /// The path or name of the chroot that could not be found.
3870    pub chroot: String,
3871}
3872
3873impl ChrootNotFound {
3874    /// Creates a new ChrootNotFound instance.
3875    ///
3876    /// # Arguments
3877    /// * `chroot` - Path or name of the chroot that could not be found
3878    ///
3879    /// # Returns
3880    /// A new ChrootNotFound instance
3881    pub fn new(chroot: String) -> Self {
3882        Self { chroot }
3883    }
3884}
3885
3886impl Problem for ChrootNotFound {
3887    fn kind(&self) -> Cow<'_, str> {
3888        "chroot-not-found".into()
3889    }
3890
3891    fn json(&self) -> serde_json::Value {
3892        serde_json::json!({
3893            "chroot": self.chroot
3894        })
3895    }
3896
3897    fn as_any(&self) -> &dyn std::any::Any {
3898        self
3899    }
3900}
3901
3902inventory::submit! {
3903    crate::ProblemKindInfo {
3904        kind: "chroot-not-found",
3905        detail_fields: &["chroot"],
3906    }
3907}
3908
3909impl Display for ChrootNotFound {
3910    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
3911        write!(f, "chroot not found: {}", self.chroot)
3912    }
3913}
3914
3915#[derive(Debug, Clone)]
3916/// Problem representing missing GNU Libtool.
3917///
3918/// This struct is used when a build process requires the GNU Libtool
3919/// utility for creating portable shared libraries, but it is not installed.
3920pub struct MissingLibtool;
3921
3922impl Default for MissingLibtool {
3923    /// Provides a default instance of MissingLibtool.
3924    ///
3925    /// # Returns
3926    /// A new MissingLibtool instance
3927    fn default() -> Self {
3928        Self::new()
3929    }
3930}
3931
3932impl MissingLibtool {
3933    /// Creates a new MissingLibtool instance.
3934    ///
3935    /// # Returns
3936    /// A new MissingLibtool instance
3937    pub fn new() -> Self {
3938        Self
3939    }
3940}
3941
3942impl Problem for MissingLibtool {
3943    fn kind(&self) -> Cow<'_, str> {
3944        "missing-libtool".into()
3945    }
3946
3947    fn json(&self) -> serde_json::Value {
3948        serde_json::json!({})
3949    }
3950
3951    fn as_any(&self) -> &dyn std::any::Any {
3952        self
3953    }
3954}
3955
3956inventory::submit! {
3957    crate::ProblemKindInfo {
3958        kind: "missing-libtool",
3959        detail_fields: &[],
3960    }
3961}
3962
3963impl Display for MissingLibtool {
3964    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
3965        write!(f, "Libtool is missing")
3966    }
3967}
3968
3969#[derive(Debug, Clone)]
3970/// Problem representing missing CMake files.
3971///
3972/// This struct is used when a CMake-based build process cannot find
3973/// required CMake module or configuration files.
3974pub struct CMakeFilesMissing {
3975    /// The names of the missing CMake files.
3976    pub filenames: Vec<String>,
3977    /// The version of CMake that was requested, if specified.
3978    pub version: Option<String>,
3979}
3980
3981impl Problem for CMakeFilesMissing {
3982    fn kind(&self) -> std::borrow::Cow<'_, str> {
3983        "missing-cmake-files".into()
3984    }
3985
3986    fn json(&self) -> serde_json::Value {
3987        serde_json::json!({
3988            "filenames": self.filenames,
3989            "version": self.version,
3990        })
3991    }
3992
3993    fn as_any(&self) -> &dyn std::any::Any {
3994        self
3995    }
3996}
3997
3998inventory::submit! {
3999    crate::ProblemKindInfo {
4000        kind: "missing-cmake-files",
4001        detail_fields: &["filenames", "version"],
4002    }
4003}
4004
4005impl std::fmt::Display for CMakeFilesMissing {
4006    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
4007        write!(f, "CMake files missing: {:?}", self.filenames)
4008    }
4009}
4010
4011#[derive(Debug, Clone)]
4012/// Problem representing missing CMake package components.
4013///
4014/// This struct is used when a CMake-based build process requires specific
4015/// components of a package, but they cannot be found.
4016pub struct MissingCMakeComponents {
4017    /// The name of the CMake package.
4018    pub name: String,
4019    /// The names of the missing components.
4020    pub components: Vec<String>,
4021}
4022
4023impl Problem for MissingCMakeComponents {
4024    fn kind(&self) -> std::borrow::Cow<'_, str> {
4025        "missing-cmake-components".into()
4026    }
4027
4028    fn json(&self) -> serde_json::Value {
4029        serde_json::json!({
4030            "name": self.name,
4031            "components": self.components,
4032        })
4033    }
4034
4035    fn as_any(&self) -> &dyn std::any::Any {
4036        self
4037    }
4038}
4039
4040inventory::submit! {
4041    crate::ProblemKindInfo {
4042        kind: "missing-cmake-components",
4043        detail_fields: &["name", "components"],
4044    }
4045}
4046
4047impl std::fmt::Display for MissingCMakeComponents {
4048    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
4049        write!(f, "Missing CMake components: {:?}", self.components)
4050    }
4051}
4052
4053#[derive(Debug, Clone)]
4054/// Problem representing a missing CMake package configuration.
4055///
4056/// This struct is used when a CMake-based build process cannot find
4057/// a required package configuration file.
4058pub struct MissingCMakeConfig {
4059    /// The name of the CMake package.
4060    pub name: String,
4061    /// The version of the package that was requested, if specified.
4062    pub version: Option<String>,
4063}
4064
4065impl Problem for MissingCMakeConfig {
4066    fn kind(&self) -> std::borrow::Cow<'_, str> {
4067        "missing-cmake-config".into()
4068    }
4069
4070    fn json(&self) -> serde_json::Value {
4071        serde_json::json!({
4072            "name": self.name,
4073            "version": self.version,
4074        })
4075    }
4076
4077    fn as_any(&self) -> &dyn std::any::Any {
4078        self
4079    }
4080}
4081
4082inventory::submit! {
4083    crate::ProblemKindInfo {
4084        kind: "missing-cmake-config",
4085        detail_fields: &["name", "version"],
4086    }
4087}
4088
4089impl std::fmt::Display for MissingCMakeConfig {
4090    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
4091        if let Some(version) = &self.version {
4092            write!(
4093                f,
4094                "Missing CMake package configuration for {} (version {})",
4095                self.name, version
4096            )
4097        } else {
4098            write!(f, "Missing CMake package configuration for {}", self.name)
4099        }
4100    }
4101}
4102
4103#[derive(Debug, Clone)]
4104/// Problem representing a CMake package with mismatched version requirements.
4105///
4106/// This struct is used when a CMake-based build process found a package,
4107/// but it requires an exact version that doesn't match the found version.
4108pub struct CMakeNeedExactVersion {
4109    /// The name of the CMake package.
4110    pub package: String,
4111    /// The version of the package that was found.
4112    pub version_found: String,
4113    /// The exact version required by the build.
4114    pub exact_version_needed: String,
4115    /// The path to the CMake package configuration file.
4116    pub path: PathBuf,
4117}
4118
4119impl Problem for CMakeNeedExactVersion {
4120    fn kind(&self) -> std::borrow::Cow<'_, str> {
4121        "cmake-exact-version-missing".into()
4122    }
4123
4124    fn json(&self) -> serde_json::Value {
4125        serde_json::json!({
4126            "package": self.package,
4127            "version_found": self.version_found,
4128            "exact_version_needed": self.exact_version_needed,
4129            "path": self.path,
4130        })
4131    }
4132
4133    fn as_any(&self) -> &dyn std::any::Any {
4134        self
4135    }
4136}
4137
4138inventory::submit! {
4139    crate::ProblemKindInfo {
4140        kind: "cmake-exact-version-missing",
4141        detail_fields: &["package", "version_found", "exact_version_needed", "path"],
4142    }
4143}
4144
4145impl std::fmt::Display for CMakeNeedExactVersion {
4146    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
4147        write!(
4148            f,
4149            "CMake needs exact package {}, version {}",
4150            self.package, self.exact_version_needed
4151        )
4152    }
4153}
4154
4155#[derive(Debug, Clone)]
4156/// Problem representing a missing static library file.
4157///
4158/// This struct is used when a build process requires a static library
4159/// (typically a .a or .lib file) but it cannot be found.
4160pub struct MissingStaticLibrary {
4161    /// The name of the library (without file extension).
4162    pub library: String,
4163    /// The expected filename of the static library.
4164    pub filename: String,
4165}
4166
4167impl Problem for MissingStaticLibrary {
4168    fn kind(&self) -> std::borrow::Cow<'_, str> {
4169        "missing-static-library".into()
4170    }
4171
4172    fn json(&self) -> serde_json::Value {
4173        serde_json::json!({
4174            "library": self.library,
4175            "filename": self.filename,
4176        })
4177    }
4178
4179    fn as_any(&self) -> &dyn std::any::Any {
4180        self
4181    }
4182}
4183
4184inventory::submit! {
4185    crate::ProblemKindInfo {
4186        kind: "missing-static-library",
4187        detail_fields: &["library", "filename"],
4188    }
4189}
4190
4191impl std::fmt::Display for MissingStaticLibrary {
4192    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
4193        write!(f, "missing static library: {}", self.library)
4194    }
4195}
4196
4197#[derive(Debug, Clone)]
4198/// Problem representing a missing Go runtime.
4199///
4200/// This struct is used when a build process requires the Go language runtime
4201/// but it is not installed or cannot be found in the system.
4202pub struct MissingGoRuntime;
4203
4204impl Problem for MissingGoRuntime {
4205    fn kind(&self) -> std::borrow::Cow<'_, str> {
4206        "missing-go-runtime".into()
4207    }
4208
4209    fn json(&self) -> serde_json::Value {
4210        serde_json::json!({})
4211    }
4212
4213    fn as_any(&self) -> &dyn std::any::Any {
4214        self
4215    }
4216}
4217
4218inventory::submit! {
4219    crate::ProblemKindInfo {
4220        kind: "missing-go-runtime",
4221        detail_fields: &[],
4222    }
4223}
4224
4225impl std::fmt::Display for MissingGoRuntime {
4226    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
4227        write!(f, "go runtime is missing")
4228    }
4229}
4230
4231#[derive(Debug, Clone)]
4232/// Problem representing an unknown SSL/TLS certificate authority.
4233///
4234/// This struct is used when a build process fails to establish a secure connection
4235/// because it cannot verify the certificate authority of a remote server.
4236pub struct UnknownCertificateAuthority(pub String);
4237
4238impl Problem for UnknownCertificateAuthority {
4239    fn kind(&self) -> std::borrow::Cow<'_, str> {
4240        "unknown-certificate-authority".into()
4241    }
4242
4243    fn json(&self) -> serde_json::Value {
4244        serde_json::json!({
4245            "url": self.0
4246        })
4247    }
4248
4249    fn as_any(&self) -> &dyn std::any::Any {
4250        self
4251    }
4252}
4253
4254inventory::submit! {
4255    crate::ProblemKindInfo {
4256        kind: "unknown-certificate-authority",
4257        detail_fields: &["url"],
4258    }
4259}
4260
4261impl std::fmt::Display for UnknownCertificateAuthority {
4262    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
4263        write!(f, "Unknown Certificate Authority for {}", self.0)
4264    }
4265}
4266
4267#[derive(Debug, Clone)]
4268/// Problem representing a missing predeclared function in Perl.
4269///
4270/// This struct is used when a Perl script tries to use a predeclared function
4271/// that is not available, often because a required module is not loaded.
4272pub struct MissingPerlPredeclared(pub String);
4273
4274impl Problem for MissingPerlPredeclared {
4275    fn kind(&self) -> std::borrow::Cow<'_, str> {
4276        "missing-perl-predeclared".into()
4277    }
4278
4279    fn json(&self) -> serde_json::Value {
4280        serde_json::json!({
4281            "name": self.0
4282        })
4283    }
4284
4285    fn as_any(&self) -> &dyn std::any::Any {
4286        self
4287    }
4288}
4289
4290inventory::submit! {
4291    crate::ProblemKindInfo {
4292        kind: "missing-perl-predeclared",
4293        detail_fields: &["name"],
4294    }
4295}
4296
4297impl std::fmt::Display for MissingPerlPredeclared {
4298    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
4299        write!(f, "missing predeclared function: {}", self.0)
4300    }
4301}
4302
4303#[derive(Debug, Clone)]
4304/// Problem representing missing Git user identity configuration.
4305///
4306/// This struct is used when Git operations that require user identity
4307/// (like commits) fail because the user.name and user.email are not configured.
4308pub struct MissingGitIdentity;
4309
4310impl Problem for MissingGitIdentity {
4311    fn kind(&self) -> std::borrow::Cow<'_, str> {
4312        "missing-git-identity".into()
4313    }
4314
4315    fn json(&self) -> serde_json::Value {
4316        serde_json::json!({})
4317    }
4318
4319    fn as_any(&self) -> &dyn std::any::Any {
4320        self
4321    }
4322}
4323
4324inventory::submit! {
4325    crate::ProblemKindInfo {
4326        kind: "missing-git-identity",
4327        detail_fields: &[],
4328    }
4329}
4330
4331impl std::fmt::Display for MissingGitIdentity {
4332    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
4333        write!(f, "Missing Git Identity")
4334    }
4335}
4336
4337#[derive(Debug, Clone)]
4338/// Problem representing a missing GPG secret key.
4339///
4340/// This struct is used when an operation requires a GPG secret key
4341/// (such as signing packages or commits) but no secret key is available.
4342pub struct MissingSecretGpgKey;
4343
4344impl Problem for MissingSecretGpgKey {
4345    fn kind(&self) -> std::borrow::Cow<'_, str> {
4346        "no-secret-gpg-key".into()
4347    }
4348
4349    fn json(&self) -> serde_json::Value {
4350        serde_json::json!({})
4351    }
4352
4353    fn as_any(&self) -> &dyn std::any::Any {
4354        self
4355    }
4356}
4357
4358inventory::submit! {
4359    crate::ProblemKindInfo {
4360        kind: "no-secret-gpg-key",
4361        detail_fields: &[],
4362    }
4363}
4364
4365impl std::fmt::Display for MissingSecretGpgKey {
4366    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
4367        write!(f, "No secret GPG key is present")
4368    }
4369}
4370
4371#[derive(Debug, Clone)]
4372/// Problem representing missing version information for vcversioner.
4373///
4374/// This struct is used when the vcversioner Python package cannot determine
4375/// the version from either a Git directory or a version.txt file.
4376pub struct MissingVcVersionerVersion;
4377
4378impl Problem for MissingVcVersionerVersion {
4379    fn kind(&self) -> std::borrow::Cow<'_, str> {
4380        "no-vcversioner-version".into()
4381    }
4382
4383    fn json(&self) -> serde_json::Value {
4384        serde_json::json!({})
4385    }
4386
4387    fn as_any(&self) -> &dyn std::any::Any {
4388        self
4389    }
4390}
4391
4392inventory::submit! {
4393    crate::ProblemKindInfo {
4394        kind: "no-vcversioner-version",
4395        detail_fields: &[],
4396    }
4397}
4398
4399impl std::fmt::Display for MissingVcVersionerVersion {
4400    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
4401        write!(
4402            f,
4403            "vcversion could not find a git directory or version.txt file"
4404        )
4405    }
4406}
4407
4408#[derive(Debug, Clone)]
4409/// Problem representing a missing LaTeX file.
4410///
4411/// This struct is used when a LaTeX build process requires a file
4412/// (such as a class file, style file, or content file) but it cannot be found.
4413pub struct MissingLatexFile(pub String);
4414
4415impl MissingLatexFile {
4416    /// Creates a new MissingLatexFile instance.
4417    ///
4418    /// # Arguments
4419    /// * `filename` - Name of the missing LaTeX file
4420    ///
4421    /// # Returns
4422    /// A new MissingLatexFile instance
4423    pub fn new(filename: String) -> Self {
4424        Self(filename)
4425    }
4426}
4427
4428impl Problem for MissingLatexFile {
4429    fn kind(&self) -> std::borrow::Cow<'_, str> {
4430        "missing-latex-file".into()
4431    }
4432
4433    fn json(&self) -> serde_json::Value {
4434        serde_json::json!({
4435            "filename": self.0
4436        })
4437    }
4438
4439    fn as_any(&self) -> &dyn std::any::Any {
4440        self
4441    }
4442}
4443
4444inventory::submit! {
4445    crate::ProblemKindInfo {
4446        kind: "missing-latex-file",
4447        detail_fields: &["filename"],
4448    }
4449}
4450
4451impl std::fmt::Display for MissingLatexFile {
4452    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
4453        write!(f, "Missing LaTeX file: {}", self.0)
4454    }
4455}
4456
4457#[derive(Debug, Clone)]
4458/// Problem representing a missing X Window System display.
4459///
4460/// This struct is used when a program requires an X11 display connection
4461/// but no display server is available (such as in headless environments).
4462pub struct MissingXDisplay;
4463
4464impl Problem for MissingXDisplay {
4465    fn kind(&self) -> std::borrow::Cow<'_, str> {
4466        "missing-x-display".into()
4467    }
4468
4469    fn json(&self) -> serde_json::Value {
4470        serde_json::json!({})
4471    }
4472
4473    fn as_any(&self) -> &dyn std::any::Any {
4474        self
4475    }
4476}
4477
4478inventory::submit! {
4479    crate::ProblemKindInfo {
4480        kind: "missing-x-display",
4481        detail_fields: &[],
4482    }
4483}
4484
4485impl std::fmt::Display for MissingXDisplay {
4486    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
4487        write!(f, "No X Display")
4488    }
4489}
4490
4491#[derive(Debug, Clone)]
4492/// Problem representing a missing font specification in LaTeX.
4493///
4494/// This struct is used when a LaTeX document requires a specific font
4495/// but the fontspec package cannot find it.
4496pub struct MissingFontspec(pub String);
4497
4498impl Problem for MissingFontspec {
4499    fn kind(&self) -> std::borrow::Cow<'_, str> {
4500        "missing-fontspec".into()
4501    }
4502
4503    fn json(&self) -> serde_json::Value {
4504        serde_json::json!({
4505            "fontspec": self.0
4506        })
4507    }
4508
4509    fn as_any(&self) -> &dyn std::any::Any {
4510        self
4511    }
4512}
4513
4514inventory::submit! {
4515    crate::ProblemKindInfo {
4516        kind: "missing-fontspec",
4517        detail_fields: &["fontspec"],
4518    }
4519}
4520
4521impl std::fmt::Display for MissingFontspec {
4522    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
4523        write!(f, "Missing font spec: {}", self.0)
4524    }
4525}
4526
4527#[derive(Debug, Clone)]
4528/// Problem representing a process killed due to inactivity.
4529///
4530/// This struct is used when a build process was killed by the system
4531/// because it was inactive for too long.
4532pub struct InactiveKilled(pub i64);
4533
4534impl Problem for InactiveKilled {
4535    fn kind(&self) -> std::borrow::Cow<'_, str> {
4536        "inactive-killed".into()
4537    }
4538
4539    fn json(&self) -> serde_json::Value {
4540        serde_json::json!({
4541            "minutes": self.0
4542        })
4543    }
4544
4545    fn as_any(&self) -> &dyn std::any::Any {
4546        self
4547    }
4548}
4549
4550inventory::submit! {
4551    crate::ProblemKindInfo {
4552        kind: "inactive-killed",
4553        detail_fields: &["minutes"],
4554    }
4555}
4556
4557impl std::fmt::Display for InactiveKilled {
4558    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
4559        write!(f, "Killed due to inactivity after {} minutes", self.0)
4560    }
4561}
4562
4563#[derive(Debug, Clone)]
4564/// Problem representing missing PAUSE credentials for Perl module upload.
4565///
4566/// This struct is used when attempting to upload a Perl module to PAUSE
4567/// (Perl Authors Upload Server) without proper authentication credentials.
4568pub struct MissingPauseCredentials;
4569
4570impl Problem for MissingPauseCredentials {
4571    fn kind(&self) -> std::borrow::Cow<'_, str> {
4572        "missing-pause-credentials".into()
4573    }
4574
4575    fn json(&self) -> serde_json::Value {
4576        serde_json::json!({})
4577    }
4578
4579    fn as_any(&self) -> &dyn std::any::Any {
4580        self
4581    }
4582}
4583
4584inventory::submit! {
4585    crate::ProblemKindInfo {
4586        kind: "missing-pause-credentials",
4587        detail_fields: &[],
4588    }
4589}
4590
4591impl std::fmt::Display for MissingPauseCredentials {
4592    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
4593        write!(f, "Missing credentials for PAUSE")
4594    }
4595}
4596
4597#[derive(Debug, Clone)]
4598/// Problem representing mismatched gettext versions.
4599///
4600/// This struct is used when there's a version mismatch between gettext versions
4601/// referenced in Makefile.in.in and the autoconf macros.
4602pub struct MismatchGettextVersions {
4603    /// The gettext version specified in the Makefile.
4604    pub makefile_version: String,
4605    /// The gettext version specified in autoconf macros.
4606    pub autoconf_version: String,
4607}
4608
4609impl Problem for MismatchGettextVersions {
4610    fn kind(&self) -> std::borrow::Cow<'_, str> {
4611        "mismatch-gettext-versions".into()
4612    }
4613
4614    fn json(&self) -> serde_json::Value {
4615        serde_json::json!({
4616            "makefile_version": self.makefile_version,
4617            "autoconf_version": self.autoconf_version
4618        })
4619    }
4620
4621    fn as_any(&self) -> &dyn std::any::Any {
4622        self
4623    }
4624}
4625
4626inventory::submit! {
4627    crate::ProblemKindInfo {
4628        kind: "mismatch-gettext-versions",
4629        detail_fields: &["makefile_version", "autoconf_version"],
4630    }
4631}
4632
4633impl std::fmt::Display for MismatchGettextVersions {
4634    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
4635        write!(
4636            f,
4637            "Mismatch versions ({}, {})",
4638            self.makefile_version, self.autoconf_version
4639        )
4640    }
4641}
4642
4643#[derive(Debug, Clone)]
4644/// Problem representing an invalid current user for a build operation.
4645///
4646/// This struct is used when a build process encounters issues because
4647/// it's running under an unexpected or inappropriate user account.
4648pub struct InvalidCurrentUser(pub String);
4649
4650impl Problem for InvalidCurrentUser {
4651    fn kind(&self) -> std::borrow::Cow<'_, str> {
4652        "invalid-current-user".into()
4653    }
4654
4655    fn json(&self) -> serde_json::Value {
4656        serde_json::json!({
4657            "user": self.0
4658        })
4659    }
4660
4661    fn as_any(&self) -> &dyn std::any::Any {
4662        self
4663    }
4664}
4665
4666inventory::submit! {
4667    crate::ProblemKindInfo {
4668        kind: "invalid-current-user",
4669        detail_fields: &["user"],
4670    }
4671}
4672
4673impl std::fmt::Display for InvalidCurrentUser {
4674    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
4675        write!(f, "Can not run as {}", self.0)
4676    }
4677}
4678
4679#[derive(Debug, Clone)]
4680/// Problem representing a missing GNU lib directory.
4681///
4682/// This struct is used when a build process requires a gnulib directory
4683/// (a collection of portable GNU utility functions) but it cannot be found.
4684pub struct MissingGnulibDirectory(pub PathBuf);
4685
4686impl Problem for MissingGnulibDirectory {
4687    fn kind(&self) -> std::borrow::Cow<'_, str> {
4688        "missing-gnulib-directory".into()
4689    }
4690
4691    fn json(&self) -> serde_json::Value {
4692        serde_json::json!({
4693            "directory": self.0
4694        })
4695    }
4696
4697    fn as_any(&self) -> &dyn std::any::Any {
4698        self
4699    }
4700}
4701
4702inventory::submit! {
4703    crate::ProblemKindInfo {
4704        kind: "missing-gnulib-directory",
4705        detail_fields: &["directory"],
4706    }
4707}
4708
4709impl std::fmt::Display for MissingGnulibDirectory {
4710    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
4711        write!(f, "Missing gnulib directory: {}", self.0.display())
4712    }
4713}
4714
4715#[derive(Debug, Clone)]
4716/// Problem representing a missing Lua module.
4717///
4718/// This struct is used when a Lua script or application attempts to
4719/// load a Lua module that is not installed or cannot be found.
4720pub struct MissingLuaModule(pub String);
4721
4722impl Problem for MissingLuaModule {
4723    fn kind(&self) -> std::borrow::Cow<'_, str> {
4724        "missing-lua-module".into()
4725    }
4726
4727    fn json(&self) -> serde_json::Value {
4728        serde_json::json!({
4729            "module": self.0
4730        })
4731    }
4732
4733    fn as_any(&self) -> &dyn std::any::Any {
4734        self
4735    }
4736}
4737
4738inventory::submit! {
4739    crate::ProblemKindInfo {
4740        kind: "missing-lua-module",
4741        detail_fields: &["module"],
4742    }
4743}
4744
4745impl std::fmt::Display for MissingLuaModule {
4746    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
4747        write!(f, "Missing Lua Module: {}", self.0)
4748    }
4749}
4750
4751#[derive(Debug, Clone)]
4752/// Problem representing a missing Go module file.
4753///
4754/// This struct is used when a Go project requires a go.mod file for
4755/// module and dependency management, but the file is missing.
4756pub struct MissingGoModFile;
4757
4758impl Problem for MissingGoModFile {
4759    fn kind(&self) -> std::borrow::Cow<'_, str> {
4760        "missing-go.mod-file".into()
4761    }
4762
4763    fn json(&self) -> serde_json::Value {
4764        serde_json::json!({})
4765    }
4766
4767    fn as_any(&self) -> &dyn std::any::Any {
4768        self
4769    }
4770}
4771
4772impl std::fmt::Display for MissingGoModFile {
4773    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
4774        write!(f, "go.mod file is missing")
4775    }
4776}
4777
4778#[derive(Debug, Clone)]
4779/// Problem representing an outdated Go module file.
4780///
4781/// This struct is used when a Go project's go.mod file needs to be
4782/// updated due to changes in dependencies or Go version requirements.
4783pub struct OutdatedGoModFile;
4784
4785impl Problem for OutdatedGoModFile {
4786    fn kind(&self) -> std::borrow::Cow<'_, str> {
4787        "outdated-go.mod-file".into()
4788    }
4789
4790    fn json(&self) -> serde_json::Value {
4791        serde_json::json!({})
4792    }
4793
4794    fn as_any(&self) -> &dyn std::any::Any {
4795        self
4796    }
4797}
4798
4799impl std::fmt::Display for OutdatedGoModFile {
4800    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
4801        write!(f, "go.mod file is outdated")
4802    }
4803}
4804
4805#[derive(Debug, Clone)]
4806/// Problem representing insufficient code test coverage.
4807///
4808/// This struct is used when a build process requires a minimum level of
4809/// code test coverage, but the actual coverage is below the required threshold.
4810pub struct CodeCoverageTooLow {
4811    /// The actual code coverage percentage achieved.
4812    pub actual: f64,
4813    /// The minimum code coverage percentage required.
4814    pub required: f64,
4815}
4816
4817impl Problem for CodeCoverageTooLow {
4818    fn kind(&self) -> std::borrow::Cow<'_, str> {
4819        "code-coverage-too-low".into()
4820    }
4821
4822    fn json(&self) -> serde_json::Value {
4823        serde_json::json!({
4824            "actual": self.actual,
4825            "required": self.required
4826        })
4827    }
4828
4829    fn as_any(&self) -> &dyn std::any::Any {
4830        self
4831    }
4832}
4833
4834inventory::submit! {
4835    crate::ProblemKindInfo {
4836        kind: "code-coverage-too-low",
4837        detail_fields: &["actual", "required"],
4838    }
4839}
4840
4841impl std::fmt::Display for CodeCoverageTooLow {
4842    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
4843        write!(
4844            f,
4845            "Code coverage too low: {:.2} < {:.2}",
4846            self.actual, self.required
4847        )
4848    }
4849}
4850
4851#[derive(Debug, Clone)]
4852/// Problem representing improper usage of ES modules.
4853///
4854/// This struct is used when a JavaScript module is using CommonJS require()
4855/// syntax to load an ES module, which must be loaded with import() instead.
4856pub struct ESModuleMustUseImport(pub String);
4857
4858impl Problem for ESModuleMustUseImport {
4859    fn kind(&self) -> std::borrow::Cow<'_, str> {
4860        "esmodule-must-use-import".into()
4861    }
4862
4863    fn json(&self) -> serde_json::Value {
4864        serde_json::json!({
4865            "path": self.0
4866        })
4867    }
4868
4869    fn as_any(&self) -> &dyn std::any::Any {
4870        self
4871    }
4872}
4873
4874inventory::submit! {
4875    crate::ProblemKindInfo {
4876        kind: "esmodule-must-use-import",
4877        detail_fields: &["path"],
4878    }
4879}
4880
4881impl std::fmt::Display for ESModuleMustUseImport {
4882    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
4883        write!(f, "ESM-only module {} must use import()", self.0)
4884    }
4885}
4886
4887#[derive(Debug, Clone)]
4888/// Problem representing a missing PHP extension.
4889///
4890/// This struct is used when a PHP application requires an extension
4891/// (like mysqli, gd, intl, etc.) that is not installed or enabled.
4892pub struct MissingPHPExtension(pub String);
4893
4894impl Problem for MissingPHPExtension {
4895    fn kind(&self) -> std::borrow::Cow<'_, str> {
4896        "missing-php-extension".into()
4897    }
4898
4899    fn json(&self) -> serde_json::Value {
4900        serde_json::json!({
4901            "extension": self.0
4902        })
4903    }
4904
4905    fn as_any(&self) -> &dyn std::any::Any {
4906        self
4907    }
4908}
4909
4910inventory::submit! {
4911    crate::ProblemKindInfo {
4912        kind: "missing-php-extension",
4913        detail_fields: &["extension"],
4914    }
4915}
4916
4917impl std::fmt::Display for MissingPHPExtension {
4918    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
4919        write!(f, "Missing PHP Extension: {}", self.0)
4920    }
4921}
4922
4923#[derive(Debug, Clone)]
4924/// Problem representing an outdated minimum autoconf version requirement.
4925///
4926/// This struct is used when a project's configure script specifies a minimum autoconf
4927/// version that is considered too old for modern builds.
4928pub struct MinimumAutoconfTooOld(pub String);
4929
4930impl Problem for MinimumAutoconfTooOld {
4931    fn kind(&self) -> std::borrow::Cow<'_, str> {
4932        "minimum-autoconf-too-old".into()
4933    }
4934
4935    fn json(&self) -> serde_json::Value {
4936        serde_json::json!({
4937            "minimum_version": self.0
4938        })
4939    }
4940
4941    fn as_any(&self) -> &dyn std::any::Any {
4942        self
4943    }
4944}
4945
4946inventory::submit! {
4947    crate::ProblemKindInfo {
4948        kind: "minimum-autoconf-too-old",
4949        detail_fields: &["minimum_version"],
4950    }
4951}
4952
4953impl std::fmt::Display for MinimumAutoconfTooOld {
4954    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
4955        write!(
4956            f,
4957            "configure.{{ac,in}} should require newer autoconf {}",
4958            self.0
4959        )
4960    }
4961}
4962
4963#[derive(Debug, Clone)]
4964/// Problem representing a missing file in a Perl distribution.
4965///
4966/// This struct is used when a Perl module build or installation process
4967/// cannot find a required file that should be part of the distribution.
4968pub struct MissingPerlDistributionFile(pub String);
4969
4970impl Problem for MissingPerlDistributionFile {
4971    fn kind(&self) -> std::borrow::Cow<'_, str> {
4972        "missing-perl-distribution-file".into()
4973    }
4974
4975    fn json(&self) -> serde_json::Value {
4976        serde_json::json!({
4977            "filename": self.0
4978        })
4979    }
4980
4981    fn as_any(&self) -> &dyn std::any::Any {
4982        self
4983    }
4984}
4985
4986inventory::submit! {
4987    crate::ProblemKindInfo {
4988        kind: "missing-perl-distribution-file",
4989        detail_fields: &["filename"],
4990    }
4991}
4992
4993impl std::fmt::Display for MissingPerlDistributionFile {
4994    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
4995        write!(f, "Missing perl distribution file: {}", self.0)
4996    }
4997}
4998
4999#[derive(Debug, Clone)]
5000/// Problem representing a missing entry in the Go checksum file.
5001///
5002/// This struct is used when a Go project requires an entry in the go.sum file
5003/// for a specific package version, but the entry is missing.
5004pub struct MissingGoSumEntry {
5005    /// The package import path.
5006    pub package: String,
5007    /// The version of the package.
5008    pub version: String,
5009}
5010
5011impl Problem for MissingGoSumEntry {
5012    fn kind(&self) -> std::borrow::Cow<'_, str> {
5013        "missing-go.sum-entry".into()
5014    }
5015
5016    fn json(&self) -> serde_json::Value {
5017        serde_json::json!({
5018            "package": self.package,
5019            "version": self.version
5020        })
5021    }
5022
5023    fn as_any(&self) -> &dyn std::any::Any {
5024        self
5025    }
5026}
5027
5028impl std::fmt::Display for MissingGoSumEntry {
5029    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
5030        write!(f, "Missing go.sum entry: {}@{}", self.package, self.version)
5031    }
5032}
5033
5034#[derive(Debug, Clone)]
5035/// Problem representing an issue with the Vala compiler.
5036///
5037/// This struct is used when the Vala compiler (valac) encounters
5038/// an error that prevents it from compiling Vala source code.
5039pub struct ValaCompilerCannotCompile;
5040
5041impl Problem for ValaCompilerCannotCompile {
5042    fn kind(&self) -> std::borrow::Cow<'_, str> {
5043        "valac-cannot-compile".into()
5044    }
5045
5046    fn json(&self) -> serde_json::Value {
5047        serde_json::json!({})
5048    }
5049
5050    fn as_any(&self) -> &dyn std::any::Any {
5051        self
5052    }
5053}
5054
5055inventory::submit! {
5056    crate::ProblemKindInfo {
5057        kind: "valac-cannot-compile",
5058        detail_fields: &[],
5059    }
5060}
5061
5062impl std::fmt::Display for ValaCompilerCannotCompile {
5063    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
5064        write!(f, "valac can not compile")
5065    }
5066}
5067
5068#[derive(Debug, Clone)]
5069/// Problem representing a missing Debian build dependency.
5070///
5071/// This struct is used when a Debian package build requires a dependency
5072/// that is listed in Build-Depends but is not installed in the build environment.
5073pub struct MissingDebianBuildDep(pub String);
5074
5075impl Problem for MissingDebianBuildDep {
5076    fn kind(&self) -> std::borrow::Cow<'_, str> {
5077        "missing-debian-build-dep".into()
5078    }
5079
5080    fn json(&self) -> serde_json::Value {
5081        serde_json::json!({
5082            "dep": self.0
5083        })
5084    }
5085
5086    fn as_any(&self) -> &dyn std::any::Any {
5087        self
5088    }
5089}
5090
5091inventory::submit! {
5092    crate::ProblemKindInfo {
5093        kind: "missing-debian-build-dep",
5094        detail_fields: &["dep"],
5095    }
5096}
5097
5098impl std::fmt::Display for MissingDebianBuildDep {
5099    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
5100        write!(f, "Missing Debian Build-Depends: {}", self.0)
5101    }
5102}
5103
5104#[derive(Debug, Clone)]
5105/// Problem representing missing Qt modules.
5106///
5107/// This struct is used when a build process requires specific Qt modules
5108/// (like QtCore, QtGui, QtWidgets, etc.) that are not available.
5109pub struct MissingQtModules(pub Vec<String>);
5110
5111impl Problem for MissingQtModules {
5112    fn kind(&self) -> std::borrow::Cow<'_, str> {
5113        "missing-qt-modules".into()
5114    }
5115
5116    fn json(&self) -> serde_json::Value {
5117        serde_json::json!({
5118            "modules": self.0
5119        })
5120    }
5121
5122    fn as_any(&self) -> &dyn std::any::Any {
5123        self
5124    }
5125}
5126
5127inventory::submit! {
5128    crate::ProblemKindInfo {
5129        kind: "missing-qt-modules",
5130        detail_fields: &["modules"],
5131    }
5132}
5133
5134impl std::fmt::Display for MissingQtModules {
5135    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
5136        write!(f, "Missing QT modules: {:?}", self.0)
5137    }
5138}
5139
5140#[derive(Debug, Clone)]
5141/// Problem representing a missing OCaml package.
5142///
5143/// This struct is used when an OCaml project requires a package
5144/// that is not installed or cannot be found in the OCaml environment.
5145pub struct MissingOCamlPackage(pub String);
5146
5147impl Problem for MissingOCamlPackage {
5148    fn kind(&self) -> std::borrow::Cow<'_, str> {
5149        "missing-ocaml-package".into()
5150    }
5151
5152    fn json(&self) -> serde_json::Value {
5153        serde_json::json!({
5154            "package": self.0
5155        })
5156    }
5157
5158    fn as_any(&self) -> &dyn std::any::Any {
5159        self
5160    }
5161}
5162
5163inventory::submit! {
5164    crate::ProblemKindInfo {
5165        kind: "missing-ocaml-package",
5166        detail_fields: &["package"],
5167    }
5168}
5169
5170impl std::fmt::Display for MissingOCamlPackage {
5171    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
5172        write!(f, "Missing OCaml package: {}", self.0)
5173    }
5174}
5175
5176#[derive(Debug, Clone)]
5177/// Problem representing a "too many open files" error.
5178///
5179/// This struct is used when a process hits the system limit for the number
5180/// of files that can be opened simultaneously, often due to a resource leak.
5181pub struct TooManyOpenFiles;
5182
5183impl Problem for TooManyOpenFiles {
5184    fn kind(&self) -> std::borrow::Cow<'_, str> {
5185        "too-many-open-files".into()
5186    }
5187
5188    fn json(&self) -> serde_json::Value {
5189        serde_json::json!({})
5190    }
5191
5192    fn as_any(&self) -> &dyn std::any::Any {
5193        self
5194    }
5195}
5196
5197inventory::submit! {
5198    crate::ProblemKindInfo {
5199        kind: "too-many-open-files",
5200        detail_fields: &[],
5201    }
5202}
5203
5204impl std::fmt::Display for TooManyOpenFiles {
5205    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
5206        write!(f, "Too many open files")
5207    }
5208}
5209
5210#[derive(Debug, Clone)]
5211/// Problem representing a missing Makefile target.
5212///
5213/// This struct is used when a build process tries to run a make target
5214/// that doesn't exist in the Makefile.
5215pub struct MissingMakeTarget(pub String, pub Option<String>);
5216
5217impl MissingMakeTarget {
5218    /// Creates a new MissingMakeTarget instance.
5219    ///
5220    /// # Arguments
5221    /// * `target` - The name of the missing make target
5222    /// * `required_by` - Optional name of the entity that requires this target
5223    ///
5224    /// # Returns
5225    /// A new MissingMakeTarget instance
5226    pub fn new(target: &str, required_by: Option<&str>) -> Self {
5227        Self(target.to_string(), required_by.map(String::from))
5228    }
5229
5230    /// Creates a simple MissingMakeTarget instance without specifying what requires it.
5231    ///
5232    /// # Arguments
5233    /// * `target` - The name of the missing make target
5234    ///
5235    /// # Returns
5236    /// A new MissingMakeTarget instance with no requirer information
5237    pub fn simple(target: &str) -> Self {
5238        Self::new(target, None)
5239    }
5240}
5241
5242impl std::fmt::Display for MissingMakeTarget {
5243    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
5244        write!(f, "Unknown make target: {}", self.0)
5245    }
5246}
5247
5248impl Problem for MissingMakeTarget {
5249    fn kind(&self) -> std::borrow::Cow<'_, str> {
5250        "missing-make-target".into()
5251    }
5252
5253    fn json(&self) -> serde_json::Value {
5254        serde_json::json!({
5255            "target": self.0,
5256            "required_by": self.1
5257        })
5258    }
5259
5260    fn as_any(&self) -> &dyn std::any::Any {
5261        self
5262    }
5263}
5264
5265inventory::submit! {
5266    crate::ProblemKindInfo {
5267        kind: "missing-make-target",
5268        detail_fields: &["target", "required_by"],
5269    }
5270}