Skip to main content

Mountain/Track/Effect/CreateEffectForRequest/
Languages.rs

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