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
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
//! Error conditions.

use std::str::Utf8Error;

/// An error encountered when trying to parse an invalid ID string.
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, thiserror::Error)]
#[non_exhaustive]
pub enum Error {
    /// The identifier or a required part of it is empty.
    #[error("identifier or required part of it is empty")]
    Empty,

    /// The identifier contains invalid characters.
    #[error("identifier contains invalid characters")]
    InvalidCharacters,

    /// The string isn't a valid Matrix ID.
    #[error("invalid matrix ID: {0}")]
    InvalidMatrixId(#[from] MatrixIdError),

    /// The string isn't a valid Matrix.to URI.
    #[error("invalid matrix.to URI: {0}")]
    InvalidMatrixToUri(#[from] MatrixToError),

    /// The string isn't a valid Matrix URI.
    #[error("invalid matrix URI: {0}")]
    InvalidMatrixUri(#[from] MatrixUriError),

    /// The mxc:// isn't a valid Matrix Content URI.
    #[error("invalid Matrix Content URI: {0}")]
    InvalidMxcUri(#[from] MxcUriError),

    /// The value isn't a valid VoIP version Id.
    #[error("invalid VoIP version ID: {0}")]
    InvalidVoipVersionId(#[from] VoipVersionIdError),

    /// The server name part of the the ID string is not a valid server name.
    #[error("server name is not a valid IP address or domain name")]
    InvalidServerName,

    /// The string isn't valid UTF-8.
    #[error("invalid UTF-8")]
    InvalidUtf8,

    /// The ID exceeds 255 bytes (or 32 codepoints for a room version ID).
    #[error("ID exceeds 255 bytes")]
    MaximumLengthExceeded,

    /// The ID is missing the colon delimiter between localpart and server name, or between key
    /// algorithm and key name / version.
    #[error("required colon is missing")]
    MissingColon,

    /// The ID is missing the correct leading sigil.
    #[error("leading sigil is incorrect or missing")]
    MissingLeadingSigil,
}

impl From<Utf8Error> for Error {
    fn from(_: Utf8Error) -> Self {
        Self::InvalidUtf8
    }
}

/// An error occurred while validating an MXC URI.
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, thiserror::Error)]
#[non_exhaustive]
pub enum MxcUriError {
    /// MXC URI did not start with `mxc://`.
    #[error("MXC URI schema was not mxc://")]
    WrongSchema,

    /// MXC URI did not have first slash, required for `server.name/media_id`.
    #[error("MXC URI does not have first slash")]
    MissingSlash,

    /// Media identifier malformed due to invalid characters detected.
    ///
    /// Valid characters are (in regex notation) `[A-Za-z0-9_-]+`.
    /// See [here](https://spec.matrix.org/v1.8/client-server-api/#security-considerations-5) for more details.
    #[error("Media Identifier malformed, invalid characters")]
    MediaIdMalformed,

    /// Server identifier malformed: invalid IP or domain name.
    #[error("invalid Server Name")]
    ServerNameMalformed,
}

/// An error occurred while validating a `MatrixId`.
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, thiserror::Error)]
#[non_exhaustive]
pub enum MatrixIdError {
    /// The string contains an invalid number of parts.
    #[error("invalid number of parts")]
    InvalidPartsNumber,

    /// The string is missing a room ID or alias.
    #[error("missing room ID or alias")]
    MissingRoom,

    /// The string contains no identifier.
    #[error("no identifier")]
    NoIdentifier,

    /// The string contains too many identifiers.
    #[error("too many identifiers")]
    TooManyIdentifiers,

    /// The string contains an unknown identifier.
    #[error("unknown identifier")]
    UnknownIdentifier,

    /// The string contains two identifiers that cannot be paired.
    #[error("unknown identifier pair")]
    UnknownIdentifierPair,

    /// The string contains an unknown identifier type.
    #[error("unknown identifier type")]
    UnknownType,
}

/// An error occurred while validating a `matrix.to` URI.
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, thiserror::Error)]
#[non_exhaustive]
pub enum MatrixToError {
    /// String is not a valid URI.
    #[error("given string is not a valid URL")]
    InvalidUrl,

    /// String did not start with `https://matrix.to/#/`.
    #[error("base URL is not https://matrix.to/#/")]
    WrongBaseUrl,

    /// String has an unknown additional argument.
    #[error("unknown additional argument")]
    UnknownArgument,
}

/// An error occurred while validating a `MatrixURI`.
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, thiserror::Error)]
#[non_exhaustive]
pub enum MatrixUriError {
    /// The string does not start with `matrix:`.
    #[error("scheme is not 'matrix:'")]
    WrongScheme,

    /// The string contains too many actions.
    #[error("too many actions")]
    TooManyActions,

    /// The string contains an unknown query item.
    #[error("unknown query item")]
    UnknownQueryItem,
}

/// An error occurred while validating a `VoipVersionId`.
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, thiserror::Error)]
#[non_exhaustive]
pub enum VoipVersionIdError {
    /// The value of the `UInt` is not 0.
    #[error("UInt value is not 0")]
    WrongUintValue,
}

#[cfg(test)]
mod tests {
    use std::mem::size_of;

    use super::Error;

    #[test]
    fn small_error_type() {
        assert!(size_of::<Error>() <= 8);
    }
}