1use std::{borrow::Cow, cmp::min, ops::Deref, process::Command, thread::sleep, time::Duration};
2
3use anyhow::Context as _;
4use applications::{AppInfo, AppInfoContext, common::SearchPath};
5use fork::daemon;
6use skim::prelude::*;
7
8use crate::SkimRun;
9
10#[derive(Default)]
11pub struct App {
12 app: applications::App,
13}
14impl Deref for App {
15 type Target = applications::App;
16
17 fn deref(&self) -> &Self::Target {
18 &self.app
19 }
20}
21impl From<applications::App> for App {
22 fn from(value: applications::App) -> Self {
23 Self { app: value }
24 }
25}
26
27impl SkimItem for App {
28 fn text(&self) -> Cow<str> {
29 if self.app.name.is_empty() {
30 Cow::Borrowed(
31 self.app
32 .app_desktop_path
33 .file_name()
34 .unwrap()
35 .to_str()
36 .unwrap()
37 .strip_suffix(".desktop")
38 .unwrap(),
39 )
40 } else {
41 Cow::Borrowed(&self.app.name)
42 }
43 }
44 fn preview(&self, context: PreviewContext) -> ItemPreview {
45 let width = u32::try_from(context.width).unwrap_or(16);
46 let height = u32::try_from(context.height).unwrap_or(16);
47 let size = min(16, min(height, width));
48 if self.icon_path.is_some() {
49 let conf = viuer::Config {
50 width: Some(size),
51 x: 0,
52 y: 0,
53 transparent: true,
54 restore_cursor: false,
55 use_kitty: false,
56 truecolor: true,
57 ..Default::default()
58 };
59 let img = image::DynamicImage::ImageRgb8(image::RgbImage::new(size, size));
60 let _ = viuer::print(&img, &conf);
61 let _ = viuer::print_from_file(self.icon_path.clone().unwrap(), &conf);
62 }
63
64 ItemPreview::Text(String::new())
65 }
66 fn output(&self) -> Cow<str> {
67 if self.app_path_exe.is_some() {
68 self.app_path_exe
69 .as_ref()
70 .unwrap()
71 .as_path()
72 .to_string_lossy()
73 } else {
74 Cow::default()
75 }
76 }
77}
78
79#[derive(Default, Clone, Copy)]
80pub struct Apps;
81
82impl SkimRun for Apps {
83 fn get(&self) -> Vec<std::sync::Arc<(dyn skim::SkimItem + 'static)>> {
84 let mut ctx = AppInfoContext::new(vec![SearchPath::new(std::path::PathBuf::from("/"), 1)]);
85 ctx.refresh_apps().unwrap(); ctx.get_all_apps()
88 .into_iter()
89 .map(|a| Arc::new(App::from(a)) as Arc<dyn SkimItem>)
90 .collect()
91 }
92 fn run(&self, output: &SkimOutput) -> anyhow::Result<()> {
93 let item = output
94 .selected_items
95 .first()
96 .context("Could not find selected item")?;
97 if let Ok(fork::Fork::Child) = daemon(false, false) {
98 Command::new(item.output().into_owned())
99 .spawn()
100 .context("Failed to spawn app")?;
101 sleep(Duration::from_secs(10));
102 }
103 Ok(())
104 }
105 fn set_options(&self, opts: &mut SkimOptions) {
106 opts.preview = Some(String::new());
107 opts.preview_window = String::from("left:16");
108 }
109}