1use serde::{Deserialize, Serialize};
4use thiserror::Error;
5
6pub type Result<T> = std::result::Result<T, McdError>;
8
9#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
11pub struct Diagnostic {
12 pub level: DiagnosticLevel,
14 pub code: String,
16 pub message: String,
18 #[serde(skip_serializing_if = "Option::is_none")]
20 pub source: Option<String>,
21 #[serde(default, skip_serializing_if = "Vec::is_empty")]
23 pub related: Vec<String>,
24}
25
26impl Diagnostic {
27 #[must_use]
29 pub fn error(code: impl Into<String>, message: impl Into<String>) -> Self {
30 Self {
31 level: DiagnosticLevel::Error,
32 code: code.into(),
33 message: message.into(),
34 source: None,
35 related: Vec::new(),
36 }
37 }
38
39 #[must_use]
41 pub fn with_source(mut self, source: impl Into<String>) -> Self {
42 self.source = Some(source.into());
43 self
44 }
45
46 #[must_use]
48 pub fn with_related(mut self, related: impl Into<String>) -> Self {
49 self.related.push(related.into());
50 self
51 }
52}
53
54#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
56#[serde(rename_all = "lowercase")]
57pub enum DiagnosticLevel {
58 Error,
60 Warning,
62 Info,
64}
65
66#[derive(Debug, Error)]
68pub enum McdError {
69 #[error("I/O error: {0}")]
71 Io(#[from] std::io::Error),
72 #[error("package archive error: {0}")]
74 Zip(#[from] zip::result::ZipError),
75 #[error("JSON error: {0}")]
77 Json(#[from] serde_json::Error),
78 #[error("UTF-8 error: {0}")]
80 Utf8(#[from] std::string::FromUtf8Error),
81 #[error("{diagnostic_code}: {diagnostic_message}")]
83 Diagnostic {
84 diagnostic_code: String,
86 diagnostic_message: String,
88 diagnostic: Box<Diagnostic>,
90 },
91}
92
93impl McdError {
94 #[must_use]
96 pub fn from_diagnostic(diagnostic: Diagnostic) -> Self {
97 Self::Diagnostic {
98 diagnostic_code: diagnostic.code.clone(),
99 diagnostic_message: diagnostic.message.clone(),
100 diagnostic: Box::new(diagnostic),
101 }
102 }
103
104 #[must_use]
106 pub fn diagnostic(&self) -> Option<&Diagnostic> {
107 match self {
108 Self::Diagnostic { diagnostic, .. } => Some(diagnostic.as_ref()),
109 Self::Io(_) | Self::Zip(_) | Self::Json(_) | Self::Utf8(_) => None,
110 }
111 }
112}