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