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