use std::{fs, io::stdin}; use abi_stable::std_types::{ROption, RString, RVec}; use anyrun_plugin::*; use fuzzy_matcher::FuzzyMatcher; use serde::Deserialize; #[derive(Deserialize)] struct Config { allow_invalid: bool, max_entries: usize, } impl Default for Config { fn default() -> Self { Self { allow_invalid: false, max_entries: 5, } } } struct State { config: Config, lines: Vec, } #[init] fn init(config_dir: RString) -> State { let config = if let Ok(content) = fs::read_to_string(format!("{}/stdin.ron", config_dir)) { ron::from_str(&content).unwrap_or_default() } else { Config::default() }; State { config, lines: stdin().lines().filter_map(|line| line.ok()).collect(), } } #[handler] fn handler(_match: Match) -> HandleResult { HandleResult::Stdout(_match.title.into_bytes()) } #[get_matches] fn get_matches(input: RString, state: &State) -> RVec { let matcher = fuzzy_matcher::skim::SkimMatcherV2::default().smart_case(); let mut lines = state .lines .clone() .into_iter() .filter_map(|line| { matcher .fuzzy_match(&line, &input) .map(|score| (line, score)) }) .collect::>(); if !lines.is_empty() { lines.sort_by(|a, b| b.1.cmp(&a.1)); lines.truncate(state.config.max_entries); } else if state.config.allow_invalid { lines.push((input.into(), 0)); } lines .into_iter() .map(|(line, _)| Match { title: line.into(), description: ROption::RNone, use_pango: false, icon: ROption::RNone, id: ROption::RNone, }) .collect::>() .into() } #[info] fn plugin_info() -> PluginInfo { PluginInfo { name: "Stdin".into(), icon: "format-indent-more".into(), } }