feat(converter): scaffold plugin crate with vtable
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
members = [
|
||||
"crates/owlry-plugin-bookmarks",
|
||||
"crates/owlry-plugin-calculator",
|
||||
"crates/owlry-plugin-converter",
|
||||
"crates/owlry-plugin-clipboard",
|
||||
"crates/owlry-plugin-emoji",
|
||||
"crates/owlry-plugin-filesearch",
|
||||
|
||||
21
crates/owlry-plugin-converter/Cargo.toml
Normal file
21
crates/owlry-plugin-converter/Cargo.toml
Normal file
@@ -0,0 +1,21 @@
|
||||
[package]
|
||||
name = "owlry-plugin-converter"
|
||||
version = "1.0.0"
|
||||
edition.workspace = true
|
||||
rust-version.workspace = true
|
||||
license.workspace = true
|
||||
repository.workspace = true
|
||||
description = "Unit and currency conversion plugin for owlry"
|
||||
keywords = ["owlry", "plugin", "converter", "units", "currency"]
|
||||
categories = ["science"]
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
owlry-plugin-api = { git = "https://somegit.dev/Owlibou/owlry.git", tag = "plugin-api-v1.0.0" }
|
||||
abi_stable = "0.11"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
reqwest = { version = "0.13", features = ["blocking"] }
|
||||
dirs = "5"
|
||||
17
crates/owlry-plugin-converter/src/currency.rs
Normal file
17
crates/owlry-plugin-converter/src/currency.rs
Normal file
@@ -0,0 +1,17 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub struct CurrencyRates {
|
||||
pub rates: HashMap<String, f64>,
|
||||
}
|
||||
|
||||
pub fn get_rates() -> Option<CurrencyRates> {
|
||||
None
|
||||
}
|
||||
|
||||
pub fn resolve_currency_code(_alias: &str) -> Option<String> {
|
||||
None
|
||||
}
|
||||
|
||||
pub fn is_currency_alias(_alias: &str) -> bool {
|
||||
false
|
||||
}
|
||||
156
crates/owlry-plugin-converter/src/lib.rs
Normal file
156
crates/owlry-plugin-converter/src/lib.rs
Normal file
@@ -0,0 +1,156 @@
|
||||
//! Converter Plugin for Owlry
|
||||
//!
|
||||
//! A dynamic provider that converts between units and currencies.
|
||||
//! Supports queries prefixed with `>` or auto-detected.
|
||||
//!
|
||||
//! Examples:
|
||||
//! - `> 100 F to C` → 37.78 °C
|
||||
//! - `50 kg in lb` → 110.23 lb
|
||||
//! - `100 eur to usd` → 108.32 USD
|
||||
|
||||
mod currency;
|
||||
mod parser;
|
||||
mod units;
|
||||
|
||||
use abi_stable::std_types::{ROption, RStr, RString, RVec};
|
||||
use owlry_plugin_api::{
|
||||
API_VERSION, PluginInfo, PluginItem, ProviderHandle, ProviderInfo, ProviderKind,
|
||||
ProviderPosition, owlry_plugin,
|
||||
};
|
||||
|
||||
const PLUGIN_ID: &str = "converter";
|
||||
const PLUGIN_NAME: &str = "Converter";
|
||||
const PLUGIN_VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
const PLUGIN_DESCRIPTION: &str = "Convert between units and currencies";
|
||||
|
||||
const PROVIDER_ID: &str = "converter";
|
||||
const PROVIDER_NAME: &str = "Converter";
|
||||
const PROVIDER_PREFIX: &str = ">";
|
||||
const PROVIDER_ICON: &str = "edit-find-replace";
|
||||
const PROVIDER_TYPE_ID: &str = "conv";
|
||||
|
||||
struct ConverterState;
|
||||
|
||||
extern "C" fn plugin_info() -> PluginInfo {
|
||||
PluginInfo {
|
||||
id: RString::from(PLUGIN_ID),
|
||||
name: RString::from(PLUGIN_NAME),
|
||||
version: RString::from(PLUGIN_VERSION),
|
||||
description: RString::from(PLUGIN_DESCRIPTION),
|
||||
api_version: API_VERSION,
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" fn plugin_providers() -> RVec<ProviderInfo> {
|
||||
vec![ProviderInfo {
|
||||
id: RString::from(PROVIDER_ID),
|
||||
name: RString::from(PROVIDER_NAME),
|
||||
prefix: ROption::RSome(RString::from(PROVIDER_PREFIX)),
|
||||
icon: RString::from(PROVIDER_ICON),
|
||||
provider_type: ProviderKind::Dynamic,
|
||||
type_id: RString::from(PROVIDER_TYPE_ID),
|
||||
position: ProviderPosition::Normal,
|
||||
priority: 9000,
|
||||
}]
|
||||
.into()
|
||||
}
|
||||
|
||||
extern "C" fn provider_init(_provider_id: RStr<'_>) -> ProviderHandle {
|
||||
let state = Box::new(ConverterState);
|
||||
ProviderHandle::from_box(state)
|
||||
}
|
||||
|
||||
extern "C" fn provider_refresh(_handle: ProviderHandle) -> RVec<PluginItem> {
|
||||
RVec::new()
|
||||
}
|
||||
|
||||
extern "C" fn provider_query(_handle: ProviderHandle, query: RStr<'_>) -> RVec<PluginItem> {
|
||||
let query_str = query.as_str().trim();
|
||||
// Strip prefix
|
||||
let input = if let Some(rest) = query_str.strip_prefix('>') {
|
||||
rest.trim()
|
||||
} else {
|
||||
query_str
|
||||
};
|
||||
|
||||
let parsed = match parser::parse_conversion(input) {
|
||||
Some(p) => p,
|
||||
None => return RVec::new(),
|
||||
};
|
||||
|
||||
let results = if let Some(ref target) = parsed.target_unit {
|
||||
units::convert_to(&parsed.value, &parsed.from_unit, target)
|
||||
.into_iter()
|
||||
.collect()
|
||||
} else {
|
||||
units::convert_common(&parsed.value, &parsed.from_unit)
|
||||
};
|
||||
|
||||
results
|
||||
.into_iter()
|
||||
.map(|r| {
|
||||
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),
|
||||
)
|
||||
.with_description(format!(
|
||||
"{} {} = {} {}",
|
||||
format_number(parsed.value),
|
||||
parsed.from_symbol,
|
||||
r.display_value,
|
||||
r.target_symbol,
|
||||
))
|
||||
.with_icon(PROVIDER_ICON)
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.into()
|
||||
}
|
||||
|
||||
extern "C" fn provider_drop(handle: ProviderHandle) {
|
||||
if !handle.ptr.is_null() {
|
||||
unsafe {
|
||||
handle.drop_as::<ConverterState>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
owlry_plugin! {
|
||||
info: plugin_info,
|
||||
providers: plugin_providers,
|
||||
init: provider_init,
|
||||
refresh: provider_refresh,
|
||||
query: provider_query,
|
||||
drop: provider_drop,
|
||||
}
|
||||
|
||||
fn format_number(n: f64) -> String {
|
||||
if n.fract() == 0.0 && n.abs() < 1e15 {
|
||||
let i = n as i64;
|
||||
if i.abs() >= 1000 {
|
||||
format_with_separators(i)
|
||||
} else {
|
||||
format!("{}", i)
|
||||
}
|
||||
} else {
|
||||
format!("{:.4}", n)
|
||||
.trim_end_matches('0')
|
||||
.trim_end_matches('.')
|
||||
.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
fn format_with_separators(n: i64) -> String {
|
||||
let s = n.abs().to_string();
|
||||
let mut result = String::new();
|
||||
for (i, c) in s.chars().rev().enumerate() {
|
||||
if i > 0 && i % 3 == 0 {
|
||||
result.push(',');
|
||||
}
|
||||
result.push(c);
|
||||
}
|
||||
if n < 0 {
|
||||
result.push('-');
|
||||
}
|
||||
result.chars().rev().collect()
|
||||
}
|
||||
10
crates/owlry-plugin-converter/src/parser.rs
Normal file
10
crates/owlry-plugin-converter/src/parser.rs
Normal file
@@ -0,0 +1,10 @@
|
||||
pub struct ParsedQuery {
|
||||
pub value: f64,
|
||||
pub from_unit: String,
|
||||
pub from_symbol: String,
|
||||
pub target_unit: Option<String>,
|
||||
}
|
||||
|
||||
pub fn parse_conversion(_input: &str) -> Option<ParsedQuery> {
|
||||
None
|
||||
}
|
||||
18
crates/owlry-plugin-converter/src/units.rs
Normal file
18
crates/owlry-plugin-converter/src/units.rs
Normal file
@@ -0,0 +1,18 @@
|
||||
pub struct ConversionResult {
|
||||
pub value: f64,
|
||||
pub raw_value: String,
|
||||
pub display_value: String,
|
||||
pub target_symbol: String,
|
||||
}
|
||||
|
||||
pub fn find_unit(_alias: &str) -> Option<&'static str> {
|
||||
None
|
||||
}
|
||||
|
||||
pub fn convert_to(_value: &f64, _from: &str, _to: &str) -> Option<ConversionResult> {
|
||||
None
|
||||
}
|
||||
|
||||
pub fn convert_common(_value: &f64, _from: &str) -> Vec<ConversionResult> {
|
||||
vec![]
|
||||
}
|
||||
Reference in New Issue
Block a user