Skip to main content

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}