tauri_plugin_licensemanager/core/
error.rs1use serde::{Deserialize, Serialize};
4
5#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
8#[serde(rename_all = "snake_case")]
9pub enum LicenseErrorCode {
10 NotFound,
12 LicenseExpired,
14 VersionNotAllowed,
16 MachineLimitReached,
18 PlanLimitExceeded,
20 ValidationError,
22 InternalError,
24 NetworkError,
26 InvalidResponse,
28}
29
30impl LicenseErrorCode {
31 pub fn as_str(self) -> &'static str {
33 match self {
34 Self::NotFound => "not_found",
35 Self::LicenseExpired => "license_expired",
36 Self::VersionNotAllowed => "version_not_allowed",
37 Self::MachineLimitReached => "machine_limit_reached",
38 Self::PlanLimitExceeded => "plan_limit_exceeded",
39 Self::ValidationError => "validation_error",
40 Self::InternalError => "internal_error",
41 Self::NetworkError => "network_error",
42 Self::InvalidResponse => "invalid_response",
43 }
44 }
45
46 pub(crate) fn from_server_code(code: &str) -> Option<Self> {
49 match code {
50 "not_found" => Some(Self::NotFound),
51 "license_expired" => Some(Self::LicenseExpired),
52 "version_not_allowed" => Some(Self::VersionNotAllowed),
53 "machine_limit_reached" => Some(Self::MachineLimitReached),
54 "plan_limit_exceeded" => Some(Self::PlanLimitExceeded),
55 "validation_error" => Some(Self::ValidationError),
56 "internal_error" => Some(Self::InternalError),
57 _ => None,
58 }
59 }
60
61 pub fn is_locking(self) -> bool {
65 matches!(
66 self,
67 Self::NotFound
68 | Self::LicenseExpired
69 | Self::VersionNotAllowed
70 | Self::MachineLimitReached
71 )
72 }
73}
74
75impl std::fmt::Display for LicenseErrorCode {
76 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
77 f.write_str(self.as_str())
78 }
79}
80
81#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
85pub enum LatestAllowedVersion {
86 #[default]
90 NotApplicable,
91 NoneAllowed,
93 Version(String),
95}
96
97impl LatestAllowedVersion {
98 pub fn as_option(&self) -> Option<Option<&str>> {
101 match self {
102 Self::NotApplicable => None,
103 Self::NoneAllowed => Some(None),
104 Self::Version(v) => Some(Some(v)),
105 }
106 }
107}
108
109#[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)]
112#[error("{message}")]
113pub struct LicenseError {
114 pub code: LicenseErrorCode,
116 pub message: String,
118 pub status: Option<u16>,
120 pub latest_allowed_version: LatestAllowedVersion,
122}
123
124impl LicenseError {
125 pub(crate) fn new(code: LicenseErrorCode, message: impl Into<String>) -> Self {
126 Self {
127 code,
128 message: message.into(),
129 status: None,
130 latest_allowed_version: LatestAllowedVersion::NotApplicable,
131 }
132 }
133
134 pub(crate) fn with_status(mut self, status: u16) -> Self {
135 self.status = Some(status);
136 self
137 }
138
139 pub fn is_locking(&self) -> bool {
141 self.code.is_locking()
142 }
143}
144
145#[derive(Debug, PartialEq, Eq, thiserror::Error)]
147pub enum ConfigError {
148 #[error("productId is required")]
150 MissingProductId,
151 #[error("publicKey is required")]
153 MissingPublicKey,
154 #[error("publicKey is not a valid Ed25519 SPKI PEM: {0}")]
156 InvalidPublicKey(String),
157}
158
159#[cfg(test)]
160mod tests {
161 use super::*;
162
163 #[test]
164 fn serializes_codes_like_electron() {
165 for code in [
166 LicenseErrorCode::NotFound,
167 LicenseErrorCode::LicenseExpired,
168 LicenseErrorCode::VersionNotAllowed,
169 LicenseErrorCode::MachineLimitReached,
170 LicenseErrorCode::PlanLimitExceeded,
171 LicenseErrorCode::ValidationError,
172 LicenseErrorCode::InternalError,
173 LicenseErrorCode::NetworkError,
174 LicenseErrorCode::InvalidResponse,
175 ] {
176 assert_eq!(
177 serde_json::to_value(code).unwrap(),
178 serde_json::Value::String(code.to_string())
179 );
180 assert_eq!(
181 LicenseErrorCode::from_server_code(code.as_str()).is_some(),
182 !matches!(
183 code,
184 LicenseErrorCode::NetworkError | LicenseErrorCode::InvalidResponse
185 )
186 );
187 }
188 }
189
190 #[test]
191 fn locking_codes() {
192 assert!(LicenseError::new(LicenseErrorCode::NotFound, "x").is_locking());
193 assert!(LicenseErrorCode::LicenseExpired.is_locking());
194 assert!(LicenseErrorCode::VersionNotAllowed.is_locking());
195 assert!(LicenseErrorCode::MachineLimitReached.is_locking());
196 assert!(!LicenseErrorCode::PlanLimitExceeded.is_locking());
197 assert!(!LicenseErrorCode::NetworkError.is_locking());
198 }
199
200 #[test]
201 fn latest_allowed_version_as_option() {
202 assert_eq!(LatestAllowedVersion::NotApplicable.as_option(), None);
203 assert_eq!(LatestAllowedVersion::NoneAllowed.as_option(), Some(None));
204 assert_eq!(
205 LatestAllowedVersion::Version("2.3".into()).as_option(),
206 Some(Some("2.3"))
207 );
208 }
209
210 #[test]
211 fn config_error_messages() {
212 assert_eq!(
213 ConfigError::MissingProductId.to_string(),
214 "productId is required"
215 );
216 assert_eq!(
217 ConfigError::MissingPublicKey.to_string(),
218 "publicKey is required"
219 );
220 }
221}