Skip to main content

Mountain/Track/Effect/CreateEffectForRequest/
Languages.rs

1//! # Languages Effect (CreateEffectForRequest)
2//!
3//! Effect constructor for the `Languages.GetAll` RPC method. Aggregates
4//! language contribution metadata from all scanned extensions (from
5//! `ApplicationState.Extension.ScannedExtensions`) and merges duplicate
6//! language IDs by unioning their aliases, extensions, filenames, filename
7//! patterns, and mimetypes. The result is returned as a flat JSON array
8//! matching the shape expected by Cocoon's `languages.getAll` API.
9//!
10//! ## Aggregation behavior
11//!
12//! - Each extension's `contributes.languages` array is scanned.
13//! - Languages with the same `id` are merged (first-wins for `configuration`).
14//! - Array fields (aliases, extensions, filenames, filenamePatterns, mimetypes)
15//!   are deduplicated across all contributing extensions.
16
17use std::{collections::HashMap, sync::Arc};
18
19use serde_json::{Value, json};
20use tauri::Runtime;
21
22use crate::{Track::Effect::MappedEffectType::MappedEffect, dev_log};
23
24pub fn CreateEffect<R:Runtime>(MethodName:&str, Parameters:Value) -> Option<Result<MappedEffect, String>> {
25	match MethodName {
26		"Languages.GetAll" => {
27			crate::effect!(run_time, {
28				let scanned = run_time
29					.Environment
30					.ApplicationState
31					.Extension
32					.ScannedExtensions
33					.ScannedExtensions
34					.clone();
35
36				let Snapshot = {
37					let Guard = match scanned.lock() {
38						Ok(g) => g,
39						Err(error) => {
40							return Err(format!("Languages.GetAll: scanned-extensions lock poisoned: {}", error));
41						},
42					};
43
44					Guard.clone()
45				};
46
47				let mut merged:HashMap<String, serde_json::Map<String, Value>> = HashMap::new();
48
49				for Dto in Snapshot.values() {
50					let Contributes = match Dto.Contributes.as_ref() {
51						Some(c) => c,
52						None => continue,
53					};
54
55					let Languages = Contributes.get("languages").and_then(Value::as_array);
56
57					let Some(Languages) = Languages else { continue };
58
59					for Entry in Languages {
60						let Id = match Entry.get("id").and_then(Value::as_str) {
61							Some(id) if !id.is_empty() => id.to_string(),
62							_ => continue,
63						};
64
65						let Existing = merged.entry(Id.clone()).or_insert_with(|| {
66							let mut seed = serde_json::Map::new();
67
68							seed.insert("id".to_string(), json!(Id));
69
70							seed.insert("aliases".to_string(), json!([]));
71
72							seed.insert("extensions".to_string(), json!([]));
73
74							seed.insert("filenames".to_string(), json!([]));
75
76							seed.insert("filenamePatterns".to_string(), json!([]));
77
78							seed.insert("mimetypes".to_string(), json!([]));
79
80							seed.insert("configuration".to_string(), Value::Null);
81
82							seed
83						});
84
85						let merge_array = |target:&mut serde_json::Map<String, Value>, key:&str, incoming:&Value| {
86							let Some(incoming_arr) = incoming.get(key).and_then(Value::as_array) else {
87								return;
88							};
89
90							let bucket = target.entry(key.to_string()).or_insert_with(|| json!([]));
91
92							if let Some(bucket_arr) = bucket.as_array_mut() {
93								for v in incoming_arr {
94									if !bucket_arr.iter().any(|e| e == v) {
95										bucket_arr.push(v.clone());
96									}
97								}
98							}
99						};
100
101						merge_array(Existing, "aliases", Entry);
102
103						merge_array(Existing, "extensions", Entry);
104
105						merge_array(Existing, "filenames", Entry);
106
107						merge_array(Existing, "filenamePatterns", Entry);
108
109						merge_array(Existing, "mimetypes", Entry);
110
111						if Existing.get("configuration").map(Value::is_null).unwrap_or(true) {
112							if let Some(cfg) = Entry.get("configuration") {
113								Existing.insert("configuration".to_string(), cfg.clone());
114							}
115						}
116					}
117				}
118
119				let result:Vec<Value> = merged.into_values().map(Value::Object).collect();
120
121				dev_log!("ipc", "[Languages.GetAll] returning {} languages", result.len());
122
123				Ok(json!(result))
124			})
125		},
126
127		_ => None,
128	}
129}