Skip to main content

trait_kit/core/
error.rs

1// Copyright © 2026 Kirky.X. All rights reserved.
2
3//! Build error types for module initialization.
4
5use std::error::Error;
6use std::fmt;
7
8/// Default build error type for modules.
9///
10/// Modules can use this enum or define their own error type that satisfies
11/// `std::error::Error + Send + Sync + 'static`.
12#[derive(Debug)]
13pub enum BuildError {
14    /// Configuration was not provided to the builder.
15    MissingConfig { module: &'static str },
16
17    /// Requirements (dependencies) were not provided to the builder.
18    MissingRequirements { module: &'static str },
19
20    /// Configuration content is invalid.
21    InvalidConfig {
22        module: &'static str,
23        reason: &'static str,
24    },
25
26    /// Module-specific failure with source error.
27    ModuleFailed {
28        module: &'static str,
29        source: Box<dyn Error + Send + Sync>,
30    },
31}
32
33impl fmt::Display for BuildError {
34    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
35        match self {
36            BuildError::MissingConfig { module } => {
37                write!(f, "missing config for module `{module}`")
38            }
39            BuildError::MissingRequirements { module } => {
40                write!(f, "missing requirements for module `{module}`")
41            }
42            BuildError::InvalidConfig { module, reason } => {
43                write!(f, "invalid config for module `{module}`: {reason}")
44            }
45            BuildError::ModuleFailed { module, source } => {
46                write!(f, "module `{module}` failed: {source}")
47            }
48        }
49    }
50}
51
52impl Error for BuildError {
53    fn source(&self) -> Option<&(dyn Error + 'static)> {
54        match self {
55            BuildError::ModuleFailed { source, .. } => Some(source.as_ref()),
56            _ => None,
57        }
58    }
59}
60
61#[cfg(test)]
62mod tests {
63    use super::*;
64
65    #[test]
66    fn missing_config_display() {
67        let error = BuildError::MissingConfig {
68            module: "my_module",
69        };
70        let msg = error.to_string();
71        assert!(msg.contains("my_module"));
72        assert!(msg.contains("missing config"));
73        assert!(error.source().is_none());
74    }
75
76    #[test]
77    fn missing_requirements_display() {
78        let error = BuildError::MissingRequirements {
79            module: "test_module",
80        };
81        let msg = error.to_string();
82        assert!(msg.contains("test_module"));
83        assert!(msg.contains("missing requirements"));
84        assert!(error.source().is_none());
85    }
86
87    #[test]
88    fn invalid_config_display() {
89        let error = BuildError::InvalidConfig {
90            module: "cfg_module",
91            reason: "bad value",
92        };
93        let msg = error.to_string();
94        assert!(msg.contains("cfg_module"));
95        assert!(msg.contains("invalid config"));
96        assert!(msg.contains("bad value"));
97        assert!(error.source().is_none());
98    }
99
100    #[test]
101    fn module_failed_display_and_source() {
102        let inner = std::io::Error::other("inner error");
103        let error = BuildError::ModuleFailed {
104            module: "fail_mod",
105            source: Box::new(inner),
106        };
107        let msg = error.to_string();
108        assert!(msg.contains("fail_mod"));
109        assert!(msg.contains("failed"));
110        assert!(msg.contains("inner error"));
111        assert!(error.source().is_some());
112    }
113}