This commit is contained in:
Val Lorentz 2023-10-29 14:33:10 +01:00
parent d86380f48d
commit 6063636d9d
10 changed files with 161 additions and 47 deletions

View File

@ -1,7 +1,12 @@
fn main() {
let git_output = std::process::Command::new("git").args(["rev-parse", "--git-dir"]).output().ok();
let git_output = std::process::Command::new("git")
.args(["rev-parse", "--git-dir"])
.output()
.ok();
let git_dir = git_output.as_ref().and_then(|output| {
std::str::from_utf8(&output.stdout).ok().and_then(|s| s.strip_suffix('\n').or_else(|| s.strip_suffix("\r\n")))
std::str::from_utf8(&output.stdout)
.ok()
.and_then(|s| s.strip_suffix('\n').or_else(|| s.strip_suffix("\r\n")))
});
// Tell cargo to rebuild if the head or any relevant refs change.
@ -22,9 +27,13 @@ fn main() {
}
}
let git_output =
std::process::Command::new("git").args(["describe", "--always", "--tags", "--long", "--dirty"]).output().ok();
let git_info = git_output.as_ref().and_then(|output| std::str::from_utf8(&output.stdout).ok().map(str::trim));
let git_output = std::process::Command::new("git")
.args(["describe", "--always", "--tags", "--long", "--dirty"])
.output()
.ok();
let git_info = git_output
.as_ref()
.and_then(|output| std::str::from_utf8(&output.stdout).ok().map(str::trim));
let cargo_pkg_version = env!("CARGO_PKG_VERSION");
// Default git_describe to cargo_pkg_version

2
rustfmt.toml Normal file
View File

@ -0,0 +1,2 @@
match_block_trailing_comma = true
tab_spaces = 2

View File

@ -49,7 +49,11 @@ impl<'de> Deserialize<'de> for Action {
Ok(Action::Error(error_msg.to_string()))
},
data if data.starts_with("Resize(") => {
let parts: Vec<&str> = data.trim_start_matches("Resize(").trim_end_matches(")").split(',').collect();
let parts: Vec<&str> = data
.trim_start_matches("Resize(")
.trim_end_matches(")")
.split(',')
.collect();
if parts.len() == 2 {
let width: u16 = parts[0].trim().parse().map_err(E::custom)?;
let height: u16 = parts[1].trim().parse().map_err(E::custom)?;

View File

@ -6,7 +6,7 @@ use tokio::sync::mpsc;
use crate::{
action::Action,
components::{home::Home, fps::FpsCounter, Component},
components::{fps::FpsCounter, home::Home, Component},
config::Config,
mode::Mode,
tui,
@ -44,7 +44,9 @@ impl App {
pub async fn run(&mut self) -> Result<()> {
let (action_tx, mut action_rx) = mpsc::unbounded_channel();
let mut tui = tui::Tui::new()?.tick_rate(self.tick_rate).frame_rate(self.frame_rate);
let mut tui = tui::Tui::new()?
.tick_rate(self.tick_rate)
.frame_rate(self.frame_rate);
// tui.mouse(true);
tui.enter()?;
@ -111,7 +113,9 @@ impl App {
for component in self.components.iter_mut() {
let r = component.draw(f, f.size());
if let Err(e) = r {
action_tx.send(Action::Error(format!("Failed to draw: {:?}", e))).unwrap();
action_tx
.send(Action::Error(format!("Failed to draw: {:?}", e)))
.unwrap();
}
}
})?;
@ -121,7 +125,9 @@ impl App {
for component in self.components.iter_mut() {
let r = component.draw(f, f.size());
if let Err(e) = r {
action_tx.send(Action::Error(format!("Failed to draw: {:?}", e))).unwrap();
action_tx
.send(Action::Error(format!("Failed to draw: {:?}", e)))
.unwrap();
}
}
})?;
@ -137,7 +143,9 @@ impl App {
if self.should_suspend {
tui.suspend()?;
action_tx.send(Action::Resume)?;
tui = tui::Tui::new()?.tick_rate(self.tick_rate).frame_rate(self.frame_rate);
tui = tui::Tui::new()?
.tick_rate(self.tick_rate)
.frame_rate(self.frame_rate);
// tui.mouse(true);
tui.enter()?;
} else if self.should_quit {

View File

@ -7,7 +7,13 @@ use crate::utils::version;
#[derive(Parser, Debug)]
#[command(author, version = version(), about)]
pub struct Cli {
#[arg(short, long, value_name = "FLOAT", help = "Tick rate, i.e. number of ticks per second", default_value_t = 1.0)]
#[arg(
short,
long,
value_name = "FLOAT",
help = "Tick rate, i.e. number of ticks per second",
default_value_t = 1.0
)]
pub tick_rate: f64,
#[arg(

View File

@ -82,7 +82,10 @@ impl Component for FpsCounter {
let rect = rects[0];
let s = format!("{:.2} ticks per sec (app) {:.2} frames per sec (render)", self.app_fps, self.render_fps);
let s = format!(
"{:.2} ticks per sec (app) {:.2} frames per sec (render)",
self.app_fps, self.render_fps
);
let block = Block::default().title(block::Title::from(s.dim()).alignment(Alignment::Right));
f.render_widget(block, rect);
Ok(())

View File

@ -37,8 +37,7 @@ impl Component for Home {
fn update(&mut self, action: Action) -> Result<Option<Action>> {
match action {
Action::Tick => {
},
Action::Tick => {},
_ => {},
}
Ok(None)
@ -49,4 +48,3 @@ impl Component for Home {
Ok(())
}
}

View File

@ -51,7 +51,11 @@ impl Config {
];
let mut found_config = false;
for (file, format) in &config_files {
builder = builder.add_source(config::File::from(config_dir.join(file)).format(*format).required(false));
builder = builder.add_source(
config::File::from(config_dir.join(file))
.format(*format)
.required(false),
);
if config_dir.join(file).exists() {
found_config = true
}
@ -65,13 +69,17 @@ impl Config {
for (mode, default_bindings) in default_config.keybindings.iter() {
let user_bindings = cfg.keybindings.entry(*mode).or_default();
for (key, cmd) in default_bindings.iter() {
user_bindings.entry(key.clone()).or_insert_with(|| cmd.clone());
user_bindings
.entry(key.clone())
.or_insert_with(|| cmd.clone());
}
}
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.iter() {
user_styles.entry(style_key.clone()).or_insert_with(|| style.clone());
user_styles
.entry(style_key.clone())
.or_insert_with(|| style.clone());
}
}
@ -92,8 +100,10 @@ impl<'de> Deserialize<'de> for KeyBindings {
let keybindings = parsed_map
.into_iter()
.map(|(mode, inner_map)| {
let converted_inner_map =
inner_map.into_iter().map(|(key_str, cmd)| (parse_key_sequence(&key_str).unwrap(), cmd)).collect();
let converted_inner_map = inner_map
.into_iter()
.map(|(key_str, cmd)| (parse_key_sequence(&key_str).unwrap(), cmd))
.collect();
(mode, converted_inner_map)
})
.collect();
@ -133,7 +143,10 @@ fn extract_modifiers(raw: &str) -> (&str, KeyModifiers) {
(current, modifiers)
}
fn parse_key_code_with_modifiers(raw: &str, mut modifiers: KeyModifiers) -> Result<KeyEvent, String> {
fn parse_key_code_with_modifiers(
raw: &str,
mut modifiers: KeyModifiers,
) -> Result<KeyEvent, String> {
let c = match raw {
"esc" => KeyCode::Esc,
"enter" => KeyCode::Enter,
@ -283,7 +296,10 @@ impl<'de> Deserialize<'de> for Styles {
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();
let converted_inner_map = inner_map
.into_iter()
.map(|(str, style)| (str, parse_style(&style)))
.collect();
(mode, converted_inner_map)
})
.collect();
@ -293,7 +309,8 @@ impl<'de> Deserialize<'de> for 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, 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 ", ""));
@ -335,13 +352,23 @@ fn parse_color(s: &str) -> Option<Color> {
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();
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();
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();
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;
@ -443,7 +470,11 @@ mod tests {
fn test_config() -> Result<()> {
let c = Config::new()?;
assert_eq!(
c.keybindings.get(&Mode::Home).unwrap().get(&parse_key_sequence("<q>").unwrap_or_default()).unwrap(),
c.keybindings
.get(&Mode::Home)
.unwrap()
.get(&parse_key_sequence("<q>").unwrap_or_default())
.unwrap(),
&Action::Quit
);
Ok(())
@ -451,27 +482,48 @@ mod tests {
#[test]
fn test_simple_keys() {
assert_eq!(parse_key_event("a").unwrap(), KeyEvent::new(KeyCode::Char('a'), KeyModifiers::empty()));
assert_eq!(
parse_key_event("a").unwrap(),
KeyEvent::new(KeyCode::Char('a'), KeyModifiers::empty())
);
assert_eq!(parse_key_event("enter").unwrap(), KeyEvent::new(KeyCode::Enter, KeyModifiers::empty()));
assert_eq!(
parse_key_event("enter").unwrap(),
KeyEvent::new(KeyCode::Enter, KeyModifiers::empty())
);
assert_eq!(parse_key_event("esc").unwrap(), KeyEvent::new(KeyCode::Esc, KeyModifiers::empty()));
assert_eq!(
parse_key_event("esc").unwrap(),
KeyEvent::new(KeyCode::Esc, KeyModifiers::empty())
);
}
#[test]
fn test_with_modifiers() {
assert_eq!(parse_key_event("ctrl-a").unwrap(), KeyEvent::new(KeyCode::Char('a'), KeyModifiers::CONTROL));
assert_eq!(
parse_key_event("ctrl-a").unwrap(),
KeyEvent::new(KeyCode::Char('a'), KeyModifiers::CONTROL)
);
assert_eq!(parse_key_event("alt-enter").unwrap(), KeyEvent::new(KeyCode::Enter, KeyModifiers::ALT));
assert_eq!(
parse_key_event("alt-enter").unwrap(),
KeyEvent::new(KeyCode::Enter, KeyModifiers::ALT)
);
assert_eq!(parse_key_event("shift-esc").unwrap(), KeyEvent::new(KeyCode::Esc, KeyModifiers::SHIFT));
assert_eq!(
parse_key_event("shift-esc").unwrap(),
KeyEvent::new(KeyCode::Esc, KeyModifiers::SHIFT)
);
}
#[test]
fn test_multiple_modifiers() {
assert_eq!(
parse_key_event("ctrl-alt-a").unwrap(),
KeyEvent::new(KeyCode::Char('a'), KeyModifiers::CONTROL | KeyModifiers::ALT)
KeyEvent::new(
KeyCode::Char('a'),
KeyModifiers::CONTROL | KeyModifiers::ALT
)
);
assert_eq!(
@ -483,7 +535,10 @@ mod tests {
#[test]
fn test_reverse_multiple_modifiers() {
assert_eq!(
key_event_to_string(&KeyEvent::new(KeyCode::Char('a'), KeyModifiers::CONTROL | KeyModifiers::ALT)),
key_event_to_string(&KeyEvent::new(
KeyCode::Char('a'),
KeyModifiers::CONTROL | KeyModifiers::ALT
)),
"ctrl-alt-a".to_string()
);
}
@ -496,8 +551,14 @@ mod tests {
#[test]
fn test_case_insensitivity() {
assert_eq!(parse_key_event("CTRL-a").unwrap(), KeyEvent::new(KeyCode::Char('a'), KeyModifiers::CONTROL));
assert_eq!(
parse_key_event("CTRL-a").unwrap(),
KeyEvent::new(KeyCode::Char('a'), KeyModifiers::CONTROL)
);
assert_eq!(parse_key_event("AlT-eNtEr").unwrap(), KeyEvent::new(KeyCode::Enter, KeyModifiers::ALT));
assert_eq!(
parse_key_event("AlT-eNtEr").unwrap(),
KeyEvent::new(KeyCode::Enter, KeyModifiers::ALT)
);
}
}

View File

@ -7,8 +7,8 @@ use color_eyre::eyre::Result;
use crossterm::{
cursor,
event::{
DisableBracketedPaste, DisableMouseCapture, EnableBracketedPaste, EnableMouseCapture, Event as CrosstermEvent,
KeyEvent, KeyEventKind, MouseEvent,
DisableBracketedPaste, DisableMouseCapture, EnableBracketedPaste, EnableMouseCapture,
Event as CrosstermEvent, KeyEvent, KeyEventKind, MouseEvent,
},
terminal::{EnterAlternateScreen, LeaveAlternateScreen},
};
@ -65,7 +65,17 @@ impl Tui {
let task = tokio::spawn(async {});
let mouse = false;
let paste = false;
Ok(Self { terminal, task, cancellation_token, event_rx, event_tx, frame_rate, tick_rate, mouse, paste })
Ok(Self {
terminal,
task,
cancellation_token,
event_rx,
event_tx,
frame_rate,
tick_rate,
mouse,
paste,
})
}
pub fn tick_rate(mut self, tick_rate: f64) -> Self {

View File

@ -5,16 +5,23 @@ use directories::ProjectDirs;
use lazy_static::lazy_static;
use tracing::error;
use tracing_error::ErrorLayer;
use tracing_subscriber::{self, prelude::__tracing_subscriber_SubscriberExt, util::SubscriberInitExt, Layer};
use tracing_subscriber::{
self, prelude::__tracing_subscriber_SubscriberExt, util::SubscriberInitExt, Layer,
};
lazy_static! {
pub static ref PROJECT_NAME: String = env!("CARGO_CRATE_NAME").to_uppercase().to_string();
pub static ref DATA_FOLDER: Option<PathBuf> =
std::env::var(format!("{}_DATA", PROJECT_NAME.clone())).ok().map(PathBuf::from);
std::env::var(format!("{}_DATA", PROJECT_NAME.clone()))
.ok()
.map(PathBuf::from);
pub static ref CONFIG_FOLDER: Option<PathBuf> =
std::env::var(format!("{}_CONFIG", PROJECT_NAME.clone())).ok().map(PathBuf::from);
std::env::var(format!("{}_CONFIG", PROJECT_NAME.clone()))
.ok()
.map(PathBuf::from);
pub static ref GIT_COMMIT_HASH: String =
std::env::var(format!("{}_GIT_INFO", PROJECT_NAME.clone())).unwrap_or_else(|_| String::from("UNKNOWN"));
std::env::var(format!("{}_GIT_INFO", PROJECT_NAME.clone()))
.unwrap_or_else(|_| String::from("UNKNOWN"));
pub static ref LOG_ENV: String = format!("{}_LOGLEVEL", PROJECT_NAME.clone());
pub static ref LOG_FILE: String = format!("{}.log", env!("CARGO_PKG_NAME"));
}
@ -25,7 +32,10 @@ fn project_directory() -> Option<ProjectDirs> {
pub fn initialize_panic_handler() -> Result<()> {
let (panic_hook, eyre_hook) = color_eyre::config::HookBuilder::default()
.panic_section(format!("This is a bug. Consider reporting it at {}", env!("CARGO_PKG_REPOSITORY")))
.panic_section(format!(
"This is a bug. Consider reporting it at {}",
env!("CARGO_PKG_REPOSITORY")
))
.capture_span_trace_by_default(false)
.display_location_section(false)
.display_env_section(false)
@ -111,7 +121,10 @@ pub fn initialize_logging() -> Result<()> {
.with_target(false)
.with_ansi(false)
.with_filter(tracing_subscriber::filter::EnvFilter::from_default_env());
tracing_subscriber::registry().with(file_subscriber).with(ErrorLayer::default()).init();
tracing_subscriber::registry()
.with(file_subscriber)
.with(ErrorLayer::default())
.init();
Ok(())
}