Mountain/ApplicationState/DTO/
WorkspaceFolderStateDTO.rs1use serde::{Deserialize, Serialize};
13use url::Url;
14use CommonLibrary::Utility::Serialization::URLSerializationHelper;
15
16const MAX_FOLDER_NAME_LENGTH:usize = 256;
18
19const MAX_WORKSPACE_FOLDERS:usize = 100;
21
22#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
25#[serde(rename_all = "PascalCase")]
26pub struct WorkspaceFolderStateDTO {
27 #[serde(with = "URLSerializationHelper")]
29 pub URI:Url,
30
31 #[serde(skip_serializing_if = "String::is_empty")]
33 pub Name:String,
34
35 pub Index:usize,
37}
38
39impl WorkspaceFolderStateDTO {
40 pub fn New(URI:Url, Name:String, Index:usize) -> Result<Self, String> {
50 if URI.as_str().is_empty() {
52 return Err("URI cannot be empty".to_string());
53 }
54
55 if Name.len() > MAX_FOLDER_NAME_LENGTH {
57 return Err(format!(
58 "Folder name exceeds maximum length of {} bytes",
59 MAX_FOLDER_NAME_LENGTH
60 ));
61 }
62
63 if Index >= MAX_WORKSPACE_FOLDERS {
65 return Err(format!(
66 "Folder index {} exceeds maximum workspace folders count of {}",
67 Index, MAX_WORKSPACE_FOLDERS
68 ));
69 }
70
71 Ok(Self { URI, Name, Index })
72 }
73
74 pub fn UpdateName(&mut self, Name:String) -> Result<(), String> {
82 if Name.len() > MAX_FOLDER_NAME_LENGTH {
83 return Err(format!(
84 "Folder name exceeds maximum length of {} bytes",
85 MAX_FOLDER_NAME_LENGTH
86 ));
87 }
88
89 self.Name = Name;
90 Ok(())
91 }
92
93 pub fn GetDisplayName(&self) -> String {
96 if !self.Name.is_empty() {
97 self.Name.clone()
98 } else {
99 self.URI
101 .path_segments()
102 .and_then(|Segments| Segments.last())
103 .unwrap_or("Untitled")
104 .to_string()
105 }
106 }
107
108 pub fn IsRoot(&self) -> bool { self.Index == 0 }
110
111 pub fn FromPath(FolderPath:&str, Index:usize) -> Result<Self, String> {
120 let URI = Url::parse(FolderPath).map_err(|Error| format!("Invalid folder path: {}", Error))?;
121
122 let IsDirectory =
125 URI.path().ends_with('/') || (URI.scheme() == "file" && URI.to_file_path().map_or(false, |p| p.is_dir()));
126
127 if !IsDirectory {
128 return Err("URI does not represent a directory".to_string());
129 }
130
131 let Name = Self::ExtractFolderName(&URI);
132
133 Self::New(URI, Name, Index)
134 }
135
136 fn ExtractFolderName(URI:&Url) -> String {
138 URI.path_segments()
139 .and_then(|Segments| Segments.last())
140 .map(String::from)
141 .unwrap_or_else(|| "Untitled".to_string())
142 }
143}
144
145#[cfg(test)]
146mod tests {
147 use super::*;
148
149 #[test]
150 fn test_creation_success() {
151 let URI = Url::parse("file:///workspace/project").unwrap();
152 let dto = WorkspaceFolderStateDTO::New(URI.clone(), "project".to_string(), 0);
153 assert!(dto.is_ok());
154 assert_eq!(dto.unwrap().Name, "project");
155 }
156
157 #[test]
158 fn test_invalid_name_length() {
159 let URI = Url::parse("file:///workspace/project").unwrap();
160 let LongName = "a".repeat(257);
161 let dto = WorkspaceFolderStateDTO::New(URI, LongName, 0);
162 assert!(dto.is_err());
163 }
164
165 #[test]
166 fn test_invalid_index() {
167 let URI = Url::parse("file:///workspace/project").unwrap();
168 let dto = WorkspaceFolderStateDTO::New(URI, "project".to_string(), 100);
169 assert!(dto.is_err());
170 }
171}