mirror of
https://github.com/alexpasmantier/television.git
synced 2025-06-07 12:05:34 +00:00
refactor: simplify configuration and build code + leaner crate
This commit is contained in:
parent
c7109044f0
commit
5ba4ededc9
1520
Cargo.lock
generated
1520
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -5,7 +5,6 @@ edition = "2021"
|
||||
description = "The revolution will be televised."
|
||||
license = "MIT"
|
||||
authors = ["Alexandre Pasmantier <alex.pasmant@gmail.com>"]
|
||||
build = "build.rs"
|
||||
repository = "https://github.com/alexpasmantier/television"
|
||||
homepage = "https://github.com/alexpasmantier/television"
|
||||
keywords = ["search", "fuzzy", "preview", "tui", "terminal"]
|
||||
@ -20,7 +19,6 @@ include = [
|
||||
"README.md",
|
||||
"themes/**/*.toml",
|
||||
"television/**",
|
||||
"build.rs",
|
||||
".config/config.toml",
|
||||
"cable",
|
||||
]
|
||||
@ -51,7 +49,6 @@ clap = { version = "4.5", default-features = false, features = [
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
ratatui = { version = "0.29", features = ["serde", "macros"] }
|
||||
better-panic = "0.3"
|
||||
config = "0.14"
|
||||
signal-hook = "0.3"
|
||||
human-panic = "2.0"
|
||||
copypasta = "0.10"
|
||||
@ -88,9 +85,6 @@ name = "tv"
|
||||
name = "results_list_benchmark"
|
||||
harness = false
|
||||
|
||||
[build-dependencies]
|
||||
vergen-gix = { version = "1.0", features = ["build", "cargo", "rustc"] }
|
||||
|
||||
[target.'cfg(target_os = "macos")'.dependencies]
|
||||
crossterm = { version = "0.28.1", features = ["serde", "use-dev-tty"] }
|
||||
|
||||
|
14
build.rs
14
build.rs
@ -1,14 +0,0 @@
|
||||
use std::error::Error;
|
||||
|
||||
use vergen_gix::{BuildBuilder, CargoBuilder, Emitter, RustcBuilder};
|
||||
|
||||
fn main() -> Result<(), Box<dyn Error>> {
|
||||
let build = BuildBuilder::default().build_date(true).build()?;
|
||||
let cargo = CargoBuilder::default().target_triple(true).build()?;
|
||||
let rustc = RustcBuilder::default().semver(true).build()?;
|
||||
Ok(Emitter::default()
|
||||
.add_instructions(&build)?
|
||||
.add_instructions(&cargo)?
|
||||
.add_instructions(&rustc)?
|
||||
.emit()?)
|
||||
}
|
@ -292,16 +292,7 @@ fn delimiter_parser(s: &str) -> Result<String, String> {
|
||||
})
|
||||
}
|
||||
|
||||
const VERSION_MESSAGE: &str = concat!(
|
||||
env!("CARGO_PKG_VERSION"),
|
||||
"\ntarget triple: ",
|
||||
env!("VERGEN_CARGO_TARGET_TRIPLE"),
|
||||
"\nbuild: ",
|
||||
env!("VERGEN_RUSTC_SEMVER"),
|
||||
" (",
|
||||
env!("VERGEN_BUILD_DATE"),
|
||||
")"
|
||||
);
|
||||
const VERSION_MESSAGE: &str = env!("CARGO_PKG_VERSION");
|
||||
|
||||
pub fn version() -> String {
|
||||
let author = clap::crate_authors!();
|
||||
|
@ -29,10 +29,10 @@ impl Display for Binding {
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct KeyBindings(pub config::Map<Mode, config::Map<Action, Binding>>);
|
||||
pub struct KeyBindings(pub FxHashMap<Mode, FxHashMap<Action, Binding>>);
|
||||
|
||||
impl Deref for KeyBindings {
|
||||
type Target = config::Map<Mode, config::Map<Action, Binding>>;
|
||||
type Target = FxHashMap<Mode, FxHashMap<Action, Binding>>;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
@ -44,6 +44,31 @@ impl DerefMut for KeyBindings {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn merge_keybindings(
|
||||
mut keybindings: KeyBindings,
|
||||
new_keybindings: &KeyBindings,
|
||||
) -> KeyBindings {
|
||||
for (mode, bindings) in new_keybindings.iter() {
|
||||
for (action, binding) in bindings {
|
||||
match keybindings.get_mut(mode) {
|
||||
Some(mode_bindings) => {
|
||||
mode_bindings.insert(action.clone(), binding.clone());
|
||||
}
|
||||
None => {
|
||||
keybindings.insert(
|
||||
*mode,
|
||||
[(action.clone(), binding.clone())]
|
||||
.iter()
|
||||
.cloned()
|
||||
.collect(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
keybindings
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum SerializedBinding {
|
||||
@ -61,7 +86,8 @@ impl<'de> Deserialize<'de> for KeyBindings {
|
||||
FxHashMap<Action, SerializedBinding>,
|
||||
>::deserialize(deserializer)?;
|
||||
|
||||
let keybindings = parsed_map
|
||||
let keybindings: FxHashMap<Mode, FxHashMap<Action, Binding>> =
|
||||
parsed_map
|
||||
.into_iter()
|
||||
.map(|(mode, inner_map)| {
|
||||
let converted_inner_map = inner_map
|
||||
@ -75,16 +101,16 @@ impl<'de> Deserialize<'de> for KeyBindings {
|
||||
parse_key(&key_str).unwrap(),
|
||||
)
|
||||
}
|
||||
SerializedBinding::MultipleKeys(keys_str) => {
|
||||
Binding::MultipleKeys(
|
||||
SerializedBinding::MultipleKeys(
|
||||
keys_str,
|
||||
) => Binding::MultipleKeys(
|
||||
keys_str
|
||||
.iter()
|
||||
.map(|key_str| {
|
||||
parse_key(key_str).unwrap()
|
||||
})
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
),
|
||||
},
|
||||
)
|
||||
})
|
||||
|
@ -1,14 +1,14 @@
|
||||
#![allow(clippy::module_name_repetitions)]
|
||||
use std::{env, path::PathBuf};
|
||||
|
||||
use color_eyre::Result;
|
||||
use color_eyre::{eyre::Context, Result};
|
||||
use directories::ProjectDirs;
|
||||
use keybindings::merge_keybindings;
|
||||
pub use keybindings::{parse_key, Binding, KeyBindings};
|
||||
use lazy_static::lazy_static;
|
||||
use previewers::PreviewersConfig;
|
||||
use serde::Deserialize;
|
||||
use shell_integration::ShellIntegrationConfig;
|
||||
use styles::Styles;
|
||||
pub use themes::Theme;
|
||||
use tracing::{debug, warn};
|
||||
use ui::UiConfig;
|
||||
@ -16,18 +16,18 @@ use ui::UiConfig;
|
||||
mod keybindings;
|
||||
mod previewers;
|
||||
mod shell_integration;
|
||||
mod styles;
|
||||
mod themes;
|
||||
mod ui;
|
||||
|
||||
const CONFIG: &str = include_str!("../.config/config.toml");
|
||||
const DEFAULT_CONFIG: &str = include_str!("../../.config/config.toml");
|
||||
|
||||
#[allow(dead_code, clippy::module_name_repetitions)]
|
||||
#[derive(Clone, Debug, Deserialize, Default)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct AppConfig {
|
||||
#[serde(default)]
|
||||
#[serde(default = "get_data_dir")]
|
||||
pub data_dir: PathBuf,
|
||||
#[serde(default)]
|
||||
#[serde(default = "get_config_dir")]
|
||||
pub config_dir: PathBuf,
|
||||
#[serde(default = "default_frame_rate")]
|
||||
pub frame_rate: f64,
|
||||
@ -36,19 +36,23 @@ pub struct AppConfig {
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Clone, Debug, Default, Deserialize)]
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct Config {
|
||||
/// General application configuration
|
||||
#[allow(clippy::struct_field_names)]
|
||||
#[serde(default, flatten)]
|
||||
pub config: AppConfig,
|
||||
/// Keybindings configuration
|
||||
#[serde(default)]
|
||||
pub keybindings: KeyBindings,
|
||||
#[serde(default)]
|
||||
pub styles: Styles,
|
||||
/// UI configuration
|
||||
#[serde(default)]
|
||||
pub ui: UiConfig,
|
||||
/// Previewers configuration
|
||||
#[serde(default)]
|
||||
pub previewers: PreviewersConfig,
|
||||
/// Shell integration configuration
|
||||
#[serde(default)]
|
||||
pub shell_integration: ShellIntegrationConfig,
|
||||
}
|
||||
@ -77,68 +81,40 @@ impl Config {
|
||||
#[allow(clippy::missing_panics_doc, clippy::missing_errors_doc)]
|
||||
pub fn new() -> Result<Self> {
|
||||
// Load the default_config values as base defaults
|
||||
let default_config: Config =
|
||||
toml::from_str(CONFIG).expect("default config should be valid");
|
||||
let default_config: Config = toml::from_str(DEFAULT_CONFIG)
|
||||
.wrap_err("Error parsing default config")?;
|
||||
|
||||
// initialize the config builder
|
||||
let data_dir = get_data_dir();
|
||||
let config_dir = get_config_dir();
|
||||
|
||||
std::fs::create_dir_all(&config_dir)
|
||||
.expect("Failed creating configuration directory");
|
||||
std::fs::create_dir_all(&data_dir)
|
||||
.expect("Failed creating data directory");
|
||||
|
||||
let mut builder = config::Config::builder()
|
||||
.set_default("data_dir", data_dir.to_str().unwrap())?
|
||||
.set_default("config_dir", config_dir.to_str().unwrap())?
|
||||
.set_default("frame_rate", default_config.config.frame_rate)?
|
||||
.set_default("tick_rate", default_config.config.tick_rate)?
|
||||
.set_default("ui", default_config.ui.clone())?
|
||||
.set_default("previewers", default_config.previewers.clone())?
|
||||
.set_default("theme", default_config.ui.theme.clone())?
|
||||
.set_default(
|
||||
"shell_integration",
|
||||
default_config.shell_integration.clone(),
|
||||
)?;
|
||||
|
||||
// Load the user's config file
|
||||
let source = config::File::from(config_dir.join(CONFIG_FILE_NAME))
|
||||
.format(config::FileFormat::Toml)
|
||||
.required(false);
|
||||
builder = builder.add_source(source);
|
||||
|
||||
if config_dir.join(CONFIG_FILE_NAME).is_file() {
|
||||
debug!("Found config file at {:?}", config_dir);
|
||||
let mut cfg: Self = builder.build()?.try_deserialize().unwrap();
|
||||
//.with_context(|| {
|
||||
// format!(
|
||||
// "Error parsing config file at {:?}",
|
||||
// config_dir.join(CONFIG_FILE_NAME)
|
||||
// )
|
||||
//})?;
|
||||
|
||||
for (mode, default_bindings) in default_config.keybindings.iter() {
|
||||
let user_bindings = cfg.keybindings.entry(*mode).or_default();
|
||||
for (command, key) in default_bindings {
|
||||
user_bindings
|
||||
.entry(command.clone())
|
||||
.or_insert_with(|| key.clone());
|
||||
}
|
||||
}
|
||||
let path = config_dir.join(CONFIG_FILE_NAME);
|
||||
let contents = std::fs::read_to_string(&path)?;
|
||||
|
||||
for (mode, default_styles) in default_config.styles.iter() {
|
||||
let user_styles = cfg.styles.entry(*mode).or_default();
|
||||
for (style_key, style) in default_styles {
|
||||
user_styles.entry(style_key.clone()).or_insert(*style);
|
||||
}
|
||||
}
|
||||
let cfg: Config = toml::from_str(&contents)
|
||||
.wrap_err(format!("error parsing config: {path:?}"))?;
|
||||
|
||||
// merge keybindings with default keybindings
|
||||
let keybindings = merge_keybindings(
|
||||
default_config.keybindings,
|
||||
&cfg.keybindings,
|
||||
);
|
||||
let cfg = Config { keybindings, ..cfg };
|
||||
|
||||
debug!("Config: {:?}", cfg);
|
||||
Ok(cfg)
|
||||
} else {
|
||||
warn!("No config file found at {:?}, creating default configuration file at that location.", config_dir);
|
||||
// create the default configuration file in the user's config directory
|
||||
std::fs::write(config_dir.join(CONFIG_FILE_NAME), CONFIG)?;
|
||||
std::fs::write(config_dir.join(CONFIG_FILE_NAME), DEFAULT_CONFIG)?;
|
||||
Ok(default_config)
|
||||
}
|
||||
}
|
@ -1,7 +1,4 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::preview::{previewers, PreviewerConfig};
|
||||
use config::ValueKind;
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Default)]
|
||||
@ -20,26 +17,11 @@ impl From<PreviewersConfig> for PreviewerConfig {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PreviewersConfig> for ValueKind {
|
||||
fn from(val: PreviewersConfig) -> Self {
|
||||
let mut m = HashMap::new();
|
||||
m.insert(String::from("basic"), val.basic.into());
|
||||
m.insert(String::from("file"), val.file.into());
|
||||
m.insert(String::from("env_var"), val.env_var.into());
|
||||
ValueKind::Table(m)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Default)]
|
||||
pub struct BasicPreviewerConfig {}
|
||||
|
||||
impl From<BasicPreviewerConfig> for ValueKind {
|
||||
fn from(_val: BasicPreviewerConfig) -> Self {
|
||||
ValueKind::Table(HashMap::new())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
#[serde(default)]
|
||||
pub struct FilePreviewerConfig {
|
||||
//pub max_file_size: u64,
|
||||
pub theme: String,
|
||||
@ -54,19 +36,5 @@ impl Default for FilePreviewerConfig {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<FilePreviewerConfig> for ValueKind {
|
||||
fn from(val: FilePreviewerConfig) -> Self {
|
||||
let mut m = HashMap::new();
|
||||
m.insert(String::from("theme"), ValueKind::String(val.theme).into());
|
||||
ValueKind::Table(m)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Default)]
|
||||
pub struct EnvVarPreviewerConfig {}
|
||||
|
||||
impl From<EnvVarPreviewerConfig> for ValueKind {
|
||||
fn from(_val: EnvVarPreviewerConfig) -> Self {
|
||||
ValueKind::Table(HashMap::new())
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,3 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use rustc_hash::FxHashMap;
|
||||
use serde::Deserialize;
|
||||
|
||||
@ -7,20 +5,3 @@ use serde::Deserialize;
|
||||
pub struct ShellIntegrationConfig {
|
||||
pub commands: FxHashMap<String, String>,
|
||||
}
|
||||
|
||||
impl From<ShellIntegrationConfig> for config::ValueKind {
|
||||
fn from(val: ShellIntegrationConfig) -> Self {
|
||||
let mut m = HashMap::new();
|
||||
m.insert(
|
||||
String::from("commands"),
|
||||
config::ValueKind::Table(
|
||||
val.commands
|
||||
.into_iter()
|
||||
.map(|(k, v)| (k, config::ValueKind::String(v).into()))
|
||||
.collect(),
|
||||
)
|
||||
.into(),
|
||||
);
|
||||
config::ValueKind::Table(m)
|
||||
}
|
||||
}
|
||||
|
@ -1,207 +0,0 @@
|
||||
use crate::screen::mode::Mode;
|
||||
use ratatui::prelude::{Color, Modifier, Style};
|
||||
use rustc_hash::FxHashMap;
|
||||
use serde::{Deserialize, Deserializer};
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct Styles(pub FxHashMap<Mode, FxHashMap<String, Style>>);
|
||||
|
||||
impl Deref for Styles {
|
||||
type Target = FxHashMap<Mode, FxHashMap<String, Style>>;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for Styles {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for Styles {
|
||||
fn deserialize<D>(deserializer: D) -> color_eyre::Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let parsed_map =
|
||||
FxHashMap::<Mode, FxHashMap<String, String>>::deserialize(
|
||||
deserializer,
|
||||
)?;
|
||||
|
||||
let styles = parsed_map
|
||||
.into_iter()
|
||||
.map(|(mode, inner_map)| {
|
||||
let converted_inner_map = inner_map
|
||||
.into_iter()
|
||||
.map(|(str, style)| (str, parse_style(&style)))
|
||||
.collect();
|
||||
(mode, converted_inner_map)
|
||||
})
|
||||
.collect();
|
||||
|
||||
Ok(Styles(styles))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_style(line: &str) -> Style {
|
||||
let (foreground, background) =
|
||||
line.split_at(line.to_lowercase().find("on ").unwrap_or(line.len()));
|
||||
let foreground = process_color_string(foreground);
|
||||
let background = process_color_string(&background.replace("on ", ""));
|
||||
|
||||
let mut style = Style::default();
|
||||
if let Some(fg) = parse_color(&foreground.0) {
|
||||
style = style.fg(fg);
|
||||
}
|
||||
if let Some(bg) = parse_color(&background.0) {
|
||||
style = style.bg(bg);
|
||||
}
|
||||
style = style.add_modifier(foreground.1 | background.1);
|
||||
style
|
||||
}
|
||||
|
||||
pub fn process_color_string(color_str: &str) -> (String, Modifier) {
|
||||
let color = color_str
|
||||
.replace("grey", "gray")
|
||||
.replace("bright ", "")
|
||||
.replace("bold ", "")
|
||||
.replace("underline ", "")
|
||||
.replace("inverse ", "");
|
||||
|
||||
let mut modifiers = Modifier::empty();
|
||||
if color_str.contains("underline") {
|
||||
modifiers |= Modifier::UNDERLINED;
|
||||
}
|
||||
if color_str.contains("bold") {
|
||||
modifiers |= Modifier::BOLD;
|
||||
}
|
||||
if color_str.contains("inverse") {
|
||||
modifiers |= Modifier::REVERSED;
|
||||
}
|
||||
|
||||
(color, modifiers)
|
||||
}
|
||||
|
||||
#[allow(clippy::cast_possible_truncation)]
|
||||
pub fn parse_color(s: &str) -> Option<Color> {
|
||||
let s = s.trim_start();
|
||||
let s = s.trim_end();
|
||||
if s.contains("bright color") {
|
||||
let s = s.trim_start_matches("bright ");
|
||||
let c = s
|
||||
.trim_start_matches("color")
|
||||
.parse::<u8>()
|
||||
.unwrap_or_default();
|
||||
Some(Color::Indexed(c.wrapping_shl(8)))
|
||||
} else if s.contains("color") {
|
||||
let c = s
|
||||
.trim_start_matches("color")
|
||||
.parse::<u8>()
|
||||
.unwrap_or_default();
|
||||
Some(Color::Indexed(c))
|
||||
} else if s.contains("gray") {
|
||||
let c = 232
|
||||
+ s.trim_start_matches("gray")
|
||||
.parse::<u8>()
|
||||
.unwrap_or_default();
|
||||
Some(Color::Indexed(c))
|
||||
} else if s.contains("rgb") {
|
||||
let red =
|
||||
(s.as_bytes()[3] as char).to_digit(10).unwrap_or_default() as u8;
|
||||
let green =
|
||||
(s.as_bytes()[4] as char).to_digit(10).unwrap_or_default() as u8;
|
||||
let blue =
|
||||
(s.as_bytes()[5] as char).to_digit(10).unwrap_or_default() as u8;
|
||||
let c = 16 + red * 36 + green * 6 + blue;
|
||||
Some(Color::Indexed(c))
|
||||
} else if s == "bold black" {
|
||||
Some(Color::Indexed(8))
|
||||
} else if s == "bold red" {
|
||||
Some(Color::Indexed(9))
|
||||
} else if s == "bold green" {
|
||||
Some(Color::Indexed(10))
|
||||
} else if s == "bold yellow" {
|
||||
Some(Color::Indexed(11))
|
||||
} else if s == "bold blue" {
|
||||
Some(Color::Indexed(12))
|
||||
} else if s == "bold magenta" {
|
||||
Some(Color::Indexed(13))
|
||||
} else if s == "bold cyan" {
|
||||
Some(Color::Indexed(14))
|
||||
} else if s == "bold white" {
|
||||
Some(Color::Indexed(15))
|
||||
} else if s == "black" {
|
||||
Some(Color::Indexed(0))
|
||||
} else if s == "red" {
|
||||
Some(Color::Indexed(1))
|
||||
} else if s == "green" {
|
||||
Some(Color::Indexed(2))
|
||||
} else if s == "yellow" {
|
||||
Some(Color::Indexed(3))
|
||||
} else if s == "blue" {
|
||||
Some(Color::Indexed(4))
|
||||
} else if s == "magenta" {
|
||||
Some(Color::Indexed(5))
|
||||
} else if s == "cyan" {
|
||||
Some(Color::Indexed(6))
|
||||
} else if s == "white" {
|
||||
Some(Color::Indexed(7))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_parse_style_default() {
|
||||
let style = parse_style("");
|
||||
assert_eq!(style, Style::default());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_style_foreground() {
|
||||
let style = parse_style("red");
|
||||
assert_eq!(style.fg, Some(Color::Indexed(1)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_style_background() {
|
||||
let style = parse_style("on blue");
|
||||
assert_eq!(style.bg, Some(Color::Indexed(4)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_style_modifiers() {
|
||||
let style = parse_style("underline red on blue");
|
||||
assert_eq!(style.fg, Some(Color::Indexed(1)));
|
||||
assert_eq!(style.bg, Some(Color::Indexed(4)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_process_color_string() {
|
||||
let (color, modifiers) =
|
||||
process_color_string("underline bold inverse gray");
|
||||
assert_eq!(color, "gray");
|
||||
assert!(modifiers.contains(Modifier::UNDERLINED));
|
||||
assert!(modifiers.contains(Modifier::BOLD));
|
||||
assert!(modifiers.contains(Modifier::REVERSED));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_color_rgb() {
|
||||
let color = parse_color("rgb123");
|
||||
let expected = 16 + 36 + 2 * 6 + 3;
|
||||
assert_eq!(color, Some(Color::Indexed(expected)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_color_unknown() {
|
||||
let color = parse_color("unknown");
|
||||
assert_eq!(color, None);
|
||||
}
|
||||
}
|
@ -1,6 +1,3 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use config::ValueKind;
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::screen::layout::{InputPosition, PreviewTitlePosition};
|
||||
@ -10,6 +7,7 @@ use super::themes::DEFAULT_THEME;
|
||||
const DEFAULT_UI_SCALE: u16 = 100;
|
||||
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
#[serde(default)]
|
||||
pub struct UiConfig {
|
||||
pub use_nerd_font_icons: bool,
|
||||
pub ui_scale: u16,
|
||||
@ -34,39 +32,3 @@ impl Default for UiConfig {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<UiConfig> for ValueKind {
|
||||
fn from(val: UiConfig) -> Self {
|
||||
let mut m = HashMap::new();
|
||||
m.insert(
|
||||
String::from("use_nerd_font_icons"),
|
||||
ValueKind::Boolean(val.use_nerd_font_icons).into(),
|
||||
);
|
||||
m.insert(
|
||||
String::from("ui_scale"),
|
||||
ValueKind::U64(val.ui_scale.into()).into(),
|
||||
);
|
||||
m.insert(
|
||||
String::from("show_help_bar"),
|
||||
ValueKind::Boolean(val.show_help_bar).into(),
|
||||
);
|
||||
m.insert(
|
||||
String::from("show_preview_panel"),
|
||||
ValueKind::Boolean(val.show_preview_panel).into(),
|
||||
);
|
||||
m.insert(
|
||||
String::from("input_position"),
|
||||
ValueKind::String(val.input_bar_position.to_string()).into(),
|
||||
);
|
||||
m.insert(
|
||||
String::from("preview_title_position"),
|
||||
match val.preview_title_position {
|
||||
Some(pos) => ValueKind::String(pos.to_string()),
|
||||
None => ValueKind::Nil,
|
||||
}
|
||||
.into(),
|
||||
);
|
||||
m.insert(String::from("theme"), ValueKind::String(val.theme).into());
|
||||
ValueKind::Table(m)
|
||||
}
|
||||
}
|
||||
|
@ -1,14 +1,14 @@
|
||||
use color_eyre::Result;
|
||||
use tracing_subscriber::{fmt, prelude::*, EnvFilter};
|
||||
|
||||
use crate::config;
|
||||
use crate::config::get_data_dir;
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
pub static ref LOG_FILE: String = format!("{}.log", env!("CARGO_PKG_NAME"));
|
||||
}
|
||||
|
||||
pub fn init() -> Result<()> {
|
||||
let directory = config::get_data_dir();
|
||||
let directory = get_data_dir();
|
||||
std::fs::create_dir_all(directory.clone())?;
|
||||
let log_path = directory.join(LOG_FILE.clone());
|
||||
let log_file = std::fs::File::create(log_path)?;
|
||||
|
@ -40,40 +40,6 @@ pub fn build_metadata_table<'a>(
|
||||
)),
|
||||
]);
|
||||
|
||||
let target_triple_row = Row::new(vec![
|
||||
Cell::from(Span::styled(
|
||||
"target triple: ",
|
||||
Style::default().fg(colorscheme.help.metadata_field_name_fg),
|
||||
)),
|
||||
Cell::from(Span::styled(
|
||||
&app_metadata.build.target_triple,
|
||||
Style::default().fg(colorscheme.help.metadata_field_value_fg),
|
||||
)),
|
||||
]);
|
||||
|
||||
let build_row = Row::new(vec![
|
||||
Cell::from(Span::styled(
|
||||
"build: ",
|
||||
Style::default().fg(colorscheme.help.metadata_field_name_fg),
|
||||
)),
|
||||
Cell::from(Span::styled(
|
||||
&app_metadata.build.rustc_version,
|
||||
Style::default().fg(colorscheme.help.metadata_field_value_fg),
|
||||
)),
|
||||
Cell::from(Span::styled(
|
||||
" (",
|
||||
Style::default().fg(colorscheme.help.metadata_field_name_fg),
|
||||
)),
|
||||
Cell::from(Span::styled(
|
||||
&app_metadata.build.build_date,
|
||||
Style::default().fg(colorscheme.help.metadata_field_value_fg),
|
||||
)),
|
||||
Cell::from(Span::styled(
|
||||
")",
|
||||
Style::default().fg(colorscheme.help.metadata_field_name_fg),
|
||||
)),
|
||||
]);
|
||||
|
||||
let current_dir_row = Row::new(vec![
|
||||
Cell::from(Span::styled(
|
||||
"current directory: ",
|
||||
@ -115,8 +81,6 @@ pub fn build_metadata_table<'a>(
|
||||
Table::new(
|
||||
vec![
|
||||
version_row,
|
||||
target_triple_row,
|
||||
build_row,
|
||||
current_dir_row,
|
||||
current_channel_row,
|
||||
current_mode_row,
|
||||
|
@ -21,7 +21,7 @@ use crate::screen::preview::draw_preview_content_block;
|
||||
use crate::screen::remote_control::draw_remote_control;
|
||||
use crate::screen::results::draw_results_list;
|
||||
use crate::screen::spinner::{Spinner, SpinnerState};
|
||||
use crate::utils::metadata::{AppMetadata, BuildMetadata};
|
||||
use crate::utils::metadata::AppMetadata;
|
||||
use crate::utils::strings::EMPTY_STRING;
|
||||
use crate::{cable::load_cable_channels, keymap::Keymap};
|
||||
use color_eyre::Result;
|
||||
@ -75,11 +75,6 @@ impl Television {
|
||||
|
||||
let app_metadata = AppMetadata::new(
|
||||
env!("CARGO_PKG_VERSION").to_string(),
|
||||
BuildMetadata::new(
|
||||
env!("VERGEN_RUSTC_SEMVER").to_string(),
|
||||
env!("VERGEN_BUILD_DATE").to_string(),
|
||||
env!("VERGEN_CARGO_TARGET_TRIPLE").to_string(),
|
||||
),
|
||||
std::env::current_dir()
|
||||
.expect("Could not get current directory")
|
||||
.to_string_lossy()
|
||||
|
@ -1,38 +1,12 @@
|
||||
pub struct BuildMetadata {
|
||||
pub rustc_version: String,
|
||||
pub build_date: String,
|
||||
pub target_triple: String,
|
||||
}
|
||||
|
||||
impl BuildMetadata {
|
||||
pub fn new(
|
||||
rustc_version: String,
|
||||
build_date: String,
|
||||
target_triple: String,
|
||||
) -> Self {
|
||||
Self {
|
||||
rustc_version,
|
||||
build_date,
|
||||
target_triple,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AppMetadata {
|
||||
pub version: String,
|
||||
pub build: BuildMetadata,
|
||||
pub current_directory: String,
|
||||
}
|
||||
|
||||
impl AppMetadata {
|
||||
pub fn new(
|
||||
version: String,
|
||||
build: BuildMetadata,
|
||||
current_directory: String,
|
||||
) -> Self {
|
||||
pub fn new(version: String, current_directory: String) -> Self {
|
||||
Self {
|
||||
version,
|
||||
build,
|
||||
current_directory,
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user