1use anyhow::Result;
2use gpui::{AppContext, AsyncAppContext, Context, Global, Model, ModelContext, SharedString, Task};
3use rpc::{
4 proto::{self, DevServerStatus},
5 TypedEnvelope,
6};
7use std::{collections::HashMap, sync::Arc};
8
9use client::{Client, ProjectId};
10pub use client::{DevServerId, DevServerProjectId};
11
12pub struct Store {
13 dev_server_projects: HashMap<DevServerProjectId, DevServerProject>,
14 dev_servers: HashMap<DevServerId, DevServer>,
15 _subscriptions: Vec<client::Subscription>,
16 client: Arc<Client>,
17}
18
19#[derive(Debug, Clone)]
20pub struct DevServerProject {
21 pub id: DevServerProjectId,
22 pub project_id: Option<ProjectId>,
23 pub path: SharedString,
24 pub dev_server_id: DevServerId,
25}
26
27impl From<proto::DevServerProject> for DevServerProject {
28 fn from(project: proto::DevServerProject) -> Self {
29 Self {
30 id: DevServerProjectId(project.id),
31 project_id: project.project_id.map(|id| ProjectId(id)),
32 path: project.path.into(),
33 dev_server_id: DevServerId(project.dev_server_id),
34 }
35 }
36}
37
38#[derive(Debug, Clone)]
39pub struct DevServer {
40 pub id: DevServerId,
41 pub name: SharedString,
42 pub status: DevServerStatus,
43}
44
45impl From<proto::DevServer> for DevServer {
46 fn from(dev_server: proto::DevServer) -> Self {
47 Self {
48 id: DevServerId(dev_server.dev_server_id),
49 status: dev_server.status(),
50 name: dev_server.name.into(),
51 }
52 }
53}
54
55struct GlobalStore(Model<Store>);
56
57impl Global for GlobalStore {}
58
59pub fn init(client: Arc<Client>, cx: &mut AppContext) {
60 let store = cx.new_model(|cx| Store::new(client, cx));
61 cx.set_global(GlobalStore(store));
62}
63
64impl Store {
65 pub fn global(cx: &AppContext) -> Model<Store> {
66 cx.global::<GlobalStore>().0.clone()
67 }
68
69 pub fn new(client: Arc<Client>, cx: &ModelContext<Self>) -> Self {
70 Self {
71 dev_server_projects: Default::default(),
72 dev_servers: Default::default(),
73 _subscriptions: vec![client
74 .add_message_handler(cx.weak_model(), Self::handle_dev_server_projects_update)],
75 client,
76 }
77 }
78
79 pub fn projects_for_server(&self, id: DevServerId) -> Vec<DevServerProject> {
80 let mut projects: Vec<DevServerProject> = self
81 .dev_server_projects
82 .values()
83 .filter(|project| project.dev_server_id == id)
84 .cloned()
85 .collect();
86 projects.sort_by_key(|p| (p.path.clone(), p.id));
87 projects
88 }
89
90 pub fn dev_servers(&self) -> Vec<DevServer> {
91 let mut dev_servers: Vec<DevServer> = self.dev_servers.values().cloned().collect();
92 dev_servers.sort_by_key(|d| (d.status == DevServerStatus::Offline, d.name.clone(), d.id));
93 dev_servers
94 }
95
96 pub fn dev_server(&self, id: DevServerId) -> Option<&DevServer> {
97 self.dev_servers.get(&id)
98 }
99
100 pub fn dev_server_status(&self, id: DevServerId) -> DevServerStatus {
101 self.dev_server(id)
102 .map(|server| server.status)
103 .unwrap_or(DevServerStatus::Offline)
104 }
105
106 pub fn dev_server_projects(&self) -> Vec<DevServerProject> {
107 let mut projects: Vec<DevServerProject> =
108 self.dev_server_projects.values().cloned().collect();
109 projects.sort_by_key(|p| (p.path.clone(), p.id));
110 projects
111 }
112
113 pub fn dev_server_project(&self, id: DevServerProjectId) -> Option<&DevServerProject> {
114 self.dev_server_projects.get(&id)
115 }
116
117 pub fn dev_server_for_project(&self, id: DevServerProjectId) -> Option<&DevServer> {
118 self.dev_server_project(id)
119 .and_then(|project| self.dev_server(project.dev_server_id))
120 }
121
122 async fn handle_dev_server_projects_update(
123 this: Model<Self>,
124 envelope: TypedEnvelope<proto::DevServerProjectsUpdate>,
125 _: Arc<Client>,
126 mut cx: AsyncAppContext,
127 ) -> Result<()> {
128 this.update(&mut cx, |this, cx| {
129 this.dev_servers = envelope
130 .payload
131 .dev_servers
132 .into_iter()
133 .map(|dev_server| (DevServerId(dev_server.dev_server_id), dev_server.into()))
134 .collect();
135 this.dev_server_projects = envelope
136 .payload
137 .dev_server_projects
138 .into_iter()
139 .map(|project| (DevServerProjectId(project.id), project.into()))
140 .collect();
141
142 cx.notify();
143 })?;
144 Ok(())
145 }
146
147 pub fn create_dev_server_project(
148 &mut self,
149 dev_server_id: DevServerId,
150 path: String,
151 cx: &mut ModelContext<Self>,
152 ) -> Task<Result<proto::CreateDevServerProjectResponse>> {
153 let client = self.client.clone();
154 cx.background_executor().spawn(async move {
155 client
156 .request(proto::CreateDevServerProject {
157 dev_server_id: dev_server_id.0,
158 path,
159 })
160 .await
161 })
162 }
163
164 pub fn create_dev_server(
165 &mut self,
166 name: String,
167 cx: &mut ModelContext<Self>,
168 ) -> Task<Result<proto::CreateDevServerResponse>> {
169 let client = self.client.clone();
170 cx.background_executor().spawn(async move {
171 let result = client.request(proto::CreateDevServer { name }).await?;
172 Ok(result)
173 })
174 }
175
176 pub fn delete_dev_server(
177 &mut self,
178 id: DevServerId,
179 cx: &mut ModelContext<Self>,
180 ) -> Task<Result<()>> {
181 let client = self.client.clone();
182 cx.background_executor().spawn(async move {
183 client
184 .request(proto::DeleteDevServer {
185 dev_server_id: id.0,
186 })
187 .await?;
188 Ok(())
189 })
190 }
191
192 pub fn delete_dev_server_project(
193 &mut self,
194 id: DevServerProjectId,
195 cx: &mut ModelContext<Self>,
196 ) -> Task<Result<()>> {
197 let client = self.client.clone();
198 cx.background_executor().spawn(async move {
199 client
200 .request(proto::DeleteDevServerProject {
201 dev_server_project_id: id.0,
202 })
203 .await?;
204 Ok(())
205 })
206 }
207}