mirror of
https://github.com/kristoferssolo/traxor.git
synced 2025-10-21 20:10:35 +00:00
refactor(config): simplify config reading
!squash me !squash me
This commit is contained in:
parent
b341b7a661
commit
b988880c41
2
.gitignore
vendored
2
.gitignore
vendored
@ -19,4 +19,4 @@ target/
|
||||
*.pdb
|
||||
|
||||
# Log dir
|
||||
.log/
|
||||
.logs/
|
||||
|
||||
File diff suppressed because one or more lines are too long
22
Cargo.lock
generated
22
Cargo.lock
generated
@ -122,9 +122,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.27"
|
||||
version = "1.2.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d487aa071b5f64da6f19a3e848e3578944b726ee5a4854b82172f02aa876bfdc"
|
||||
checksum = "5c1599538de2394445747c8cf7935946e3cc27e9625f889d979bfb2aaf569362"
|
||||
dependencies = [
|
||||
"shlex",
|
||||
]
|
||||
@ -314,6 +314,15 @@ dependencies = [
|
||||
"powerfmt",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_macro"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_more"
|
||||
version = "2.0.1"
|
||||
@ -610,9 +619,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "hyper-util"
|
||||
version = "0.1.14"
|
||||
version = "0.1.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc2fdfdbff08affe55bb779f33b053aa1fe5dd5b54c257343c17edfa55711bdb"
|
||||
checksum = "7f66d5bd4c6f02bf0542fad85d626775bab9258cf795a4256dcaf3161114d1df"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"bytes",
|
||||
@ -1326,9 +1335,9 @@ checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
||||
|
||||
[[package]]
|
||||
name = "reqwest"
|
||||
version = "0.12.20"
|
||||
version = "0.12.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eabf4c97d9130e2bf606614eb937e86edac8292eaa6f422f995d7e8de1eb1813"
|
||||
checksum = "cbc931937e6ca3a06e3b6c0aa7841849b160a90351d6ab467a8b9b9959767531"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"bytes",
|
||||
@ -2035,6 +2044,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"color-eyre",
|
||||
"crossterm 0.29.0",
|
||||
"derive_macro",
|
||||
"dirs",
|
||||
"merge",
|
||||
"ratatui",
|
||||
|
||||
@ -3,9 +3,10 @@ name = "traxor"
|
||||
version = "0.1.0"
|
||||
authors = ["Kristofers Solo <dev@kristofers.xyz>"]
|
||||
license = "GPLv3"
|
||||
edition = "2021"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
derive_macro = { path = "derive_macro" }
|
||||
color-eyre = "0.6"
|
||||
crossterm = "0.29"
|
||||
dirs = "6.0"
|
||||
|
||||
47
derive_macro/Cargo.lock
generated
Normal file
47
derive_macro/Cargo.lock
generated
Normal file
@ -0,0 +1,47 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "derive_macro"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.95"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.104"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
|
||||
12
derive_macro/Cargo.toml
Normal file
12
derive_macro/Cargo.toml
Normal file
@ -0,0 +1,12 @@
|
||||
[package]
|
||||
name = "derive_macro"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[lib]
|
||||
crate-type = ["proc-macro"]
|
||||
|
||||
[dependencies]
|
||||
proc-macro2 = "1.0"
|
||||
quote = "1.0"
|
||||
syn = { version = "2.0", features = ["full", "extra-traits"] }
|
||||
42
derive_macro/src/from_file.rs
Normal file
42
derive_macro/src/from_file.rs
Normal file
@ -0,0 +1,42 @@
|
||||
use proc_macro::TokenStream;
|
||||
use quote::quote;
|
||||
use syn::{Data, DeriveInput, Fields, Ident};
|
||||
|
||||
pub fn impl_from_file(input: DeriveInput) -> TokenStream {
|
||||
let name = &input.ident;
|
||||
let file_name = format!("{name}File");
|
||||
let file_ident = Ident::new(&file_name, name.span());
|
||||
|
||||
let fields = match &input.data {
|
||||
Data::Struct(data) => match &data.fields {
|
||||
Fields::Named(fields) => &fields.named,
|
||||
_ => panic!("Only named fields are supported"),
|
||||
},
|
||||
_ => panic!("Only structs are supported"),
|
||||
};
|
||||
|
||||
let field_idents = fields.iter().map(|f| &f.ident);
|
||||
let field_idents2 = field_idents.clone();
|
||||
|
||||
quote! {
|
||||
impl #name {
|
||||
fn from_file(file: Option<#file_ident>) -> Self
|
||||
where
|
||||
#file_ident: Default + Clone
|
||||
{
|
||||
let default = #file_ident::default();
|
||||
let file = file.unwrap_or_else(|| default.clone());
|
||||
|
||||
Self {
|
||||
#(#field_idents: file.#field_idents2.unwrap_or_else(|| default.#field_idents.clone().unwrap())),*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Option<#file_ident>> for #name {
|
||||
fn from(value: Option<#file_ident>) -> Self {
|
||||
Self::from_file(value)
|
||||
}
|
||||
}
|
||||
}.into()
|
||||
}
|
||||
10
derive_macro/src/lib.rs
Normal file
10
derive_macro/src/lib.rs
Normal file
@ -0,0 +1,10 @@
|
||||
mod from_file;
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
use syn::{DeriveInput, parse_macro_input};
|
||||
|
||||
#[proc_macro_derive(FromFile)]
|
||||
pub fn derive_from_file(input: TokenStream) -> TokenStream {
|
||||
let input = parse_macro_input!(input as DeriveInput);
|
||||
from_file::impl_from_file(input)
|
||||
}
|
||||
@ -1,45 +1,52 @@
|
||||
use merge::Merge;
|
||||
use derive_macro::FromFile;
|
||||
use merge::{Merge, option::overwrite_none};
|
||||
use ratatui::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, Merge)]
|
||||
pub struct ColorsConfig {
|
||||
#[merge(strategy = merge::option::overwrite_none)]
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, Merge)]
|
||||
pub struct ColorsConfigFile {
|
||||
#[merge(strategy = overwrite_none)]
|
||||
pub highlight_background: Option<String>,
|
||||
#[merge(strategy = merge::option::overwrite_none)]
|
||||
#[merge(strategy = overwrite_none)]
|
||||
pub highlight_foreground: Option<String>,
|
||||
#[merge(strategy = merge::option::overwrite_none)]
|
||||
#[merge(strategy = overwrite_none)]
|
||||
pub warning_foreground: Option<String>,
|
||||
#[merge(strategy = merge::option::overwrite_none)]
|
||||
#[merge(strategy = overwrite_none)]
|
||||
pub info_foreground: Option<String>,
|
||||
#[merge(strategy = merge::option::overwrite_none)]
|
||||
#[merge(strategy = overwrite_none)]
|
||||
pub error_foreground: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, FromFile)]
|
||||
pub struct ColorsConfig {
|
||||
pub highlight_background: String,
|
||||
pub highlight_foreground: String,
|
||||
pub warning_foreground: String,
|
||||
pub info_foreground: String,
|
||||
pub error_foreground: String,
|
||||
}
|
||||
|
||||
impl ColorsConfig {
|
||||
pub fn get_color(&self, color_name: &Option<String>) -> Color {
|
||||
match color_name {
|
||||
Some(name) => match name.to_lowercase().as_str() {
|
||||
"black" => Color::Black,
|
||||
"blue" => Color::Blue,
|
||||
"cyan" => Color::Cyan,
|
||||
"darkgray" => Color::DarkGray,
|
||||
"gray" => Color::Gray,
|
||||
"green" => Color::Green,
|
||||
"lightgreen" => Color::LightGreen,
|
||||
"lightred" => Color::LightRed,
|
||||
"magenta" => Color::Magenta,
|
||||
"red" => Color::Red,
|
||||
"white" => Color::White,
|
||||
"yellow" => Color::Yellow,
|
||||
_ => Color::Reset, // Default to reset, if color name is not recognized
|
||||
},
|
||||
None => Color::Reset,
|
||||
pub fn get_color(&self, color_name: &str) -> Color {
|
||||
match color_name.to_lowercase().as_str() {
|
||||
"black" => Color::Black,
|
||||
"blue" => Color::Blue,
|
||||
"cyan" => Color::Cyan,
|
||||
"darkgray" => Color::DarkGray,
|
||||
"gray" => Color::Gray,
|
||||
"green" => Color::Green,
|
||||
"lightgreen" => Color::LightGreen,
|
||||
"lightred" => Color::LightRed,
|
||||
"magenta" => Color::Magenta,
|
||||
"red" => Color::Red,
|
||||
"white" => Color::White,
|
||||
"yellow" => Color::Yellow,
|
||||
_ => Color::Reset, // Default to reset, if color name is not recognized
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ColorsConfig {
|
||||
impl Default for ColorsConfigFile {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
highlight_background: Some("magenta".to_string()),
|
||||
|
||||
@ -1,41 +1,61 @@
|
||||
use merge::Merge;
|
||||
use derive_macro::FromFile;
|
||||
use merge::{Merge, option::overwrite_none};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, Merge)]
|
||||
pub struct KeybindsConfig {
|
||||
#[merge(strategy = merge::option::overwrite_none)]
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, Merge)]
|
||||
pub struct KeybindsConfigFile {
|
||||
#[merge(strategy = overwrite_none)]
|
||||
pub quit: Option<String>,
|
||||
#[merge(strategy = merge::option::overwrite_none)]
|
||||
#[merge(strategy = overwrite_none)]
|
||||
pub next_tab: Option<String>,
|
||||
#[merge(strategy = merge::option::overwrite_none)]
|
||||
#[merge(strategy = overwrite_none)]
|
||||
pub prev_tab: Option<String>,
|
||||
#[merge(strategy = merge::option::overwrite_none)]
|
||||
#[merge(strategy = overwrite_none)]
|
||||
pub next_torrent: Option<String>,
|
||||
#[merge(strategy = merge::option::overwrite_none)]
|
||||
#[merge(strategy = overwrite_none)]
|
||||
pub prev_torrent: Option<String>,
|
||||
#[merge(strategy = merge::option::overwrite_none)]
|
||||
#[merge(strategy = overwrite_none)]
|
||||
pub switch_tab_1: Option<String>,
|
||||
#[merge(strategy = merge::option::overwrite_none)]
|
||||
#[merge(strategy = overwrite_none)]
|
||||
pub switch_tab_2: Option<String>,
|
||||
#[merge(strategy = merge::option::overwrite_none)]
|
||||
#[merge(strategy = overwrite_none)]
|
||||
pub switch_tab_3: Option<String>,
|
||||
#[merge(strategy = merge::option::overwrite_none)]
|
||||
#[merge(strategy = overwrite_none)]
|
||||
pub toggle_torrent: Option<String>,
|
||||
#[merge(strategy = merge::option::overwrite_none)]
|
||||
#[merge(strategy = overwrite_none)]
|
||||
pub toggle_all: Option<String>,
|
||||
#[merge(strategy = merge::option::overwrite_none)]
|
||||
#[merge(strategy = overwrite_none)]
|
||||
pub delete: Option<String>,
|
||||
#[merge(strategy = merge::option::overwrite_none)]
|
||||
#[merge(strategy = overwrite_none)]
|
||||
pub delete_force: Option<String>,
|
||||
#[merge(strategy = merge::option::overwrite_none)]
|
||||
#[merge(strategy = overwrite_none)]
|
||||
pub select: Option<String>,
|
||||
#[merge(strategy = merge::option::overwrite_none)]
|
||||
#[merge(strategy = overwrite_none)]
|
||||
pub toggle_help: Option<String>,
|
||||
#[merge(strategy = merge::option::overwrite_none)]
|
||||
#[merge(strategy = overwrite_none)]
|
||||
pub move_torrent: Option<String>,
|
||||
}
|
||||
|
||||
impl Default for KeybindsConfig {
|
||||
#[derive(Debug, Clone, FromFile)]
|
||||
pub struct KeybindsConfig {
|
||||
pub quit: String,
|
||||
pub next_tab: String,
|
||||
pub prev_tab: String,
|
||||
pub next_torrent: String,
|
||||
pub prev_torrent: String,
|
||||
pub switch_tab_1: String,
|
||||
pub switch_tab_2: String,
|
||||
pub switch_tab_3: String,
|
||||
pub toggle_torrent: String,
|
||||
pub toggle_all: String,
|
||||
pub delete: String,
|
||||
pub delete_force: String,
|
||||
pub select: String,
|
||||
pub toggle_help: String,
|
||||
pub move_torrent: String,
|
||||
}
|
||||
|
||||
impl Default for KeybindsConfigFile {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
quit: Some("q".to_string()),
|
||||
|
||||
30
src/config/log.rs
Normal file
30
src/config/log.rs
Normal file
@ -0,0 +1,30 @@
|
||||
use derive_macro::FromFile;
|
||||
use merge::{option::overwrite_none, Merge};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, Merge)]
|
||||
pub struct LogConfigFile {
|
||||
#[merge(strategy = overwrite_none)]
|
||||
pub traxor: Option<String>,
|
||||
#[merge(strategy = overwrite_none)]
|
||||
pub ratatui: Option<String>,
|
||||
#[merge(strategy = overwrite_none)]
|
||||
pub transmission_rpc: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, FromFile)]
|
||||
pub struct LogConfig {
|
||||
pub traxor: String,
|
||||
pub ratatui: String,
|
||||
pub transmission_rpc: String,
|
||||
}
|
||||
|
||||
impl Default for LogConfigFile {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
traxor: Some("warn".to_string()),
|
||||
ratatui: Some("warn".to_string()),
|
||||
transmission_rpc: Some("warn".to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,28 +1,41 @@
|
||||
mod colors;
|
||||
mod keybinds;
|
||||
mod log;
|
||||
|
||||
use color_eyre::Result;
|
||||
use colors::ColorsConfig;
|
||||
use keybinds::KeybindsConfig;
|
||||
use merge::Merge;
|
||||
use colors::{ColorsConfig, ColorsConfigFile};
|
||||
use keybinds::{KeybindsConfig, KeybindsConfigFile};
|
||||
use log::{LogConfig, LogConfigFile};
|
||||
use merge::{Merge, option::overwrite_none};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[derive(Debug, Default, Deserialize, Serialize, Merge)]
|
||||
#[derive(Debug, Clone, Default, Deserialize, Serialize, Merge)]
|
||||
pub struct ConfigFile {
|
||||
#[merge(strategy = overwrite_none)]
|
||||
pub keybinds: Option<KeybindsConfigFile>,
|
||||
#[merge(strategy = overwrite_none)]
|
||||
pub colors: Option<ColorsConfigFile>,
|
||||
#[merge(strategy = overwrite_none)]
|
||||
pub log: Option<LogConfigFile>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Config {
|
||||
pub keybinds: KeybindsConfig,
|
||||
pub colors: ColorsConfig,
|
||||
pub log: LogConfig,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn load() -> Result<Self> {
|
||||
let mut config = Self::default();
|
||||
let mut config = ConfigFile::default();
|
||||
|
||||
// Load system-wide config
|
||||
let system_config_path = PathBuf::from("/etc/xdg/traxor/config.toml");
|
||||
if system_config_path.exists() {
|
||||
let config_str = std::fs::read_to_string(&system_config_path)?;
|
||||
let system_config: Config = toml::from_str(&config_str)?;
|
||||
let system_config = toml::from_str::<ConfigFile>(&config_str)?;
|
||||
config.merge(system_config);
|
||||
}
|
||||
|
||||
@ -30,18 +43,11 @@ impl Config {
|
||||
let user_config_path = Self::get_config_path()?;
|
||||
if user_config_path.exists() {
|
||||
let config_str = std::fs::read_to_string(&user_config_path)?;
|
||||
let user_config: Config = toml::from_str(&config_str)?;
|
||||
let user_config = toml::from_str::<ConfigFile>(&config_str)?;
|
||||
config.merge(user_config);
|
||||
}
|
||||
|
||||
Ok(config)
|
||||
}
|
||||
|
||||
pub fn save(&self) -> Result<()> {
|
||||
let config_path = Self::get_config_path()?;
|
||||
let config_str = toml::to_string_pretty(self)?;
|
||||
std::fs::write(&config_path, config_str)?;
|
||||
Ok(())
|
||||
Ok(config.into())
|
||||
}
|
||||
|
||||
fn get_config_path() -> Result<PathBuf> {
|
||||
@ -55,3 +61,13 @@ impl Config {
|
||||
Ok(config_dir.join("traxor").join("config.toml"))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ConfigFile> for Config {
|
||||
fn from(value: ConfigFile) -> Self {
|
||||
Self {
|
||||
keybinds: value.keybinds.into(),
|
||||
colors: value.colors.into(),
|
||||
log: value.log.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
use crate::app::{action::Action, App};
|
||||
use crate::app::{App, action::Action};
|
||||
use color_eyre::Result;
|
||||
use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
|
||||
use tracing::{event, info_span, Level};
|
||||
use tracing::{Level, event, info_span};
|
||||
|
||||
async fn handle_input(key_event: KeyEvent, app: &mut App<'_>) -> Result<Option<Action>> {
|
||||
match key_event.code {
|
||||
@ -96,12 +96,8 @@ pub async fn update(app: &mut App<'_>, action: Action) -> Result<()> {
|
||||
}
|
||||
|
||||
/// Check if a KeyEvent matches a configured keybind string
|
||||
fn matches_keybind(event: &KeyEvent, config_key: &Option<String>) -> bool {
|
||||
let Some(key_str) = config_key else {
|
||||
return false;
|
||||
};
|
||||
|
||||
let (modifiers, key_code) = parse_keybind(key_str);
|
||||
fn matches_keybind(event: &KeyEvent, config_key: &str) -> bool {
|
||||
let (modifiers, key_code) = parse_keybind(config_key);
|
||||
let Some(key_code) = key_code else {
|
||||
return false;
|
||||
};
|
||||
|
||||
@ -2,6 +2,7 @@ pub mod app;
|
||||
pub mod config;
|
||||
pub mod event;
|
||||
pub mod handler;
|
||||
pub mod log;
|
||||
pub mod telemetry;
|
||||
pub mod tui;
|
||||
pub mod ui;
|
||||
|
||||
|
||||
18
src/log.rs
18
src/log.rs
@ -1,18 +0,0 @@
|
||||
use color_eyre::Result;
|
||||
use tracing_appender::rolling;
|
||||
use tracing_subscriber::{self, layer::SubscriberExt, util::SubscriberInitExt, EnvFilter};
|
||||
|
||||
pub fn setup_logger() -> Result<()> {
|
||||
std::fs::create_dir_all(".logs")?;
|
||||
let logfile = rolling::daily(".log", "traxor.log");
|
||||
let log_layer = tracing_subscriber::fmt::layer()
|
||||
.with_writer(logfile)
|
||||
.with_ansi(false);
|
||||
|
||||
tracing_subscriber::registry()
|
||||
.with(log_layer)
|
||||
.with(EnvFilter::from_default_env())
|
||||
.init();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
12
src/main.rs
12
src/main.rs
@ -1,14 +1,12 @@
|
||||
mod log;
|
||||
|
||||
use color_eyre::Result;
|
||||
use log::setup_logger;
|
||||
use ratatui::{backend::CrosstermBackend, Terminal};
|
||||
use ratatui::{Terminal, backend::CrosstermBackend};
|
||||
use std::io;
|
||||
use traxor::{
|
||||
app::App,
|
||||
config::Config,
|
||||
event::{Event, EventHandler},
|
||||
handler::{get_action, update},
|
||||
telemetry::setup_logger,
|
||||
tui::Tui,
|
||||
};
|
||||
|
||||
@ -16,12 +14,12 @@ use traxor::{
|
||||
async fn main() -> Result<()> {
|
||||
color_eyre::install()?;
|
||||
|
||||
// Setup the logger.
|
||||
setup_logger()?;
|
||||
|
||||
// Load configuration.
|
||||
let config = Config::load()?;
|
||||
|
||||
// Setup the logger.
|
||||
setup_logger(&config)?;
|
||||
|
||||
// Create an application.
|
||||
let mut app = App::new(config)?;
|
||||
|
||||
|
||||
37
src/telemetry.rs
Normal file
37
src/telemetry.rs
Normal file
@ -0,0 +1,37 @@
|
||||
use crate::config::Config;
|
||||
use color_eyre::{Result, eyre::eyre};
|
||||
use std::{fs::create_dir_all, path::PathBuf};
|
||||
use tracing_appender::rolling;
|
||||
use tracing_bunyan_formatter::{BunyanFormattingLayer, JsonStorageLayer};
|
||||
use tracing_subscriber::{EnvFilter, layer::SubscriberExt, util::SubscriberInitExt};
|
||||
|
||||
pub fn setup_logger(config: &Config) -> Result<()> {
|
||||
let log_dir_path = if cfg!(debug_assertions) {
|
||||
PathBuf::from(".logs")
|
||||
} else {
|
||||
let mut path =
|
||||
dirs::data_local_dir().ok_or_else(|| eyre!("Failed to get local data directory"))?;
|
||||
path.push("traxor/logs");
|
||||
path
|
||||
};
|
||||
create_dir_all(&log_dir_path)?;
|
||||
|
||||
let logfile = if cfg!(debug_assertions) {
|
||||
rolling::daily(log_dir_path, "traxor.log")
|
||||
} else {
|
||||
rolling::never(log_dir_path, "traxor.log")
|
||||
};
|
||||
|
||||
let formatter = BunyanFormattingLayer::new("traxor".into(), logfile);
|
||||
|
||||
tracing_subscriber::registry()
|
||||
.with(JsonStorageLayer)
|
||||
.with(formatter)
|
||||
.with(EnvFilter::new(format!(
|
||||
"traxor={},ratatui={},transmission_rpc={}",
|
||||
config.log.traxor, config.log.ratatui, config.log.transmission_rpc,
|
||||
)))
|
||||
.init();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -8,63 +8,48 @@ pub fn render_help(frame: &mut Frame, app: &App) {
|
||||
.borders(Borders::ALL)
|
||||
.border_type(BorderType::Rounded);
|
||||
|
||||
let keybinds = &app.config.keybinds;
|
||||
let keybinds = app.config.keybinds.clone();
|
||||
|
||||
let rows = vec![
|
||||
Row::new(vec![
|
||||
Cell::from(keybinds.toggle_help.as_deref().unwrap_or("?")),
|
||||
Cell::from(keybinds.toggle_help),
|
||||
Cell::from("Show help"),
|
||||
]),
|
||||
Row::new(vec![Cell::from(keybinds.quit), Cell::from("Quit")]),
|
||||
Row::new(vec![Cell::from(keybinds.prev_tab), Cell::from("Left")]),
|
||||
Row::new(vec![Cell::from(keybinds.next_tab), Cell::from("Right")]),
|
||||
Row::new(vec![Cell::from(keybinds.next_torrent), Cell::from("Down")]),
|
||||
Row::new(vec![Cell::from(keybinds.prev_torrent), Cell::from("Up")]),
|
||||
Row::new(vec![
|
||||
Cell::from(keybinds.quit.as_deref().unwrap_or("q")),
|
||||
Cell::from("Quit"),
|
||||
]),
|
||||
Row::new(vec![
|
||||
Cell::from(keybinds.prev_tab.as_deref().unwrap_or("h")),
|
||||
Cell::from("Left"),
|
||||
]),
|
||||
Row::new(vec![
|
||||
Cell::from(keybinds.next_tab.as_deref().unwrap_or("l")),
|
||||
Cell::from("Right"),
|
||||
]),
|
||||
Row::new(vec![
|
||||
Cell::from(keybinds.next_torrent.as_deref().unwrap_or("j")),
|
||||
Cell::from("Down"),
|
||||
]),
|
||||
Row::new(vec![
|
||||
Cell::from(keybinds.prev_torrent.as_deref().unwrap_or("k")),
|
||||
Cell::from("Up"),
|
||||
]),
|
||||
Row::new(vec![
|
||||
Cell::from(keybinds.switch_tab_1.as_deref().unwrap_or("1")),
|
||||
Cell::from(keybinds.switch_tab_1),
|
||||
Cell::from("Switch to All tab"),
|
||||
]),
|
||||
Row::new(vec![
|
||||
Cell::from(keybinds.switch_tab_2.as_deref().unwrap_or("2")),
|
||||
Cell::from(keybinds.switch_tab_2),
|
||||
Cell::from("Switch to Active tab"),
|
||||
]),
|
||||
Row::new(vec![
|
||||
Cell::from(keybinds.switch_tab_3.as_deref().unwrap_or("3")),
|
||||
Cell::from(keybinds.switch_tab_3),
|
||||
Cell::from("Switch to Downloading tab"),
|
||||
]),
|
||||
Row::new(vec![
|
||||
Cell::from(keybinds.toggle_torrent.as_deref().unwrap_or("t")),
|
||||
Cell::from(keybinds.toggle_torrent),
|
||||
Cell::from("Toggle torrent"),
|
||||
]),
|
||||
Row::new(vec![
|
||||
Cell::from(keybinds.toggle_all.as_deref().unwrap_or("a")),
|
||||
Cell::from(keybinds.toggle_all),
|
||||
Cell::from("Toggle all torrents"),
|
||||
]),
|
||||
Row::new(vec![
|
||||
Cell::from(keybinds.delete.as_deref().unwrap_or("d")),
|
||||
Cell::from(keybinds.delete),
|
||||
Cell::from("Delete torrent"),
|
||||
]),
|
||||
Row::new(vec![
|
||||
Cell::from(keybinds.delete_force.as_deref().unwrap_or("D")),
|
||||
Cell::from(keybinds.delete_force),
|
||||
Cell::from("Delete torrent and data"),
|
||||
]),
|
||||
Row::new(vec![
|
||||
Cell::from(keybinds.select.as_deref().unwrap_or(" ")),
|
||||
Cell::from(keybinds.select),
|
||||
Cell::from("Select torrent"),
|
||||
]),
|
||||
];
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
use crate::app::{utils::Wrapper, App, Tab};
|
||||
use crate::app::{App, Tab, utils::Wrapper};
|
||||
use ratatui::{
|
||||
layout::Constraint,
|
||||
style::{Style, Styled},
|
||||
|
||||
Loading…
Reference in New Issue
Block a user