Skip to main content

DevelopmentNodeEnvironment_MicrosoftVSCodeDependency_22NodeVersion_Bundle_Clean_Debug_ElectronProfile_EsbuildCompiler_Mountain/ExtensionManagement/
NLSResolver.rs

1#![allow(non_snake_case, unused_variables, dead_code, unused_imports)]
2
3//! NLS (National Language Support) placeholder resolution for extension
4//! manifests. VS Code extensions embed `%key%` tokens in their `package.json`
5//! that are resolved at runtime from a `package.nls.json` bundle.
6//!
7//! Three functions work together:
8//! - `ManifestContainsNLSPlaceholders` - fast pre-scan to skip bundle I/O
9//! - `LoadNLSBundle` - read and parse `package.nls.json`
10//! - `ResolveNLSPlaceholdersInner` - in-place recursive token substitution
11
12use std::{path::PathBuf, sync::Arc};
13
14use CommonLibrary::{Effect::ApplicationRunTime::ApplicationRunTime as _, FileSystem::ReadFile::ReadFile};
15use serde_json::{Map, Value};
16
17use crate::{RunTime::ApplicationRunTime::ApplicationRunTime, dev_log};
18
19/// Return `true` if `Value` contains any `%placeholder%` token anywhere in
20/// the tree. Used to skip bundle I/O for manifests that have no tokens.
21pub fn ManifestContainsNLSPlaceholders(Value:&Value) -> bool {
22	match Value {
23		serde_json::Value::String(Text) => {
24			Text.len() >= 2 && Text.starts_with('%') && Text.ends_with('%') && !Text[1..Text.len() - 1].contains('%')
25		},
26		serde_json::Value::Array(Items) => Items.iter().any(ManifestContainsNLSPlaceholders),
27		serde_json::Value::Object(Object) => Object.values().any(ManifestContainsNLSPlaceholders),
28		_ => false,
29	}
30}
31
32/// Load an extension's NLS bundle (`package.nls.json`) into a `{key → string}`
33/// map. Returns `None` if absent or unreadable - placeholders remain as-is.
34/// Entries can be bare strings or `{message, comment}` objects; only `message`
35/// is kept. The `PlaceholdersNeeded` flag downgrades the "no bundle" warning
36/// when the manifest has no `%placeholder%` entries (absence is benign).
37pub async fn LoadNLSBundle(
38	RunTime:&Arc<ApplicationRunTime>,
39	ExtensionPath:&PathBuf,
40	PlaceholdersNeeded:bool,
41) -> Option<Map<String, Value>> {
42	let NLSPath = ExtensionPath.join("package.nls.json");
43	let Content = match RunTime.Run(ReadFile(NLSPath.clone())).await {
44		Ok(Bytes) => Bytes,
45		Err(Error) => {
46			if PlaceholdersNeeded {
47				dev_log!("nls", "[LandFix:NLS] no bundle for {} ({})", ExtensionPath.display(), Error);
48			} else {
49				dev_log!(
50					"nls",
51					"[LandFix:NLS] {} has no placeholders, no bundle needed",
52					ExtensionPath.display()
53				);
54			}
55			return None;
56		},
57	};
58	let Parsed:Value = match serde_json::from_slice(&Content) {
59		Ok(V) => V,
60		Err(Error) => {
61			dev_log!("nls", "warn: [LandFix:NLS] failed to parse {}: {}", NLSPath.display(), Error);
62			return None;
63		},
64	};
65	let Object = Parsed.as_object()?;
66	let mut Resolved = Map::with_capacity(Object.len());
67	for (Key, RawValue) in Object {
68		let Text = if let Some(s) = RawValue.as_str() {
69			Some(s.to_string())
70		} else if let Some(obj) = RawValue.as_object() {
71			obj.get("message").and_then(|m| m.as_str()).map(|s| s.to_string())
72		} else {
73			None
74		};
75		if let Some(t) = Text {
76			Resolved.insert(Key.clone(), Value::String(t));
77		}
78	}
79	dev_log!(
80		"nls",
81		"[LandFix:NLS] loaded {} keys for {}",
82		Resolved.len(),
83		ExtensionPath.display()
84	);
85	Some(Resolved)
86}
87
88/// In-place recursive substitution of `%key%` tokens using the NLS map.
89/// `Replaced` and `Unresolved` accumulate counts for the outer scanner's
90/// one-line summary log.
91pub fn ResolveNLSPlaceholdersInner(Value:&mut Value, NLS:&Map<String, Value>, Replaced:&mut u32, Unresolved:&mut u32) {
92	match Value {
93		serde_json::Value::String(Text) => {
94			if Text.len() >= 2 && Text.starts_with('%') && Text.ends_with('%') {
95				let Key = &Text[1..Text.len() - 1];
96				if !Key.is_empty() && !Key.contains('%') {
97					if let Some(Replacement) = NLS.get(Key).and_then(|v| v.as_str()) {
98						*Text = Replacement.to_string();
99						*Replaced += 1;
100					} else {
101						*Unresolved += 1;
102					}
103				}
104			}
105		},
106		serde_json::Value::Array(Items) => {
107			for Item in Items {
108				ResolveNLSPlaceholdersInner(Item, NLS, Replaced, Unresolved);
109			}
110		},
111		serde_json::Value::Object(Map) => {
112			for (_, FieldValue) in Map {
113				ResolveNLSPlaceholdersInner(FieldValue, NLS, Replaced, Unresolved);
114			}
115		},
116		_ => {},
117	}
118}