Skip to main content

kinetic_core/error/
names.rs

1//! Name validation error types (`KIN-NAM-NNN`).
2//!
3//! [`NamesError`] is returned by [`is_valid_apex_name`](crate::types::names::is_valid_apex_name)
4//! when a submitted name fails any of the Kinetic naming rules:
5//!
6//! - **LDH rule** (RFC 5891): only lowercase letters, digits, and internal hyphens.
7//! - **Length limits**: total ≤253 chars; each label ≤63 chars (RFC 1035).
8//! - **Apex-only**: subnames are managed by the apex owner, not the DHT directly.
9//! - **Category 1 reserved** (RFC 2606/6761): `localhost`, `test`, `example`, etc.
10//! - **Category 2 infrastructure**: `seed`, `explorer`, `docs`, etc. locked until Phase 2.
11use super::Severity;
12use thiserror::Error;
13
14/// Errors related to name validation and RFC reserved name checks.
15#[derive(Error, Debug, PartialEq, Eq, Clone)]
16pub enum NamesError {
17    /// The name exceeds the 253 character limit or is completely empty.
18    #[error("Name is empty or exceeds the 253 character limit")]
19    NameTooLong,
20
21    /// A single label (word between dots) exceeds 63 characters or is empty.
22    #[error("Label is empty or exceeds the 63 character limit")]
23    LabelTooLong,
24
25    /// The name contains invalid characters not permitted by the LDH rule.
26    #[error("Name contains invalid characters (only lowercase letters, digits, and internal hyphens allowed)")]
27    InvalidCharacter,
28
29    /// The name is a permanently reserved public utility name (e.g., localhost).
30    #[error("Name is a protected public utility name (e.g., localhost, test)")]
31    ReservedName,
32
33    /// The name is reserved for critical network infrastructure.
34    #[error("Name is a protected infrastructure name (e.g., seed, explorer)")]
35    InfrastructureName,
36
37    /// The name has an invalid TLD.
38    #[error("Name has an invalid Top-Level Domain")]
39    InvalidTLD,
40
41    /// The name is a subname, but the operation requires an apex name.
42    #[error("Only apex names are allowed (subnames must be managed by the apex owner)")]
43    NotAnApexName,
44}
45
46impl NamesError {
47    /// Stable protocol error code.
48    pub fn code(&self) -> &'static str {
49        match self {
50            Self::NameTooLong => "KIN-NAM-001",
51            Self::LabelTooLong => "KIN-NAM-002",
52            Self::InvalidCharacter => "KIN-NAM-003",
53            Self::ReservedName => "KIN-NAM-004",
54            Self::InfrastructureName => "KIN-NAM-005",
55            Self::InvalidTLD => "KIN-NAM-006",
56            Self::NotAnApexName => "KIN-NAM-007",
57        }
58    }
59
60    /// RFC 7807 type URI for this error.
61    pub fn error_type_uri(&self) -> String {
62        format!("{}/errors/{}", crate::constants::DOCS_URL, self.code())
63    }
64
65    /// Severity level for logging and monitoring.
66    pub fn severity(&self) -> Severity {
67        Severity::Warning
68    }
69
70    /// Whether the client should offer a retry action.
71    pub fn is_retryable(&self) -> bool {
72        false
73    }
74
75    /// Returns the user-facing message.
76    pub fn user_message(&self) -> String {
77        match self {
78            Self::NameTooLong => {
79                "The name is empty or exceeds the 253-character limit.".to_string()
80            }
81            Self::LabelTooLong => {
82                "A label within the name exceeds the 63-character limit.".to_string()
83            }
84            Self::InvalidCharacter => {
85                "The name contains invalid characters. Only lowercase letters, digits, and internal hyphens are allowed.".to_string()
86            }
87            Self::ReservedName => {
88                "This name is a permanently protected public utility name.".to_string()
89            }
90            Self::InfrastructureName => {
91                "This name is reserved for critical network infrastructure.".to_string()
92            }
93            Self::InvalidTLD => {
94                "The name does not end with a valid network TLD.".to_string()
95            }
96            Self::NotAnApexName => {
97                "Only apex names (e.g. 'example.kin') can be registered directly.".to_string()
98            }
99        }
100    }
101}