domain_check_lib/types.rs
1//! Core data types for domain availability checking.
2//!
3//! This module defines all the main data structures used throughout the library,
4//! including domain results, configuration options, and output formatting.
5
6use serde::{Deserialize, Serialize};
7use std::collections::HashMap;
8use std::time::Duration;
9
10/// Result of a domain availability check.
11///
12/// Contains all information about a domain's availability status,
13/// registration details, and metadata about the check itself.
14#[derive(Debug, Clone, Serialize, Deserialize)]
15pub struct DomainResult {
16 /// The domain name that was checked (e.g., "example.com")
17 pub domain: String,
18
19 /// Whether the domain is available for registration.
20 /// - `Some(true)`: Domain is available
21 /// - `Some(false)`: Domain is taken/registered
22 /// - `None`: Status could not be determined
23 pub available: Option<bool>,
24
25 /// Detailed registration information (only available for taken domains)
26 #[serde(skip_serializing_if = "Option::is_none")]
27 pub info: Option<DomainInfo>,
28
29 /// How long the domain check took to complete
30 #[serde(skip_serializing_if = "Option::is_none")]
31 pub check_duration: Option<Duration>,
32
33 /// Which method was used to check the domain
34 pub method_used: CheckMethod,
35
36 /// Any error message if the check failed
37 #[serde(skip_serializing_if = "Option::is_none")]
38 pub error_message: Option<String>,
39}
40
41/// Detailed information about a registered domain.
42///
43/// This information is typically extracted from RDAP responses
44/// and provides insights into the domain's registration details.
45#[derive(Debug, Clone, Serialize, Deserialize, Default)]
46pub struct DomainInfo {
47 /// The registrar that manages this domain
48 #[serde(skip_serializing_if = "Option::is_none")]
49 pub registrar: Option<String>,
50
51 /// When the domain was first registered
52 #[serde(skip_serializing_if = "Option::is_none")]
53 pub creation_date: Option<String>,
54
55 /// When the domain registration expires
56 #[serde(skip_serializing_if = "Option::is_none")]
57 pub expiration_date: Option<String>,
58
59 /// Domain status codes (e.g., "clientTransferProhibited")
60 pub status: Vec<String>,
61
62 /// Last update date of the domain record
63 #[serde(skip_serializing_if = "Option::is_none")]
64 pub updated_date: Option<String>,
65
66 /// Nameservers associated with the domain
67 pub nameservers: Vec<String>,
68}
69
70/// Configuration options for domain checking operations.
71///
72/// This struct allows fine-tuning of the domain checking behavior,
73/// including performance, timeout, and protocol preferences.
74#[derive(Debug, Clone, Serialize, Deserialize)]
75pub struct CheckConfig {
76 /// Maximum number of concurrent domain checks
77 /// Default: 10, Range: 1-100
78 pub concurrency: usize,
79
80 /// Timeout for each individual domain check
81 /// Default: 5 seconds
82 #[serde(skip)] // Don't serialize Duration directly
83 pub timeout: Duration,
84
85 /// Whether to automatically fall back to WHOIS when RDAP fails
86 /// Default: true
87 pub enable_whois_fallback: bool,
88
89 /// Whether to use IANA bootstrap registry for unknown TLDs
90 /// Default: false (uses built-in registry only)
91 pub enable_bootstrap: bool,
92
93 /// Whether to extract detailed domain information for taken domains
94 /// Default: false (just availability status)
95 pub detailed_info: bool,
96
97 /// List of TLDs to check for base domain names
98 /// If None, defaults to ["com"]
99 pub tlds: Option<Vec<String>>,
100
101 /// Custom timeout for RDAP requests (separate from overall timeout)
102 /// Default: 3 seconds
103 #[serde(skip)] // Don't serialize Duration directly
104 pub rdap_timeout: Duration,
105
106 /// Custom timeout for WHOIS requests
107 /// Default: 5 seconds
108 #[serde(skip)] // Don't serialize Duration directly
109 pub whois_timeout: Duration,
110
111 /// Custom user-defined TLD presets from config files
112 /// Default: empty
113 #[serde(skip)] // Handled separately in config merging
114 pub custom_presets: HashMap<String, Vec<String>>,
115}
116
117/// Method used to check domain availability.
118///
119/// This helps users understand which protocol was used
120/// and can be useful for debugging or performance analysis.
121#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
122pub enum CheckMethod {
123 /// Domain checked via RDAP protocol
124 #[serde(rename = "rdap")]
125 Rdap,
126
127 /// Domain checked via WHOIS protocol
128 #[serde(rename = "whois")]
129 Whois,
130
131 /// RDAP endpoint discovered via IANA bootstrap registry
132 #[serde(rename = "bootstrap")]
133 Bootstrap,
134
135 /// Check failed or method unknown
136 #[serde(rename = "unknown")]
137 Unknown,
138}
139
140/// Output mode for displaying results.
141///
142/// This controls how and when results are presented to the user,
143/// affecting both performance perception and data formatting.
144#[derive(Debug, Clone, PartialEq)]
145pub enum OutputMode {
146 /// Stream results as they become available (good for interactive use)
147 Streaming,
148
149 /// Collect all results before displaying (good for formatting/sorting)
150 Collected,
151
152 /// Automatically choose based on context (terminal vs pipe, etc.)
153 Auto,
154}
155
156impl Default for CheckConfig {
157 /// Create a sensible default configuration.
158 ///
159 /// These defaults are chosen to work well for most use cases
160 /// while being conservative about resource usage.
161 fn default() -> Self {
162 Self {
163 concurrency: 20,
164 timeout: Duration::from_secs(5),
165 enable_whois_fallback: true,
166 enable_bootstrap: true,
167 detailed_info: false,
168 tlds: None, // Will default to ["com"] when needed
169 rdap_timeout: Duration::from_secs(3),
170 whois_timeout: Duration::from_secs(5),
171 custom_presets: HashMap::new(),
172 }
173 }
174}
175
176impl CheckConfig {
177 /// Create a new configuration with custom concurrency.
178 ///
179 /// Automatically caps concurrency at 100 to prevent resource exhaustion.
180 pub fn with_concurrency(mut self, concurrency: usize) -> Self {
181 self.concurrency = concurrency.clamp(1, 100);
182 self
183 }
184
185 /// Set custom timeout for domain checks.
186 pub fn with_timeout(mut self, timeout: Duration) -> Self {
187 self.timeout = timeout;
188 self
189 }
190
191 /// Enable or disable WHOIS fallback.
192 pub fn with_whois_fallback(mut self, enabled: bool) -> Self {
193 self.enable_whois_fallback = enabled;
194 self
195 }
196
197 /// Enable or disable IANA bootstrap registry.
198 pub fn with_bootstrap(mut self, enabled: bool) -> Self {
199 self.enable_bootstrap = enabled;
200 self
201 }
202
203 /// Enable detailed domain information extraction.
204 pub fn with_detailed_info(mut self, enabled: bool) -> Self {
205 self.detailed_info = enabled;
206 self
207 }
208
209 /// Set TLDs to check for base domain names.
210 pub fn with_tlds(mut self, tlds: Vec<String>) -> Self {
211 self.tlds = Some(tlds);
212 self
213 }
214}
215
216/// Configuration for domain name generation.
217///
218/// Controls pattern expansion, prefix/suffix permutation, and the generation pipeline.
219/// Used by the `generate` module to produce base domain names before TLD expansion.
220#[derive(Debug, Clone, Default)]
221pub struct GenerateConfig {
222 /// Patterns to expand (e.g., "test\d\d", "app?")
223 /// Supports: \w (a-z + hyphen), \d (0-9), ? (alphanumeric + hyphen), literals
224 pub patterns: Vec<String>,
225
226 /// Prefixes to prepend to base names (e.g., ["get", "my", "try"])
227 pub prefixes: Vec<String>,
228
229 /// Suffixes to append to base names (e.g., ["hub", "ly", "ify"])
230 pub suffixes: Vec<String>,
231
232 /// Whether to include the bare base name when prefixes/suffixes are provided.
233 /// Default: true. When false, only affixed variants are generated.
234 pub include_bare: bool,
235}
236
237/// Result of the domain name generation pipeline.
238#[derive(Debug, Clone)]
239pub struct GenerationResult {
240 /// Generated base names (validated, ready for TLD expansion)
241 pub names: Vec<String>,
242
243 /// Pre-filter estimate of how many names the patterns would produce.
244 /// May be higher than `names.len()` due to validation filtering.
245 pub estimated_count: usize,
246}
247
248impl GenerateConfig {
249 /// Create a new GenerateConfig with default settings.
250 pub fn new() -> Self {
251 Self {
252 patterns: Vec::new(),
253 prefixes: Vec::new(),
254 suffixes: Vec::new(),
255 include_bare: true,
256 }
257 }
258
259 /// Returns true if this config would actually generate anything.
260 pub fn has_generation(&self) -> bool {
261 !self.patterns.is_empty()
262 }
263
264 /// Returns true if affixes are configured.
265 pub fn has_affixes(&self) -> bool {
266 !self.prefixes.is_empty() || !self.suffixes.is_empty()
267 }
268}
269
270impl std::fmt::Display for CheckMethod {
271 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
272 match self {
273 CheckMethod::Rdap => write!(f, "RDAP"),
274 CheckMethod::Whois => write!(f, "WHOIS"),
275 CheckMethod::Bootstrap => write!(f, "Bootstrap"),
276 CheckMethod::Unknown => write!(f, "Unknown"),
277 }
278 }
279}
280
281impl std::fmt::Display for OutputMode {
282 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
283 match self {
284 OutputMode::Streaming => write!(f, "Streaming"),
285 OutputMode::Collected => write!(f, "Collected"),
286 OutputMode::Auto => write!(f, "Auto"),
287 }
288 }
289}