diff --git a/crates/assistant_slash_command/src/assistant_slash_command.rs b/crates/assistant_slash_command/src/assistant_slash_command.rs index 2e6bb7325e14ac109d77854e1d848c541a685458..883f2f98c2693be5ae915cdab53ea94786222341 100644 --- a/crates/assistant_slash_command/src/assistant_slash_command.rs +++ b/crates/assistant_slash_command/src/assistant_slash_command.rs @@ -13,7 +13,7 @@ use language::CodeLabelBuilder; use language::HighlightId; use language::{BufferSnapshot, CodeLabel, LspAdapterDelegate, OffsetRangeExt}; pub use language_model::Role; -use serde::{Deserialize, Serialize}; +use serde::{Deserialize, Deserializer, Serialize}; use std::{ ops::Range, sync::{Arc, atomic::AtomicBool}, @@ -21,6 +21,18 @@ use std::{ use ui::ActiveTheme; use workspace::{Workspace, ui::IconName}; +/// Deserializes IconName, falling back to Code for unknown variants. +/// This handles old saved data that may contain removed or renamed icon variants. +fn deserialize_icon_with_fallback<'de, D>(deserializer: D) -> Result +where + D: Deserializer<'de>, +{ + Ok(String::deserialize(deserializer) + .ok() + .and_then(|string| serde_json::from_value(serde_json::Value::String(string)).ok()) + .unwrap_or(IconName::Code)) +} + pub fn init(cx: &mut App) { SlashCommandRegistry::default_global(cx); extension_slash_command::init(cx); @@ -256,6 +268,7 @@ impl SlashCommandOutput { #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct SlashCommandOutputSection { pub range: Range, + #[serde(deserialize_with = "deserialize_icon_with_fallback")] pub icon: IconName, pub label: SharedString, pub metadata: Option, @@ -570,4 +583,35 @@ mod tests { assert_eq!(new_output, output); } } + + #[test] + fn test_deserialize_with_valid_icon_pascal_case() { + // Test that PascalCase icons (serde default) deserialize correctly + let json = json!({ + "range": { + "start": 0, + "end": 5 + }, + "icon": "AcpRegistry", + "label": "Test", + "metadata": null + }); + let section: SlashCommandOutputSection = serde_json::from_value(json).unwrap(); + assert_eq!(section.icon, IconName::AcpRegistry); + } + #[test] + fn test_deserialize_with_unknown_icon() { + // Test that unknown icon variants fall back to Code + let json = json!({ + "range": { + "start": 0, + "end": 5 + }, + "icon": "removed_icon", + "label": "Old Icon", + "metadata": null + }); + let section: SlashCommandOutputSection = serde_json::from_value(json).unwrap(); + assert_eq!(section.icon, IconName::Code); + } }