snix_castore/
errors.rs

1use bstr::ByteSlice;
2use thiserror::Error;
3use tokio::task::JoinError;
4use tonic::Status;
5
6use crate::{
7    SymlinkTargetError,
8    path::{PathComponent, PathComponentError},
9};
10
11/// Errors related to communication with the store.
12#[derive(Debug, Error, PartialEq)]
13pub enum Error {
14    #[error("invalid request: {0}")]
15    InvalidRequest(String),
16
17    #[error("internal storage error: {0}")]
18    StorageError(String),
19}
20
21/// Errors that occur during construction of [crate::Node]
22#[derive(Debug, thiserror::Error, PartialEq)]
23pub enum ValidateNodeError {
24    /// Invalid digest length encountered
25    #[error("invalid digest length: {0}")]
26    InvalidDigestLen(usize),
27    /// Invalid symlink target
28    #[error("Invalid symlink target: {0}")]
29    InvalidSymlinkTarget(SymlinkTargetError),
30    /// Invalid hash type encountered
31    #[error("invalid hash type: expected a 'blake3-' prefixed digest")]
32    InvalidHashType,
33}
34
35impl From<crate::digests::Error> for ValidateNodeError {
36    fn from(e: crate::digests::Error) -> Self {
37        match e {
38            crate::digests::Error::InvalidDigestLen(n) => ValidateNodeError::InvalidDigestLen(n),
39            crate::digests::Error::InvalidHashType => ValidateNodeError::InvalidHashType,
40        }
41    }
42}
43
44/// Errors that can occur when populating [crate::Directory] messages,
45/// or parsing [crate::proto::Directory]
46#[derive(Debug, thiserror::Error, PartialEq)]
47pub enum DirectoryError {
48    /// Multiple elements with the same name encountered
49    #[error("{:?} is a duplicate name", .0)]
50    DuplicateName(PathComponent),
51    /// Node failed validation
52    #[error("invalid node with name {}: {:?}", .0.as_bstr(), .1.to_string())]
53    InvalidNode(bytes::Bytes, ValidateNodeError),
54    #[error("Total size exceeds u64::MAX")]
55    SizeOverflow,
56    /// Invalid name encountered
57    #[error("Invalid name: {0}")]
58    InvalidName(PathComponentError),
59    /// This can occur if a protobuf node with a name is passed where we expect
60    /// it to be anonymous.
61    #[error("Name is set when it shouldn't")]
62    NameInAnonymousNode,
63    /// Elements are not in sorted order. Can only happen on protos
64    #[error("{:?} is not sorted", .0.as_bstr())]
65    WrongSorting(bytes::Bytes),
66    /// This can only happen if there's an unknown entry type (on protos)
67    #[error("No entry set")]
68    NoEntrySet,
69}
70
71impl From<JoinError> for Error {
72    fn from(value: JoinError) -> Self {
73        Error::StorageError(value.to_string())
74    }
75}
76
77impl From<Error> for Status {
78    fn from(value: Error) -> Self {
79        match value {
80            Error::InvalidRequest(msg) => Status::invalid_argument(msg),
81            Error::StorageError(msg) => Status::data_loss(format!("storage error: {}", msg)),
82        }
83    }
84}
85
86impl From<crate::tonic::Error> for Error {
87    fn from(value: crate::tonic::Error) -> Self {
88        Self::StorageError(value.to_string())
89    }
90}
91
92impl From<redb::Error> for Error {
93    fn from(value: redb::Error) -> Self {
94        Error::StorageError(value.to_string())
95    }
96}
97
98impl From<redb::DatabaseError> for Error {
99    fn from(value: redb::DatabaseError) -> Self {
100        Error::StorageError(value.to_string())
101    }
102}
103
104impl From<redb::TableError> for Error {
105    fn from(value: redb::TableError) -> Self {
106        Error::StorageError(value.to_string())
107    }
108}
109
110impl From<redb::TransactionError> for Error {
111    fn from(value: redb::TransactionError) -> Self {
112        Error::StorageError(value.to_string())
113    }
114}
115
116impl From<redb::StorageError> for Error {
117    fn from(value: redb::StorageError) -> Self {
118        Error::StorageError(value.to_string())
119    }
120}
121
122impl From<redb::CommitError> for Error {
123    fn from(value: redb::CommitError) -> Self {
124        Error::StorageError(value.to_string())
125    }
126}
127
128impl From<std::io::Error> for Error {
129    fn from(value: std::io::Error) -> Self {
130        if value.kind() == std::io::ErrorKind::InvalidInput {
131            Error::InvalidRequest(value.to_string())
132        } else {
133            Error::StorageError(value.to_string())
134        }
135    }
136}
137
138// TODO: this should probably go somewhere else?
139impl From<Error> for std::io::Error {
140    fn from(value: Error) -> Self {
141        match value {
142            Error::InvalidRequest(msg) => Self::new(std::io::ErrorKind::InvalidInput, msg),
143            Error::StorageError(msg) => Self::new(std::io::ErrorKind::Other, msg),
144        }
145    }
146}