1use std::{path::PathBuf, str::FromStr, sync::Arc};
2
3use anyhow::{Context as _, Result, bail};
4
5use async_trait::async_trait;
6use collections::{BTreeMap, IndexSet};
7
8use gpui::{
9 App, AppContext as _, AsyncApp, Context, Entity, EventEmitter, Subscription, Task, WeakEntity,
10};
11use language::{
12 LanguageName, LanguageRegistry, LanguageToolchainStore, ManifestDelegate, Toolchain,
13 ToolchainList, ToolchainScope,
14};
15use rpc::{
16 AnyProtoClient, TypedEnvelope,
17 proto::{
18 self, ResolveToolchainResponse,
19 resolve_toolchain_response::Response as ResolveResponsePayload,
20 },
21};
22use settings::WorktreeId;
23use task::Shell;
24use util::{ResultExt as _, rel_path::RelPath};
25
26use crate::{
27 ProjectEnvironment, ProjectPath,
28 manifest_tree::{ManifestQueryDelegate, ManifestTree},
29 worktree_store::WorktreeStore,
30};
31
32pub struct ToolchainStore {
33 mode: ToolchainStoreInner,
34 user_toolchains: BTreeMap<ToolchainScope, IndexSet<Toolchain>>,
35 worktree_store: Entity<WorktreeStore>,
36 _sub: Subscription,
37}
38
39enum ToolchainStoreInner {
40 Local(Entity<LocalToolchainStore>),
41 Remote(Entity<RemoteToolchainStore>),
42}
43
44pub struct Toolchains {
45 /// Auto-detected toolchains.
46 pub toolchains: ToolchainList,
47 /// Path of the project root at which we ran the automatic toolchain detection.
48 pub root_path: Arc<RelPath>,
49 pub user_toolchains: BTreeMap<ToolchainScope, IndexSet<Toolchain>>,
50}
51impl EventEmitter<ToolchainStoreEvent> for ToolchainStore {}
52impl ToolchainStore {
53 pub fn init(client: &AnyProtoClient) {
54 client.add_entity_request_handler(Self::handle_activate_toolchain);
55 client.add_entity_request_handler(Self::handle_list_toolchains);
56 client.add_entity_request_handler(Self::handle_active_toolchain);
57 client.add_entity_request_handler(Self::handle_resolve_toolchain);
58 }
59
60 pub fn local(
61 languages: Arc<LanguageRegistry>,
62 worktree_store: Entity<WorktreeStore>,
63 project_environment: Entity<ProjectEnvironment>,
64 manifest_tree: Entity<ManifestTree>,
65 cx: &mut Context<Self>,
66 ) -> Self {
67 let entity = cx.new(|_| LocalToolchainStore {
68 languages,
69 worktree_store: worktree_store.clone(),
70 project_environment,
71 active_toolchains: Default::default(),
72 manifest_tree,
73 });
74 let _sub = cx.subscribe(&entity, |_, _, e: &ToolchainStoreEvent, cx| {
75 cx.emit(e.clone())
76 });
77 Self {
78 mode: ToolchainStoreInner::Local(entity),
79 worktree_store,
80 user_toolchains: Default::default(),
81 _sub,
82 }
83 }
84
85 pub(super) fn remote(
86 project_id: u64,
87 worktree_store: Entity<WorktreeStore>,
88 client: AnyProtoClient,
89 cx: &mut Context<Self>,
90 ) -> Self {
91 let entity = cx.new(|_| RemoteToolchainStore { client, project_id });
92 let _sub = cx.subscribe(&entity, |_, _, e: &ToolchainStoreEvent, cx| {
93 cx.emit(e.clone())
94 });
95 Self {
96 mode: ToolchainStoreInner::Remote(entity),
97 user_toolchains: Default::default(),
98 worktree_store,
99 _sub,
100 }
101 }
102 pub(crate) fn activate_toolchain(
103 &self,
104 path: ProjectPath,
105 toolchain: Toolchain,
106 cx: &mut App,
107 ) -> Task<Option<()>> {
108 match &self.mode {
109 ToolchainStoreInner::Local(local) => {
110 local.update(cx, |this, cx| this.activate_toolchain(path, toolchain, cx))
111 }
112 ToolchainStoreInner::Remote(remote) => {
113 remote.update(cx, |this, cx| this.activate_toolchain(path, toolchain, cx))
114 }
115 }
116 }
117
118 pub(crate) fn user_toolchains(&self) -> BTreeMap<ToolchainScope, IndexSet<Toolchain>> {
119 self.user_toolchains.clone()
120 }
121 pub(crate) fn add_toolchain(
122 &mut self,
123 toolchain: Toolchain,
124 scope: ToolchainScope,
125 cx: &mut Context<Self>,
126 ) {
127 let did_insert = self
128 .user_toolchains
129 .entry(scope)
130 .or_default()
131 .insert(toolchain);
132 if did_insert {
133 cx.emit(ToolchainStoreEvent::CustomToolchainsModified);
134 }
135 }
136
137 pub(crate) fn remove_toolchain(
138 &mut self,
139 toolchain: Toolchain,
140 scope: ToolchainScope,
141 cx: &mut Context<Self>,
142 ) {
143 let mut did_remove = false;
144 self.user_toolchains
145 .entry(scope)
146 .and_modify(|toolchains| did_remove = toolchains.shift_remove(&toolchain));
147 if did_remove {
148 cx.emit(ToolchainStoreEvent::CustomToolchainsModified);
149 }
150 }
151
152 pub(crate) fn resolve_toolchain(
153 &self,
154 abs_path: PathBuf,
155 language_name: LanguageName,
156 cx: &mut Context<Self>,
157 ) -> Task<Result<Toolchain>> {
158 debug_assert!(abs_path.is_absolute());
159 match &self.mode {
160 ToolchainStoreInner::Local(local) => local.update(cx, |this, cx| {
161 this.resolve_toolchain(abs_path, language_name, cx)
162 }),
163 ToolchainStoreInner::Remote(remote) => remote.update(cx, |this, cx| {
164 this.resolve_toolchain(abs_path, language_name, cx)
165 }),
166 }
167 }
168 pub(crate) fn list_toolchains(
169 &self,
170 path: ProjectPath,
171 language_name: LanguageName,
172 cx: &mut Context<Self>,
173 ) -> Task<Option<Toolchains>> {
174 let Some(worktree) = self
175 .worktree_store
176 .read(cx)
177 .worktree_for_id(path.worktree_id, cx)
178 else {
179 return Task::ready(None);
180 };
181 let target_root_path = worktree.read_with(cx, |this, _| this.abs_path());
182
183 let user_toolchains = self
184 .user_toolchains
185 .iter()
186 .filter(|(scope, _)| {
187 if let ToolchainScope::Subproject(subproject_root_path, relative_path) = scope {
188 target_root_path == *subproject_root_path
189 && relative_path.starts_with(&path.path)
190 } else {
191 true
192 }
193 })
194 .map(|(scope, toolchains)| {
195 (
196 scope.clone(),
197 toolchains
198 .iter()
199 .filter(|toolchain| toolchain.language_name == language_name)
200 .cloned()
201 .collect::<IndexSet<_>>(),
202 )
203 })
204 .collect::<BTreeMap<_, _>>();
205 let task = match &self.mode {
206 ToolchainStoreInner::Local(local) => {
207 local.update(cx, |this, cx| this.list_toolchains(path, language_name, cx))
208 }
209 ToolchainStoreInner::Remote(remote) => {
210 remote.read(cx).list_toolchains(path, language_name, cx)
211 }
212 };
213 cx.spawn(async move |_, _| {
214 let (mut toolchains, root_path) = task.await?;
215 toolchains.toolchains.retain(|toolchain| {
216 !user_toolchains
217 .values()
218 .any(|toolchains| toolchains.contains(toolchain))
219 });
220
221 Some(Toolchains {
222 toolchains,
223 root_path,
224 user_toolchains,
225 })
226 })
227 }
228
229 pub(crate) fn active_toolchain(
230 &self,
231 path: ProjectPath,
232 language_name: LanguageName,
233 cx: &App,
234 ) -> Task<Option<Toolchain>> {
235 match &self.mode {
236 ToolchainStoreInner::Local(local) => Task::ready(local.read(cx).active_toolchain(
237 path.worktree_id,
238 &path.path,
239 language_name,
240 )),
241 ToolchainStoreInner::Remote(remote) => {
242 remote.read(cx).active_toolchain(path, language_name, cx)
243 }
244 }
245 }
246 async fn handle_activate_toolchain(
247 this: Entity<Self>,
248 envelope: TypedEnvelope<proto::ActivateToolchain>,
249 mut cx: AsyncApp,
250 ) -> Result<proto::Ack> {
251 this.update(&mut cx, |this, cx| {
252 let language_name = LanguageName::from_proto(envelope.payload.language_name);
253 let Some(toolchain) = envelope.payload.toolchain else {
254 bail!("Missing `toolchain` in payload");
255 };
256 let toolchain = Toolchain {
257 name: toolchain.name.into(),
258 // todo(windows)
259 // Do we need to convert path to native string?
260 path: toolchain.path.into(),
261 as_json: serde_json::Value::from_str(&toolchain.raw_json)?,
262 language_name,
263 };
264 let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
265 let path = if let Some(path) = envelope.payload.path {
266 RelPath::from_proto(&path)?
267 } else {
268 RelPath::empty().into()
269 };
270 Ok(this.activate_toolchain(ProjectPath { worktree_id, path }, toolchain, cx))
271 })?
272 .await;
273 Ok(proto::Ack {})
274 }
275 async fn handle_active_toolchain(
276 this: Entity<Self>,
277 envelope: TypedEnvelope<proto::ActiveToolchain>,
278 mut cx: AsyncApp,
279 ) -> Result<proto::ActiveToolchainResponse> {
280 let path = RelPath::unix(envelope.payload.path.as_deref().unwrap_or(""))?;
281 let toolchain = this
282 .update(&mut cx, |this, cx| {
283 let language_name = LanguageName::from_proto(envelope.payload.language_name);
284 let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
285 this.active_toolchain(
286 ProjectPath {
287 worktree_id,
288 path: Arc::from(path),
289 },
290 language_name,
291 cx,
292 )
293 })
294 .await;
295
296 Ok(proto::ActiveToolchainResponse {
297 toolchain: toolchain.map(|toolchain| {
298 let path = PathBuf::from(toolchain.path.to_string());
299 proto::Toolchain {
300 name: toolchain.name.into(),
301 path: path.to_string_lossy().into_owned(),
302 raw_json: toolchain.as_json.to_string(),
303 }
304 }),
305 })
306 }
307
308 async fn handle_list_toolchains(
309 this: Entity<Self>,
310 envelope: TypedEnvelope<proto::ListToolchains>,
311 mut cx: AsyncApp,
312 ) -> Result<proto::ListToolchainsResponse> {
313 let toolchains = this
314 .update(&mut cx, |this, cx| {
315 let language_name = LanguageName::from_proto(envelope.payload.language_name);
316 let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
317 let path = RelPath::from_proto(envelope.payload.path.as_deref().unwrap_or(""))?;
318 anyhow::Ok(this.list_toolchains(
319 ProjectPath { worktree_id, path },
320 language_name,
321 cx,
322 ))
323 })?
324 .await;
325 let has_values = toolchains.is_some();
326 let groups = if let Some(Toolchains { toolchains, .. }) = &toolchains {
327 toolchains
328 .groups
329 .iter()
330 .filter_map(|group| {
331 Some(proto::ToolchainGroup {
332 start_index: u64::try_from(group.0).ok()?,
333 name: String::from(group.1.as_ref()),
334 })
335 })
336 .collect()
337 } else {
338 vec![]
339 };
340 let (toolchains, relative_path) = if let Some(Toolchains {
341 toolchains,
342 root_path: relative_path,
343 ..
344 }) = toolchains
345 {
346 let toolchains = toolchains
347 .toolchains
348 .into_iter()
349 .map(|toolchain| {
350 let path = PathBuf::from(toolchain.path.to_string());
351 proto::Toolchain {
352 name: toolchain.name.to_string(),
353 path: path.to_string_lossy().into_owned(),
354 raw_json: toolchain.as_json.to_string(),
355 }
356 })
357 .collect::<Vec<_>>();
358 (toolchains, relative_path)
359 } else {
360 (vec![], Arc::from(RelPath::empty()))
361 };
362
363 Ok(proto::ListToolchainsResponse {
364 has_values,
365 toolchains,
366 groups,
367 relative_worktree_path: Some(relative_path.to_proto()),
368 })
369 }
370
371 async fn handle_resolve_toolchain(
372 this: Entity<Self>,
373 envelope: TypedEnvelope<proto::ResolveToolchain>,
374 mut cx: AsyncApp,
375 ) -> Result<proto::ResolveToolchainResponse> {
376 let toolchain = this
377 .update(&mut cx, |this, cx| {
378 let language_name = LanguageName::from_proto(envelope.payload.language_name);
379 let path = PathBuf::from(envelope.payload.abs_path);
380 this.resolve_toolchain(path, language_name, cx)
381 })
382 .await;
383 let response = match toolchain {
384 Ok(toolchain) => {
385 let toolchain = proto::Toolchain {
386 name: toolchain.name.to_string(),
387 path: toolchain.path.to_string(),
388 raw_json: toolchain.as_json.to_string(),
389 };
390 ResolveResponsePayload::Toolchain(toolchain)
391 }
392 Err(e) => ResolveResponsePayload::Error(e.to_string()),
393 };
394 Ok(ResolveToolchainResponse {
395 response: Some(response),
396 })
397 }
398
399 pub fn as_language_toolchain_store(&self) -> Arc<dyn LanguageToolchainStore> {
400 match &self.mode {
401 ToolchainStoreInner::Local(local) => Arc::new(LocalStore(local.downgrade())),
402 ToolchainStoreInner::Remote(remote) => Arc::new(RemoteStore(remote.downgrade())),
403 }
404 }
405 pub fn as_local_store(&self) -> Option<&Entity<LocalToolchainStore>> {
406 match &self.mode {
407 ToolchainStoreInner::Local(local) => Some(local),
408 ToolchainStoreInner::Remote(_) => None,
409 }
410 }
411}
412
413pub struct LocalToolchainStore {
414 languages: Arc<LanguageRegistry>,
415 worktree_store: Entity<WorktreeStore>,
416 project_environment: Entity<ProjectEnvironment>,
417 active_toolchains: BTreeMap<(WorktreeId, LanguageName), BTreeMap<Arc<RelPath>, Toolchain>>,
418 manifest_tree: Entity<ManifestTree>,
419}
420
421#[async_trait(?Send)]
422impl language::LocalLanguageToolchainStore for LocalStore {
423 fn active_toolchain(
424 self: Arc<Self>,
425 worktree_id: WorktreeId,
426 path: &Arc<RelPath>,
427 language_name: LanguageName,
428 cx: &mut AsyncApp,
429 ) -> Option<Toolchain> {
430 self.0
431 .update(cx, |this, _| {
432 this.active_toolchain(worktree_id, path, language_name)
433 })
434 .ok()?
435 }
436}
437
438#[async_trait(?Send)]
439impl language::LanguageToolchainStore for RemoteStore {
440 async fn active_toolchain(
441 self: Arc<Self>,
442 worktree_id: WorktreeId,
443 path: Arc<RelPath>,
444 language_name: LanguageName,
445 cx: &mut AsyncApp,
446 ) -> Option<Toolchain> {
447 self.0
448 .update(cx, |this, cx| {
449 this.active_toolchain(ProjectPath { worktree_id, path }, language_name, cx)
450 })
451 .ok()?
452 .await
453 }
454}
455
456pub struct EmptyToolchainStore;
457impl language::LocalLanguageToolchainStore for EmptyToolchainStore {
458 fn active_toolchain(
459 self: Arc<Self>,
460 _: WorktreeId,
461 _: &Arc<RelPath>,
462 _: LanguageName,
463 _: &mut AsyncApp,
464 ) -> Option<Toolchain> {
465 None
466 }
467}
468pub(crate) struct LocalStore(WeakEntity<LocalToolchainStore>);
469struct RemoteStore(WeakEntity<RemoteToolchainStore>);
470
471#[derive(Clone)]
472pub enum ToolchainStoreEvent {
473 ToolchainActivated,
474 CustomToolchainsModified,
475}
476
477impl EventEmitter<ToolchainStoreEvent> for LocalToolchainStore {}
478
479impl LocalToolchainStore {
480 pub(crate) fn activate_toolchain(
481 &self,
482 path: ProjectPath,
483 toolchain: Toolchain,
484 cx: &mut Context<Self>,
485 ) -> Task<Option<()>> {
486 cx.spawn(async move |this, cx| {
487 this.update(cx, |this, cx| {
488 this.active_toolchains
489 .entry((path.worktree_id, toolchain.language_name.clone()))
490 .or_default()
491 .insert(path.path, toolchain.clone());
492 cx.emit(ToolchainStoreEvent::ToolchainActivated);
493 })
494 .ok();
495 Some(())
496 })
497 }
498 pub(crate) fn list_toolchains(
499 &mut self,
500 path: ProjectPath,
501 language_name: LanguageName,
502 cx: &mut Context<Self>,
503 ) -> Task<Option<(ToolchainList, Arc<RelPath>)>> {
504 let registry = self.languages.clone();
505
506 let manifest_tree = self.manifest_tree.downgrade();
507
508 let environment = self.project_environment.clone();
509 cx.spawn(async move |this, cx| {
510 let language = cx
511 .background_spawn(registry.language_for_name(language_name.as_ref()))
512 .await
513 .ok()?;
514 let toolchains = language.toolchain_lister()?;
515 let manifest_name = toolchains.meta().manifest_name;
516 let (snapshot, worktree) = this
517 .update(cx, |this, cx| {
518 this.worktree_store
519 .read(cx)
520 .worktree_for_id(path.worktree_id, cx)
521 .map(|worktree| (worktree.read(cx).snapshot(), worktree))
522 })
523 .ok()
524 .flatten()?;
525 let worktree_id = snapshot.id();
526 let worktree_root = snapshot.abs_path().to_path_buf();
527 let delegate =
528 Arc::from(ManifestQueryDelegate::new(snapshot)) as Arc<dyn ManifestDelegate>;
529 let relative_path = manifest_tree
530 .update(cx, |this, cx| {
531 this.root_for_path(&path, &manifest_name, &delegate, cx)
532 })
533 .ok()?
534 .unwrap_or_else(|| ProjectPath {
535 path: Arc::from(RelPath::empty()),
536 worktree_id,
537 });
538 let abs_path = worktree.update(cx, |this, _| this.absolutize(&relative_path.path));
539
540 let project_env = environment
541 .update(cx, |environment, cx| {
542 environment.local_directory_environment(
543 &Shell::System,
544 abs_path.as_path().into(),
545 cx,
546 )
547 })
548 .await;
549
550 cx.background_spawn(async move {
551 Some((
552 toolchains
553 .list(worktree_root, relative_path.path.clone(), project_env)
554 .await,
555 relative_path.path,
556 ))
557 })
558 .await
559 })
560 }
561 pub(crate) fn active_toolchain(
562 &self,
563 worktree_id: WorktreeId,
564 relative_path: &Arc<RelPath>,
565 language_name: LanguageName,
566 ) -> Option<Toolchain> {
567 let ancestors = relative_path.ancestors();
568
569 self.active_toolchains
570 .get(&(worktree_id, language_name))
571 .and_then(|paths| {
572 ancestors
573 .into_iter()
574 .find_map(|root_path| paths.get(root_path))
575 })
576 .cloned()
577 }
578
579 fn resolve_toolchain(
580 &self,
581 path: PathBuf,
582 language_name: LanguageName,
583 cx: &mut Context<Self>,
584 ) -> Task<Result<Toolchain>> {
585 let registry = self.languages.clone();
586 let environment = self.project_environment.clone();
587 cx.spawn(async move |_, cx| {
588 let language = cx
589 .background_spawn(registry.language_for_name(&language_name.0))
590 .await
591 .with_context(|| format!("Language {} not found", language_name.0))?;
592 let toolchain_lister = language.toolchain_lister().with_context(|| {
593 format!("Language {} does not support toolchains", language_name.0)
594 })?;
595
596 let project_env = environment
597 .update(cx, |environment, cx| {
598 environment.local_directory_environment(
599 &Shell::System,
600 path.as_path().into(),
601 cx,
602 )
603 })
604 .await;
605 cx.background_spawn(async move { toolchain_lister.resolve(path, project_env).await })
606 .await
607 })
608 }
609}
610
611impl EventEmitter<ToolchainStoreEvent> for RemoteToolchainStore {}
612struct RemoteToolchainStore {
613 client: AnyProtoClient,
614 project_id: u64,
615}
616
617impl RemoteToolchainStore {
618 pub(crate) fn activate_toolchain(
619 &self,
620 project_path: ProjectPath,
621 toolchain: Toolchain,
622 cx: &mut Context<Self>,
623 ) -> Task<Option<()>> {
624 let project_id = self.project_id;
625 let client = self.client.clone();
626 cx.spawn(async move |this, cx| {
627 let did_activate = cx
628 .background_spawn(async move {
629 let path = PathBuf::from(toolchain.path.to_string());
630 let _ = client
631 .request(proto::ActivateToolchain {
632 project_id,
633 worktree_id: project_path.worktree_id.to_proto(),
634 language_name: toolchain.language_name.into(),
635 toolchain: Some(proto::Toolchain {
636 name: toolchain.name.into(),
637 path: path.to_string_lossy().into_owned(),
638 raw_json: toolchain.as_json.to_string(),
639 }),
640 path: Some(project_path.path.to_proto()),
641 })
642 .await
643 .log_err()?;
644 Some(())
645 })
646 .await;
647 did_activate.and_then(|_| {
648 this.update(cx, |_, cx| {
649 cx.emit(ToolchainStoreEvent::ToolchainActivated);
650 })
651 .ok()
652 })
653 })
654 }
655
656 pub(crate) fn list_toolchains(
657 &self,
658 path: ProjectPath,
659 language_name: LanguageName,
660 cx: &App,
661 ) -> Task<Option<(ToolchainList, Arc<RelPath>)>> {
662 let project_id = self.project_id;
663 let client = self.client.clone();
664 cx.background_spawn(async move {
665 let response = client
666 .request(proto::ListToolchains {
667 project_id,
668 worktree_id: path.worktree_id.to_proto(),
669 language_name: language_name.clone().into(),
670 path: Some(path.path.to_proto()),
671 })
672 .await
673 .log_err()?;
674 if !response.has_values {
675 return None;
676 }
677 let toolchains = response
678 .toolchains
679 .into_iter()
680 .filter_map(|toolchain| {
681 Some(Toolchain {
682 language_name: language_name.clone(),
683 name: toolchain.name.into(),
684 path: toolchain.path.into(),
685 as_json: serde_json::Value::from_str(&toolchain.raw_json).ok()?,
686 })
687 })
688 .collect();
689 let groups = response
690 .groups
691 .into_iter()
692 .filter_map(|group| {
693 Some((usize::try_from(group.start_index).ok()?, group.name.into()))
694 })
695 .collect();
696 let relative_path = RelPath::from_proto(
697 response
698 .relative_worktree_path
699 .as_deref()
700 .unwrap_or_default(),
701 )
702 .log_err()?;
703 Some((
704 ToolchainList {
705 toolchains,
706 default: None,
707 groups,
708 },
709 relative_path,
710 ))
711 })
712 }
713 pub(crate) fn active_toolchain(
714 &self,
715 path: ProjectPath,
716 language_name: LanguageName,
717 cx: &App,
718 ) -> Task<Option<Toolchain>> {
719 let project_id = self.project_id;
720 let client = self.client.clone();
721 cx.background_spawn(async move {
722 let response = client
723 .request(proto::ActiveToolchain {
724 project_id,
725 worktree_id: path.worktree_id.to_proto(),
726 language_name: language_name.clone().into(),
727 path: Some(path.path.to_proto()),
728 })
729 .await
730 .log_err()?;
731
732 response.toolchain.and_then(|toolchain| {
733 Some(Toolchain {
734 language_name: language_name.clone(),
735 name: toolchain.name.into(),
736 path: toolchain.path.into(),
737 as_json: serde_json::Value::from_str(&toolchain.raw_json).ok()?,
738 })
739 })
740 })
741 }
742
743 fn resolve_toolchain(
744 &self,
745 abs_path: PathBuf,
746 language_name: LanguageName,
747 cx: &mut Context<Self>,
748 ) -> Task<Result<Toolchain>> {
749 let project_id = self.project_id;
750 let client = self.client.clone();
751 cx.background_spawn(async move {
752 let response: proto::ResolveToolchainResponse = client
753 .request(proto::ResolveToolchain {
754 project_id,
755 language_name: language_name.clone().into(),
756 abs_path: abs_path.to_string_lossy().into_owned(),
757 })
758 .await?;
759
760 let response = response
761 .response
762 .context("Failed to resolve toolchain via RPC")?;
763 use proto::resolve_toolchain_response::Response;
764 match response {
765 Response::Toolchain(toolchain) => Ok(Toolchain {
766 language_name: language_name.clone(),
767 name: toolchain.name.into(),
768 path: toolchain.path.into(),
769 as_json: serde_json::Value::from_str(&toolchain.raw_json)
770 .context("Deserializing ResolveToolchain LSP response")?,
771 }),
772 Response::Error(error) => {
773 anyhow::bail!("{error}");
774 }
775 }
776 })
777 }
778}