output.rs 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. use crate::types::{DomainResult, DomainStatus, ErrorKind};
  2. use colored::*;
  3. use std::io::Write;
  4. use std::path::PathBuf;
  5. pub fn print_available_table(results: &[DomainResult], no_color: bool, no_unicode: bool) {
  6. let available: Vec<&DomainResult> = results.iter().filter(|r| r.is_available()).collect();
  7. if available.is_empty() {
  8. println!("No available domains found.");
  9. return;
  10. }
  11. let max_len = available.iter().map(|r| r.full.len()).max().unwrap_or(20);
  12. let width = max_len + 4; // padding
  13. let title = "Available Domains";
  14. let title_padded = format!("{:^width$}", title, width = width);
  15. if no_unicode {
  16. // ASCII box
  17. let border = format!("+{}+", "-".repeat(width));
  18. println!("{}", border);
  19. if no_color {
  20. println!("|{}|", title_padded);
  21. } else {
  22. println!("|{}|", title_padded.green());
  23. }
  24. println!("+{}+", "-".repeat(width));
  25. for r in &available {
  26. println!("| {:<pad$} |", r.full, pad = width - 2);
  27. }
  28. println!("{}", border);
  29. } else {
  30. // Unicode box
  31. let top = format!("┌{}┐", "─".repeat(width));
  32. let sep = format!("├{}┤", "─".repeat(width));
  33. let bot = format!("└{}┘", "─".repeat(width));
  34. println!("{}", top);
  35. if no_color {
  36. println!("│{}│", title_padded);
  37. } else {
  38. println!("│{}│", title_padded.green());
  39. }
  40. println!("{}", sep);
  41. for r in &available {
  42. println!("│ {:<pad$} │", r.full, pad = width - 2);
  43. }
  44. println!("{}", bot);
  45. }
  46. }
  47. pub fn print_full_table(results: &[DomainResult], no_color: bool, no_unicode: bool) {
  48. if results.is_empty() {
  49. println!("No results.");
  50. return;
  51. }
  52. // calc column widths
  53. let domain_w = results
  54. .iter()
  55. .map(|r| r.full.len())
  56. .max()
  57. .unwrap_or(10)
  58. .max(7);
  59. let status_w = 10; // "registered" is the longest
  60. let note_w = results
  61. .iter()
  62. .map(|r| r.note_str().len())
  63. .max()
  64. .unwrap_or(4)
  65. .max(4);
  66. let domain_col = domain_w + 2;
  67. let status_col = status_w + 2;
  68. let note_col = note_w + 2;
  69. if no_unicode {
  70. print_full_table_ascii(results, domain_col, status_col, note_col, no_color);
  71. } else {
  72. print_full_table_unicode(results, domain_col, status_col, note_col, no_color);
  73. }
  74. }
  75. fn print_full_table_unicode(
  76. results: &[DomainResult],
  77. dc: usize,
  78. sc: usize,
  79. nc: usize,
  80. no_color: bool,
  81. ) {
  82. let top = format!("┌{}┬{}┬{}┐", "─".repeat(dc), "─".repeat(sc), "─".repeat(nc));
  83. let sep = format!("├{}┼{}┼{}┤", "─".repeat(dc), "─".repeat(sc), "─".repeat(nc));
  84. let bot = format!("└{}┴{}┴{}┘", "─".repeat(dc), "─".repeat(sc), "─".repeat(nc));
  85. println!("{}", top);
  86. println!(
  87. "│{:^dc$}│{:^sc$}│{:^nc$}│",
  88. "Domains",
  89. "Status",
  90. "Note",
  91. dc = dc,
  92. sc = sc,
  93. nc = nc,
  94. );
  95. println!("{}", sep);
  96. for r in results {
  97. let domain_str = format!(" {:<width$} ", r.full, width = dc - 2);
  98. let status_str = format!(" {:<width$} ", r.status_str(), width = sc - 2);
  99. let note_str = format!(" {:<width$} ", r.note_str(), width = nc - 2);
  100. if no_color {
  101. println!("│{}│{}│{}│", domain_str, status_str, note_str);
  102. } else {
  103. let colored_domain = color_domain(&domain_str, &r.status);
  104. println!("│{}│{}│{}│", colored_domain, status_str, note_str);
  105. }
  106. }
  107. println!("{}", bot);
  108. }
  109. fn print_full_table_ascii(
  110. results: &[DomainResult],
  111. dc: usize,
  112. sc: usize,
  113. nc: usize,
  114. no_color: bool,
  115. ) {
  116. let border = format!("+{}+{}+{}+", "-".repeat(dc), "-".repeat(sc), "-".repeat(nc));
  117. println!("{}", border);
  118. println!(
  119. "|{:^dc$}|{:^sc$}|{:^nc$}|",
  120. "Domains",
  121. "Status",
  122. "Note",
  123. dc = dc,
  124. sc = sc,
  125. nc = nc,
  126. );
  127. println!("{}", border);
  128. for r in results {
  129. let domain_str = format!(" {:<width$} ", r.full, width = dc - 2);
  130. let status_str = format!(" {:<width$} ", r.status_str(), width = sc - 2);
  131. let note_str = format!(" {:<width$} ", r.note_str(), width = nc - 2);
  132. if no_color {
  133. println!("|{}|{}|{}|", domain_str, status_str, note_str);
  134. } else {
  135. let colored_domain = color_domain(&domain_str, &r.status);
  136. println!("|{}|{}|{}|", colored_domain, status_str, note_str);
  137. }
  138. }
  139. println!("{}", border);
  140. }
  141. fn color_domain(domain: &str, status: &DomainStatus) -> ColoredString {
  142. match status {
  143. DomainStatus::Available => domain.green(),
  144. DomainStatus::Registered { .. } => domain.red(),
  145. DomainStatus::Error { kind, .. } => match kind {
  146. ErrorKind::InvalidTld => domain.yellow(),
  147. _ => domain.blue(),
  148. },
  149. }
  150. }
  151. pub fn print_csv(results: &[DomainResult]) {
  152. println!("Domains, Status, Note");
  153. for r in results {
  154. println!("{}, {}, {}", r.full, r.status_str(), r.note_str());
  155. }
  156. }
  157. pub fn write_csv_file(results: &[DomainResult], path: &PathBuf) -> Result<(), String> {
  158. let mut file =
  159. std::fs::File::create(path).map_err(|e| format!("Could not create CSV file: {}", e))?;
  160. writeln!(file, "Domains, Status, Note").map_err(|e| format!("Write error: {}", e))?;
  161. for r in results {
  162. writeln!(file, "{}, {}, {}", r.full, r.status_str(), r.note_str())
  163. .map_err(|e| format!("Write error: {}", e))?;
  164. }
  165. Ok(())
  166. }
  167. pub fn print_errors(results: &[DomainResult], verbose: bool) {
  168. for r in results {
  169. if let DomainStatus::Error { kind, message } = &r.status {
  170. match kind {
  171. ErrorKind::InvalidTld => {
  172. eprintln!("Error for {}, tld does not seem to exist", r.full);
  173. }
  174. _ => {
  175. if verbose {
  176. eprintln!("Error for {}, {} (raw: {})", r.full, message, message);
  177. } else {
  178. eprintln!(
  179. "Error for {}, unknown error (enable verbose to see raw error)",
  180. r.full
  181. );
  182. }
  183. }
  184. }
  185. }
  186. }
  187. }
  188. pub fn print_progress(current: usize, total: usize) {
  189. use std::sync::Mutex;
  190. use std::time::Instant;
  191. static START: Mutex<Option<Instant>> = Mutex::new(None);
  192. let mut lock = START.lock().unwrap();
  193. let start = *lock.get_or_insert_with(Instant::now);
  194. let percent = (current as f64 / total as f64 * 100.0) as u32;
  195. eprint!("\rParsing results : {}%", percent);
  196. if current == total {
  197. let secs = start.elapsed().as_secs_f64();
  198. eprintln!("\rParsing results : Done (Took {:.1}s) ", secs);
  199. *lock = None; // reset for next search duh
  200. }
  201. }