Skip to main content

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/// VS Code extensions use camelCase in package.json. Serde renames from
44/// PascalCase Rust fields to camelCase JSON automatically. Fields that
45/// don't exist in package.json (Identifier, ExtensionLocation, IsBuiltin)
46/// default to their zero values on deserialization.
47#[derive(Serialize, Deserialize, Clone, Debug)]
48#[serde(rename_all = "camelCase")]
49pub struct ExtensionDescriptionStateDTO {
50	// --- Core Metadata ---
51	/// Extension identifier: { value: string, uuid?: string }
52	/// Not present in package.json — constructed from publisher.name after
53	/// parsing.
54	#[serde(default)]
55	pub Identifier:Value,
56
57	/// Extension name (from package.json "name")
58	#[serde(default, skip_serializing_if = "String::is_empty")]
59	pub Name:String,
60
61	/// Semantic version string (e.g., "1.0.0")
62	#[serde(default, skip_serializing_if = "String::is_empty")]
63	pub Version:String,
64
65	/// Publisher name or identifier
66	#[serde(default, skip_serializing_if = "String::is_empty")]
67	pub Publisher:String,
68
69	/// Engine compatibility requirements: { vscode: string }
70	#[serde(default)]
71	pub Engines:Value,
72
73	// --- Entry Points ---
74	/// Main entry point path (Node.js runtime)
75	#[serde(default, skip_serializing_if = "Option::is_none")]
76	pub Main:Option<String>,
77
78	/// Browser entry point path (web extension)
79	#[serde(default, skip_serializing_if = "Option::is_none")]
80	pub Browser:Option<String>,
81
82	// --- Type & Flags ---
83	/// Module type: commonjs or esm
84	#[serde(rename = "type", default, skip_serializing_if = "Option::is_none")]
85	pub ModuleType:Option<String>,
86
87	/// Whether this is a built-in extension (not in package.json, set by
88	/// scanner)
89	#[serde(default)]
90	pub IsBuiltin:bool,
91
92	/// Whether extension is under active development
93	#[serde(default)]
94	pub IsUnderDevelopment:bool,
95
96	// --- Location & Activation ---
97	/// Installation location URI (set by scanner, not in package.json)
98	#[serde(default)]
99	pub ExtensionLocation:Value,
100
101	/// Activation event triggers (e.g., "onStartupFinished")
102	#[serde(default, skip_serializing_if = "Option::is_none")]
103	pub ActivationEvents:Option<Vec<String>>,
104
105	// --- Contributions ---
106	/// Extension contributions (commands, views, etc.)
107	#[serde(default, skip_serializing_if = "Option::is_none")]
108	pub Contributes:Option<Value>,
109}
110
111impl ExtensionDescriptionStateDTO {
112	/// Validates the extension description data.
113	///
114	/// # Returns
115	/// Result indicating success or validation error with reason
116	pub fn Validate(&self) -> Result<(), String> {
117		// Validate Name length
118		if self.Name.len() > MAX_EXTENSION_NAME_LENGTH {
119			return Err(format!(
120				"Extension name exceeds maximum length of {} bytes",
121				MAX_EXTENSION_NAME_LENGTH
122			));
123		}
124
125		// Validate Version length
126		if self.Version.len() > MAX_VERSION_LENGTH {
127			return Err(format!("Version string exceeds maximum length of {} bytes", MAX_VERSION_LENGTH));
128		}
129
130		// Validate Publisher length
131		if self.Publisher.len() > MAX_PUBLISHER_LENGTH {
132			return Err(format!("Publisher exceeds maximum length of {} bytes", MAX_PUBLISHER_LENGTH));
133		}
134
135		// Validate ActivationEvents count
136		if let Some(Events) = &self.ActivationEvents {
137			if Events.len() > MAX_ACTIVATION_EVENTS {
138				return Err(format!("Activation events exceed maximum count of {}", MAX_ACTIVATION_EVENTS));
139			}
140		}
141
142		Ok(())
143	}
144
145	/// Creates a minimal extension description for testing or placeholder use.
146	///
147	/// # Arguments
148	/// * `Identifier` - Extension identifier value
149	/// * `Name` - Extension name
150	/// * `Version` - Extension version
151	/// * `Publisher` - Publisher name
152	///
153	/// # Returns
154	/// A new ExtensionDescriptionStateDTO with minimal required fields
155	pub fn CreateMinimal(Identifier:Value, Name:String, Version:String, Publisher:String) -> Result<Self, String> {
156		let Description = Self {
157			Identifier,
158			Name:Name.clone(),
159			Version:Version.clone(),
160			Publisher:Publisher.clone(),
161			Engines:serde_json::json!({ "vscode": "*" }),
162			Main:None,
163			Browser:None,
164			ModuleType:None,
165			IsBuiltin:false,
166			IsUnderDevelopment:false,
167			ExtensionLocation:serde_json::json!(null),
168			ActivationEvents:None,
169			Contributes:None,
170		};
171
172		Description.Validate()?;
173		Ok(Description)
174	}
175}