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
use crate::BoxError;
use derive_more::Display;
use thiserror::Error;

/// The Error returned by storage backends. Storage backend implementations should choose the
/// `ErrorKind` chosen for errors carefully since that will determine what is returned to the FTP
/// client.
#[derive(Debug, Error)]
#[error("storage error: {kind}")]
pub struct Error {
    kind: ErrorKind,
    #[source]
    source: Option<BoxError>,
}

impl Error {
    /// Creates a new storage error
    pub fn new<E>(kind: ErrorKind, error: E) -> Error
    where
        E: Into<Box<dyn std::error::Error + Send + Sync>>,
    {
        Error {
            kind,
            source: Some(error.into()),
        }
    }

    /// Detailed information about what the FTP server should do with the failure
    pub fn kind(&self) -> ErrorKind {
        self.kind
    }
}

impl From<ErrorKind> for Error {
    fn from(kind: ErrorKind) -> Error {
        Error { kind, source: None }
    }
}

/// The `ErrorKind` variants that can be produced by the [`StorageBackend`] implementations.
///
/// [`StorageBackend`]: trait.StorageBackend.html
#[derive(Copy, Clone, Eq, PartialEq, Debug, Display)]
pub enum ErrorKind {
    /// Error that will cause an FTP reply code of 450 to be returned to the FTP client.
    /// The storage back-end implementation should return this if a error occurred that my be
    /// retried for example in the case where a file is busy.
    #[display(fmt = "450 Transient file not available")]
    TransientFileNotAvailable,
    /// Error that will cause an FTP reply code of 550 to be returned to the FTP client.
    /// The storage back-end implementation should return this if a error occurred where it doesn't
    /// make sense for it to be retried. For example in the case where a file is busy.
    #[display(fmt = "550 Permanent file not available")]
    PermanentFileNotAvailable,
    /// Error that will cause an FTP reply code of 550 to be returned to the FTP client.
    /// The storage back-end implementation should return this if a error occurred where it doesn't
    /// make sense for it to be retried. For example in the case where file access is denied.
    #[display(fmt = "550 Permission denied")]
    PermissionDenied,
    /// Error that will cause an FTP reply code of 451 to be returned to the FTP client. Its means
    /// the requested action was aborted due to a local error (internal storage back-end error) in
    /// processing.
    #[display(fmt = "451 Local error")]
    LocalError,
    /// 551 Requested action aborted. Page type unknown.
    #[display(fmt = "551 Page type unknown")]
    PageTypeUnknown,
    /// 452 Requested action not taken. Insufficient storage space in system.
    #[display(fmt = "452 Insufficient storage space error")]
    InsufficientStorageSpaceError,
    /// 552 Requested file action aborted. Exceeded storage allocation (for current directory or
    /// dataset).
    #[display(fmt = "552 Exceeded storage allocation error")]
    ExceededStorageAllocationError,
    /// Error that will cause an FTP reply code of 553 to be returned to the FTP client. Its means
    /// the requested action was not taken due to an illegal file name.
    #[display(fmt = "553 File name not allowed error")]
    FileNameNotAllowedError,
    /// Error that will cause an FTP reply code of 502. The indicates to the client that the command
    /// is not implemented for the storage back-end. For instance the GCS back-end don't implement
    /// RMD (remove directory) but returns this error instead from its StorageBackend::rmd
    /// implementation.
    #[display(fmt = "502 Command not implemented")]
    CommandNotImplemented,
}