Mountain/ApplicationState/DTO/ExtensionDescriptionStateDTO.rs
1//! # ExtensionDescriptionStateDTO
2//!
3//! # RESPONSIBILITY
4//! - Data transfer object for extension description/state
5//! - Serializable format for gRPC/IPC transmission
6//! - Used by Mountain to track extension metadata and capabilities
7//!
8//! # FIELDS
9//! - Identifier: Unique extension identifier
10//! - Name: Extension display name
11//! - Version: Semantic version string
12//! - Publisher: Publisher name
13//! - Engines: Engine compatibility requirements
14//! - Main: Main entry point path (Node.js)
15//! - Browser: Browser entry point path
16//! - ModuleType: Module type (commonjs/esm)
17//! - IsBuiltin: Built-in extension flag
18//! - IsUnderDevelopment: Development flag
19//! - ExtensionLocation: Installation location URI
20//! - ActivationEvents: Activation event triggers
21//! - Contributes: Extension contributions configuration
22
23use serde::{Deserialize, Serialize};
24use serde_json::Value;
25
26/// Maximum length for extension name
27const MAX_EXTENSION_NAME_LENGTH:usize = 128;
28
29/// Maximum length for version string
30const MAX_VERSION_LENGTH:usize = 64;
31
32/// Maximum length for publisher name
33const MAX_PUBLISHER_LENGTH:usize = 64;
34
35/// Maximum number of activation events
36const MAX_ACTIVATION_EVENTS:usize = 100;
37
38/// Represents the deserialized content of an extension's `package.json` file,
39/// augmented with location information and other metadata.
40///
41/// This is stored in `ApplicationState` to provide the extension host with the
42/// list of available extensions and their capabilities.
43#[derive(Serialize, Deserialize, Clone, Debug)]
44#[serde(rename_all = "PascalCase")]
45pub struct ExtensionDescriptionStateDTO {
46 // --- Core Metadata ---
47 // DTO: { value: string, uuid?: string }
48 pub Identifier:Value,
49
50 /// Extension display name
51 #[serde(skip_serializing_if = "String::is_empty")]
52 pub Name:String,
53
54 /// Semantic version string (e.g., "1.0.0")
55 #[serde(skip_serializing_if = "String::is_empty")]
56 pub Version:String,
57
58 /// Publisher name or identifier
59 #[serde(skip_serializing_if = "String::is_empty")]
60 pub Publisher:String,
61
62 // DTO: { vscode: string }
63 /// Engine compatibility requirements
64 pub Engines:Value,
65
66 // --- Entry Points ---
67 /// Main entry point path (Node.js runtime)
68 #[serde(skip_serializing_if = "Option::is_none")]
69 pub Main:Option<String>,
70
71 /// Browser entry point path (web extension)
72 #[serde(skip_serializing_if = "Option::is_none")]
73 pub Browser:Option<String>,
74
75 // --- Type & Flags ---
76 /// Module type: commonjs or esm
77 #[serde(rename = "Type", skip_serializing_if = "Option::is_none")]
78 pub ModuleType:Option<String>,
79
80 /// Whether this is a built-in extension
81 #[serde(default)]
82 pub IsBuiltin:bool,
83
84 /// Whether extension is under active development
85 #[serde(default)]
86 pub IsUnderDevelopment:bool,
87
88 // --- Location & Activation ---
89 // Serialized UriComponents DTO
90 /// Installation location URI
91 pub ExtensionLocation:Value,
92
93 /// Activation event triggers (e.g., "onStartupFinished")
94 #[serde(skip_serializing_if = "Option::is_none")]
95 pub ActivationEvents:Option<Vec<String>>,
96
97 // --- Contributions ---
98 /// Extension contributions (commands, views, etc.)
99 #[serde(skip_serializing_if = "Option::is_none")]
100 pub Contributes:Option<Value>,
101}
102
103impl ExtensionDescriptionStateDTO {
104 /// Validates the extension description data.
105 ///
106 /// # Returns
107 /// Result indicating success or validation error with reason
108 pub fn Validate(&self) -> Result<(), String> {
109 // Validate Name length
110 if self.Name.len() > MAX_EXTENSION_NAME_LENGTH {
111 return Err(format!(
112 "Extension name exceeds maximum length of {} bytes",
113 MAX_EXTENSION_NAME_LENGTH
114 ));
115 }
116
117 // Validate Version length
118 if self.Version.len() > MAX_VERSION_LENGTH {
119 return Err(format!("Version string exceeds maximum length of {} bytes", MAX_VERSION_LENGTH));
120 }
121
122 // Validate Publisher length
123 if self.Publisher.len() > MAX_PUBLISHER_LENGTH {
124 return Err(format!("Publisher exceeds maximum length of {} bytes", MAX_PUBLISHER_LENGTH));
125 }
126
127 // Validate ActivationEvents count
128 if let Some(Events) = &self.ActivationEvents {
129 if Events.len() > MAX_ACTIVATION_EVENTS {
130 return Err(format!("Activation events exceed maximum count of {}", MAX_ACTIVATION_EVENTS));
131 }
132 }
133
134 Ok(())
135 }
136
137 /// Creates a minimal extension description for testing or placeholder use.
138 ///
139 /// # Arguments
140 /// * `Identifier` - Extension identifier value
141 /// * `Name` - Extension name
142 /// * `Version` - Extension version
143 /// * `Publisher` - Publisher name
144 ///
145 /// # Returns
146 /// A new ExtensionDescriptionStateDTO with minimal required fields
147 pub fn CreateMinimal(Identifier:Value, Name:String, Version:String, Publisher:String) -> Result<Self, String> {
148 let Description = Self {
149 Identifier,
150 Name:Name.clone(),
151 Version:Version.clone(),
152 Publisher:Publisher.clone(),
153 Engines:serde_json::json!({ "vscode": "*" }),
154 Main:None,
155 Browser:None,
156 ModuleType:None,
157 IsBuiltin:false,
158 IsUnderDevelopment:false,
159 ExtensionLocation:serde_json::json!(null),
160 ActivationEvents:None,
161 Contributes:None,
162 };
163
164 Description.Validate()?;
165 Ok(Description)
166 }
167}