sos_protocol/
error.rs

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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
//! Error type for the wire protocol.
use crate::{MaybeConflict, SyncStatus};
use http::StatusCode;
use serde_json::Value;
use sos_sdk::time;
use thiserror::Error;

/// Trait for error implementations that
/// support a conflict error.
pub trait AsConflict {
    /// Determine if this is a conflict error.
    fn is_conflict(&self) -> bool;

    /// Determine if this is a hard conflict error.
    fn is_hard_conflict(&self) -> bool;

    /// Take an underlying conflict error.
    fn take_conflict(self) -> Option<ConflictError>;
}

/// Errors generated by the wire protocol.
#[derive(Debug, Error)]
pub enum Error {
    /// Reached EOF decoding a relay packet.
    #[error("relay packet end of file")]
    EndOfFile,

    /// Error generated when a conflict is detected.
    #[error(transparent)]
    Conflict(#[from] ConflictError),

    /// Error generated by the IO module.
    #[error(transparent)]
    Io(#[from] std::io::Error),

    /// Error generated converting from a slice.
    #[error(transparent)]
    TryFromSlice(#[from] std::array::TryFromSliceError),

    /// Error generated by the protobuf library when encoding.
    #[error(transparent)]
    ProtoBufEncode(#[from] prost::EncodeError),

    /// Error generated by the protobuf library when decoding.
    #[error(transparent)]
    ProtoBufDecode(#[from] prost::DecodeError),

    /// Error generated by the protobuf library when converting enums.
    #[error(transparent)]
    ProtoEnum(#[from] prost::UnknownEnumValue),

    /// Error generated by the SDK library.
    #[error(transparent)]
    Sdk(#[from] crate::sdk::Error),

    /// Error generated by the merkle tree library.
    #[error(transparent)]
    Merkle(#[from] rs_merkle::Error),

    /// Error generated converting time types.
    #[error(transparent)]
    Time(#[from] time::error::ComponentRange),

    /// Error generated joining a task.
    #[error(transparent)]
    Join(#[from] tokio::task::JoinError),

    /// Error generated parsing URLs.
    #[error(transparent)]
    UrlParse(#[from] crate::sdk::url::ParseError),

    /// Error generated by the HTTP library.
    #[error(transparent)]
    Http(#[from] http::Error),

    /// Error generated by the HTTP library.
    #[error(transparent)]
    StatusCode(#[from] http::status::InvalidStatusCode),

    /// Error generated by the JSON library.
    #[error(transparent)]
    Json(#[from] serde_json::Error),

    /// Error generated by network communication.
    #[error(transparent)]
    Network(#[from] NetworkError),
}

/// Error created communicating over the network.
#[derive(Debug, Error)]
pub enum NetworkError {
    /// Error generated when an unexpected response code is received.
    #[error("unexpected response status code {0}")]
    ResponseCode(StatusCode),

    /// Error generated when an unexpected response code is received.
    #[error("unexpected response {1} (code: {0})")]
    ResponseJson(StatusCode, Value),

    /// Error generated when an unexpected content type is returend.
    #[error("unexpected content type {0}, expected: {1}")]
    ContentType(String, String),
}

/// Error created whan a conflict is detected.
#[derive(Debug, Error)]
pub enum ConflictError {
    /// Error generated when a soft conflict was detected.
    ///
    /// A soft conflict may be resolved by searching for a
    /// common ancestor commit and merging changes since
    /// the common ancestor commit.
    #[error("soft conflict")]
    Soft {
        /// Conflict information.
        conflict: MaybeConflict,
        /// Local information sent to the remote.
        local: SyncStatus,
        /// Remote information in the server reply.
        remote: SyncStatus,
    },

    /// Error generated when a hard conflict was detected.
    ///
    /// A hard conflict is triggered after a soft conflict
    /// attempted to scan proofs on a remote and was unable
    /// to find a common ancestor commit.
    #[error("hard conflict")]
    Hard,
}

impl AsConflict for Error {
    fn is_conflict(&self) -> bool {
        matches!(self, Error::Conflict(_))
    }

    fn is_hard_conflict(&self) -> bool {
        matches!(self, Error::Conflict(ConflictError::Hard))
    }

    fn take_conflict(self) -> Option<ConflictError> {
        match self {
            Self::Conflict(err) => Some(err),
            _ => None,
        }
    }
}