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}