Skip to main content

libplasmoid_updater/
error.rs

1// SPDX-License-Identifier: GPL-3.0-or-later
2
3/// Errors that can occur during plasmoid-updater operations.
4#[derive(thiserror::Error, Debug)]
5pub enum Error {
6    #[error("unsupported operating system: {0}")]
7    UnsupportedOS(String),
8
9    #[error("KDE Plasma desktop environment not detected")]
10    NotKDE,
11
12    #[error("network request failed: {0}")]
13    Network(#[from] reqwest::Error),
14
15    #[error("api rate limited, retry after backoff")]
16    RateLimited,
17
18    #[error("api returned error status: {0}")]
19    ApiError(u16),
20
21    #[error("failed to parse xml: {0}")]
22    XmlParse(String),
23
24    #[error("failed to parse metadata.json: {0}")]
25    MetadataParse(#[from] serde_json::Error),
26
27    #[error("io error: {0}")]
28    Io(#[from] std::io::Error),
29
30    #[error("component not found: {0}")]
31    ComponentNotFound(String),
32
33    #[error("extraction failed: {0}")]
34    ExtractionFailed(String),
35
36    #[error("installation failed: {0}")]
37    InstallFailed(String),
38
39    #[error("could not resolve content id for: {0}")]
40    IdResolutionFailed(String),
41
42    #[error("config error: {0}")]
43    Config(String),
44
45    #[error("invalid version: {0}")]
46    InvalidVersion(String),
47
48    #[error("download failed: {0}")]
49    DownloadFailed(String),
50
51    #[error("checksum mismatch: expected {expected}, got {actual}")]
52    ChecksumMismatch { expected: String, actual: String },
53
54    #[error("metadata not found in package")]
55    MetadataNotFound,
56
57    #[error("backup failed: {0}")]
58    BackupFailed(String),
59
60    #[error("restart failed: {0}")]
61    RestartFailed(String),
62
63    #[error("{0}")]
64    Other(String),
65
66    #[error("no updates available")]
67    NoUpdatesAvailable,
68}
69
70impl Error {
71    /// Returns `true` for expected, non-error conditions (e.g., no updates found).
72    pub fn is_skippable(&self) -> bool {
73        matches!(self, Self::NoUpdatesAvailable | Self::ComponentNotFound(_))
74    }
75
76    /// Returns `true` for temporary failures that may succeed on retry.
77    pub fn is_transient(&self) -> bool {
78        matches!(self, Self::Network(_) | Self::RateLimited)
79    }
80
81    /// Returns `true` for permanent failures that require user intervention.
82    pub fn is_fatal(&self) -> bool {
83        !self.is_skippable() && !self.is_transient()
84    }
85}
86
87macro_rules! error_ctor {
88    ($($name:ident => $variant:ident),* $(,)?) => {
89        $(
90            pub(crate) fn $name(msg: impl Into<String>) -> Self {
91                Self::$variant(msg.into())
92            }
93        )*
94    };
95}
96
97impl Error {
98    error_ctor!(
99        xml_parse => XmlParse,
100        extraction => ExtractionFailed,
101        install => InstallFailed,
102        download => DownloadFailed,
103        backup => BackupFailed,
104        restart => RestartFailed,
105    );
106
107    pub fn other(msg: impl Into<String>) -> Self {
108        Self::Other(msg.into())
109    }
110
111    pub(crate) fn checksum(expected: impl Into<String>, actual: impl Into<String>) -> Self {
112        Self::ChecksumMismatch {
113            expected: expected.into(),
114            actual: actual.into(),
115        }
116    }
117}