nika_media/tools/
error.rs1#[derive(Debug, thiserror::Error)]
8pub enum MediaToolError {
9 #[error("[NIKA-290] nika:{tool}: {reason}")]
11 ToolError { tool: String, reason: String },
12
13 #[error("[NIKA-291] nika:{tool}: unsupported format '{mime}'")]
15 UnsupportedFormat { tool: String, mime: String },
16
17 #[error("[NIKA-292] nika:{tool}: feature '{feature}' required but not enabled")]
19 DependencyMissing { tool: String, feature: String },
20
21 #[error("[NIKA-293] nika:{tool}: operation timed out")]
23 Timeout { tool: String },
24
25 #[error("[NIKA-294] nika:{tool}: {reason}")]
27 InvalidArgs { tool: String, reason: String },
28
29 #[error("[NIKA-295] nika:pipeline: step {step} failed: {reason}")]
31 PipelineStepFailed { step: usize, reason: String },
32
33 #[error("[NIKA-296] nika:pipeline: pipeline has no steps")]
35 PipelineEmpty,
36
37 #[error("[NIKA-297] nika:{tool}: security violation: {reason}")]
39 SecurityViolation { tool: String, reason: String },
40
41 #[error(transparent)]
43 Media(#[from] crate::error::MediaError),
44}
45
46pub type Result<T> = std::result::Result<T, MediaToolError>;
48
49pub fn tool_error(tool: &str, reason: impl Into<String>) -> MediaToolError {
53 MediaToolError::ToolError {
54 tool: tool.to_string(),
55 reason: reason.into(),
56 }
57}
58
59pub fn unsupported_format(tool: &str, mime: &str) -> MediaToolError {
61 MediaToolError::UnsupportedFormat {
62 tool: tool.to_string(),
63 mime: mime.to_string(),
64 }
65}
66
67#[allow(dead_code)]
69pub fn dependency_missing(tool: &str, feature: &str) -> MediaToolError {
70 MediaToolError::DependencyMissing {
71 tool: tool.to_string(),
72 feature: feature.to_string(),
73 }
74}
75
76pub fn timeout_error(tool: &str) -> MediaToolError {
78 MediaToolError::Timeout {
79 tool: tool.to_string(),
80 }
81}
82
83pub fn invalid_args(tool: &str, reason: impl Into<String>) -> MediaToolError {
85 MediaToolError::InvalidArgs {
86 tool: tool.to_string(),
87 reason: reason.into(),
88 }
89}
90
91pub fn pipeline_step_failed(step: usize, reason: impl Into<String>) -> MediaToolError {
93 MediaToolError::PipelineStepFailed {
94 step,
95 reason: reason.into(),
96 }
97}
98
99pub fn pipeline_empty() -> MediaToolError {
101 MediaToolError::PipelineEmpty
102}
103
104pub fn security_violation(tool: &str, reason: impl Into<String>) -> MediaToolError {
106 MediaToolError::SecurityViolation {
107 tool: tool.to_string(),
108 reason: reason.into(),
109 }
110}
111
112#[cfg(test)]
113mod tests {
114 use super::*;
115
116 #[test]
117 fn tool_error_contains_code() {
118 let err = tool_error("thumbnail", "decode failed");
119 assert!(err.to_string().contains("NIKA-290"));
120 assert!(err.to_string().contains("decode failed"));
121 }
122
123 #[test]
124 fn unsupported_format_contains_mime() {
125 let err = unsupported_format("dimensions", "audio/wav");
126 assert!(err.to_string().contains("NIKA-291"));
127 assert!(err.to_string().contains("audio/wav"));
128 }
129
130 #[test]
131 fn dependency_missing_shows_feature() {
132 let err = dependency_missing("thumbnail", "media-thumbnail");
133 assert!(err.to_string().contains("NIKA-292"));
134 assert!(err.to_string().contains("media-thumbnail"));
135 }
136
137 #[test]
138 fn timeout_error_code() {
139 let err = timeout_error("optimize");
140 assert!(err.to_string().contains("NIKA-293"));
141 }
142
143 #[test]
144 fn invalid_args_code() {
145 let err = invalid_args("thumbnail", "width must be > 0");
146 assert!(err.to_string().contains("NIKA-294"));
147 assert!(err.to_string().contains("width must be > 0"));
148 }
149
150 #[test]
151 fn security_violation_code() {
152 let err = security_violation("svg_render", "SVG contains <script>");
153 assert!(err.to_string().contains("NIKA-297"));
154 assert!(err.to_string().contains("<script>"));
155 }
156
157 #[test]
158 fn pipeline_errors() {
159 let err = pipeline_step_failed(2, "resize failed");
160 assert!(err.to_string().contains("NIKA-295"));
161 assert!(err.to_string().contains("step 2"));
162
163 let err = pipeline_empty();
164 assert!(err.to_string().contains("NIKA-296"));
165 }
166}