mirror of
https://github.com/kristoferssolo/traxor.git
synced 2026-01-14 20:46:14 +00:00
refactor: use config file as single source of truth for defaults
This commit is contained in:
parent
eba2eefc5e
commit
dabb434011
71
Cargo.lock
generated
71
Cargo.lock
generated
@ -523,31 +523,6 @@ dependencies = [
|
|||||||
"regex",
|
"regex",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "filecaster"
|
|
||||||
version = "0.2.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "cc97921cbd5451445637b91a0237b3c9316fa550ea7ff166a40be7ce5afad335"
|
|
||||||
dependencies = [
|
|
||||||
"filecaster-derive",
|
|
||||||
"merge",
|
|
||||||
"serde",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "filecaster-derive"
|
|
||||||
version = "0.2.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "7f5c7bc432f529eac3ec2d60812e05c1eae39fde9d7434fe59e64c03c27da882"
|
|
||||||
dependencies = [
|
|
||||||
"merge",
|
|
||||||
"proc-macro-error2",
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"serde",
|
|
||||||
"syn 2.0.104",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "filedescriptor"
|
name = "filedescriptor"
|
||||||
version = "0.8.3"
|
version = "0.8.3"
|
||||||
@ -1156,28 +1131,6 @@ dependencies = [
|
|||||||
"autocfg",
|
"autocfg",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "merge"
|
|
||||||
version = "0.2.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "56e520ba58faea3487f75df198b1d079644ec226ea3b0507d002c6fa4b8cf93a"
|
|
||||||
dependencies = [
|
|
||||||
"merge_derive",
|
|
||||||
"num-traits",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "merge_derive"
|
|
||||||
version = "0.2.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5c8f8ce6efff81cbc83caf4af0905c46e58cb46892f63ad3835e81b47eaf7968"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro-error2",
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn 2.0.104",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "minimal-lexical"
|
name = "minimal-lexical"
|
||||||
version = "0.2.1"
|
version = "0.2.1"
|
||||||
@ -1481,28 +1434,6 @@ dependencies = [
|
|||||||
"zerocopy",
|
"zerocopy",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "proc-macro-error-attr2"
|
|
||||||
version = "2.0.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "proc-macro-error2"
|
|
||||||
version = "2.0.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro-error-attr2",
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn 2.0.104",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.95"
|
version = "1.0.95"
|
||||||
@ -2582,8 +2513,6 @@ dependencies = [
|
|||||||
"crossterm",
|
"crossterm",
|
||||||
"derive_more",
|
"derive_more",
|
||||||
"dirs",
|
"dirs",
|
||||||
"filecaster",
|
|
||||||
"merge",
|
|
||||||
"ratatui",
|
"ratatui",
|
||||||
"serde",
|
"serde",
|
||||||
"thiserror 2.0.12",
|
"thiserror 2.0.12",
|
||||||
|
|||||||
@ -6,13 +6,11 @@ license = "GPLv3"
|
|||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
filecaster = { version = "0.2", features = ["derive", "merge"] }
|
|
||||||
color-eyre = "0.6"
|
color-eyre = "0.6"
|
||||||
crossterm = "0.29"
|
crossterm = "0.29"
|
||||||
derive_more = { version = "2.1", features = ["display"] }
|
derive_more = { version = "2.1", features = ["display"] }
|
||||||
dirs = "6.0"
|
dirs = "6.0"
|
||||||
merge = "0.2"
|
ratatui = "0.30"
|
||||||
ratatui = { version = "0.30" }
|
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
thiserror = "2.0"
|
thiserror = "2.0"
|
||||||
tokio = { version = "1", features = ["macros", "rt-multi-thread", "fs"] }
|
tokio = { version = "1", features = ["macros", "rt-multi-thread", "fs"] }
|
||||||
|
|||||||
@ -1,49 +1,93 @@
|
|||||||
|
# ============================================================================
|
||||||
|
# TRAXOR CONFIGURATION
|
||||||
|
# ============================================================================
|
||||||
|
# This file defines all default settings. User config (~/.config/traxor/config.toml)
|
||||||
|
# overrides these values. Only specify what you want to change in your user config.
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# KEYBINDS
|
||||||
|
# ============================================================================
|
||||||
[keybinds]
|
[keybinds]
|
||||||
quit = "q"
|
# Navigation
|
||||||
next_tab = "l"
|
|
||||||
prev_tab = "h"
|
|
||||||
next_torrent = "j"
|
|
||||||
prev_torrent = "k"
|
prev_torrent = "k"
|
||||||
|
next_torrent = "j"
|
||||||
|
prev_tab = "h"
|
||||||
|
next_tab = "l"
|
||||||
|
|
||||||
|
# Tab switching
|
||||||
switch_tab_1 = "1"
|
switch_tab_1 = "1"
|
||||||
switch_tab_2 = "2"
|
switch_tab_2 = "2"
|
||||||
switch_tab_3 = "3"
|
switch_tab_3 = "3"
|
||||||
|
|
||||||
|
# Torrent actions
|
||||||
toggle_torrent = "enter"
|
toggle_torrent = "enter"
|
||||||
toggle_all = "a"
|
toggle_all = "a"
|
||||||
|
select = " "
|
||||||
|
move_torrent = "m"
|
||||||
|
rename_torrent = "r"
|
||||||
delete = "d"
|
delete = "d"
|
||||||
delete_force = "D"
|
delete_force = "D"
|
||||||
select = " "
|
|
||||||
toggle_help = "?"
|
|
||||||
move = "m"
|
|
||||||
|
|
||||||
|
# General
|
||||||
|
toggle_help = "?"
|
||||||
|
quit = "q"
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# COLORS
|
||||||
|
# ============================================================================
|
||||||
[colors]
|
[colors]
|
||||||
|
# UI colors
|
||||||
highlight_background = "magenta"
|
highlight_background = "magenta"
|
||||||
highlight_foreground = "black"
|
highlight_foreground = "black"
|
||||||
header_foreground = "yellow"
|
header_foreground = "yellow"
|
||||||
info_foreground = "blue"
|
info_foreground = "blue"
|
||||||
error_foreground = "red"
|
|
||||||
|
|
||||||
|
# Status colors (torrent state)
|
||||||
|
status_downloading = "cyan"
|
||||||
|
status_seeding = "white"
|
||||||
|
status_stopped = "dark_gray"
|
||||||
|
status_verifying = "yellow"
|
||||||
|
status_queued = "light_blue"
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# LOGGING
|
||||||
|
# ============================================================================
|
||||||
[log]
|
[log]
|
||||||
traxor = "info"
|
traxor = "info"
|
||||||
ratatui = "warn"
|
ratatui = "warn"
|
||||||
transmission_rpc = "warn"
|
transmission_rpc = "warn"
|
||||||
|
|
||||||
# Custom tabs configuration
|
# ============================================================================
|
||||||
# Each [[tabs]] entry defines a tab with a name and list of columns.
|
# TABS
|
||||||
|
# ============================================================================
|
||||||
|
# Define custom tabs with specific columns. Each tab needs a name and columns list.
|
||||||
|
#
|
||||||
# Available columns:
|
# Available columns:
|
||||||
# name, status, size, downloaded, uploaded, ratio, progress, eta,
|
# name, status, size, downloaded, uploaded, ratio, progress, eta,
|
||||||
# peers, seeds, leeches, downspeed, upspeed, path, added, done,
|
# peers, seeds, leeches, downspeed, upspeed, path, added, done,
|
||||||
# left, queue, error, labels, tracker, hash, private, stalled,
|
# left, queue, error, labels, tracker, hash, private, stalled,
|
||||||
# finished, files, activity
|
# finished, files, activity
|
||||||
#
|
|
||||||
# Example:
|
[[tabs]]
|
||||||
# [[tabs]]
|
name = "All"
|
||||||
# name = "All"
|
columns = ["status", "leeches", "ratio", "size", "uploaded", "path", "name"]
|
||||||
# columns = ["status", "ratio", "size", "uploaded", "path", "name"]
|
|
||||||
#
|
[[tabs]]
|
||||||
# [[tabs]]
|
name = "Active"
|
||||||
# name = "Active"
|
columns = [
|
||||||
# columns = ["progress", "downspeed", "upspeed", "eta", "name"]
|
"size",
|
||||||
#
|
"uploaded",
|
||||||
# [[tabs]]
|
"ratio",
|
||||||
# name = "Downloading"
|
"leeches",
|
||||||
# columns = ["size", "left", "progress", "downspeed", "eta", "name"]
|
"seeds",
|
||||||
|
"status",
|
||||||
|
"eta",
|
||||||
|
"progress",
|
||||||
|
"downspeed",
|
||||||
|
"upspeed",
|
||||||
|
"name",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[tabs]]
|
||||||
|
name = "Downloading"
|
||||||
|
columns = ["size", "left", "progress", "downspeed", "eta", "path", "name"]
|
||||||
|
|||||||
@ -1,25 +1,14 @@
|
|||||||
use filecaster::FromFile;
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Debug, Clone, FromFile)]
|
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||||
pub struct ColorConfig {
|
pub struct ColorConfig {
|
||||||
#[from_file(default = "magenta")]
|
|
||||||
pub highlight_background: String,
|
pub highlight_background: String,
|
||||||
#[from_file(default = "black")]
|
|
||||||
pub highlight_foreground: String,
|
pub highlight_foreground: String,
|
||||||
#[from_file(default = "yellow")]
|
|
||||||
pub header_foreground: String,
|
pub header_foreground: String,
|
||||||
#[from_file(default = "blue")]
|
|
||||||
pub info_foreground: String,
|
pub info_foreground: String,
|
||||||
|
|
||||||
// Status colors
|
|
||||||
#[from_file(default = "cyan")]
|
|
||||||
pub status_downloading: String,
|
pub status_downloading: String,
|
||||||
#[from_file(default = "white")]
|
|
||||||
pub status_seeding: String,
|
pub status_seeding: String,
|
||||||
#[from_file(default = "dark_gray")]
|
|
||||||
pub status_stopped: String,
|
pub status_stopped: String,
|
||||||
#[from_file(default = "yellow")]
|
|
||||||
pub status_verifying: String,
|
pub status_verifying: String,
|
||||||
#[from_file(default = "light_blue")]
|
|
||||||
pub status_queued: String,
|
pub status_queued: String,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,37 +1,21 @@
|
|||||||
use filecaster::FromFile;
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Debug, Clone, FromFile)]
|
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||||
pub struct KeybindsConfig {
|
pub struct KeybindsConfig {
|
||||||
#[from_file(default = "q")]
|
|
||||||
pub quit: String,
|
pub quit: String,
|
||||||
#[from_file(default = "l")]
|
|
||||||
pub next_tab: String,
|
pub next_tab: String,
|
||||||
#[from_file(default = "h")]
|
|
||||||
pub prev_tab: String,
|
pub prev_tab: String,
|
||||||
#[from_file(default = "j")]
|
|
||||||
pub next_torrent: String,
|
pub next_torrent: String,
|
||||||
#[from_file(default = "k")]
|
|
||||||
pub prev_torrent: String,
|
pub prev_torrent: String,
|
||||||
#[from_file(default = "1")]
|
|
||||||
pub switch_tab_1: String,
|
pub switch_tab_1: String,
|
||||||
#[from_file(default = "2")]
|
|
||||||
pub switch_tab_2: String,
|
pub switch_tab_2: String,
|
||||||
#[from_file(default = "3")]
|
|
||||||
pub switch_tab_3: String,
|
pub switch_tab_3: String,
|
||||||
#[from_file(default = "enter")]
|
|
||||||
pub toggle_torrent: String,
|
pub toggle_torrent: String,
|
||||||
#[from_file(default = "a")]
|
|
||||||
pub toggle_all: String,
|
pub toggle_all: String,
|
||||||
#[from_file(default = "d")]
|
|
||||||
pub delete: String,
|
pub delete: String,
|
||||||
#[from_file(default = "D")]
|
|
||||||
pub delete_force: String,
|
pub delete_force: String,
|
||||||
#[from_file(default = " ")]
|
|
||||||
pub select: String,
|
pub select: String,
|
||||||
#[from_file(default = "?")]
|
|
||||||
pub toggle_help: String,
|
pub toggle_help: String,
|
||||||
#[from_file(default = "m")]
|
|
||||||
pub move_torrent: String,
|
pub move_torrent: String,
|
||||||
#[from_file(default = "r")]
|
|
||||||
pub rename_torrent: String,
|
pub rename_torrent: String,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,11 +1,8 @@
|
|||||||
use filecaster::FromFile;
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Debug, Clone, FromFile)]
|
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||||
pub struct LogConfig {
|
pub struct LogConfig {
|
||||||
#[from_file(default = "warn")]
|
|
||||||
pub traxor: String,
|
pub traxor: String,
|
||||||
#[from_file(default = "warn")]
|
|
||||||
pub ratatui: String,
|
pub ratatui: String,
|
||||||
#[from_file(default = "warn")]
|
|
||||||
pub transmission_rpc: String,
|
pub transmission_rpc: String,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,133 +6,104 @@ pub mod tabs;
|
|||||||
use color::ColorConfig;
|
use color::ColorConfig;
|
||||||
use color_eyre::{
|
use color_eyre::{
|
||||||
Result,
|
Result,
|
||||||
eyre::{Context, ContextCompat, Ok},
|
eyre::{Context, ContextCompat},
|
||||||
};
|
};
|
||||||
use filecaster::FromFile;
|
|
||||||
use keybinds::KeybindsConfig;
|
use keybinds::KeybindsConfig;
|
||||||
use log::LogConfig;
|
use log::LogConfig;
|
||||||
use merge::Merge;
|
use serde::{Deserialize, Serialize};
|
||||||
use serde::Deserialize;
|
|
||||||
use std::{
|
use std::{
|
||||||
fs::read_to_string,
|
fs::read_to_string,
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
};
|
};
|
||||||
use tabs::TabConfig;
|
use tabs::TabConfig;
|
||||||
use tracing::{debug, info, warn};
|
use toml::Value;
|
||||||
|
use tracing::{debug, info};
|
||||||
|
|
||||||
const DEFAULT_CONFIG_STR: &str = include_str!("../../config/default.toml");
|
/// Embedded default configuration - single source of truth for all defaults.
|
||||||
|
const DEFAULT_CONFIG: &str = include_str!("../../config/default.toml");
|
||||||
|
|
||||||
#[derive(Debug, Clone, FromFile)]
|
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
pub keybinds: KeybindsConfig,
|
pub keybinds: KeybindsConfig,
|
||||||
pub colors: ColorConfig,
|
pub colors: ColorConfig,
|
||||||
pub log: LogConfig,
|
pub log: LogConfig,
|
||||||
#[from_file(skip)]
|
|
||||||
pub tabs: Vec<TabConfig>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Helper struct for parsing tabs from TOML.
|
|
||||||
#[derive(Debug, Deserialize, Default)]
|
|
||||||
struct TabsFile {
|
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
tabs: Vec<TabConfig>,
|
pub tabs: Vec<TabConfig>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
/// Load configuration with fallback to embedded defaults.
|
/// Load configuration with fallback to embedded defaults.
|
||||||
///
|
///
|
||||||
/// Merge order:
|
/// Merge order:
|
||||||
/// 1. Embedded defaults
|
/// 1. Embedded defaults (config/default.toml - compiled in)
|
||||||
/// 2. System-wide config (`/etc/xdg/traxor/config.toml`)
|
/// 2. System-wide config (`/etc/xdg/traxor/config.toml`)
|
||||||
/// 3. User config (`~/.config/traxor/config.toml`)
|
/// 3. User config (`~/.config/traxor/config.toml`)
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
///
|
///
|
||||||
/// Returns an error if:
|
/// Returns an error if the TOML in any config file is invalid.
|
||||||
/// - The embedded default config cannot be parsed (should never happen unless corrupted at build time).
|
|
||||||
/// - The system-wide or user config file cannot be read due to I/O errors.
|
|
||||||
/// - The TOML in any config file is invalid and cannot be parsed.
|
|
||||||
#[tracing::instrument(name = "Loading configuration")]
|
#[tracing::instrument(name = "Loading configuration")]
|
||||||
pub fn load() -> Result<Self> {
|
pub fn load() -> Result<Self> {
|
||||||
let mut cfg_file = toml::from_str::<ConfigFile>(DEFAULT_CONFIG_STR)
|
let mut config: Value =
|
||||||
.context("Failed to parse embedded default config")?;
|
toml::from_str(DEFAULT_CONFIG).context("Failed to parse embedded default config")?;
|
||||||
|
|
||||||
let candidates = [
|
let candidates = [
|
||||||
("system-wide", PathBuf::from("/etc/xdg/traxor/config.toml")),
|
("system-wide", PathBuf::from("/etc/xdg/traxor/config.toml")),
|
||||||
("user-specific", get_config_path()?),
|
("user-specific", get_config_path()?),
|
||||||
];
|
];
|
||||||
|
|
||||||
let mut tabs: Option<Vec<TabConfig>> = None;
|
|
||||||
|
|
||||||
for (label, path) in &candidates {
|
for (label, path) in &candidates {
|
||||||
merge_config(&mut cfg_file, label, path)?;
|
if let Some(user_config) = load_toml_file(label, path)? {
|
||||||
// Load tabs separately (last one wins)
|
deep_merge(&mut config, user_config);
|
||||||
if let Some(t) = load_tabs(path) {
|
|
||||||
tabs = Some(t);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut config: Self = cfg_file.into();
|
let config: Self = config
|
||||||
config.tabs = tabs.unwrap_or_else(tabs::default_tabs);
|
.try_into()
|
||||||
|
.context("Failed to deserialize merged config")?;
|
||||||
|
|
||||||
debug!("Configuration loaded successfully.");
|
debug!("Configuration loaded successfully.");
|
||||||
Ok(config)
|
Ok(config)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_tabs(path: &Path) -> Option<Vec<TabConfig>> {
|
|
||||||
if !path.exists() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
let content = read_to_string(path).ok()?;
|
|
||||||
let tabs_file: TabsFile = toml::from_str(&content).ok()?;
|
|
||||||
if tabs_file.tabs.is_empty() {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(tabs_file.tabs)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tracing::instrument(name = "Getting config path")]
|
|
||||||
fn get_config_path() -> Result<PathBuf> {
|
fn get_config_path() -> Result<PathBuf> {
|
||||||
let config_dir =
|
let config_dir =
|
||||||
dirs::config_dir().context("Could not determine user configuration directory")?;
|
dirs::config_dir().context("Could not determine user configuration directory")?;
|
||||||
Ok(config_dir.join("traxor").join("config.toml"))
|
Ok(config_dir.join("traxor").join("config.toml"))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(name = "Merging config", skip(cfg_file, path))]
|
fn load_toml_file(label: &str, path: &Path) -> Result<Option<Value>> {
|
||||||
fn merge_config(cfg_file: &mut ConfigFile, label: &str, path: &Path) -> Result<()> {
|
if !path.exists() {
|
||||||
if !exists_and_log(label, path) {
|
debug!("{} config not found at: {:?}", label, path);
|
||||||
return Ok(());
|
return Ok(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
info!("Loading {} config from: {:?}", label, path);
|
info!("Loading {} config from: {:?}", label, path);
|
||||||
let s = read_config_str(label, path)?;
|
let content = read_to_string(path)
|
||||||
let other = parse_config_toml(label, &s)?;
|
.with_context(|| format!("Failed to read {label} config: {}", path.display()))?;
|
||||||
|
|
||||||
|
let value: Value =
|
||||||
|
toml::from_str(&content).with_context(|| format!("Failed to parse {label} config TOML"))?;
|
||||||
|
|
||||||
cfg_file.merge(other);
|
|
||||||
info!("Successfully loaded {} config.", label);
|
info!("Successfully loaded {} config.", label);
|
||||||
Ok(())
|
Ok(Some(value))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn exists_and_log(label: &str, path: &Path) -> bool {
|
/// Deep merge two TOML values. User values override defaults.
|
||||||
if !path.exists() {
|
/// For tables, merge recursively. For arrays (like tabs), replace entirely.
|
||||||
warn!("{} config not found at: {:?}", label, path);
|
fn deep_merge(base: &mut Value, other: Value) {
|
||||||
return false;
|
match (base, other) {
|
||||||
|
(Value::Table(base_table), Value::Table(other_table)) => {
|
||||||
|
for (key, other_value) in other_table {
|
||||||
|
match base_table.get_mut(&key) {
|
||||||
|
Some(base_value) => deep_merge(base_value, other_value),
|
||||||
|
None => {
|
||||||
|
base_table.insert(key, other_value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(base, other) => *base = other,
|
||||||
}
|
}
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_config_str(label: &str, path: &Path) -> Result<String> {
|
|
||||||
read_to_string(path).with_context(|| {
|
|
||||||
format!(
|
|
||||||
"Failed to read {label} config file at {}",
|
|
||||||
path.to_string_lossy()
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_config_toml(label: &str, s: &str) -> Result<ConfigFile> {
|
|
||||||
toml::from_str::<ConfigFile>(s)
|
|
||||||
.with_context(|| format!("Failed to parse TOML in {label} config"))
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,7 +16,6 @@ impl TabConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn parse_field(s: &str) -> Option<TorrentGetField> {
|
fn parse_field(s: &str) -> Option<TorrentGetField> {
|
||||||
// Match against known field names (case-insensitive)
|
|
||||||
Some(match s.to_lowercase().as_str() {
|
Some(match s.to_lowercase().as_str() {
|
||||||
"name" => TorrentGetField::Name,
|
"name" => TorrentGetField::Name,
|
||||||
"status" => TorrentGetField::Status,
|
"status" => TorrentGetField::Status,
|
||||||
@ -49,50 +48,3 @@ fn parse_field(s: &str) -> Option<TorrentGetField> {
|
|||||||
_ => return None,
|
_ => return None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Default tabs matching original hardcoded behavior.
|
|
||||||
#[must_use]
|
|
||||||
pub fn default_tabs() -> Vec<TabConfig> {
|
|
||||||
vec![
|
|
||||||
TabConfig {
|
|
||||||
name: "All".into(),
|
|
||||||
columns: vec![
|
|
||||||
"status".into(),
|
|
||||||
"peers_getting".into(),
|
|
||||||
"ratio".into(),
|
|
||||||
"size".into(),
|
|
||||||
"uploaded".into(),
|
|
||||||
"path".into(),
|
|
||||||
"name".into(),
|
|
||||||
],
|
|
||||||
},
|
|
||||||
TabConfig {
|
|
||||||
name: "Active".into(),
|
|
||||||
columns: vec![
|
|
||||||
"size".into(),
|
|
||||||
"uploaded".into(),
|
|
||||||
"ratio".into(),
|
|
||||||
"peers_getting".into(),
|
|
||||||
"seeds".into(),
|
|
||||||
"status".into(),
|
|
||||||
"eta".into(),
|
|
||||||
"progress".into(),
|
|
||||||
"downspeed".into(),
|
|
||||||
"upspeed".into(),
|
|
||||||
"name".into(),
|
|
||||||
],
|
|
||||||
},
|
|
||||||
TabConfig {
|
|
||||||
name: "Downloading".into(),
|
|
||||||
columns: vec![
|
|
||||||
"size".into(),
|
|
||||||
"left".into(),
|
|
||||||
"progress".into(),
|
|
||||||
"downspeed".into(),
|
|
||||||
"eta".into(),
|
|
||||||
"path".into(),
|
|
||||||
"name".into(),
|
|
||||||
],
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user