solid_pod_rs/error.rs
1//! Crate-wide error type.
2//!
3//! All public APIs return `Result<T, PodError>`.
4
5use thiserror::Error;
6
7/// Errors emitted by solid-pod-rs.
8#[derive(Debug, Error)]
9pub enum PodError {
10 /// The requested resource does not exist (HTTP 404).
11 #[error("resource not found: {0}")]
12 NotFound(String),
13
14 /// A resource already exists at the target path (HTTP 409).
15 #[error("resource already exists: {0}")]
16 AlreadyExists(String),
17
18 /// The authenticated agent lacks the required WAC access mode (HTTP 403).
19 #[error("access forbidden")]
20 Forbidden,
21
22 /// No valid credentials were presented (HTTP 401).
23 #[error("authentication required")]
24 Unauthenticated,
25
26 /// Filesystem or network I/O failure.
27 #[error("I/O error: {0}")]
28 Io(#[from] std::io::Error),
29
30 /// The request path is syntactically invalid or escapes the pod root.
31 #[error("invalid path: {0}")]
32 InvalidPath(String),
33
34 /// The supplied `Content-Type` is not acceptable for this operation.
35 #[error("invalid content type: {0}")]
36 InvalidContentType(String),
37
38 /// JSON serialisation or deserialisation failed.
39 #[error("JSON error: {0}")]
40 Json(#[from] serde_json::Error),
41
42 /// A URL could not be parsed.
43 #[error("URL parse error: {0}")]
44 UrlParse(#[from] url::ParseError),
45
46 /// Base64 decoding failed (e.g. in a NIP-98 or DPoP token).
47 #[error("base64 decode error: {0}")]
48 Base64(#[from] base64::DecodeError),
49
50 /// Hex decoding failed.
51 #[error("hex decode error: {0}")]
52 Hex(#[from] hex::FromHexError),
53
54 /// An ACL document could not be parsed as JSON-LD or Turtle.
55 #[error("ACL parse error: {0}")]
56 AclParse(String),
57
58 /// NIP-98 authentication verification failed.
59 #[error("NIP-98: {0}")]
60 Nip98(String),
61
62 /// The storage watch/notification subsystem encountered an error.
63 #[error("watch subsystem error: {0}")]
64 Watch(String),
65
66 /// A storage backend reported an unclassified error.
67 #[error("backend error: {0}")]
68 Backend(String),
69
70 /// An `If-Match` / `If-None-Match` precondition was not satisfied (HTTP 412).
71 #[error("precondition failed: {0}")]
72 PreconditionFailed(String),
73
74 /// The requested operation is not supported by this configuration.
75 #[error("unsupported: {0}")]
76 Unsupported(String),
77
78 /// The request is malformed (HTTP 400).
79 #[error("bad request: {0}")]
80 BadRequest(String),
81
82 /// Request body exceeds a size limit (HTTP 413 equivalent).
83 ///
84 /// Used by the ACL parsers (`parse_turtle_acl`, `parse_jsonld_acl`) to
85 /// reject oversized documents before parsing begins.
86 #[error("payload too large: {0}")]
87 PayloadTooLarge(String),
88
89 /// Sprint 7: pod-level byte quota exceeded. Wraps the detailed
90 /// [`crate::quota::QuotaExceeded`] struct so consumers can surface
91 /// pod name / used / limit in their HTTP response bodies.
92 #[cfg(feature = "quota")]
93 #[error(transparent)]
94 QuotaExceeded(#[from] crate::quota::QuotaExceeded),
95}
96
97// `notify` is an optional dep activated by `fs-backend`. Wasm32 / `core`
98// consumers compile this crate without it; the `Watch` variant remains
99// constructible from a string when needed by other backends.
100#[cfg(feature = "fs-backend")]
101impl From<notify::Error> for PodError {
102 fn from(e: notify::Error) -> Self {
103 PodError::Watch(e.to_string())
104 }
105}