Skip to main content

xdg_thumbnail/
error.rs

1// SPDX-FileCopyrightText: 2026 KIM Hyunjae
2// SPDX-License-Identifier: MPL-2.0
3
4use std::path::PathBuf;
5
6use thiserror::Error;
7
8/// Problem found in an explicitly supplied cache root path.
9#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
10#[non_exhaustive]
11pub enum CacheRootProblem {
12    /// The supplied cache root is not absolute.
13    NotAbsolute,
14}
15
16/// Problem found in an explicitly supplied cache entry path.
17#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
18#[non_exhaustive]
19pub enum CachePathProblem {
20    /// The supplied cache entry path has no parent directory.
21    MissingParentDirectory,
22}
23
24/// Privacy or type problem found in an existing personal-cache directory.
25#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
26#[non_exhaustive]
27pub enum CacheDirectoryProblem {
28    /// The directory path is a symbolic link.
29    Symlink,
30    /// The path exists but is not a directory.
31    NotDirectory,
32    /// The directory is not owned by the current user.
33    WrongOwner,
34    /// The directory grants group or other access.
35    GroupOrOtherAccessible,
36}
37
38/// Errors returned by thumbnail cache identity and filesystem operations.
39///
40/// Reason strings carried by variants are diagnostic text for humans and logs. They are not stable
41/// machine-matchable API values; callers should use structured variants and typed validation
42/// problems for control flow.
43#[derive(Debug, Error)]
44#[non_exhaustive]
45pub enum ThumbnailError {
46    /// A URI or filename input is not valid for the requested thumbnail context.
47    #[error("invalid thumbnail URI identity: {reason}")]
48    #[non_exhaustive]
49    InvalidUriIdentity {
50        /// Diagnostic reason.
51        reason: &'static str,
52    },
53    /// A cache namespace is not valid for filesystem use.
54    #[error("invalid cache namespace: {reason}")]
55    #[non_exhaustive]
56    InvalidNamespace {
57        /// Diagnostic reason.
58        reason: &'static str,
59    },
60    /// The cache root could not be resolved from XDG environment variables.
61    #[error("cache root could not be resolved: {reason}")]
62    #[non_exhaustive]
63    CacheRootUnavailable {
64        /// Diagnostic reason.
65        reason: &'static str,
66    },
67    /// An explicitly supplied cache root path is invalid.
68    #[error("invalid cache root {path:?}: {problem:?}")]
69    #[non_exhaustive]
70    InvalidCacheRoot {
71        /// Invalid cache root path.
72        path: PathBuf,
73        /// Typed root problem.
74        problem: CacheRootProblem,
75    },
76    /// An explicitly computed cache entry path is invalid.
77    #[error("invalid cache path {path:?}: {problem:?}")]
78    #[non_exhaustive]
79    InvalidCachePath {
80        /// Invalid cache entry path.
81        path: PathBuf,
82        /// Typed path problem.
83        problem: CachePathProblem,
84    },
85    /// An existing cache directory violates thumbnail cache privacy requirements.
86    #[error("insecure cache directory {path:?}: {problem:?}")]
87    #[non_exhaustive]
88    InsecureCacheDirectory {
89        /// Existing directory path.
90        path: PathBuf,
91        /// Typed directory problem.
92        problem: CacheDirectoryProblem,
93    },
94    /// Filesystem I/O failed.
95    #[error("{context}: {source}")]
96    #[non_exhaustive]
97    Io {
98        /// Operation that failed.
99        context: &'static str,
100        /// Path involved in the operation, when available.
101        path: Option<PathBuf>,
102        /// Underlying I/O error.
103        #[source]
104        source: std::io::Error,
105    },
106    /// PNG data could not be decoded or encoded.
107    #[error("png error: {message}")]
108    #[non_exhaustive]
109    Png {
110        /// PNG diagnostic message.
111        message: String,
112    },
113    /// Thumbnail metadata is invalid.
114    #[error("invalid thumbnail metadata: {reason}")]
115    #[non_exhaustive]
116    InvalidMetadata {
117        /// Diagnostic reason.
118        reason: &'static str,
119    },
120    /// Rendered thumbnail bytes are unsupported.
121    #[error("unsupported rendered thumbnail: {reason}")]
122    #[non_exhaustive]
123    UnsupportedRenderedThumbnail {
124        /// Diagnostic reason.
125        reason: &'static str,
126    },
127    /// PNG parsing or thumbnail normalization would exceed configured resource limits.
128    #[error("resource limit exceeded: {reason}")]
129    #[non_exhaustive]
130    ResourceLimitExceeded {
131        /// Diagnostic reason.
132        reason: &'static str,
133    },
134    /// Cache entry removal was refused by safety checks.
135    #[error("refused to remove cache entry: {reason}")]
136    #[non_exhaustive]
137    UnsafeRemoval {
138        /// Diagnostic reason.
139        reason: &'static str,
140    },
141}
142
143impl ThumbnailError {
144    pub(crate) const fn invalid_uri(reason: &'static str) -> Self {
145        Self::InvalidUriIdentity { reason }
146    }
147
148    pub(crate) const fn invalid_namespace(reason: &'static str) -> Self {
149        Self::InvalidNamespace { reason }
150    }
151
152    pub(crate) const fn cache_root_unavailable(reason: &'static str) -> Self {
153        Self::CacheRootUnavailable { reason }
154    }
155
156    pub(crate) const fn invalid_metadata(reason: &'static str) -> Self {
157        Self::InvalidMetadata { reason }
158    }
159
160    pub(crate) const fn unsupported_rendered_thumbnail(reason: &'static str) -> Self {
161        Self::UnsupportedRenderedThumbnail { reason }
162    }
163
164    pub(crate) const fn resource_limit_exceeded(reason: &'static str) -> Self {
165        Self::ResourceLimitExceeded { reason }
166    }
167
168    pub(crate) const fn unsafe_removal(reason: &'static str) -> Self {
169        Self::UnsafeRemoval { reason }
170    }
171
172    pub(crate) fn png(message: impl Into<String>) -> Self {
173        Self::Png {
174            message: message.into(),
175        }
176    }
177
178    pub(crate) fn io(
179        context: &'static str,
180        path: impl Into<Option<PathBuf>>,
181        source: std::io::Error,
182    ) -> Self {
183        Self::Io {
184            context,
185            path: path.into(),
186            source,
187        }
188    }
189}
190
191/// Result type used by this crate.
192pub type Result<T, E = ThumbnailError> = std::result::Result<T, E>;