nvim_gtk/plug_manager/
vimawesome.rs

1use std::io;
2use std::process::{Command, Stdio};
3use std::rc::Rc;
4use std::thread;
5
6use serde_json;
7
8use glib;
9use gtk;
10use gtk::prelude::*;
11
12use super::store::PlugInfo;
13
14pub fn call<F>(query: Option<String>, cb: F)
15where
16    F: FnOnce(io::Result<DescriptionList>) + Send + 'static,
17{
18    thread::spawn(move || {
19        let mut result = Some(request(query.as_ref().map(|s| s.as_ref())));
20        let mut cb = Some(cb);
21
22        glib::idle_add(move || {
23            cb.take().unwrap()(result.take().unwrap());
24            Continue(false)
25        })
26    });
27}
28
29fn request(query: Option<&str>) -> io::Result<DescriptionList> {
30    let child = Command::new("curl")
31        .arg("-s")
32        .arg(format!(
33            "https://vimawesome.com/api/plugins?query={}&page=1",
34            query.unwrap_or("")
35        ))
36        .stdout(Stdio::piped())
37        .spawn()?;
38
39    let out = child.wait_with_output()?;
40
41    if out.status.success() {
42        if out.stdout.is_empty() {
43            Ok(DescriptionList::empty())
44        } else {
45            let description_list: DescriptionList = serde_json::from_slice(&out.stdout)
46                .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
47            Ok(description_list)
48        }
49    } else {
50        Err(io::Error::new(
51            io::ErrorKind::Other,
52            format!(
53                "curl exit with error:\n{}",
54                match out.status.code() {
55                    Some(code) => format!("Exited with status code: {}", code),
56                    None => "Process terminated by signal".to_owned(),
57                }
58            ),
59        ))
60    }
61}
62
63pub fn build_result_panel<F: Fn(PlugInfo) + 'static>(
64    list: &DescriptionList,
65    add_cb: F,
66) -> gtk::ScrolledWindow {
67    let scroll = gtk::ScrolledWindow::new(
68        Option::<&gtk::Adjustment>::None,
69        Option::<&gtk::Adjustment>::None,
70    );
71    scroll.get_style_context().add_class("view");
72    let panel = gtk::ListBox::new();
73
74    let cb_ref = Rc::new(add_cb);
75    for plug in list.plugins.iter() {
76        let row = create_plug_row(plug, cb_ref.clone());
77
78        panel.add(&row);
79    }
80
81    scroll.add(&panel);
82    scroll.show_all();
83    scroll
84}
85
86fn create_plug_row<F: Fn(PlugInfo) + 'static>(
87    plug: &Description,
88    add_cb: Rc<F>,
89) -> gtk::ListBoxRow {
90    let row = gtk::ListBoxRow::new();
91    let row_container = gtk::Box::new(gtk::Orientation::Vertical, 5);
92    row_container.set_border_width(5);
93    let hbox = gtk::Box::new(gtk::Orientation::Horizontal, 5);
94    let label_box = create_plug_label(plug);
95
96    let button_box = gtk::Box::new(gtk::Orientation::Horizontal, 0);
97    button_box.set_halign(gtk::Align::End);
98
99    let add_btn = gtk::Button::new_with_label("Install");
100    button_box.pack_start(&add_btn, false, true, 0);
101
102    row_container.pack_start(&hbox, true, true, 0);
103    hbox.pack_start(&label_box, true, true, 0);
104    hbox.pack_start(&button_box, false, true, 0);
105
106    row.add(&row_container);
107
108    add_btn.connect_clicked(clone!(plug => move |btn| {
109        if let Some(ref github_url) = plug.github_url {
110            btn.set_sensitive(false);
111            add_cb(PlugInfo::new(plug.name.clone(), github_url.clone()));
112        }
113    }));
114
115    row
116}
117
118fn create_plug_label(plug: &Description) -> gtk::Box {
119    let label_box = gtk::Box::new(gtk::Orientation::Vertical, 5);
120
121    let name_lbl = gtk::Label::new(None);
122    name_lbl.set_markup(&format!(
123        "<b>{}</b> by {}",
124        plug.name,
125        plug.author
126            .as_ref()
127            .map(|s| s.as_ref())
128            .unwrap_or("unknown",)
129    ));
130    name_lbl.set_halign(gtk::Align::Start);
131    let url_lbl = gtk::Label::new(None);
132    if let Some(url) = plug.github_url.as_ref() {
133        url_lbl.set_markup(&format!("<a href=\"{}\">{}</a>", url, url));
134    }
135    url_lbl.set_halign(gtk::Align::Start);
136
137    label_box.pack_start(&name_lbl, true, true, 0);
138    label_box.pack_start(&url_lbl, true, true, 0);
139    label_box
140}
141
142#[derive(Deserialize, Debug)]
143pub struct DescriptionList {
144    pub plugins: Box<[Description]>,
145}
146
147impl DescriptionList {
148    fn empty() -> DescriptionList {
149        DescriptionList {
150            plugins: Box::new([]),
151        }
152    }
153}
154
155#[derive(Deserialize, Debug, Clone)]
156pub struct Description {
157    pub name: String,
158    pub github_url: Option<String>,
159    pub author: Option<String>,
160    pub github_stars: Option<i64>,
161}