Mountain/ApplicationState/DTO/
OutputChannelStateDTO.rs

1//! # OutputChannelStateDTO
2//!
3//! # RESPONSIBILITY
4//! - Data transfer object for output channel state
5//! - Serializable format for gRPC/IPC transmission
6//! - Used by Mountain to track output channel lifecycle and content
7//!
8//! # FIELDS
9//! - Name: Channel display name
10//! - LanguageIdentifier: Language for syntax highlighting
11//! - Buffer: Buffered output content
12//! - IsVisible: Channel visibility status
13
14use serde::{Deserialize, Serialize};
15
16/// Maximum channel name length
17const MAX_CHANNEL_NAME_LENGTH:usize = 128;
18
19/// Maximum language identifier length
20const MAX_LANGUAGE_ID_LENGTH:usize = 128;
21
22/// Maximum buffer size per channel (prevents memory exhaustion)
23/// Set to 10MB to prevent unbounded memory growth from excessive output
24/// accumulation.
25const MAX_BUFFER_SIZE:usize = 10_000_000;
26
27/// Represents the complete state of a single output channel, including its
28/// buffered content and visibility status.
29#[derive(Serialize, Deserialize, Clone, Debug, Default)]
30#[serde(rename_all = "PascalCase")]
31pub struct OutputChannelStateDTO {
32	/// Channel display name
33	#[serde(skip_serializing_if = "String::is_empty")]
34	pub Name:String,
35
36	/// Language identifier for syntax highlighting
37	#[serde(skip_serializing_if = "Option::is_none")]
38	pub LanguageIdentifier:Option<String>,
39
40	/// Buffered output content
41	#[serde(skip_serializing_if = "String::is_empty")]
42	pub Buffer:String,
43
44	/// Whether the channel is currently visible in UI
45	pub IsVisible:bool,
46}
47
48impl OutputChannelStateDTO {
49	/// Creates a new `OutputChannelStateDTO` with validation.
50	///
51	/// # Arguments
52	/// * `Name` - Channel name
53	/// * `LanguageIdentifier` - Optional language identifier
54	///
55	/// # Returns
56	/// Result containing the DTO or validation error
57	pub fn Create(Name:&str, LanguageIdentifier:Option<String>) -> Result<Self, String> {
58		// Validate name length
59		if Name.len() > MAX_CHANNEL_NAME_LENGTH {
60			return Err(format!(
61				"Channel name exceeds maximum length of {} bytes",
62				MAX_CHANNEL_NAME_LENGTH
63			));
64		}
65
66		// Validate language identifier length
67		if let Some(LangID) = &LanguageIdentifier {
68			if LangID.len() > MAX_LANGUAGE_ID_LENGTH {
69				return Err(format!(
70					"Language identifier exceeds maximum length of {} bytes",
71					MAX_LANGUAGE_ID_LENGTH
72				));
73			}
74		}
75
76		Ok(Self { Name:Name.to_string(), LanguageIdentifier, Buffer:String::new(), IsVisible:false })
77	}
78
79	/// Appends content to the buffer with size validation.
80	///
81	/// # Arguments
82	/// * `Content` - Content to append
83	///
84	/// # Returns
85	/// Result indicating success or error if buffer would exceed limit
86	pub fn Append(&mut self, Content:&str) -> Result<(), String> {
87		let NewSize = self.Buffer.len() + Content.len();
88		if NewSize > MAX_BUFFER_SIZE {
89			return Err(format!("Buffer would exceed maximum size of {} bytes", MAX_BUFFER_SIZE));
90		}
91
92		self.Buffer.push_str(Content);
93		Ok(())
94	}
95
96	/// Clears the buffer content.
97	pub fn Clear(&mut self) { self.Buffer.clear(); }
98
99	/// Returns the current buffer size in bytes.
100	pub fn GetBufferSize(&self) -> usize { self.Buffer.len() }
101
102	/// Returns the current buffer size as a human-readable string.
103	pub fn GetFormattedBufferSize(&self) -> String { FormatBytes(self.Buffer.len()) }
104
105	/// Sets the visibility status.
106	///
107	/// # Arguments
108	/// * `IsVisible` - New visibility status
109	pub fn SetVisibility(&mut self, IsVisible:bool) { self.IsVisible = IsVisible; }
110}
111
112/// Formats a byte count into a human-readable string.
113fn FormatBytes(Bytes:usize) -> String {
114	const UNITS:&[&str] = &["B", "KB", "MB", "GB"];
115
116	if Bytes == 0 {
117		return "0 B".to_string();
118	}
119
120	let mut Size = Bytes as f64;
121	let mut MutIndex = 0usize;
122
123	while Size >= 1024.0 && MutIndex < UNITS.len() - 1 {
124		Size /= 1024.0;
125		MutIndex += 1;
126	}
127
128	format!("{:.2} {}", Size, UNITS[MutIndex])
129}