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