dev_server_projects.rs

  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 paths: Vec<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            paths: project.paths.into_iter().map(|path| path.into()).collect(),
 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 ssh_connection_string: Option<SharedString>,
 43    pub status: DevServerStatus,
 44}
 45
 46impl From<proto::DevServer> for DevServer {
 47    fn from(dev_server: proto::DevServer) -> Self {
 48        Self {
 49            id: DevServerId(dev_server.dev_server_id),
 50            status: dev_server.status(),
 51            name: dev_server.name.into(),
 52            ssh_connection_string: dev_server.ssh_connection_string.map(|s| s.into()),
 53        }
 54    }
 55}
 56
 57struct GlobalStore(Model<Store>);
 58
 59impl Global for GlobalStore {}
 60
 61pub fn init(client: Arc<Client>, cx: &mut AppContext) {
 62    let store = cx.new_model(|cx| Store::new(client, cx));
 63    cx.set_global(GlobalStore(store));
 64}
 65
 66impl Store {
 67    pub fn global(cx: &AppContext) -> Model<Store> {
 68        cx.global::<GlobalStore>().0.clone()
 69    }
 70
 71    pub fn new(client: Arc<Client>, cx: &ModelContext<Self>) -> Self {
 72        Self {
 73            dev_server_projects: Default::default(),
 74            dev_servers: Default::default(),
 75            _subscriptions: vec![client
 76                .add_message_handler(cx.weak_model(), Self::handle_dev_server_projects_update)],
 77            client,
 78        }
 79    }
 80
 81    pub fn projects_for_server(&self, id: DevServerId) -> Vec<DevServerProject> {
 82        let mut projects: Vec<DevServerProject> = self
 83            .dev_server_projects
 84            .values()
 85            .filter(|project| project.dev_server_id == id)
 86            .cloned()
 87            .collect();
 88        projects.sort_by_key(|p| (p.paths.clone(), p.id));
 89        projects
 90    }
 91
 92    pub fn dev_servers(&self) -> Vec<DevServer> {
 93        let mut dev_servers: Vec<DevServer> = self.dev_servers.values().cloned().collect();
 94        dev_servers.sort_by_key(|d| (d.status == DevServerStatus::Offline, d.name.clone(), d.id));
 95        dev_servers
 96    }
 97
 98    pub fn dev_server(&self, id: DevServerId) -> Option<&DevServer> {
 99        self.dev_servers.get(&id)
100    }
101
102    pub fn dev_server_status(&self, id: DevServerId) -> DevServerStatus {
103        self.dev_server(id)
104            .map(|server| server.status)
105            .unwrap_or(DevServerStatus::Offline)
106    }
107
108    pub fn dev_server_projects(&self) -> Vec<DevServerProject> {
109        let mut projects: Vec<DevServerProject> =
110            self.dev_server_projects.values().cloned().collect();
111        projects.sort_by_key(|p| (p.paths.clone(), p.id));
112        projects
113    }
114
115    pub fn dev_server_project(&self, id: DevServerProjectId) -> Option<&DevServerProject> {
116        self.dev_server_projects.get(&id)
117    }
118
119    pub fn dev_server_for_project(&self, id: DevServerProjectId) -> Option<&DevServer> {
120        self.dev_server_project(id)
121            .and_then(|project| self.dev_server(project.dev_server_id))
122    }
123
124    async fn handle_dev_server_projects_update(
125        this: Model<Self>,
126        envelope: TypedEnvelope<proto::DevServerProjectsUpdate>,
127        mut cx: AsyncAppContext,
128    ) -> Result<()> {
129        this.update(&mut cx, |this, cx| {
130            this.dev_servers = envelope
131                .payload
132                .dev_servers
133                .into_iter()
134                .map(|dev_server| (DevServerId(dev_server.dev_server_id), dev_server.into()))
135                .collect();
136            this.dev_server_projects = envelope
137                .payload
138                .dev_server_projects
139                .into_iter()
140                .map(|project| (DevServerProjectId(project.id), project.into()))
141                .collect();
142
143            cx.notify();
144        })?;
145        Ok(())
146    }
147
148    pub fn create_dev_server_project(
149        &mut self,
150        dev_server_id: DevServerId,
151        path: String,
152        cx: &mut ModelContext<Self>,
153    ) -> Task<Result<proto::CreateDevServerProjectResponse>> {
154        let client = self.client.clone();
155        cx.background_executor().spawn(async move {
156            client
157                .request(proto::CreateDevServerProject {
158                    dev_server_id: dev_server_id.0,
159                    path,
160                })
161                .await
162        })
163    }
164
165    pub fn create_dev_server(
166        &mut self,
167        name: String,
168        ssh_connection_string: Option<String>,
169        cx: &mut ModelContext<Self>,
170    ) -> Task<Result<proto::CreateDevServerResponse>> {
171        let client = self.client.clone();
172        cx.background_executor().spawn(async move {
173            let result = client
174                .request(proto::CreateDevServer {
175                    name,
176                    ssh_connection_string,
177                })
178                .await?;
179            Ok(result)
180        })
181    }
182
183    pub fn rename_dev_server(
184        &mut self,
185        dev_server_id: DevServerId,
186        name: String,
187        ssh_connection_string: Option<String>,
188        cx: &mut ModelContext<Self>,
189    ) -> Task<Result<()>> {
190        let client = self.client.clone();
191        cx.background_executor().spawn(async move {
192            client
193                .request(proto::RenameDevServer {
194                    dev_server_id: dev_server_id.0,
195                    name,
196                    ssh_connection_string,
197                })
198                .await?;
199            Ok(())
200        })
201    }
202
203    pub fn regenerate_dev_server_token(
204        &mut self,
205        dev_server_id: DevServerId,
206        cx: &mut ModelContext<Self>,
207    ) -> Task<Result<proto::RegenerateDevServerTokenResponse>> {
208        let client = self.client.clone();
209        cx.background_executor().spawn(async move {
210            client
211                .request(proto::RegenerateDevServerToken {
212                    dev_server_id: dev_server_id.0,
213                })
214                .await
215        })
216    }
217
218    pub fn delete_dev_server(
219        &mut self,
220        id: DevServerId,
221        cx: &mut ModelContext<Self>,
222    ) -> Task<Result<()>> {
223        let client = self.client.clone();
224        cx.background_executor().spawn(async move {
225            client
226                .request(proto::DeleteDevServer {
227                    dev_server_id: id.0,
228                })
229                .await?;
230            Ok(())
231        })
232    }
233
234    pub fn delete_dev_server_project(
235        &mut self,
236        id: DevServerProjectId,
237        cx: &mut ModelContext<Self>,
238    ) -> Task<Result<()>> {
239        let client = self.client.clone();
240        cx.background_executor().spawn(async move {
241            client
242                .request(proto::DeleteDevServerProject {
243                    dev_server_project_id: id.0,
244                })
245                .await?;
246            Ok(())
247        })
248    }
249}