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}