Skip to content

Commit

Permalink
Add config option to whitelist or disable plugins
Browse files Browse the repository at this point in the history
  • Loading branch information
thorio committed Jul 4, 2024
1 parent 448049d commit 9a57cf8
Show file tree
Hide file tree
Showing 7 changed files with 84 additions and 24 deletions.
3 changes: 3 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ jobs:
- name: upload packages
uses: svenstaro/upload-release-action@v2
with:
draft: true
file_glob: true
file: target/artifacts/*

Expand Down Expand Up @@ -69,6 +70,7 @@ jobs:
- name: upload packages
uses: svenstaro/upload-release-action@v2
with:
draft: true
file_glob: true
file: target/artifacts/*

Expand All @@ -95,5 +97,6 @@ jobs:
- name: upload packages
uses: svenstaro/upload-release-action@v2
with:
draft: true
file_glob: true
file: target/artifacts/*
22 changes: 18 additions & 4 deletions config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,20 @@
## Set to null to disable.
single_instance: gravel

## Governs if external plugin libraries are loaded.
## Libraries are discovered in ~/.local/share/gravel/plugins/*.{so,dll}
## as well as in $XDG_DATA_DIRS with the same structure.
##
## Valid values are `disabled`, `all` and `whitelist` (see below)
external_plugins: disabled

## Filters libraries by their filename, sans extension. Absolute paths *do not*
## work, the library must still be placed in one of the supported folders.
# external_plugins:
# whitelist:
# - libgravel_provider_calculator
# - my_fancy_plugin

## Binds global hotkeys to actions.
## The hotkey is configured as an emacs-like binding, though note that some
## features, such as chords, are not supported.
Expand All @@ -31,14 +45,14 @@ single_instance: gravel
hotkeys:

## Shows or hides the frontend. These actions are also available seperately,
## as `Show` and `Hide`
## as `show` and `hide`
- binding: A-<Space>
action: ShowHide
action: show_hide

## Shows the frontend and pre-populates with the given query.
# - binding: M-r
# action:
# ShowWith: example
# show_with: example

## Frontend, or UI, to use. This is responsible for the elements you
## interact with.
Expand Down Expand Up @@ -220,7 +234,7 @@ providers:
# command_linux: systemctl suspend

## Process killer.
## Lists running processes on your system and will allow you to kill them.
## Lists running processes on your system and allows you to kill them.
## There is currently no further configuration for this plugin.
- plugin: kill

Expand Down
37 changes: 26 additions & 11 deletions gravel-core/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use serde::Deserialize;
pub const DEFAULT_CONFIG: &str = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/../config.yml"));

pub struct ConfigManager {
root: RootConfig,
root: Config,
config_sources: Vec<ConfigLayer>,
}

Expand All @@ -26,16 +26,16 @@ impl ConfigManager {
Self { config_sources, root }
}

pub fn root(&self) -> &RootConfig {
pub fn root(&self) -> &Config {
&self.root
}

pub fn adapt_provider(&self, index: usize) -> PluginConfigAdapter<'_> {
self.adapt(format!("{}.{index}", name_of!(providers in RootConfig)))
self.adapt(format!("{}.{index}", name_of!(providers in Config)))
}

pub fn adapt_frontend(&self) -> PluginConfigAdapter<'_> {
self.adapt(name_of!(frontend in RootConfig))
self.adapt(name_of!(frontend in Config))
}

fn adapt(&self, key: impl Into<RString>) -> PluginConfigAdapter<'_> {
Expand All @@ -44,24 +44,39 @@ impl ConfigManager {
}

#[derive(Debug, Deserialize)]
pub struct RootConfig {
pub struct Config {
pub single_instance: Option<String>,
pub hotkeys: Vec<HotkeyConfig>,
pub frontend: FrontendConfig,
pub providers: Vec<ProviderConfig>,
pub external_plugins: ExternalPlugins,
pub hotkeys: Vec<Hotkey>,
pub frontend: Frontend,
pub providers: Vec<Provider>,
}

#[derive(Debug, Deserialize)]
pub struct HotkeyConfig {
#[serde(rename_all = "snake_case")]
pub enum ExternalPlugins {
Disabled,
All,
Whitelist(Vec<String>),
}

#[derive(Debug, Deserialize)]
pub struct Hotkey {
pub binding: String,
pub action: HotkeyAction,
}

#[derive(Clone, Debug, PartialEq, Eq, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum HotkeyAction {
// TODO: remove aliases on next breaking
#[serde(alias = "ShowHide")]
ShowHide,
#[serde(alias = "Show")]
Show,
#[serde(alias = "Hide")]
Hide,
#[serde(alias = "ShowWith")]
ShowWith(String),
}

Expand All @@ -77,13 +92,13 @@ impl From<HotkeyAction> for FrontendMessage {
}

#[derive(Debug, Deserialize)]
pub struct FrontendConfig {
pub struct Frontend {
pub plugin: String,
pub alias: Option<String>,
}

#[derive(Debug, Deserialize)]
pub struct ProviderConfig {
pub struct Provider {
pub plugin: String,
pub keyword: Option<String>,
// Technically expected here but is deserialized differently, see `gravel_ffi::PluginConfigAdapter`
Expand Down
6 changes: 5 additions & 1 deletion gravel-ffi/src/paths.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,11 @@ pub fn xdg_data_dirs() -> Vec<PathBuf> {
return path.split(':').map(PathBuf::from).collect();
}

vec![PathBuf::from("/usr/local/share/"), PathBuf::from("/usr/share/")]
#[cfg(unix)]
return vec![PathBuf::from("/usr/local/share/"), PathBuf::from("/usr/share/")];

#[cfg(windows)]
return vec![];
}

/// Returns XDG_DATA_HOME plus XDG_DATA_DIRS, postfixed with `path`.
Expand Down
4 changes: 2 additions & 2 deletions gravel/src/init/hotkeys.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
use abi_stable::external_types::crossbeam_channel::RSender;
use gravel_core::config::HotkeyConfig;
use gravel_core::config::Hotkey;
use gravel_core::hotkeys::Listener;
use gravel_ffi::FrontendMessageNe;

/// Initializes a hotkey listener on a different thread.
/// See [`Listener`].
pub fn hotkeys(hotkeys: &[HotkeyConfig], sender: RSender<FrontendMessageNe>) {
pub fn hotkeys(hotkeys: &[Hotkey], sender: RSender<FrontendMessageNe>) {
if hotkeys.is_empty() {
log::debug!("no hotkeys configured");
return;
Expand Down
34 changes: 29 additions & 5 deletions gravel/src/init/plugins.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
use external::register_externals;
use gravel_core::plugin::PluginRegistry;
use gravel_core::{config::ExternalPlugins, plugin::PluginRegistry};

/// Initializes the [`PluginRegistry`] and registers built-in plugins.
pub fn plugins() -> PluginRegistry {
pub fn plugins(config: &ExternalPlugins) -> PluginRegistry {
let mut registry = PluginRegistry::default();
register_builtins(&mut registry);
register_externals(&mut registry);
register_externals(&mut registry, config);

registry
}
Expand Down Expand Up @@ -36,19 +36,28 @@ mod external {
use abi_stable::library::{lib_header_from_path, LibHeader, LibraryError, RootModule};
use abi_stable::sabi_types::VersionNumber;
use glob::{glob, Paths};
use gravel_core::{paths, plugin::PluginRegistry};
use gravel_core::{config::ExternalPlugins, paths, plugin::PluginRegistry};
use gravel_ffi::{logging::StaticLogTarget, PluginLibRef};
use itertools::Itertools;
use std::path::PathBuf;

pub fn register_externals(registry: &mut PluginRegistry) {
pub fn register_externals(registry: &mut PluginRegistry, config: &ExternalPlugins) {
const EMPTY: &[String] = &[];

let filter = match config {
ExternalPlugins::Disabled => return,
ExternalPlugins::All => EMPTY,
ExternalPlugins::Whitelist(names) => names,
};

log::trace!("looking for external plugin libraries");

let definitions = paths::plugin_globs()
.filter_map(expand_glob)
.flatten()
.filter_map(Result::ok)
.unique_by(|p| p.file_name().map(ToOwned::to_owned))
.filter(filter_libs(filter))
.filter_map(load_lib)
.flat_map(|l| l.plugin()(StaticLogTarget::get()));

Expand All @@ -57,6 +66,21 @@ mod external {
}
}

fn filter_libs(names: &[String]) -> impl Fn(&PathBuf) -> bool + '_ {
|p| {
if names.is_empty() {
return true;
}

let stem = p
.file_stem()
.expect("was matched by glob, so must have a stem")
.to_string_lossy();

names.iter().any(|n| n == &stem)
}
}

fn expand_glob(pattern: PathBuf) -> Option<Paths> {
glob(&pattern.to_string_lossy())
.inspect_err(|e| log::error!("unable to expand glob {pattern:?}: {e}"))
Expand Down
2 changes: 1 addition & 1 deletion gravel/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ fn run() -> Result<()> {

let single_instance = init::single_instance(config.root().single_instance.as_deref());

let registry = init::plugins();
let registry = init::plugins(&config.root().external_plugins);

let (sender, receiver) = crossbeam_channel::bounded::<FrontendMessageNe>(8);
let engine = init::engine(sender.clone(), &registry, &config);
Expand Down

0 comments on commit 9a57cf8

Please sign in to comment.