Add synor-hosting crate for decentralized web hosting with: - Name Registry: On-chain name→CID mapping with ownership, expiry, and custom domain linking - Domain Verification: CNAME/TXT DNS verification for custom domains - Hosting Router: Host-based routing with SPA support, redirects, and custom headers - synor.json: Project configuration for build, routes, and error pages Users can deploy to myapp.synor.cc and optionally link custom domains. 23 tests passing.
129 lines
3.2 KiB
Rust
129 lines
3.2 KiB
Rust
//! Hosting Error Types
|
|
//!
|
|
//! Error definitions for the Synor Hosting system.
|
|
|
|
use thiserror::Error;
|
|
|
|
/// Hosting errors
|
|
#[derive(Debug, Error)]
|
|
pub enum Error {
|
|
/// Invalid name format
|
|
#[error("invalid name: {0}")]
|
|
InvalidName(String),
|
|
|
|
/// Name is reserved
|
|
#[error("name '{0}' is reserved")]
|
|
ReservedName(String),
|
|
|
|
/// Name is too similar to a reserved name (confusable)
|
|
#[error("name '{0}' is too similar to reserved name '{1}'")]
|
|
ConfusableName(String, String),
|
|
|
|
/// Name not found in registry
|
|
#[error("name '{0}' not found")]
|
|
NameNotFound(String),
|
|
|
|
/// Name already registered
|
|
#[error("name '{0}' is already registered")]
|
|
NameAlreadyRegistered(String),
|
|
|
|
/// Name is already taken
|
|
#[error("name '{0}' is already taken")]
|
|
NameTaken(String),
|
|
|
|
/// Invalid operation
|
|
#[error("invalid operation: {0}")]
|
|
InvalidOperation(String),
|
|
|
|
/// Name has expired
|
|
#[error("name '{0}' has expired")]
|
|
NameExpired(String),
|
|
|
|
/// Not the owner of this name
|
|
#[error("not the owner of this name")]
|
|
NotOwner,
|
|
|
|
/// Domain already linked to another name
|
|
#[error("domain '{0}' is already linked to another name")]
|
|
DomainAlreadyLinked(String),
|
|
|
|
/// Domain not verified
|
|
#[error("domain '{0}' is not verified")]
|
|
DomainNotVerified(String),
|
|
|
|
/// Domain verification pending
|
|
#[error("domain '{0}' verification is pending")]
|
|
DomainVerificationPending(String),
|
|
|
|
/// Domain verification expired
|
|
#[error("domain '{0}' verification has expired")]
|
|
DomainVerificationExpired(String),
|
|
|
|
/// Domain not found
|
|
#[error("domain '{0}' not found")]
|
|
DomainNotFound(String),
|
|
|
|
/// Domain verification failed
|
|
#[error("verification failed for domain '{0}': {1}")]
|
|
VerificationFailed(String, String),
|
|
|
|
/// Invalid domain format
|
|
#[error("invalid domain: {0}")]
|
|
InvalidDomain(String),
|
|
|
|
/// Unknown host
|
|
#[error("unknown host: {0}")]
|
|
UnknownHost(String),
|
|
|
|
/// Redirect response
|
|
#[error("redirect to {to} with status {status}")]
|
|
Redirect {
|
|
/// Target URL
|
|
to: String,
|
|
/// HTTP status code
|
|
status: u16,
|
|
},
|
|
|
|
/// Configuration error
|
|
#[error("configuration error: {0}")]
|
|
Config(String),
|
|
|
|
/// Storage error
|
|
#[error("storage error: {0}")]
|
|
Storage(String),
|
|
|
|
/// DNS resolution error
|
|
#[error("DNS error: {0}")]
|
|
Dns(String),
|
|
|
|
/// IO error
|
|
#[error("IO error: {0}")]
|
|
Io(#[from] std::io::Error),
|
|
|
|
/// JSON parsing error
|
|
#[error("JSON error: {0}")]
|
|
Json(#[from] serde_json::Error),
|
|
}
|
|
|
|
/// Result type alias
|
|
pub type Result<T> = std::result::Result<T, Error>;
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn test_error_display() {
|
|
let err = Error::InvalidName("bad-name!".to_string());
|
|
assert_eq!(err.to_string(), "invalid name: bad-name!");
|
|
|
|
let err = Error::NameNotFound("myapp".to_string());
|
|
assert_eq!(err.to_string(), "name 'myapp' not found");
|
|
|
|
let err = Error::Redirect {
|
|
to: "https://example.com".to_string(),
|
|
status: 301,
|
|
};
|
|
assert!(err.to_string().contains("redirect"));
|
|
}
|
|
}
|