fix: quality — config-based terminal/engine, emoji init perf, safer shell commands
This commit is contained in:
Generated
+3
@@ -971,6 +971,7 @@ dependencies = [
|
||||
"abi_stable",
|
||||
"dirs",
|
||||
"owlry-plugin-api",
|
||||
"toml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1007,7 +1008,9 @@ name = "owlry-plugin-websearch"
|
||||
version = "1.0.0"
|
||||
dependencies = [
|
||||
"abi_stable",
|
||||
"dirs",
|
||||
"owlry-plugin-api",
|
||||
"toml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
@@ -136,7 +136,7 @@ fn evaluate_expression(expr: &str) -> Option<PluginItem> {
|
||||
PluginItem::new(
|
||||
format!("calc:{}", expr),
|
||||
result_str.clone(),
|
||||
format!("sh -c 'echo -n \"{}\" | wl-copy'", result_str),
|
||||
format!("printf '%s' '{}' | wl-copy", result_str.replace('\'', "'\\''")),
|
||||
)
|
||||
.with_description(format!("= {}", expr))
|
||||
.with_icon(PROVIDER_ICON)
|
||||
|
||||
@@ -92,7 +92,7 @@ extern "C" fn provider_query(_handle: ProviderHandle, query: RStr<'_>) -> RVec<P
|
||||
PluginItem::new(
|
||||
format!("conv:{}:{}:{}", parsed.from_unit, r.target_symbol, r.value),
|
||||
r.display_value.clone(),
|
||||
format!("sh -c 'echo -n \"{}\" | wl-copy'", r.raw_value),
|
||||
format!("printf '%s' '{}' | wl-copy", r.raw_value.replace('\'', "'\\''")),
|
||||
)
|
||||
.with_description(format!(
|
||||
"{} {} = {} {}",
|
||||
|
||||
@@ -33,7 +33,9 @@ struct EmojiState {
|
||||
|
||||
impl EmojiState {
|
||||
fn new() -> Self {
|
||||
Self { items: Vec::new() }
|
||||
let mut state = Self { items: Vec::new() };
|
||||
state.load_emojis();
|
||||
state
|
||||
}
|
||||
|
||||
fn load_emojis(&mut self) {
|
||||
@@ -471,12 +473,9 @@ extern "C" fn provider_refresh(handle: ProviderHandle) -> RVec<PluginItem> {
|
||||
}
|
||||
|
||||
// SAFETY: We created this handle from Box<EmojiState>
|
||||
let state = unsafe { &mut *(handle.ptr as *mut EmojiState) };
|
||||
let state = unsafe { &*(handle.ptr as *const EmojiState) };
|
||||
|
||||
// Load emojis
|
||||
state.load_emojis();
|
||||
|
||||
// Return items
|
||||
// Return cached items (loaded once at init)
|
||||
state.items.to_vec().into()
|
||||
}
|
||||
|
||||
@@ -515,20 +514,15 @@ mod tests {
|
||||
#[test]
|
||||
fn test_emoji_state_new() {
|
||||
let state = EmojiState::new();
|
||||
assert!(state.items.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_emoji_count() {
|
||||
let mut state = EmojiState::new();
|
||||
state.load_emojis();
|
||||
assert!(state.items.len() > 100, "Should have more than 100 emojis");
|
||||
assert!(
|
||||
state.items.len() > 100,
|
||||
"Should have more than 100 emojis loaded at init"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_emoji_has_grinning_face() {
|
||||
let mut state = EmojiState::new();
|
||||
state.load_emojis();
|
||||
let state = EmojiState::new();
|
||||
|
||||
let grinning = state
|
||||
.items
|
||||
@@ -542,8 +536,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_emoji_command_format() {
|
||||
let mut state = EmojiState::new();
|
||||
state.load_emojis();
|
||||
let state = EmojiState::new();
|
||||
|
||||
let item = &state.items[0];
|
||||
assert!(item.command.as_str().contains("wl-copy"));
|
||||
@@ -552,8 +545,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_emojis_have_keywords() {
|
||||
let mut state = EmojiState::new();
|
||||
state.load_emojis();
|
||||
let state = EmojiState::new();
|
||||
|
||||
// Check that items have keywords for searching
|
||||
let heart = state.items.iter().find(|i| i.name.as_str() == "red heart");
|
||||
|
||||
@@ -21,3 +21,6 @@ abi_stable = "0.11"
|
||||
|
||||
# For finding ~/.ssh/config
|
||||
dirs = "5.0"
|
||||
|
||||
# For reading owlry config.toml
|
||||
toml = "0.8"
|
||||
|
||||
@@ -28,9 +28,6 @@ const PROVIDER_PREFIX: &str = ":ssh";
|
||||
const PROVIDER_ICON: &str = "utilities-terminal";
|
||||
const PROVIDER_TYPE_ID: &str = "ssh";
|
||||
|
||||
// Default terminal command (TODO: make configurable via plugin config)
|
||||
const DEFAULT_TERMINAL: &str = "kitty";
|
||||
|
||||
/// SSH provider state - holds cached items
|
||||
struct SshState {
|
||||
items: Vec<PluginItem>,
|
||||
@@ -39,15 +36,36 @@ struct SshState {
|
||||
|
||||
impl SshState {
|
||||
fn new() -> Self {
|
||||
// Try to detect terminal from environment, fall back to default
|
||||
let terminal = std::env::var("TERMINAL").unwrap_or_else(|_| DEFAULT_TERMINAL.to_string());
|
||||
|
||||
let terminal = Self::load_terminal_from_config();
|
||||
Self {
|
||||
items: Vec::new(),
|
||||
terminal_command: terminal,
|
||||
}
|
||||
}
|
||||
|
||||
fn load_terminal_from_config() -> String {
|
||||
// Try [plugins.ssh] in config.toml
|
||||
let config_path = dirs::config_dir().map(|d| d.join("owlry").join("config.toml"));
|
||||
if let Some(content) = config_path.and_then(|p| fs::read_to_string(p).ok())
|
||||
&& let Ok(toml) = content.parse::<toml::Table>()
|
||||
{
|
||||
if let Some(plugins) = toml.get("plugins").and_then(|v| v.as_table())
|
||||
&& let Some(ssh) = plugins.get("ssh").and_then(|v| v.as_table())
|
||||
&& let Some(terminal) = ssh.get("terminal").and_then(|v| v.as_str())
|
||||
{
|
||||
return terminal.to_string();
|
||||
}
|
||||
}
|
||||
|
||||
// Fall back to $TERMINAL env var
|
||||
if let Ok(terminal) = std::env::var("TERMINAL") {
|
||||
return terminal;
|
||||
}
|
||||
|
||||
// Last resort
|
||||
"xdg-terminal-exec".to_string()
|
||||
}
|
||||
|
||||
fn ssh_config_path() -> Option<PathBuf> {
|
||||
dirs::home_dir().map(|h| h.join(".ssh").join("config"))
|
||||
}
|
||||
|
||||
@@ -18,3 +18,7 @@ owlry-plugin-api = { git = "https://somegit.dev/Owlibou/owlry.git", tag = "plugi
|
||||
|
||||
# ABI-stable types (re-exported from owlry-plugin-api, but needed for RString etc)
|
||||
abi_stable = "0.11"
|
||||
|
||||
# For reading owlry config.toml
|
||||
toml = "0.8"
|
||||
dirs = "5.0"
|
||||
|
||||
@@ -13,6 +13,7 @@ use owlry_plugin_api::{
|
||||
API_VERSION, PluginInfo, PluginItem, ProviderHandle, ProviderInfo, ProviderKind,
|
||||
ProviderPosition, owlry_plugin,
|
||||
};
|
||||
use std::fs;
|
||||
|
||||
// Plugin metadata
|
||||
const PLUGIN_ID: &str = "websearch";
|
||||
@@ -143,6 +144,21 @@ impl WebSearchState {
|
||||
}
|
||||
}
|
||||
|
||||
fn load_engine_from_config() -> String {
|
||||
let config_path = dirs::config_dir().map(|d| d.join("owlry").join("config.toml"));
|
||||
if let Some(content) = config_path.and_then(|p| fs::read_to_string(p).ok())
|
||||
&& let Ok(toml) = content.parse::<toml::Table>()
|
||||
{
|
||||
if let Some(plugins) = toml.get("plugins").and_then(|v| v.as_table())
|
||||
&& let Some(websearch) = plugins.get("websearch").and_then(|v| v.as_table())
|
||||
&& let Some(engine) = websearch.get("engine").and_then(|v| v.as_str())
|
||||
{
|
||||
return engine.to_string();
|
||||
}
|
||||
}
|
||||
DEFAULT_ENGINE.to_string()
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Plugin Interface Implementation
|
||||
// ============================================================================
|
||||
@@ -172,8 +188,8 @@ extern "C" fn plugin_providers() -> RVec<ProviderInfo> {
|
||||
}
|
||||
|
||||
extern "C" fn provider_init(_provider_id: RStr<'_>) -> ProviderHandle {
|
||||
// TODO: Read search engine from config when plugin config is available
|
||||
let state = Box::new(WebSearchState::new());
|
||||
let engine = load_engine_from_config();
|
||||
let state = Box::new(WebSearchState::with_engine(&engine));
|
||||
ProviderHandle::from_box(state)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user