Read settings from configuration file or env
This commit is contained in:
parent
d1e9866bd3
commit
3b7bff5bd7
8 changed files with 274 additions and 25 deletions
119
Cargo.lock
generated
119
Cargo.lock
generated
|
@ -162,6 +162,22 @@ dependencies = [
|
|||
"objc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "config"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b1b9d958c2b1368a663f05538fc1b5975adce1e19f435acceae987aceeeb369"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"nom 5.1.2",
|
||||
"rust-ini",
|
||||
"serde 1.0.130",
|
||||
"serde-hjson",
|
||||
"serde_json",
|
||||
"toml",
|
||||
"yaml-rust",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation"
|
||||
version = "0.7.0"
|
||||
|
@ -584,6 +600,12 @@ dependencies = [
|
|||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "0.4.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4"
|
||||
|
||||
[[package]]
|
||||
name = "jni-sys"
|
||||
version = "0.3.0"
|
||||
|
@ -634,6 +656,12 @@ dependencies = [
|
|||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "linked-hash-map"
|
||||
version = "0.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.5"
|
||||
|
@ -785,6 +813,17 @@ dependencies = [
|
|||
"memoffset",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nom"
|
||||
version = "5.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af"
|
||||
dependencies = [
|
||||
"lexical-core",
|
||||
"memchr",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nom"
|
||||
version = "6.1.2"
|
||||
|
@ -818,6 +857,24 @@ dependencies = [
|
|||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.1.43"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31"
|
||||
dependencies = [
|
||||
"num-traits 0.2.14",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num_cpus"
|
||||
version = "1.13.0"
|
||||
|
@ -1109,6 +1166,7 @@ dependencies = [
|
|||
"anyhow",
|
||||
"cairo-rs",
|
||||
"cairo-sys-rs",
|
||||
"config",
|
||||
"env_logger",
|
||||
"flx-rs",
|
||||
"freedesktop_entry_parser",
|
||||
|
@ -1118,12 +1176,19 @@ dependencies = [
|
|||
"nix 0.23.0",
|
||||
"pangocairo",
|
||||
"rayon",
|
||||
"serde 1.0.130",
|
||||
"walkdir",
|
||||
"winit",
|
||||
"xcb",
|
||||
"xcb-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rust-ini"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3e52c148ef37f8c375d49d5a73aa70713125b7f19095948a923f80afdeb22ec2"
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.5"
|
||||
|
@ -1151,11 +1216,54 @@ version = "1.1.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "0.8.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9dad3f759919b92c3068c696c15c3d17238234498bbdcc80f2c469606f948ac8"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.130"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde-hjson"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a3a4e0ea8a88553209f6cc6cfe8724ecad22e1acf372793c27d995290fe74f8"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"num-traits 0.1.43",
|
||||
"regex",
|
||||
"serde 0.8.23",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.130"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.71"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "063bf466a64011ac24040a49009724ee60a57da1b437617ceb32e53ad61bfb19"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde 1.0.130",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
|
@ -1297,7 +1405,7 @@ version = "0.5.8"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde 1.0.130",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1588,3 +1696,12 @@ name = "xml-rs"
|
|||
version = "0.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d2d7d3948613f75c98fd9328cfdcc45acc4d360655289d0a7d4ec931392200a3"
|
||||
|
||||
[[package]]
|
||||
name = "yaml-rust"
|
||||
version = "0.4.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85"
|
||||
dependencies = [
|
||||
"linked-hash-map",
|
||||
]
|
||||
|
|
|
@ -10,6 +10,8 @@ log = "0.4"
|
|||
env_logger = "0.9"
|
||||
rayon = "1.5"
|
||||
anyhow = "1.0"
|
||||
config = "0.11"
|
||||
serde = {version = "1.0", features = ["derive"]}
|
||||
|
||||
# ui
|
||||
winit = {git="https://github.com/rust-windowing/winit"}
|
||||
|
|
14
default.toml
Normal file
14
default.toml
Normal file
|
@ -0,0 +1,14 @@
|
|||
log_level = "trace"
|
||||
|
||||
[completions]
|
||||
"e" = "emacs"
|
||||
"f" = "firefox"
|
||||
"t" = "terminal"
|
||||
|
||||
[sources.primary]
|
||||
matcher = "Prefix"
|
||||
source = "Windows"
|
||||
|
||||
[sources.additional]
|
||||
matcher = "Flex"
|
||||
source = ["Apps", "Shell", "Test"]
|
|
@ -1,5 +1,6 @@
|
|||
pub mod shared;
|
||||
pub mod roftl;
|
||||
pub mod settings;
|
||||
|
||||
use super::matcher;
|
||||
use super::sources;
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use std::collections::HashMap;
|
||||
use std::sync::RwLock;
|
||||
use std::sync::Arc;
|
||||
use log::debug;
|
||||
|
@ -24,7 +25,8 @@ pub struct Roftl {
|
|||
// switch where surprises run counter to muscle memory and are annoying.
|
||||
primary_matcher: MatcherRef,
|
||||
entries: Vec<Entry>,
|
||||
narrow_map: Vec<usize>
|
||||
narrow_map: Vec<usize>,
|
||||
completions: HashMap<String, String>
|
||||
}
|
||||
|
||||
impl Roftl {
|
||||
|
@ -36,7 +38,8 @@ impl Roftl {
|
|||
matcher: PrefixMatcher::new(),
|
||||
primary_matcher: PrefixMatcher::new(),
|
||||
entries: vec![],
|
||||
narrow_map: vec![]
|
||||
narrow_map: vec![],
|
||||
completions: HashMap::default()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -65,6 +68,11 @@ impl Roftl {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn with_completions(mut self, completions: HashMap<String, String>) -> Self {
|
||||
self.completions = completions;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn source(&mut self) {
|
||||
self.source_primary();
|
||||
self.source_additional();
|
||||
|
@ -96,10 +104,6 @@ impl Roftl {
|
|||
}
|
||||
|
||||
pub fn narrow(&mut self, input: &str) -> Vec<(&Entry, Vec<usize>)> {
|
||||
let mut completions = std::collections::HashMap::new();
|
||||
completions.insert("e", "emacs");
|
||||
completions.insert("f", "firefox");
|
||||
|
||||
// check if we received anything in self.
|
||||
if let Some(ref rx) = self.source_chan {
|
||||
if let Ok(mut entries) = rx.try_recv() {
|
||||
|
@ -112,6 +116,7 @@ impl Roftl {
|
|||
|
||||
// we need to primary_matcher here s.t. only the primary matcher
|
||||
// and not self is captured by the filter_map lambda
|
||||
let completions = &self.completions;
|
||||
let primary_matcher = &self.primary_matcher;
|
||||
let mut scored_entries: Vec<(f64, usize, &Entry, Vec<usize>)> = self.entries
|
||||
.par_iter()
|
||||
|
@ -121,7 +126,9 @@ impl Roftl {
|
|||
|
||||
if input.is_empty() { return Some((0.0, idx, entry, vec![])) }
|
||||
|
||||
let input = completions.get(input).unwrap_or(&input);
|
||||
let input = completions.get(input)
|
||||
.map(|s| s.as_str()) // &String -> &str
|
||||
.unwrap_or(input);
|
||||
|
||||
let match_result = primary_matcher.try_match(&entry.name, input);
|
||||
return match_result.map(|(score, indices)| (score, idx, entry, indices) )
|
||||
|
|
107
src/core/settings.rs
Normal file
107
src/core/settings.rs
Normal file
|
@ -0,0 +1,107 @@
|
|||
use std::collections::HashMap;
|
||||
use config::ConfigError;
|
||||
use config::Config;
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct Theme {
|
||||
border: f64,
|
||||
divider: f64
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub enum Matcher {
|
||||
Prefix, Fuse, Flex, Skim
|
||||
}
|
||||
|
||||
impl Matcher {
|
||||
pub fn init(&self) -> Box<dyn super::shared::Matcher> {
|
||||
match self {
|
||||
Matcher::Prefix => super::matcher::PrefixMatcher::new(),
|
||||
Matcher::Fuse => super::matcher::FuseMatcher::new(),
|
||||
Matcher::Flex => super::matcher::FlxMatcher::new(),
|
||||
Matcher::Skim => super::matcher::SkimMatcher::new()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub enum Source {
|
||||
Apps, Shell, Windows, Test
|
||||
}
|
||||
|
||||
impl Source {
|
||||
pub fn init(&self) -> Box<dyn super::shared::Source> {
|
||||
match self {
|
||||
Source::Apps => super::sources::Apps::new(),
|
||||
Source::Shell => super::sources::ShellHost::new(),
|
||||
Source::Windows => super::sources::Window::new(),
|
||||
Source::Test => super::sources::TestSource::new("ts1")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct SourceConfig<T> {
|
||||
matcher: Matcher,
|
||||
source: T
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct Sources {
|
||||
primary: SourceConfig<Source>,
|
||||
additional: SourceConfig<Vec<Source>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "lowercase", remote = "log::Level")]
|
||||
enum LogLevelDef {
|
||||
Error, Warn, Info, Debug, Trace,
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct Settings {
|
||||
#[serde(with = "LogLevelDef")]
|
||||
log_level: log::Level,
|
||||
completions: HashMap<String, String>,
|
||||
sources: Sources,
|
||||
}
|
||||
|
||||
impl Settings {
|
||||
pub fn parse() -> Result<Self, ConfigError> {
|
||||
let mut config: Config = Config::default();
|
||||
|
||||
config.merge(config::File::with_name("default"))?;
|
||||
|
||||
config.merge(config::Environment::new()
|
||||
.prefix("roftl")
|
||||
.separator("_")
|
||||
.ignore_empty(true))?;
|
||||
|
||||
config.try_into()
|
||||
}
|
||||
|
||||
pub fn primary_matcher(&self) -> Box<dyn super::shared::Matcher> {
|
||||
self.sources.primary.matcher.init()
|
||||
}
|
||||
|
||||
pub fn primary_source(&self) -> Box<dyn super::shared::Source> {
|
||||
self.sources.primary.source.init()
|
||||
}
|
||||
|
||||
pub fn matcher(&self) -> Box<dyn super::shared::Matcher> {
|
||||
self.sources.additional.matcher.init()
|
||||
}
|
||||
|
||||
pub fn sources(&self) -> Vec<Box<dyn super::shared::Source>> {
|
||||
self.sources.additional.source
|
||||
.iter()
|
||||
.map(|s| s.init())
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn completions(&self) -> HashMap<String, String> {
|
||||
self.completions.clone()
|
||||
}
|
||||
}
|
21
src/main.rs
21
src/main.rs
|
@ -6,8 +6,6 @@ use winit::event_loop::{ControlFlow, EventLoop};
|
|||
use winit::event::{ElementState, Event, KeyboardInput, VirtualKeyCode,
|
||||
WindowEvent::{CloseRequested, ReceivedCharacter}};
|
||||
|
||||
use crate::matcher::{FuseMatcher, PrefixMatcher};
|
||||
|
||||
use self::core::roftl::Roftl;
|
||||
|
||||
mod ui;
|
||||
|
@ -18,14 +16,19 @@ mod core;
|
|||
fn main() -> Result<(), Box<dyn Error>> {
|
||||
env_logger::init();
|
||||
|
||||
let settings = core::settings::Settings::parse()?;
|
||||
debug!{"Settings {:?}", settings}
|
||||
|
||||
debug!{"Set up roftl"};
|
||||
let mut roftl = Roftl::new()
|
||||
.add_source(sources::TestSource::new("ts1"))
|
||||
.add_source(sources::Apps::new())
|
||||
.add_source(sources::ShellHost::new())
|
||||
.with_matcher(FuseMatcher::new())
|
||||
.with_primary_source(sources::Window::new())
|
||||
.with_primary_matcher(PrefixMatcher::new());
|
||||
.with_primary_source(settings.primary_source())
|
||||
.with_primary_matcher(settings.primary_matcher())
|
||||
.with_matcher(settings.matcher())
|
||||
.with_completions(settings.completions());
|
||||
|
||||
for source in settings.sources() {
|
||||
roftl = roftl.add_source(source);
|
||||
}
|
||||
|
||||
debug!{"Source roftl sources"}
|
||||
roftl.source();
|
||||
|
@ -279,5 +282,3 @@ impl RoftlLoop {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ impl Window {
|
|||
Box::new(Window { action_data: HashMap::<u64, (u32,i32)>::new() })
|
||||
}
|
||||
|
||||
fn is_normal_window(&self, con: &ewmh::Connection, window: u32) -> Result<bool, xcb::GenericError> {
|
||||
fn is_normal_window(&self, con: &ewmh::Connection, window: u32) -> Result<bool, xcb::ReplyError> {
|
||||
let wm_type_reply = ewmh::get_wm_window_type(con, window).get_reply()?;
|
||||
|
||||
for atom in wm_type_reply.atoms() {
|
||||
|
@ -28,15 +28,15 @@ impl Window {
|
|||
Ok(false)
|
||||
}
|
||||
|
||||
fn window_category(&self, con: &ewmh::Connection, window: u32) -> Result<String, xcb::GenericError> {
|
||||
fn window_category(&self, con: &ewmh::Connection, window: u32) -> Result<String, xcb::ReplyError> {
|
||||
Ok(icccm::get_wm_class(&con, window).get_reply()?.class().into())
|
||||
}
|
||||
|
||||
fn window_title(&self, con: &ewmh::Connection, window: u32) -> Result<String, xcb::GenericError> {
|
||||
fn window_title(&self, con: &ewmh::Connection, window: u32) -> Result<String, xcb::ReplyError> {
|
||||
Ok(ewmh::get_wm_name(con, window).get_reply()?.string().into())
|
||||
}
|
||||
|
||||
fn switch_window(&self, con: &ewmh::Connection, window: u32, screen: i32) -> Result<(), xcb::GenericError> {
|
||||
fn switch_window(&self, con: &ewmh::Connection, window: u32, screen: i32) -> Result<(), xcb::ReplyError> {
|
||||
let window_desktop = ewmh::get_wm_desktop(con, window).get_reply()?;
|
||||
let active_window = ewmh::get_active_window(con, screen).get_reply()?;
|
||||
ewmh::set_current_desktop(con, screen, window_desktop);
|
||||
|
@ -47,7 +47,7 @@ impl Window {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn toggle_maximize_window(&self, con: &ewmh::Connection, window: u32, screen: i32) -> Result<(), xcb::GenericError> {
|
||||
fn toggle_maximize_window(&self, con: &ewmh::Connection, window: u32, screen: i32) -> Result<(), xcb::ReplyError> {
|
||||
let max_horz_atom = ewmh::Connection::WM_STATE_MAXIMIZED_HORZ(con);
|
||||
let max_vert_atom = ewmh::Connection::WM_STATE_MAXIMIZED_VERT(con);
|
||||
let action_atom = ewmh::STATE_TOGGLE;
|
||||
|
@ -58,13 +58,13 @@ impl Window {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn close_window(&self, con: &ewmh::Connection, window: u32, screen: i32) -> Result<(), xcb::GenericError> {
|
||||
fn close_window(&self, con: &ewmh::Connection, window: u32, screen: i32) -> Result<(), xcb::ReplyError> {
|
||||
debug!{"Toggle maximize for {}", window}
|
||||
ewmh::request_close_window(con, screen, window, XCB_CURRENT_TIME, 0).request_check()
|
||||
|
||||
}
|
||||
|
||||
fn toggle_hide_window(&self, con: &ewmh::Connection, window: u32, screen: i32) -> Result<(), xcb::GenericError> {
|
||||
fn toggle_hide_window(&self, con: &ewmh::Connection, window: u32, screen: i32) -> Result<(), xcb::ReplyError> {
|
||||
let hidden_atom = ewmh::Connection::WM_STATE_HIDDEN(con);
|
||||
let action_atom = ewmh::STATE_TOGGLE;
|
||||
|
||||
|
|
Loading…
Reference in a new issue