Consolidate more extension API structs that were duplicated btwn client and server (#9797)

Max Brunsfeld created

Release Notes:

- N/A

Change summary

crates/collab/src/api/extensions.rs        | 11 ++------
crates/collab/src/db/queries/extensions.rs |  4 +-
crates/extension/src/extension_store.rs    | 23 ++----------------
crates/extension_cli/src/main.rs           |  2 
crates/extensions_ui/src/extensions_ui.rs  | 30 +++++++++++++----------
crates/rpc/src/extension.rs                | 12 +++++++--
6 files changed, 35 insertions(+), 47 deletions(-)

Detailed changes

crates/collab/src/api/extensions.rs 🔗

@@ -9,8 +9,8 @@ use axum::{
     Extension, Json, Router,
 };
 use collections::HashMap;
-use rpc::{ExtensionApiManifest, ExtensionMetadata};
-use serde::{Deserialize, Serialize};
+use rpc::{ExtensionApiManifest, GetExtensionsResponse};
+use serde::Deserialize;
 use std::{sync::Arc, time::Duration};
 use time::PrimitiveDateTime;
 use util::ResultExt;
@@ -46,11 +46,6 @@ struct DownloadExtensionParams {
     version: String,
 }
 
-#[derive(Debug, Serialize)]
-struct GetExtensionsResponse {
-    pub data: Vec<ExtensionMetadata>,
-}
-
 async fn get_extensions(
     Extension(app): Extension<Arc<AppState>>,
     Query(params): Query<GetExtensionsParams>,
@@ -75,7 +70,7 @@ async fn download_latest_extension(
         Extension(app),
         Path(DownloadExtensionParams {
             extension_id: params.extension_id,
-            version: extension.manifest.version,
+            version: extension.manifest.version.to_string(),
         }),
     )
     .await

crates/collab/src/db/queries/extensions.rs 🔗

@@ -243,10 +243,10 @@ fn metadata_from_extension_and_version(
     version: extension_version::Model,
 ) -> ExtensionMetadata {
     ExtensionMetadata {
-        id: extension.external_id,
+        id: extension.external_id.into(),
         manifest: rpc::ExtensionApiManifest {
             name: extension.name,
-            version: version.version,
+            version: version.version.into(),
             authors: version
                 .authors
                 .split(',')

crates/extension/src/extension_store.rs 🔗

@@ -10,7 +10,7 @@ use crate::{extension_lsp_adapter::ExtensionLspAdapter, wasm_host::wit};
 use anyhow::{anyhow, bail, Context as _, Result};
 use async_compression::futures::bufread::GzipDecoder;
 use async_tar::Archive;
-use client::{telemetry::Telemetry, Client};
+use client::{telemetry::Telemetry, Client, ExtensionMetadata, GetExtensionsResponse};
 use collections::{hash_map, BTreeMap, HashMap, HashSet};
 use extension_builder::{CompileExtensionOptions, ExtensionBuilder};
 use fs::{Fs, RemoveOptions};
@@ -54,22 +54,6 @@ const FS_WATCH_LATENCY: Duration = Duration::from_millis(100);
 
 const CURRENT_SCHEMA_VERSION: i64 = 1;
 
-#[derive(Deserialize)]
-pub struct ExtensionsApiResponse {
-    pub data: Vec<ExtensionApiResponse>,
-}
-
-#[derive(Clone, Deserialize)]
-pub struct ExtensionApiResponse {
-    pub id: Arc<str>,
-    pub name: String,
-    pub version: Arc<str>,
-    pub description: Option<String>,
-    pub authors: Vec<String>,
-    pub repository: String,
-    pub download_count: usize,
-}
-
 pub struct ExtensionStore {
     builder: Arc<ExtensionBuilder>,
     extension_index: ExtensionIndex,
@@ -386,7 +370,7 @@ impl ExtensionStore {
         &self,
         search: Option<&str>,
         cx: &mut ModelContext<Self>,
-    ) -> Task<Result<Vec<ExtensionApiResponse>>> {
+    ) -> Task<Result<Vec<ExtensionMetadata>>> {
         let version = CURRENT_SCHEMA_VERSION.to_string();
         let mut query = vec![("max_schema_version", version.as_str())];
         if let Some(search) = search {
@@ -415,8 +399,7 @@ impl ExtensionStore {
                 );
             }
 
-            let response: ExtensionsApiResponse = serde_json::from_slice(&body)?;
-
+            let response: GetExtensionsResponse = serde_json::from_slice(&body)?;
             Ok(response.data)
         })
     }

crates/extension_cli/src/main.rs 🔗

@@ -92,7 +92,7 @@ async fn main() -> Result<()> {
 
     let manifest_json = serde_json::to_string(&rpc::ExtensionApiManifest {
         name: manifest.name,
-        version: manifest.version.to_string(),
+        version: manifest.version,
         description: manifest.description,
         authors: manifest.authors,
         schema_version: Some(manifest.schema_version),

crates/extensions_ui/src/extensions_ui.rs 🔗

@@ -3,8 +3,9 @@ mod extension_suggest;
 
 use crate::components::ExtensionCard;
 use client::telemetry::Telemetry;
+use client::ExtensionMetadata;
 use editor::{Editor, EditorElement, EditorStyle};
-use extension::{ExtensionApiResponse, ExtensionManifest, ExtensionStatus, ExtensionStore};
+use extension::{ExtensionManifest, ExtensionStatus, ExtensionStore};
 use fuzzy::{match_strings, StringMatchCandidate};
 use gpui::{
     actions, canvas, uniform_list, AnyElement, AppContext, EventEmitter, FocusableView, FontStyle,
@@ -97,7 +98,7 @@ pub struct ExtensionsPage {
     telemetry: Arc<Telemetry>,
     is_fetching_extensions: bool,
     filter: ExtensionFilter,
-    remote_extension_entries: Vec<ExtensionApiResponse>,
+    remote_extension_entries: Vec<ExtensionMetadata>,
     dev_extension_entries: Vec<Arc<ExtensionManifest>>,
     filtered_remote_extension_indices: Vec<usize>,
     query_editor: View<Editor>,
@@ -385,7 +386,7 @@ impl ExtensionsPage {
 
     fn render_remote_extension(
         &self,
-        extension: &ExtensionApiResponse,
+        extension: &ExtensionMetadata,
         cx: &mut ViewContext<Self>,
     ) -> ExtensionCard {
         let status = ExtensionStore::global(cx)
@@ -394,7 +395,7 @@ impl ExtensionsPage {
 
         let (install_or_uninstall_button, upgrade_button) =
             self.buttons_for_entry(extension, &status, cx);
-        let repository_url = extension.repository.clone();
+        let repository_url = extension.manifest.repository.clone();
 
         ExtensionCard::new()
             .child(
@@ -404,9 +405,12 @@ impl ExtensionsPage {
                         h_flex()
                             .gap_2()
                             .items_end()
-                            .child(Headline::new(extension.name.clone()).size(HeadlineSize::Medium))
                             .child(
-                                Headline::new(format!("v{}", extension.version))
+                                Headline::new(extension.manifest.name.clone())
+                                    .size(HeadlineSize::Medium),
+                            )
+                            .child(
+                                Headline::new(format!("v{}", extension.manifest.version))
                                     .size(HeadlineSize::XSmall),
                             ),
                     )
@@ -424,12 +428,12 @@ impl ExtensionsPage {
                     .child(
                         Label::new(format!(
                             "{}: {}",
-                            if extension.authors.len() > 1 {
+                            if extension.manifest.authors.len() > 1 {
                                 "Authors"
                             } else {
                                 "Author"
                             },
-                            extension.authors.join(", ")
+                            extension.manifest.authors.join(", ")
                         ))
                         .size(LabelSize::Small),
                     )
@@ -442,7 +446,7 @@ impl ExtensionsPage {
                 h_flex()
                     .gap_2()
                     .justify_between()
-                    .children(extension.description.as_ref().map(|description| {
+                    .children(extension.manifest.description.as_ref().map(|description| {
                         h_flex().overflow_x_hidden().child(
                             Label::new(description.clone())
                                 .size(LabelSize::Small)
@@ -470,7 +474,7 @@ impl ExtensionsPage {
 
     fn buttons_for_entry(
         &self,
-        extension: &ExtensionApiResponse,
+        extension: &ExtensionMetadata,
         status: &ExtensionStatus,
         cx: &mut ViewContext<Self>,
     ) -> (Button, Option<Button>) {
@@ -479,7 +483,7 @@ impl ExtensionsPage {
                 Button::new(SharedString::from(extension.id.clone()), "Install").on_click(
                     cx.listener({
                         let extension_id = extension.id.clone();
-                        let version = extension.version.clone();
+                        let version = extension.manifest.version.clone();
                         move |this, _, cx| {
                             this.telemetry
                                 .report_app_event("extensions: install extension".to_string());
@@ -514,14 +518,14 @@ impl ExtensionsPage {
                         }
                     }),
                 ),
-                if installed_version == extension.version {
+                if installed_version == extension.manifest.version {
                     None
                 } else {
                     Some(
                         Button::new(SharedString::from(extension.id.clone()), "Upgrade").on_click(
                             cx.listener({
                                 let extension_id = extension.id.clone();
-                                let version = extension.version.clone();
+                                let version = extension.manifest.version.clone();
                                 move |this, _, cx| {
                                     this.telemetry.report_app_event(
                                         "extensions: install extension".to_string(),

crates/rpc/src/extension.rs 🔗

@@ -1,10 +1,11 @@
 use chrono::{DateTime, Utc};
 use serde::{Deserialize, Serialize};
+use std::sync::Arc;
 
 #[derive(Serialize, Deserialize, Debug, PartialEq)]
 pub struct ExtensionApiManifest {
     pub name: String,
-    pub version: String,
+    pub version: Arc<str>,
     pub description: Option<String>,
     pub authors: Vec<String>,
     pub repository: String,
@@ -12,11 +13,16 @@ pub struct ExtensionApiManifest {
     pub wasm_api_version: Option<String>,
 }
 
-#[derive(Debug, Serialize, PartialEq)]
+#[derive(Serialize, Deserialize, Debug, PartialEq)]
 pub struct ExtensionMetadata {
-    pub id: String,
+    pub id: Arc<str>,
     #[serde(flatten)]
     pub manifest: ExtensionApiManifest,
     pub published_at: DateTime<Utc>,
     pub download_count: u64,
 }
+
+#[derive(Serialize, Deserialize)]
+pub struct GetExtensionsResponse {
+    pub data: Vec<ExtensionMetadata>,
+}