runnable_inventory.rs

 1//! Project-wide storage of the runnables available, capable of updating itself from the sources set.
 2
 3use std::{any::TypeId, path::Path, sync::Arc};
 4
 5use gpui::{AppContext, Context, Model, ModelContext, Subscription};
 6use runnable::{Runnable, RunnableId, Source};
 7
 8/// Inventory tracks available runnables for a given project.
 9pub struct Inventory {
10    sources: Vec<SourceInInventory>,
11    pub last_scheduled_runnable: Option<RunnableId>,
12}
13
14struct SourceInInventory {
15    source: Model<Box<dyn Source>>,
16    _subscription: Subscription,
17    type_id: TypeId,
18}
19
20impl Inventory {
21    pub(crate) fn new(cx: &mut AppContext) -> Model<Self> {
22        cx.new_model(|_| Self {
23            sources: Vec::new(),
24            last_scheduled_runnable: None,
25        })
26    }
27
28    /// Registers a new runnables source, that would be fetched for available runnables.
29    pub fn add_source(&mut self, source: Model<Box<dyn Source>>, cx: &mut ModelContext<Self>) {
30        let _subscription = cx.observe(&source, |_, _, cx| {
31            cx.notify();
32        });
33        let type_id = source.read(cx).type_id();
34        let source = SourceInInventory {
35            source,
36            _subscription,
37            type_id,
38        };
39        self.sources.push(source);
40        cx.notify();
41    }
42    pub fn source<T: Source>(&self) -> Option<Model<Box<dyn Source>>> {
43        let target_type_id = std::any::TypeId::of::<T>();
44        self.sources.iter().find_map(
45            |SourceInInventory {
46                 type_id, source, ..
47             }| {
48                if &target_type_id == type_id {
49                    Some(source.clone())
50                } else {
51                    None
52                }
53            },
54        )
55    }
56
57    /// Pulls its sources to list runanbles for the path given (up to the source to decide what to return for no path).
58    pub fn list_runnables(
59        &self,
60        path: Option<&Path>,
61        cx: &mut AppContext,
62    ) -> Vec<Arc<dyn Runnable>> {
63        let mut runnables = Vec::new();
64        for source in &self.sources {
65            runnables.extend(
66                source
67                    .source
68                    .update(cx, |source, cx| source.runnables_for_path(path, cx)),
69            );
70        }
71        runnables
72    }
73
74    /// Returns the last scheduled runnable, if any of the sources contains one with the matching id.
75    pub fn last_scheduled_runnable(&self, cx: &mut AppContext) -> Option<Arc<dyn Runnable>> {
76        self.last_scheduled_runnable.as_ref().and_then(|id| {
77            // TODO straighten the `Path` story to understand what has to be passed here: or it will break in the future.
78            self.list_runnables(None, cx)
79                .into_iter()
80                .find(|runnable| runnable.id() == id)
81        })
82    }
83}