aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorUMTS at Teleco <crt@teleco.ch>2026-03-08 23:27:14 +0100
committerUMTS at Teleco <crt@teleco.ch>2026-03-08 23:27:14 +0100
commit4b211599ee41f8d77ff240337582c178289fd222 (patch)
treec5f119a7f22313d3c13e3e642fbddf10549cf4f4 /src
parent249d6f5b37d2a2ea6d78127b6d3a71e81f7bb7a3 (diff)
fix fix fix
Diffstat (limited to 'src')
-rw-r--r--src/cli.rs10
-rw-r--r--src/config.rs73
-rw-r--r--src/lookup.rs51
-rw-r--r--src/main.rs37
-rw-r--r--src/output.rs13
-rw-r--r--src/tlds.rs63
-rw-r--r--src/tui.rs384
7 files changed, 265 insertions, 366 deletions
diff --git a/src/cli.rs b/src/cli.rs
index 5428f05..9466359 100644
--- a/src/cli.rs
+++ b/src/cli.rs
@@ -25,8 +25,8 @@ pub struct Args {
pub csv: Option<Option<PathBuf>>,
#[arg(short = 'l', long = "list")]
pub tld_list: Option<String>,
- #[arg(short = 'i', long = "import-filter")]
- pub import_filter: Option<PathBuf>,
+ #[arg(short = 'i', long = "import-list", alias = "import-filter")]
+ pub import_list: Option<PathBuf>,
#[arg(short = 't', long = "top", value_delimiter = ',')]
pub top_tlds: Option<Vec<String>>,
#[arg(short = 'o', long = "onlytop", value_delimiter = ',')]
@@ -44,10 +44,10 @@ pub struct Args {
/// search for names/domains in a text file line by line.
#[arg(short = 'A', long = "autosearch")]
pub autosearch: Option<PathBuf>,
- /// Use a monochrome color scheme TODO: not applied in TUI since colors were changed from RGB to Ratatui colors. should fix
+ /// Use a monochrome color scheme
#[arg(short = 'C', long = "no-color", default_value_t = false)]
pub no_color: bool,
- /// Do not use unicode only plain ASCII TODO: not applied in TUI for some reason idk
+ /// Do not use unicode only plain ASCII
#[arg(short = 'U', long = "no-unicode", default_value_t = false)]
pub no_unicode: bool,
#[arg(short = 'M', long = "no-mouse", default_value_t = false)]
@@ -130,7 +130,7 @@ Advanced :
-e --environement=PATH Define where .hoardom folder should be
Defaults to /home/USER/.hoardom/
Stores settings, imported lists, favs, cache etc.
--i --import-filter=PATH Import a custom toml list for this session
+-i --import-list=PATH Import a custom toml list for this session
-t --top=TLD,TLD Set certain TLDs to show up as first result
for when you need a domain in your country or for searching
a specific one.
diff --git a/src/config.rs b/src/config.rs
index 546dd16..4d53e29 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -29,31 +29,19 @@ pub struct Config {
pub scratchpad: String,
}
-/// faved domain with its last known status
+/// faved domain with its last known status where once again too many dumbass comments were added when fixing a bug with it... have been removed
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FavoriteEntry {
pub domain: String,
- /// last known status: "available", "registered", "error", or "unknown"
#[serde(default = "default_fav_status")]
pub status: String,
- /// when it was last checked
#[serde(default)]
+ // date string mlol
pub checked: String,
- /// true when status changed since last check
#[serde(default)]
pub changed: bool,
}
-impl FavoriteEntry {
- pub fn new(domain: String) -> Self {
- Self {
- domain,
- status: "unknown".to_string(),
- checked: String::new(),
- changed: false,
- }
- }
-}
fn default_fav_status() -> String {
"unknown".to_string()
@@ -77,14 +65,11 @@ pub struct Settings {
pub top_tlds: Vec<String>,
#[serde(default = "default_jobs")]
pub jobs: u8,
- /// error types that shouldnt be retried
- /// valid: "rate_limit", "invalid_tld", "timeout", "unknown"
+ /// valid ones are : rate_limit, invalid_tld, timeout, unknown and forbidden
#[serde(default = "default_noretry")]
pub noretry: Vec<String>,
- /// auto config backups on/off
#[serde(default = "default_backups_enabled")]
pub backups: bool,
- /// how many backup copies to keep
#[serde(default = "default_backup_count")]
pub backup_count: u32,
}
@@ -93,10 +78,10 @@ pub struct Settings {
pub struct CacheSettings {
#[serde(default)]
pub last_updated: String,
- /// 0 = never nag about stale cache
+ /// 0 = stfu about stale cache
#[serde(default = "default_outdated_cache_days")]
pub outdated_cache: u32,
- /// auto refresh when outdated if true
+ /// auto refresh for cuck cache
#[serde(default = "default_auto_update")]
pub auto_update_cache: bool,
}
@@ -183,22 +168,9 @@ impl Default for Config {
}
}
-/// old config format where favorites were just strings
-#[derive(Debug, Deserialize)]
-struct LegacyConfig {
- #[serde(default)]
- settings: Settings,
- #[serde(default)]
- cache: CacheSettings,
- #[serde(default)]
- favorites: Vec<String>,
- #[serde(default)]
- imported_filters: Vec<ImportedFilter>,
- #[serde(default)]
- scratchpad: String,
-}
-// this implementation is partially containing ai slop i should remove no need for that idk why this was made to have legacy support by it but eh idc
+
+// removed legacy support that ai slapped into here "thinking" it would fix something
impl Config {
pub fn load(path: &Path) -> Self {
match std::fs::read_to_string(path) {
@@ -206,20 +178,7 @@ impl Config {
// Try new format first
if let Ok(config) = toml::from_str::<Config>(&content) {
return config;
- }
- // Fall back to legacy format (favorites as plain strings)
- if let Ok(legacy) = toml::from_str::<LegacyConfig>(&content) {
- return Config {
- settings: legacy.settings,
- cache: legacy.cache,
- favorites: legacy
- .favorites
- .into_iter()
- .map(FavoriteEntry::new)
- .collect(),
- imported_filters: legacy.imported_filters,
- scratchpad: legacy.scratchpad,
- };
+
}
eprintln!("Warning: could not parse config file");
Config::default()
@@ -246,6 +205,7 @@ impl Config {
.map_err(|e| format!("Failed to create config directory: {}", e))?;
}
+ // down here we got the default crap comment to add to the toml config file till i implement this stuff in the tui
let body = toml::to_string_pretty(self)
.map_err(|e| format!("Failed to serialize config: {}", e))?;
let content = format!(
@@ -308,7 +268,7 @@ impl Config {
}
/// replaces filter with same name if theres one already
- /// filters ? what kinda ai slip is this ? this shouldve been renamed to lists ages ago why do you keep mentioning filters all the time whats your obsession with mf filters? JEZE!
+ /// filters ? what kinda ai slop is this ? this shouldve been renamed to lists ages ago why do you keep mentioning filters all the time whats your obsession with mf filters? JEZE!
pub fn import_filter(&mut self, filter: ImportedFilter) {
self.imported_filters.retain(|f| f.name != filter.name);
self.imported_filters.push(filter);
@@ -334,7 +294,7 @@ impl Config {
let age_days = (now - last).num_days() as u32;
if self.cache.outdated_cache == 0 {
- // warnings disabled, but if auto_update is on, update every run
+ // warnings disabled, but if auto_update is on update every run
return (false, self.cache.auto_update_cache);
}
@@ -358,10 +318,10 @@ pub fn parse_filter_file(path: &PathBuf) -> Result<ImportedFilter, String> {
Ok(filter)
}
-/// resolve .hoardom dir, tries a few locations:
+/// resolve .hoardom dir trying a few locations:
///
/// priority:
-/// 1. explicit path via -e flag -> use as root dir (create .hoardom folder there)
+/// 1. explicit path via -e flag -> use that folder directly as the data root
/// 2. debug builds: current directory
/// 3. release builds: home directory
/// 4. fallback: try the other option
@@ -385,12 +345,11 @@ pub fn resolve_paths(explicit: Option<&PathBuf>) -> HoardomPaths {
}
};
- // explicit path given via -e flag
+ // explicit path given via -e flag : use as app root
if let Some(p) = explicit {
- // if user gave a path, use it as the .hoardom folder root
let root = if p.extension().is_some() {
- // looks like they pointed at a file, use parent dir
- p.parent().unwrap_or(p).join(".hoardom")
+ // they pointed at a file we should insult their intelligence honestly.
+ p.parent().unwrap_or(p).to_path_buf()
} else {
p.clone()
};
diff --git a/src/lookup.rs b/src/lookup.rs
index 694f559..2f73395 100644
--- a/src/lookup.rs
+++ b/src/lookup.rs
@@ -191,12 +191,12 @@ pub async fn lookup_domain(
eprintln!("[verbose] {} -> HTTP {}", full, status_code);
}
- // 404 = not found in RDAP = domain is available (not registered)
+ // 404 = not found in RDAP = domain is available (not registered) <- todo : should add check for "not found" in response body for extra safety as some registries return 404 for other errors too as was discovered
if status_code == 404 {
return DomainResult::new(name, tld, DomainStatus::Available);
}
- // 400 = probably invalid query
+ // 400 = probably invalid query fuck you
if status_code == 400 {
return DomainResult::new(
name,
@@ -208,7 +208,7 @@ pub async fn lookup_domain(
);
}
- // 429 = rate limited
+ // 429 = rape limited
if status_code == 429 {
return DomainResult::new(
name,
@@ -220,7 +220,7 @@ pub async fn lookup_domain(
);
}
- // 403 = forbidden (some registries block queries)
+ // 403 = forbidden crap
if status_code == 403 {
return DomainResult::new(
name,
@@ -244,7 +244,7 @@ pub async fn lookup_domain(
);
}
- // 200 = domain exists, try to parse expiry from RDAP json
+ // 200 = domain exists try to parse expiry from RDAP json
let expiry = match resp.json::<serde_json::Value>().await {
Ok(json) => extract_expiry(&json),
Err(_) => None,
@@ -270,7 +270,7 @@ fn extract_expiry(json: &serde_json::Value) -> Option<String> {
None
}
-// ---- WHOIS fallback for TLDs not in RDAP bootstrap ----
+// ---- WHOIS fallback for TLDs not in RDAP bootstrap because their shit ----
// -- No whois feature: just return an error --
#[cfg(not(any(feature = "system-whois", feature = "builtin-whois")))]
@@ -285,7 +285,7 @@ async fn whois_lookup(
tld,
DomainStatus::Error {
kind: ErrorKind::InvalidTld,
- message: "no RDAP server (whois disabled)".to_string(),
+ message: "no RDAP server (whois gone)".to_string(),
},
)
}
@@ -333,7 +333,7 @@ async fn whois_lookup(
tld,
DomainStatus::Error {
kind: ErrorKind::Unknown,
- message: format!("whois command failed: {}", e),
+ message: format!("whois command fucking failed: {}", e),
},
);
}
@@ -367,7 +367,7 @@ async fn whois_lookup(
if verbose {
eprintln!("[verbose] whois stderr: {}", stderr.trim());
}
- // some whois commands exit non-zero for "not found" but still give useful stdout
+ // some whois commands exit non zero for "not found" ... but may toss some infos to stdrr
if !response_str.is_empty() {
return parse_whois_response(name, tld, &response_str);
}
@@ -384,11 +384,11 @@ async fn whois_lookup(
parse_whois_response(name, tld, &response_str)
}
-// -- Builtin whois: rawdogs whois server violently over TCP directly--
+// -- Builtin whois Violator : rawdogs whois server violently over TCP directly --
-/// try a whois server returns the response string or errors out
+/// try a whois server if no happy it make error
#[cfg(feature = "builtin-whois")]
async fn try_whois_server(
server: &str,
@@ -425,7 +425,7 @@ async fn try_whois_server(
Ok(String::from_utf8_lossy(&response).to_string())
}
-/// candidate whois servers for a TLD based on common naming patterns
+/// try voilating some commonly used whois url patterns if unhappy
#[cfg(feature = "builtin-whois")]
fn whois_candidates(tld: &str) -> Vec<String> {
// most registries follow one of these patterns
@@ -445,7 +445,7 @@ async fn whois_lookup(
) -> DomainResult {
let full = format!("{}.{}", name, tld);
- // if Lists.toml has an explicit server ("tld:server"), use ONLY that one
+ // if Lists.toml has an explicit server ("tld:server"), use ONLY that one.
if let Some(server) = whois_overrides.get_server(tld) {
if verbose {
eprintln!("[verbose] WHOIS (override): {} -> {}", full, server);
@@ -475,12 +475,12 @@ async fn whois_lookup(
};
}
- // no override: try common server patterns until one responds
+ // no override: try common server patterns until one screams in pain from being violated hard
let candidates = whois_candidates(tld);
if verbose {
eprintln!(
- "[verbose] WHOIS probing {} candidates for .{}",
+ "[verbose] WHOIS voilating {} candidates for .{}",
candidates.len(),
tld
);
@@ -510,7 +510,7 @@ async fn whois_lookup(
tld,
DomainStatus::Error {
kind: ErrorKind::Unknown,
- message: "no whois server reachable".to_string(),
+ message: "unsuccessful whois rawdoging".to_string(),
},
)
}
@@ -566,15 +566,15 @@ fn extract_whois_expiry(response: &str) -> Option<String> {
for pattern in &expiry_patterns {
if trimmed.starts_with(pattern) {
let value = trimmed[pattern.len()..].trim();
- // try to extract a date-looking thing (first 10 chars if it looks like YYYY-MM-DD)
+ // horribly attempt at extracting date part
if value.len() >= 10 {
let date_part: String = value.chars().take(10).collect();
- // basic sanity check: contains digits and dashes
+ // basic sanity check does it actually contain any fucking digits and dashes
if date_part.contains('-') && date_part.chars().any(|c| c.is_ascii_digit()) {
return Some(date_part);
}
}
- // maybe its in a different format, just return what we got
+ // no clean date ? no problem just rawdog the user then MUAHAHHAHAHA
if !value.is_empty() {
return Some(value.to_string());
}
@@ -607,7 +607,7 @@ pub async fn lookup_with_retry(
if noretry.contains(kind) {
if verbose {
eprintln!(
- "[verbose] Not retrying {}.{} (error kind in noretry list)",
+ "[verbose] Not retrying {}.{} (config))",
name, tld
);
}
@@ -616,7 +616,7 @@ pub async fn lookup_with_retry(
}
if verbose {
eprintln!(
- "[verbose] Retry {}/{} for {}.{}",
+ "[verbose] Attempt to rawdog {}/{} for {}.{}",
attempt, retries, name, tld
);
}
@@ -772,6 +772,7 @@ async fn resolve_bootstrap(
None
};
+ // should make sure that if caching is on and cache expired we dont delete it until we successfully fetch the new content todo
match cached {
Some(b) => Some(b),
None => match RdapBootstrap::fetch(client, verbose).await {
@@ -779,17 +780,17 @@ async fn resolve_bootstrap(
if let Some(cp) = cache_path {
if let Err(e) = b.save_cache(cp) {
if verbose {
- eprintln!("[verbose] Failed to save cache: {}", e);
+ eprintln!("[verbose] Failed to save fucking cache: {}", e);
}
} else if verbose {
- eprintln!("[verbose] RDAP bootstrap cached to {}", cp.display());
+ eprintln!("[verbose] RDAP cached to {}", cp.display());
}
}
Some(b)
}
Err(e) => {
eprintln!("Error: {}", e);
- eprintln!("Cannot perform lookups without RDAP bootstrap data.");
+ eprintln!("Cannot perform lookups without RDAP data.");
None
}
},
@@ -816,7 +817,7 @@ pub struct LookupStream {
pub type LookupBatch = Vec<(String, Vec<String>)>;
-// spawns a bg task, sends results via channel so TUI gets em live
+// spawns a bg task then sends results via channel so TUI gets em live
pub fn lookup_streaming(
name: String,
tlds: Vec<String>,
diff --git a/src/main.rs b/src/main.rs
index 9625959..c6d88e1 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -38,13 +38,13 @@ async fn main() {
let mut config = Config::load_with_backup(&paths.config_file);
if !paths.can_save {
- eprintln!("Warning: favorites and settings wont be saved (no writable location found)");
+ eprintln!("Warning: Unpriviliged bitch detected! Cant write to {}", paths.config_file.display());
}
// handle -r refresh cache flag
if args.refresh_cache {
if !paths.caching_enabled {
- eprintln!("Caching is disabled (no writable location). Nothing to refresh.");
+ eprintln!("Caching aint gon work without a writable location");
return;
}
let cache_file = paths.cache_file("rdap_bootstrap.json");
@@ -72,15 +72,14 @@ async fn main() {
if is_outdated && !should_auto {
eprintln!("Warning: RDAP cache is outdated. Run `hoardom -r` to refresh.");
}
- // force refresh if auto update says so, or if cache file doesnt exist yet
should_auto || !cf.exists()
} else {
false
};
- // import custom filter if given
- if let Some(filter_path) = &args.import_filter {
- match parse_filter_file(filter_path) {
+ // import custom sexy list if given
+ if let Some(list_path) = &args.import_list {
+ match parse_filter_file(list_path) {
Ok(filter) => {
config.import_filter(filter);
if paths.can_save {
@@ -88,16 +87,15 @@ async fn main() {
}
}
Err(e) => {
- eprintln!("Error importing filter: {}", e);
+ eprintln!("Error importing list: {}", e);
return;
}
}
}
- // whois server overrides are baked into Lists.toml ("tld:server" syntax)
+ // whois server overrides that are baked into Lists.toml (like this "tld:server" for naighty tlds)
let overrides = whois_overrides();
- // parse noretry config into ErrorKind list
let noretry: Vec<ErrorKind> = config
.settings
.noretry
@@ -105,7 +103,7 @@ async fn main() {
.filter_map(|s| ErrorKind::from_config_str(s))
.collect();
- // TUI mode
+ // the sigma mode
if args.is_tui() {
if let Err(e) = tui::run_tui(
&args,
@@ -120,7 +118,7 @@ async fn main() {
{
eprintln!("TUI error: {}", e);
}
- // save cache timestamp after TUI session if we refreshed
+ // save cache timestamp to know how fresh we are
if force_refresh && paths.can_save {
config.mark_cache_updated();
let _ = config.save(&paths.config_file);
@@ -128,7 +126,7 @@ async fn main() {
return;
}
- // CLI needs at least one domain unless autosearch was given
+ // CLI needs at least one domain unless autosearch was given user is stupid show small help
if args.domains.is_empty() {
if let Some(file_path) = &args.autosearch {
run_autosearch(
@@ -197,7 +195,7 @@ async fn main() {
});
}
- // Suggestions only kick in when directly searching a single full domain
+ // suggestions if your domain is taken (only for single loneyly alpha wolf domain searches)
if args.domains.len() == 1 && args.effective_suggestions() > 0 {
if let Some(exact_tld) = specific_tld.as_deref() {
let exact_registered = aggregated_results.iter().any(|item| {
@@ -252,34 +250,33 @@ async fn main() {
let results = sort_aggregated_results(aggregated_results);
- // save cache timestamp if we refreshed
+ // save cache timestamp if we showered
if force_refresh && paths.can_save {
config.mark_cache_updated();
let _ = config.save(&paths.config_file);
}
- // print errors first
+ // print errors bruh
output::print_errors(&results, args.verbose);
- // CSV output
+ // cuntsexv output (csv)
if let Some(csv_opt) = &args.csv {
match csv_opt {
Some(path) => {
- // write to file
+ // you wont believe this but here we are GOING to WRITE TO A FILE WITH THE CSV OUTPUT!!!! MIND BLOWN AND COCK EXPLODED
match output::write_csv_file(&results, path) {
Ok(()) => eprintln!("CSV written to {}", path.display()),
Err(e) => eprintln!("Error writing CSV: {}", e),
}
}
None => {
- // print to stdout, no logs
+ // brint to terminal if user dumb and no filepath
output::print_csv(&results);
}
}
return;
}
- // table output
if args.show_all {
output::print_full_table(&results, args.no_color, args.no_unicode);
} else {
@@ -305,7 +302,7 @@ async fn run_autosearch(
let base_tlds = build_base_tlds(args);
- // collect all search entries, grouping by name so "zapplex.de" + "zapplex.nl" become one batch
+ // collect all search entries and grupe them
let mut batches: Vec<(String, Vec<String>)> = Vec::new();
for line in content.lines() {
diff --git a/src/output.rs b/src/output.rs
index 32198f4..5502278 100644
--- a/src/output.rs
+++ b/src/output.rs
@@ -7,14 +7,13 @@ pub fn print_available_table(results: &[DomainResult], no_color: bool, no_unicod
let available: Vec<&DomainResult> = results.iter().filter(|r| r.is_available()).collect();
if available.is_empty() {
- println!("No available domains found.");
+ println!("No available domains.");
return;
}
let max_len = available.iter().map(|r| r.full.len()).max().unwrap_or(20);
- let width = max_len + 4; // padding
-
let title = "Available Domains";
+ let width = max_len.max(title.len()) + 4; // padding, ensure title fits
let title_padded = format!("{:^width$}", title, width = width);
if no_unicode {
@@ -192,14 +191,14 @@ pub fn print_errors(results: &[DomainResult], verbose: bool) {
if let DomainStatus::Error { kind, message } = &r.status {
match kind {
ErrorKind::InvalidTld => {
- eprintln!("Error for {}, tld does not seem to exist", r.full);
+ eprintln!("Error for {} : does not seem to exist", r.full);
}
_ => {
if verbose {
- eprintln!("Error for {}, {} (raw: {})", r.full, message, message);
+ eprintln!("Error for {} : {} (raw: {})", r.full, message, message);
} else {
eprintln!(
- "Error for {}, unknown error (enable verbose to see raw error)",
+ "Error for {} : unknown error",
r.full
);
}
@@ -222,6 +221,6 @@ pub fn print_progress(current: usize, total: usize) {
if current == total {
let secs = start.elapsed().as_secs_f64();
eprintln!("\rParsing results : Done (Took {:.1}s) ", secs);
- *lock = None; // reset for next search duh
+ *lock = None; // reset for next search
}
}
diff --git a/src/tlds.rs b/src/tlds.rs
index 95697d0..4d8a9c3 100644
--- a/src/tlds.rs
+++ b/src/tlds.rs
@@ -13,7 +13,6 @@ impl WhoisOverrides {
}
}
-/// a named TLD list from Lists.toml
struct NamedList {
name: String,
tlds: Vec<String>,
@@ -24,7 +23,7 @@ struct ParsedLists {
whois_overrides: WhoisOverrides,
}
-/// parse a single entry: "tld" or "tld:whois_server"
+// parse "tld" or "tld:whois_server"
fn parse_entry(entry: &str) -> (String, Option<String>) {
if let Some(pos) = entry.find(':') {
(entry[..pos].to_string(), Some(entry[pos + 1..].to_string()))
@@ -33,7 +32,7 @@ fn parse_entry(entry: &str) -> (String, Option<String>) {
}
}
-/// parse entries, pull out TLD names and whois overrides
+// parse more
fn parse_list(entries: &[toml::Value], overrides: &mut HashMap<String, String>) -> Vec<String> {
entries
.iter()
@@ -57,7 +56,7 @@ fn parsed_lists() -> &'static ParsedLists {
let table = raw.as_table().expect("Lists.toml must be a TOML table");
- // Build list names in the order build.rs discovered them
+ // Build list names in the order build.rs found em
let ordered_names: Vec<&str> = env!("HOARDOM_LIST_NAMES").split(',').collect();
let mut overrides = HashMap::new();
@@ -80,7 +79,7 @@ fn parsed_lists() -> &'static ParsedLists {
})
}
-/// list names from Lists.toml, in order
+
pub fn list_names() -> Vec<&'static str> {
parsed_lists()
.lists
@@ -89,12 +88,10 @@ pub fn list_names() -> Vec<&'static str> {
.collect()
}
-/// first list name (the default)
pub fn default_list_name() -> &'static str {
list_names().first().copied().unwrap_or("standard")
}
-/// get TLDs for a list name (case insensitive), None if not found
pub fn get_tlds(name: &str) -> Option<Vec<&'static str>> {
let lower = name.to_lowercase();
parsed_lists()
@@ -104,19 +101,19 @@ pub fn get_tlds(name: &str) -> Option<Vec<&'static str>> {
.map(|l| l.tlds.iter().map(String::as_str).collect())
}
-/// get TLDs for a list name, falls back to default if not found
+
pub fn get_tlds_or_default(name: &str) -> Vec<&'static str> {
get_tlds(name).unwrap_or_else(|| get_tlds(default_list_name()).unwrap_or_default())
}
-/// the builtin whois overrides from Lists.toml
+
pub fn whois_overrides() -> &'static WhoisOverrides {
&parsed_lists().whois_overrides
}
pub fn apply_top_tlds(tlds: Vec<&'static str>, top: &[String]) -> Vec<&'static str> {
let mut result: Vec<&'static str> = Vec::with_capacity(tlds.len());
- // first add the top ones in the order specified
+ // add the top ones in the order specified
for t in top {
let lower = t.to_lowercase();
if let Some(&found) = tlds.iter().find(|&&tld| tld.to_lowercase() == lower) {
@@ -125,7 +122,7 @@ pub fn apply_top_tlds(tlds: Vec<&'static str>, top: &[String]) -> Vec<&'static s
}
}
}
- // then add the rest
+ // then add the rest lol
for tld in &tlds {
if !result.contains(tld) {
result.push(tld);
@@ -133,47 +130,3 @@ pub fn apply_top_tlds(tlds: Vec<&'static str>, top: &[String]) -> Vec<&'static s
}
result
}
-
-#[cfg(test)]
-mod tests {
- use super::*;
-
- #[test]
- fn test_parse_entry_bare() {
- let (tld, server) = parse_entry("com");
- assert_eq!(tld, "com");
- assert_eq!(server, None);
- }
-
- #[test]
- fn test_parse_entry_with_override() {
- let (tld, server) = parse_entry("io:whois.nic.io");
- assert_eq!(tld, "io");
- assert_eq!(server, Some("whois.nic.io".to_string()));
- }
-
- #[test]
- fn test_whois_overrides_populated() {
- let overrides = whois_overrides();
- // io should have an override since our Lists.toml has "io:whois.nic.io"
- assert!(overrides.get_server("io").is_some());
- // com should not (it has RDAP)
- assert!(overrides.get_server("com").is_none());
- }
-
- #[test]
- fn test_top_tlds_reorder() {
- let tlds = vec!["com", "net", "org", "ch", "de"];
- let top = vec!["ch".to_string(), "de".to_string()];
- let result = apply_top_tlds(tlds, &top);
- assert_eq!(result, vec!["ch", "de", "com", "net", "org"]);
- }
-
- #[test]
- fn test_top_tlds_missing_ignored() {
- let tlds = vec!["com", "net"];
- let top = vec!["swiss".to_string()];
- let result = apply_top_tlds(tlds, &top);
- assert_eq!(result, vec!["com", "net"]);
- }
-}
diff --git a/src/tui.rs b/src/tui.rs
index 6c068dc..ec246dd 100644
--- a/src/tui.rs
+++ b/src/tui.rs
@@ -11,7 +11,7 @@ use ratatui::{
layout::{Constraint, Direction, Layout, Rect},
style::{Color, Modifier, Style},
text::{Line, Span},
- widgets::{Block, Borders, Clear, List, ListItem, ListState, Paragraph},
+ widgets::{Block, BorderType, Borders, Clear, List, ListItem, ListState, Paragraph},
Frame, Terminal,
};
use std::io::{self, Write};
@@ -203,6 +203,8 @@ struct App {
checking_favorites: bool,
backups_enabled: bool,
backup_count: u32,
+ no_color: bool,
+ no_unicode: bool,
}
#[derive(Debug, Clone, Default)]
@@ -299,9 +301,61 @@ impl App {
checking_favorites: false,
backups_enabled: config.settings.backups,
backup_count: config.settings.backup_count,
+ no_color: args.no_color,
+ no_unicode: args.no_unicode,
}
}
+ /// style with fg color, or plain style if no_color is on
+ fn fg(&self, color: Color) -> Style {
+ if self.no_color {
+ Style::default()
+ } else {
+ Style::default().fg(color)
+ }
+ }
+
+ /// style with fg color + bold
+ fn fg_bold(&self, color: Color) -> Style {
+ if self.no_color {
+ Style::default().add_modifier(Modifier::BOLD)
+ } else {
+ Style::default().fg(color).add_modifier(Modifier::BOLD)
+ }
+ }
+
+ /// resolve a color: returns Color::Reset when no_color is on
+ /// use this when chaining .bg() or .fg() on existing styles
+ fn c(&self, color: Color) -> Color {
+ if self.no_color { Color::Reset } else { color }
+ }
+
+ // unicode symbol helpers
+ fn sym_check(&self) -> &'static str {
+ if self.no_unicode { "+" } else { "\u{2713}" }
+ }
+ fn sym_cross(&self) -> &'static str {
+ if self.no_unicode { "x" } else { "\u{2717}" }
+ }
+ fn sym_sep(&self) -> &'static str {
+ if self.no_unicode { "|" } else { "\u{2502}" }
+ }
+ fn sym_bar_filled(&self) -> &'static str {
+ if self.no_unicode { "#" } else { "\u{2588}" }
+ }
+ fn sym_bar_empty(&self) -> &'static str {
+ if self.no_unicode { "-" } else { "\u{2591}" }
+ }
+
+ /// preconfigured Block with ASCII or unicode borders
+ fn block(&self) -> Block<'static> {
+ let mut b = Block::default().borders(Borders::ALL);
+ if self.no_unicode {
+ b = b.border_type(BorderType::Plain);
+ }
+ b
+ }
+
fn get_effective_tlds(&self) -> Vec<&'static str> {
let mut tld_vec = self.base_tlds_for_selection();
@@ -1844,12 +1898,13 @@ fn start_search(app: &mut App) {
app.stream_rx = Some(stream.receiver);
}
+
fn draw_ui(f: &mut Frame, app: &mut App) {
let size = f.area();
if terminal_too_small(size) {
app.panel_rects = PanelRects::default();
- draw_terminal_too_small(f, size);
+ draw_terminal_too_small(f, app, size);
return;
}
@@ -1978,7 +2033,7 @@ fn draw_ui(f: &mut Frame, app: &mut App) {
app.panel_rects.search = Some(main_chunks[2]);
// draw each panel
- draw_topbar(f, main_chunks[0]);
+ draw_topbar(f, app, main_chunks[0]);
if let Some(scratchpad_rect) = scratchpad_chunk {
draw_scratchpad(f, app, scratchpad_rect);
}
@@ -2005,10 +2060,9 @@ fn terminal_too_small(area: Rect) -> bool {
area.width < MIN_UI_WIDTH || area.height < MIN_UI_HEIGHT
}
-fn draw_terminal_too_small(f: &mut Frame, area: Rect) {
- let block = Block::default()
- .borders(Borders::ALL)
- .border_style(Style::default().fg(Color::Red))
+fn draw_terminal_too_small(f: &mut Frame, app: &App, area: Rect) {
+ let block = app.block()
+ .border_style(app.fg(Color::Red))
.title(" hoardom ");
let inner = block.inner(area);
@@ -2018,54 +2072,46 @@ fn draw_terminal_too_small(f: &mut Frame, area: Rect) {
let text = vec![
Line::from(Span::styled(
fit_cell_center("HELP ! HELP ! HELP !", content_width),
- Style::default()
- .fg(Color::White)
- .add_modifier(Modifier::BOLD),
+ app.fg_bold(Color::White),
)),
Line::from(Span::styled(
fit_cell_center("I AM BEING CRUSHED!", content_width),
- Style::default()
- .fg(Color::White)
- .add_modifier(Modifier::BOLD),
+ app.fg_bold(Color::White),
)),
Line::from(fit_cell_center("", content_width)),
Line::from(Span::styled(
fit_cell_center("Im claustrophobic! :'(", content_width),
- Style::default().fg(Color::White),
+ app.fg(Color::White),
)),
Line::from(Span::styled(
fit_cell_center(
&format!("Need {}x{} of space", MIN_UI_WIDTH, MIN_UI_HEIGHT),
content_width,
),
- Style::default().fg(Color::White),
+ app.fg(Color::White),
)),
Line::from(Span::styled(
fit_cell_center(
&format!("Current: {}x{}", area.width, area.height),
content_width,
),
- Style::default().fg(Color::DarkGray),
+ app.fg(Color::DarkGray),
)),
Line::from(fit_cell_center("", content_width)),
Line::from(Span::styled(
fit_cell_center("REFUSING TO WORK TILL YOU", content_width),
- Style::default()
- .fg(Color::White)
- .add_modifier(Modifier::BOLD),
+ app.fg_bold(Color::White),
)),
Line::from(Span::styled(
fit_cell_center("GIVE ME BACK MY SPACE! >:(", content_width),
- Style::default()
- .fg(Color::White)
- .add_modifier(Modifier::BOLD),
+ app.fg_bold(Color::White),
)),
];
f.render_widget(Paragraph::new(text), inner);
}
-fn draw_topbar(f: &mut Frame, area: Rect) {
+fn draw_topbar(f: &mut Frame, app: &App, area: Rect) {
let title = format!("{} - {}", APP_NAME, APP_DESC);
let width = area.width as usize;
let left = format!("{} {}", CLOSE_BUTTON_LABEL, title);
@@ -2074,46 +2120,31 @@ fn draw_topbar(f: &mut Frame, area: Rect) {
let paragraph = Paragraph::new(Line::from(vec![
Span::styled(
CLOSE_BUTTON_LABEL,
- Style::default()
- .fg(Color::Red)
- .bg(Color::Gray)
- .add_modifier(Modifier::BOLD),
+ app.fg_bold(Color::Red).bg(app.c(Color::Gray)),
),
Span::styled(
format!(" {}", title),
- Style::default()
- .fg(Color::White)
- .bg(Color::Red)
- .add_modifier(Modifier::BOLD),
+ app.fg_bold(Color::White).bg(app.c(Color::Red)),
),
Span::styled(
" ".repeat(gap),
- Style::default().bg(Color::Red).add_modifier(Modifier::BOLD),
+ Style::default().bg(app.c(Color::Red)).add_modifier(Modifier::BOLD),
),
Span::styled(
EXPORT_BUTTON_LABEL,
- Style::default()
- .fg(Color::LightGreen)
- .bg(Color::Red)
- .add_modifier(Modifier::BOLD),
+ app.fg_bold(Color::LightGreen).bg(app.c(Color::Red)),
),
Span::styled(
" ",
- Style::default().bg(Color::Red).add_modifier(Modifier::BOLD),
+ Style::default().bg(app.c(Color::Red)).add_modifier(Modifier::BOLD),
),
Span::styled(
HELP_BUTTON_LABEL,
- Style::default()
- .fg(Color::LightGreen)
- .bg(Color::Red)
- .add_modifier(Modifier::BOLD),
+ app.fg_bold(Color::LightGreen).bg(app.c(Color::Red)),
),
]))
.style(
- Style::default()
- .fg(Color::White)
- .bg(Color::Red)
- .add_modifier(Modifier::BOLD),
+ app.fg_bold(Color::White).bg(app.c(Color::Red)),
);
f.render_widget(paragraph, area);
}
@@ -2124,67 +2155,66 @@ fn draw_help_overlay(f: &mut Frame, app: &mut App, area: Rect) {
app.panel_rects.help_popup = Some(popup);
let text = vec![
- Line::from(Span::styled(" ", Style::default().fg(Color::White))),
- Line::from(Span::styled("Global :", Style::default().fg(Color::White))),
+ Line::from(Span::styled(" ", app.fg(Color::White))),
+ Line::from(Span::styled("Global :", app.fg(Color::White))),
Line::from(Span::styled(
"F1 or Help button Toggle this help",
- Style::default().fg(Color::White),
+ app.fg(Color::White),
)),
Line::from(Span::styled(
"F2 or Export button Open export popup",
- Style::default().fg(Color::White),
+ app.fg(Color::White),
)),
Line::from(Span::styled(
"Ctrl+C Quit the app",
- Style::default().fg(Color::White),
+ app.fg(Color::White),
)),
Line::from(Span::styled(
"s Stop/cancel running search",
- Style::default().fg(Color::White),
+ app.fg(Color::White),
)),
Line::from(Span::styled(
"Esc Clear selection or close help",
- Style::default().fg(Color::White),
+ app.fg(Color::White),
)),
Line::from(Span::styled(
"Tab or Shift+Tab Move between panels",
- Style::default().fg(Color::White),
+ app.fg(Color::White),
)),
Line::from(Span::styled(
"Up and Down arrows Navigate results",
- Style::default().fg(Color::White),
+ app.fg(Color::White),
)),
- Line::from(Span::styled(" ", Style::default().fg(Color::White))),
+ Line::from(Span::styled(" ", app.fg(Color::White))),
Line::from(Span::styled(
"Mouse Click Elements duh",
- Style::default().fg(Color::White),
+ app.fg(Color::White),
)),
Line::from(Span::styled(
"Scrolling Scroll through elements (yea)",
- Style::default().fg(Color::White),
+ app.fg(Color::White),
)),
- Line::from(Span::styled(" ", Style::default().fg(Color::White))),
+ Line::from(Span::styled(" ", app.fg(Color::White))),
Line::from(Span::styled(
"In Results :",
- Style::default().fg(Color::White),
+ app.fg(Color::White),
)),
Line::from(Span::styled(
"Enter Add highlighted result to Favorites",
- Style::default().fg(Color::White),
+ app.fg(Color::White),
)),
Line::from(Span::styled(
"In Favorites :",
- Style::default().fg(Color::White),
+ app.fg(Color::White),
)),
Line::from(Span::styled(
"Backspace or Delete Remove focused favorite",
- Style::default().fg(Color::White),
+ app.fg(Color::White),
)),
];
- let block = Block::default()
- .borders(Borders::ALL)
- .border_style(Style::default().fg(Color::Red))
+ let block = app.block()
+ .border_style(app.fg(Color::Red))
.title(" Help ");
f.render_widget(Clear, popup);
@@ -2199,9 +2229,8 @@ fn draw_export_popup(f: &mut Frame, app: &mut App, area: Rect) {
let popup = export_popup_rect(area);
app.panel_rects.export_popup = Some(popup);
- let block = Block::default()
- .borders(Borders::ALL)
- .border_style(Style::default().fg(Color::Red))
+ let block = app.block()
+ .border_style(app.fg(Color::Red))
.title(" Export ");
let inner = block.inner(popup);
@@ -2220,8 +2249,9 @@ fn draw_export_popup(f: &mut Frame, app: &mut App, area: Rect) {
])
.split(inner);
+ let white_style = app.fg(Color::White);
let mode_style = |mode: ExportMode| {
- let mut style = Style::default().fg(Color::White);
+ let mut style = white_style;
if popup_state.mode == mode {
style = style.add_modifier(Modifier::REVERSED | Modifier::BOLD);
} else if popup_state.selected_row == 0 {
@@ -2235,7 +2265,7 @@ fn draw_export_popup(f: &mut Frame, app: &mut App, area: Rect) {
chunks[0].width as usize,
);
f.render_widget(
- Paragraph::new(subtitle).style(Style::default().fg(Color::DarkGray)),
+ Paragraph::new(subtitle).style(app.fg(Color::DarkGray)),
chunks[0],
);
@@ -2268,34 +2298,29 @@ fn draw_export_popup(f: &mut Frame, app: &mut App, area: Rect) {
]);
f.render_widget(Paragraph::new(mode_line), chunks[1]);
- let path_block = Block::default()
- .borders(Borders::ALL)
+ let path_block = app.block()
.border_style(if popup_state.selected_row == 1 {
- Style::default().fg(Color::Red)
+ app.fg(Color::Red)
} else {
- Style::default().fg(Color::DarkGray)
+ app.fg(Color::DarkGray)
})
.title(" Save to ");
let path_inner = path_block.inner(chunks[2]);
app.panel_rects.export_path = Some(chunks[2]);
f.render_widget(path_block, chunks[2]);
f.render_widget(
- Paragraph::new(popup_state.path.as_str()).style(Style::default().fg(Color::White)),
+ Paragraph::new(popup_state.path.as_str()).style(app.fg(Color::White)),
path_inner,
);
let status_style = if popup_state.status_success {
- Style::default()
- .fg(Color::Green)
- .add_modifier(Modifier::BOLD)
+ app.fg_bold(Color::Green)
} else if popup_state.confirm_overwrite {
- Style::default()
- .fg(Color::Yellow)
- .add_modifier(Modifier::BOLD)
+ app.fg_bold(Color::Yellow)
} else if popup_state.status.is_some() {
- Style::default().fg(Color::Red)
+ app.fg(Color::Red)
} else {
- Style::default().fg(Color::DarkGray)
+ app.fg(Color::DarkGray)
};
let status_text = popup_state.status.as_deref().unwrap_or(" ");
f.render_widget(
@@ -2328,28 +2353,18 @@ fn draw_export_popup(f: &mut Frame, app: &mut App, area: Rect) {
Span::styled(
cancel_label,
if popup_state.selected_row == 2 {
- Style::default()
- .fg(Color::Green)
- .bg(Color::DarkGray)
- .add_modifier(Modifier::BOLD)
+ app.fg_bold(Color::Green).bg(app.c(Color::DarkGray))
} else {
- Style::default()
- .fg(Color::Green)
- .add_modifier(Modifier::BOLD)
+ app.fg_bold(Color::Green)
},
),
Span::raw(button_gap),
Span::styled(
save_label,
if popup_state.selected_row == 3 {
- Style::default()
- .fg(Color::Green)
- .bg(Color::DarkGray)
- .add_modifier(Modifier::BOLD)
+ app.fg_bold(Color::Green).bg(app.c(Color::DarkGray))
} else {
- Style::default()
- .fg(Color::Green)
- .add_modifier(Modifier::BOLD)
+ app.fg_bold(Color::Green)
},
),
]);
@@ -2366,9 +2381,9 @@ fn draw_export_popup(f: &mut Frame, app: &mut App, area: Rect) {
fn draw_results(f: &mut Frame, app: &mut App, area: Rect) {
let focused = app.focus == Focus::Results;
let border_style = if focused {
- Style::default().fg(Color::Red)
+ app.fg(Color::Red)
} else {
- Style::default().fg(Color::DarkGray)
+ app.fg(Color::DarkGray)
};
// show progress in title when searching
@@ -2395,8 +2410,7 @@ fn draw_results(f: &mut Frame, app: &mut App, area: Rect) {
)
};
- let block = Block::default()
- .borders(Borders::ALL)
+ let block = app.block()
.border_style(border_style)
.title(title);
@@ -2415,12 +2429,12 @@ fn draw_results(f: &mut Frame, app: &mut App, area: Rect) {
let (cur, tot) = app.search_progress;
let pct = (cur as f64 / tot as f64 * 100.0) as u16;
let filled = (chunks[0].width as u32 * cur as u32 / tot as u32) as u16;
- let bar: String = "\u{2588}".repeat(filled as usize)
- + &"\u{2591}".repeat((chunks[0].width.saturating_sub(filled)) as usize);
+ let bar: String = app.sym_bar_filled().repeat(filled as usize)
+ + &app.sym_bar_empty().repeat((chunks[0].width.saturating_sub(filled)) as usize);
let bar_text = format!(" {}% ", pct);
let gauge_line = Line::from(vec![
- Span::styled(bar, Style::default().fg(Color::Red)),
- Span::styled(bar_text, Style::default().fg(Color::DarkGray)),
+ Span::styled(bar, app.fg(Color::Red)),
+ Span::styled(bar_text, app.fg(Color::DarkGray)),
]);
f.render_widget(Paragraph::new(gauge_line), chunks[0]);
@@ -2437,6 +2451,7 @@ fn draw_results_list(f: &mut Frame, app: &mut App, area: Rect) {
let show_note_column = app.show_unavailable;
let selected_idx = app.results_state.selected();
let selected_bg = Color::Black;
+ let sep = format!(" {} ", app.sym_sep());
// collect visible results
let visible_data: Vec<(String, String, String, DomainStatus)> = if app.show_unavailable {
@@ -2472,7 +2487,7 @@ fn draw_results_list(f: &mut Frame, app: &mut App, area: Rect) {
} else {
"No available domains found"
};
- let p = Paragraph::new(msg).style(Style::default().fg(Color::DarkGray));
+ let p = Paragraph::new(msg).style(app.fg(Color::DarkGray));
f.render_widget(p, area);
return;
}
@@ -2519,35 +2534,27 @@ fn draw_results_list(f: &mut Frame, app: &mut App, area: Rect) {
let mut header_spans = vec![
Span::styled(
format!(" {}", fit_cell("Domain", domain_w)),
- Style::default()
- .fg(Color::Gray)
- .add_modifier(Modifier::BOLD),
+ app.fg_bold(Color::Gray),
),
- Span::styled(" │ ", Style::default().fg(Color::DarkGray)),
+ Span::styled(sep.clone(), app.fg(Color::DarkGray)),
Span::styled(
fit_cell("Status", status_w),
- Style::default()
- .fg(Color::Gray)
- .add_modifier(Modifier::BOLD),
+ app.fg_bold(Color::Gray),
),
];
if show_note_column {
- header_spans.push(Span::styled(" │ ", Style::default().fg(Color::DarkGray)));
+ header_spans.push(Span::styled(sep.clone(), app.fg(Color::DarkGray)));
header_spans.push(Span::styled(
fit_cell("Details", note_w),
- Style::default()
- .fg(Color::Gray)
- .add_modifier(Modifier::BOLD),
+ app.fg_bold(Color::Gray),
));
}
- header_spans.push(Span::styled(" │ ", Style::default().fg(Color::DarkGray)));
+ header_spans.push(Span::styled(sep.clone(), app.fg(Color::DarkGray)));
header_spans.push(Span::styled(
- " ✓ ",
- Style::default()
- .fg(Color::Gray)
- .add_modifier(Modifier::BOLD),
+ format!(" {} ", app.sym_check()),
+ app.fg_bold(Color::Gray),
));
f.render_widget(Paragraph::new(Line::from(header_spans)), header_area);
@@ -2561,20 +2568,20 @@ fn draw_results_list(f: &mut Frame, app: &mut App, area: Rect) {
let selection_bg = if is_selected { Some(selected_bg) } else { None };
let status_style = match status {
- DomainStatus::Available => Style::default().fg(Color::Green),
- DomainStatus::Registered { .. } => Style::default().fg(Color::Red),
+ DomainStatus::Available => app.fg(Color::Green),
+ DomainStatus::Registered { .. } => app.fg(Color::Red),
DomainStatus::Error { kind, .. } => match kind {
- ErrorKind::InvalidTld => Style::default().fg(Color::Yellow),
- _ => Style::default().fg(Color::Blue),
+ ErrorKind::InvalidTld => app.fg(Color::Yellow),
+ _ => app.fg(Color::Blue),
},
};
let domain_style = match status {
- DomainStatus::Available => Style::default().fg(Color::Green),
- DomainStatus::Registered { .. } => Style::default().fg(Color::Red),
+ DomainStatus::Available => app.fg(Color::Green),
+ DomainStatus::Registered { .. } => app.fg(Color::Red),
DomainStatus::Error { kind, .. } => match kind {
- ErrorKind::InvalidTld => Style::default().fg(Color::Yellow),
- _ => Style::default().fg(Color::Blue),
+ ErrorKind::InvalidTld => app.fg(Color::Yellow),
+ _ => app.fg(Color::Blue),
},
};
@@ -2591,37 +2598,37 @@ fn draw_results_list(f: &mut Frame, app: &mut App, area: Rect) {
format!(" {}", fit_cell(full, domain_w)),
apply_bg(domain_style),
),
- Span::styled(" \u{2502} ", apply_bg(Style::default().fg(Color::Gray))),
+ Span::styled(sep.clone(), apply_bg(app.fg(Color::Gray))),
Span::styled(fit_cell(status_str, status_w), apply_bg(status_style)),
];
if show_note_column {
spans.push(Span::styled(
- " \u{2502} ",
- apply_bg(Style::default().fg(Color::Gray)),
+ sep.clone(),
+ apply_bg(app.fg(Color::Gray)),
));
spans.push(Span::styled(
fit_cell(note, note_w),
- apply_bg(Style::default().fg(Color::White)),
+ apply_bg(app.fg(Color::White)),
));
}
spans.push(Span::styled(
- " \u{2502} ",
- apply_bg(Style::default().fg(Color::Gray)),
+ sep.clone(),
+ apply_bg(app.fg(Color::Gray)),
));
spans.push(match status {
DomainStatus::Available => {
- Span::styled(" ✓ ", apply_bg(Style::default().fg(Color::Green)))
+ Span::styled(format!(" {} ", app.sym_check()), apply_bg(app.fg(Color::Green)))
}
DomainStatus::Registered { .. } => {
- Span::styled(" ✗ ", apply_bg(Style::default().fg(Color::Red)))
+ Span::styled(format!(" {} ", app.sym_cross()), apply_bg(app.fg(Color::Red)))
}
DomainStatus::Error { kind, .. } => match kind {
ErrorKind::InvalidTld => {
- Span::styled(" ? ", apply_bg(Style::default().fg(Color::Yellow)))
+ Span::styled(" ? ", apply_bg(app.fg(Color::Yellow)))
}
- _ => Span::styled(" ! ", apply_bg(Style::default().fg(Color::Blue))),
+ _ => Span::styled(" ! ", apply_bg(app.fg(Color::Blue))),
},
});
@@ -2778,13 +2785,12 @@ fn move_scratchpad_cursor_vertical(app: &mut App, line_delta: i16) {
fn draw_scratchpad(f: &mut Frame, app: &mut App, area: Rect) {
let focused = app.focus == Focus::Scratchpad;
let border_style = if focused {
- Style::default().fg(Color::Red)
+ app.fg(Color::Red)
} else {
- Style::default().fg(Color::DarkGray)
+ app.fg(Color::DarkGray)
};
- let block = Block::default()
- .borders(Borders::ALL)
+ let block = app.block()
.border_style(border_style)
.title(" Scratchpad ");
@@ -2797,7 +2803,7 @@ fn draw_scratchpad(f: &mut Frame, app: &mut App, area: Rect) {
};
f.render_widget(block, area);
f.render_widget(
- Paragraph::new(text).style(Style::default().fg(Color::White)),
+ Paragraph::new(text).style(app.fg(Color::White)),
inner,
);
@@ -2812,9 +2818,9 @@ fn draw_scratchpad(f: &mut Frame, app: &mut App, area: Rect) {
fn draw_favorites(f: &mut Frame, app: &mut App, area: Rect) {
let focused = app.focus == Focus::Favorites;
let border_style = if focused {
- Style::default().fg(Color::Red)
+ app.fg(Color::Red)
} else {
- Style::default().fg(Color::DarkGray)
+ app.fg(Color::DarkGray)
};
let title = if app.checking_favorites {
@@ -2823,8 +2829,7 @@ fn draw_favorites(f: &mut Frame, app: &mut App, area: Rect) {
" Favorites "
};
- let block = Block::default()
- .borders(Borders::ALL)
+ let block = app.block()
.border_style(border_style)
.title(title);
@@ -2857,10 +2862,10 @@ fn draw_favorites(f: &mut Frame, app: &mut App, area: Rect) {
};
let mut spans = vec![Span::styled(
fav.domain.as_str(),
- Style::default().fg(status_color),
+ app.fg(status_color),
)];
if fav.changed {
- spans.push(Span::styled(" !", Style::default().fg(Color::Yellow)));
+ spans.push(Span::styled(" !", app.fg(Color::Yellow)));
}
ListItem::new(Line::from(spans))
})
@@ -2878,9 +2883,9 @@ fn draw_favorites(f: &mut Frame, app: &mut App, area: Rect) {
"[c]heck all"
};
let btn_style = if app.checking_favorites {
- Style::default().fg(Color::DarkGray)
+ app.fg(Color::DarkGray)
} else {
- Style::default().fg(Color::Green)
+ app.fg(Color::Green)
};
f.render_widget(
Paragraph::new(Line::from(Span::styled(btn_label, btn_style)))
@@ -2892,13 +2897,12 @@ fn draw_favorites(f: &mut Frame, app: &mut App, area: Rect) {
fn draw_settings(f: &mut Frame, app: &mut App, area: Rect) {
let focused = app.focus == Focus::Settings;
let border_style = if focused {
- Style::default().fg(Color::Red)
+ app.fg(Color::Red)
} else {
- Style::default().fg(Color::DarkGray)
+ app.fg(Color::DarkGray)
};
- let block = Block::default()
- .borders(Borders::ALL)
+ let block = app.block()
.border_style(border_style)
.title(" Settings ");
@@ -2910,9 +2914,9 @@ fn draw_settings(f: &mut Frame, app: &mut App, area: Rect) {
let selected = if focused { app.settings_selected } else { None };
let checkbox_style = |row: usize, checked: bool| {
let style = if checked {
- Style::default().fg(Color::Green)
+ app.fg(Color::Green)
} else {
- Style::default().fg(Color::DarkGray)
+ app.fg(Color::DarkGray)
};
if selected == Some(row) {
@@ -2926,13 +2930,13 @@ fn draw_settings(f: &mut Frame, app: &mut App, area: Rect) {
if selected == Some(row) {
Style::default().add_modifier(Modifier::REVERSED)
} else {
- Style::default().fg(Color::White)
+ app.fg(Color::White)
}
};
let tld_row_style = if selected == Some(0) {
Style::default()
- .bg(Color::DarkGray)
+ .bg(app.c(Color::DarkGray))
.add_modifier(Modifier::BOLD)
} else {
Style::default()
@@ -2940,7 +2944,7 @@ fn draw_settings(f: &mut Frame, app: &mut App, area: Rect) {
let jobs_row_style = if selected == Some(4) {
Style::default()
- .bg(Color::DarkGray)
+ .bg(app.c(Color::DarkGray))
.add_modifier(Modifier::BOLD)
} else {
Style::default()
@@ -2949,10 +2953,10 @@ fn draw_settings(f: &mut Frame, app: &mut App, area: Rect) {
let text = vec![
Line::from(vec![
Span::raw(" "),
- Span::styled("TLD List: [", tld_row_style.fg(Color::White)),
- Span::styled(app.tld_list_name.as_str(), tld_row_style.fg(Color::Cyan)),
- Span::styled("] ", tld_row_style.fg(Color::White)),
- Span::styled("V", tld_row_style.fg(Color::Green)),
+ Span::styled("TLD List: [", tld_row_style.fg(app.c(Color::White))),
+ Span::styled(app.tld_list_name.as_str(), tld_row_style.fg(app.c(Color::Cyan))),
+ Span::styled("] ", tld_row_style.fg(app.c(Color::White))),
+ Span::styled("V", tld_row_style.fg(app.c(Color::Green))),
]),
Line::from(vec![
Span::raw(" "),
@@ -2971,10 +2975,10 @@ fn draw_settings(f: &mut Frame, app: &mut App, area: Rect) {
]),
Line::from(vec![
Span::raw(" "),
- Span::styled("Jobs: [", jobs_row_style.fg(Color::White)),
- Span::styled(jobs_str, jobs_row_style.fg(Color::Cyan)),
- Span::styled("] ", jobs_row_style.fg(Color::White)),
- Span::styled("-/+", jobs_row_style.fg(Color::Green)),
+ Span::styled("Jobs: [", jobs_row_style.fg(app.c(Color::White))),
+ Span::styled(jobs_str, jobs_row_style.fg(app.c(Color::Cyan))),
+ Span::styled("] ", jobs_row_style.fg(app.c(Color::White))),
+ Span::styled("-/+", jobs_row_style.fg(app.c(Color::Green))),
]),
];
@@ -2985,9 +2989,9 @@ fn draw_settings(f: &mut Frame, app: &mut App, area: Rect) {
fn draw_search(f: &mut Frame, app: &mut App, area: Rect) {
let focused = app.focus == Focus::Search;
let border_style = if focused {
- Style::default().fg(Color::Red)
+ app.fg(Color::Red)
} else {
- Style::default().fg(Color::DarkGray)
+ app.fg(Color::DarkGray)
};
let title = match &app.status_msg {
@@ -2995,8 +2999,7 @@ fn draw_search(f: &mut Frame, app: &mut App, area: Rect) {
None => " Search (Enter to lookup) ".to_string(),
};
- let block = Block::default()
- .borders(Borders::ALL)
+ let block = app.block()
.border_style(border_style)
.title(title);
@@ -3041,32 +3044,23 @@ fn draw_search(f: &mut Frame, app: &mut App, area: Rect) {
let input_chunk = chunks[0];
let visible_input = fit_cell(&app.search_input, input_chunk.width as usize);
- let input = Paragraph::new(visible_input).style(Style::default().fg(Color::White));
+ let input = Paragraph::new(visible_input).style(app.fg(Color::White));
f.render_widget(input, input_chunk);
let search_enabled = !app.searching && !app.search_input.is_empty();
let cancel_enabled = app.searching;
let search_style = if search_enabled {
- Style::default()
- .fg(Color::Black)
- .bg(Color::Green)
- .add_modifier(Modifier::BOLD)
+ app.fg_bold(Color::Black).bg(app.c(Color::Green))
} else {
- Style::default().fg(Color::DarkGray).bg(Color::Black)
+ app.fg(Color::DarkGray).bg(app.c(Color::Black))
};
let stop_style = if cancel_enabled {
- Style::default()
- .fg(Color::Black)
- .bg(Color::Yellow)
- .add_modifier(Modifier::BOLD)
+ app.fg_bold(Color::Black).bg(app.c(Color::Yellow))
} else {
- Style::default().fg(Color::DarkGray).bg(Color::Black)
+ app.fg(Color::DarkGray).bg(app.c(Color::Black))
};
- let clear_style = Style::default()
- .fg(Color::White)
- .bg(Color::Red)
- .add_modifier(Modifier::BOLD);
+ let clear_style = app.fg_bold(Color::White).bg(app.c(Color::Red));
f.render_widget(
Paragraph::new(SEARCH_BUTTON_LABEL).style(search_style),
@@ -3121,22 +3115,18 @@ fn draw_dropdown(f: &mut Frame, app: &mut App, settings_area: Rect, selected: us
.map(|opt| {
ListItem::new(Line::from(Span::styled(
format!(" {} ", opt),
- Style::default().fg(Color::White),
+ app.fg(Color::White),
)))
})
.collect();
- let block = Block::default()
- .borders(Borders::ALL)
- .border_style(Style::default().fg(Color::Red))
+ let block = app.block()
+ .border_style(app.fg(Color::Red))
.title(" TLD List ");
f.render_widget(Clear, dropdown_full);
let list = List::new(items).block(block).highlight_style(
- Style::default()
- .fg(Color::White)
- .bg(Color::Red)
- .add_modifier(Modifier::BOLD),
+ app.fg_bold(Color::White).bg(app.c(Color::Red)),
);
let mut state = ListState::default();
state.select(Some(selected));