mangater_sdk/errors.rs
1// mangater-sdk - the sdk interface for Mangater, includes traits, models and utilities.
2// Copyright (C) 2026 Takara-Mono <quoeamaster@gmail.com>
3//
4// For a copy of the MIT license, see <https://opensource.org/licenses/MIT>.
5//
6// The MIT License (MIT)
7//
8// Permission is hereby granted, free of charge, to any person obtaining a copy
9// of this software and associated documentation files (the "Software"), to deal
10// in the Software without restriction, including without limitation the rights
11// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12// copies of the Software, and to permit persons to whom the Software is
13// furnished to do so, subject to the following conditions:
14//
15// The above copyright notice and this permission notice shall be included in
16// all copies or substantial portions of the Software.
17//
18// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24// THE SOFTWARE.
25
26use thiserror::Error;
27
28/// SdkError is the error type for the Mangater SDK.
29/// This defines the error contract between:
30/// - Core engine
31/// - Site plugins
32/// - CLI
33#[derive(Debug, Error)]
34pub enum SdkError {
35 /// Invalid configuration.
36 #[error("invalid configuration: {0}")]
37 InvalidConfig(String),
38
39 /// Network-level failure (HTTP, DNS, timeout, etc.)
40 #[error("network error: {0}")]
41 Network(String),
42
43 /// Parsing failure (HTML, JSON, selector mismatch, etc.)
44 #[error("parse error: {0}")]
45 Parse(String),
46
47 /// The requested resource does not exist.
48 #[error("not found: {0}")]
49 NotFound(String),
50
51 /// Website structure changed or unsupported format.
52 #[error("unsupported domain: {0}")]
53 Unsupported(String),
54
55 /// Rate limited by remote server.
56 #[error("rate limited")]
57 RateLimited,
58
59 /// Authentication required or failed.
60 #[error("authentication failed")]
61 Authentication,
62
63 /// Generic plugin error for site-specific cases.
64 #[error("site error: {0}")]
65 Site(String),
66
67 /// Storage error propagated from filesystem operations.
68 #[error(transparent)]
69 Storage(#[from] std::io::Error),
70
71 /// Catch-all fallback.
72 #[error("unknown error: {0}")]
73 Other(String),
74}
75
76// SDK-wide result alias.
77//pub type Result<T> = std::result::Result<T, SdkError>;
78
79#[cfg(test)]
80mod tests {
81 use super::*;
82
83 #[test]
84 fn test_error_display() {
85 let err = SdkError::NotFound("chapter".into());
86 assert_eq!(err.to_string(), "not found: chapter");
87 }
88
89 #[test]
90 fn test_io_conversion() {
91 let io_err = std::io::Error::new(std::io::ErrorKind::Other, "disk");
92 let sdk_err: SdkError = io_err.into();
93
94 match sdk_err {
95 SdkError::Storage(_) => {
96 assert!(sdk_err.to_string().contains("disk"));
97 }
98 _ => panic!(
99 "Expected Storage variant, but failed or not the expected error type: {}",
100 sdk_err
101 ),
102 }
103 }
104}