Skip to main content

Mountain/Environment/LanguageFeatureProvider/
ProviderLookup.rs

1//! Provider lookup and matching utilities.
2
3use CommonLibrary::{Error::CommonError::CommonError, LanguageFeature::DTO::ProviderType::ProviderType};
4use url::Url;
5
6use crate::{
7	ApplicationState::DTO::ProviderRegistrationDTO::ProviderRegistrationDTO,
8	Environment::Utility::ErrorMapping::MapApplicationStateLockErrorToCommonError,
9	dev_log,
10};
11
12pub(super) async fn get_matching_provider(
13	environment:&crate::Environment::MountainEnvironment::MountainEnvironment,
14
15	document_uri:&Url,
16
17	feature_type:ProviderType,
18) -> Result<Option<ProviderRegistrationDTO>, CommonError> {
19	let providers = environment
20		.ApplicationState
21		.Extension
22		.ProviderRegistration
23		.LanguageProviders
24		.lock()
25		.map_err(MapApplicationStateLockErrorToCommonError)?;
26
27	let open_documents = environment
28		.ApplicationState
29		.Feature
30		.Documents
31		.OpenDocuments
32		.lock()
33		.map_err(MapApplicationStateLockErrorToCommonError)?;
34
35	// Derive language: prefer DocumentState record, fall back to URI extension.
36	let LanguageId:String = if let Some(Document) = open_documents.get(document_uri.as_str()) {
37		Document.LanguageIdentifier.clone()
38	} else {
39		// Document not yet opened via model:open - infer from file extension.
40		document_uri
41			.path()
42			.split('.')
43			.next_back()
44			.map(|Ext| {
45				match Ext {
46					"rs" => "rust",
47					"ts" | "tsx" => "typescript",
48					"js" | "jsx" | "mjs" | "cjs" => "javascript",
49					"json" | "jsonc" => "json",
50					"toml" => "toml",
51					"yaml" | "yml" => "yaml",
52					"md" => "markdown",
53					"py" => "python",
54					"go" => "go",
55					"c" | "h" => "c",
56					"cpp" | "cc" | "cxx" | "hpp" => "cpp",
57					Other => Other,
58				}
59			})
60			.unwrap_or("plaintext")
61			.to_string()
62	};
63
64	for Provider in providers.values() {
65		if Provider.ProviderType != feature_type {
66			continue;
67		}
68
69		// Selector shapes (all stored as JSON from CocoonService.RegisterProvider):
70		//   Canonical: [{ "language": "typescript" }]
71		//   Wildcard:  [{ "language": "*" }]
72		//   Legacy obj: { "language": ["typescript"] }
73		//   Plain str: "*"
74		let Matched = if let Some(SelectorArray) = Provider.Selector.as_array() {
75			SelectorArray.iter().any(|S| {
76				match S.get("language") {
77					Some(L) if L.as_str() == Some(&LanguageId) => true,
78					Some(L) if L.as_str() == Some("*") => true,
79					Some(L) => {
80						L.as_array()
81							.map(|Arr| {
82								Arr.iter()
83									.any(|Item| Item.as_str() == Some(&LanguageId) || Item.as_str() == Some("*"))
84							})
85							.unwrap_or(false)
86					},
87					None => false,
88				}
89			})
90		} else if let Some(LangValue) = Provider.Selector.get("language") {
91			LangValue.as_str() == Some(&LanguageId)
92				|| LangValue.as_str() == Some("*")
93				|| LangValue
94					.as_array()
95					.map(|Arr| {
96						Arr.iter()
97							.any(|Item| Item.as_str() == Some(&LanguageId) || Item.as_str() == Some("*"))
98					})
99					.unwrap_or(false)
100		} else if let Some(LangStr) = Provider.Selector.as_str() {
101			LangStr == &LanguageId || LangStr == "*"
102		} else {
103			false
104		};
105
106		if Matched {
107			return Ok(Some(Provider.clone()));
108		}
109	}
110
111	dev_log!(
112		"extensions",
113		"warn: [ProviderLookup] No {:?} provider for language '{}' (uri={})",
114		feature_type,
115		LanguageId,
116		document_uri
117	);
118
119	Ok(None)
120}