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, RemoteProjectId};
11
12pub struct Store {
13 remote_projects: HashMap<RemoteProjectId, RemoteProject>,
14 dev_servers: HashMap<DevServerId, DevServer>,
15 _subscriptions: Vec<client::Subscription>,
16 client: Arc<Client>,
17}
18
19#[derive(Debug, Clone)]
20pub struct RemoteProject {
21 pub id: RemoteProjectId,
22 pub project_id: Option<ProjectId>,
23 pub path: SharedString,
24 pub dev_server_id: DevServerId,
25}
26
27impl From<proto::RemoteProject> for RemoteProject {
28 fn from(project: proto::RemoteProject) -> Self {
29 Self {
30 id: RemoteProjectId(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 remote_projects: Default::default(),
72 dev_servers: Default::default(),
73 _subscriptions: vec![
74 client.add_message_handler(cx.weak_model(), Self::handle_remote_projects_update)
75 ],
76 client,
77 }
78 }
79
80 pub fn remote_projects_for_server(&self, id: DevServerId) -> Vec<RemoteProject> {
81 let mut projects: Vec<RemoteProject> = self
82 .remote_projects
83 .values()
84 .filter(|project| project.dev_server_id == id)
85 .cloned()
86 .collect();
87 projects.sort_by_key(|p| (p.path.clone(), p.id));
88 projects
89 }
90
91 pub fn dev_servers(&self) -> Vec<DevServer> {
92 let mut dev_servers: Vec<DevServer> = self.dev_servers.values().cloned().collect();
93 dev_servers.sort_by_key(|d| (d.status == DevServerStatus::Offline, d.name.clone(), d.id));
94 dev_servers
95 }
96
97 pub fn dev_server(&self, id: DevServerId) -> Option<&DevServer> {
98 self.dev_servers.get(&id)
99 }
100
101 pub fn dev_server_status(&self, id: DevServerId) -> DevServerStatus {
102 self.dev_server(id)
103 .map(|server| server.status)
104 .unwrap_or(DevServerStatus::Offline)
105 }
106
107 pub fn remote_projects(&self) -> Vec<RemoteProject> {
108 let mut projects: Vec<RemoteProject> = self.remote_projects.values().cloned().collect();
109 projects.sort_by_key(|p| (p.path.clone(), p.id));
110 projects
111 }
112
113 pub fn remote_project(&self, id: RemoteProjectId) -> Option<&RemoteProject> {
114 self.remote_projects.get(&id)
115 }
116
117 async fn handle_remote_projects_update(
118 this: Model<Self>,
119 envelope: TypedEnvelope<proto::RemoteProjectsUpdate>,
120 _: Arc<Client>,
121 mut cx: AsyncAppContext,
122 ) -> Result<()> {
123 this.update(&mut cx, |this, cx| {
124 this.dev_servers = envelope
125 .payload
126 .dev_servers
127 .into_iter()
128 .map(|dev_server| (DevServerId(dev_server.dev_server_id), dev_server.into()))
129 .collect();
130 this.remote_projects = envelope
131 .payload
132 .remote_projects
133 .into_iter()
134 .map(|project| (RemoteProjectId(project.id), project.into()))
135 .collect();
136
137 cx.notify();
138 })?;
139 Ok(())
140 }
141
142 pub fn create_remote_project(
143 &mut self,
144 dev_server_id: DevServerId,
145 path: String,
146 cx: &mut ModelContext<Self>,
147 ) -> Task<Result<proto::CreateRemoteProjectResponse>> {
148 let client = self.client.clone();
149 cx.background_executor().spawn(async move {
150 client
151 .request(proto::CreateRemoteProject {
152 dev_server_id: dev_server_id.0,
153 path,
154 })
155 .await
156 })
157 }
158
159 pub fn create_dev_server(
160 &mut self,
161 name: String,
162 cx: &mut ModelContext<Self>,
163 ) -> Task<Result<proto::CreateDevServerResponse>> {
164 let client = self.client.clone();
165 cx.background_executor().spawn(async move {
166 let result = client.request(proto::CreateDevServer { name }).await?;
167 Ok(result)
168 })
169 }
170
171 pub fn delete_dev_server(
172 &mut self,
173 id: DevServerId,
174 cx: &mut ModelContext<Self>,
175 ) -> Task<Result<()>> {
176 let client = self.client.clone();
177 cx.background_executor().spawn(async move {
178 client
179 .request(proto::DeleteDevServer {
180 dev_server_id: id.0,
181 })
182 .await?;
183 Ok(())
184 })
185 }
186}