Skip to repository content780 lines · 27.2 KB · rust
tenant.openagents/omega
No repository description is available.
OpenAgents Git authority 2026-07-28T01:47:43.024Z Public web read
NIP-34 coordinate
30617:7649603503856e5148d571eac2766b288a8ff1e9e35d380337a1d2b0015b4f92:omegaMaintainersHidden in public view
References2 branches · 1 tag
Read-only clone
git clone https://openagents.com/git/tenant.openagents/omega.gitBrowse files
toolchain_store.rs
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_unix_str(&path)?.into()
267 } else {
268 RelPath::empty_arc()
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::from_unix_str(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 =
318 RelPath::from_unix_str(envelope.payload.path.as_deref().unwrap_or(""))?.into();
319 anyhow::Ok(this.list_toolchains(
320 ProjectPath { worktree_id, path },
321 language_name,
322 cx,
323 ))
324 })?
325 .await;
326 let has_values = toolchains.is_some();
327 let groups = if let Some(Toolchains { toolchains, .. }) = &toolchains {
328 toolchains
329 .groups
330 .iter()
331 .filter_map(|group| {
332 Some(proto::ToolchainGroup {
333 start_index: u64::try_from(group.0).ok()?,
334 name: String::from(group.1.as_ref()),
335 })
336 })
337 .collect()
338 } else {
339 vec![]
340 };
341 let (toolchains, relative_path) = if let Some(Toolchains {
342 toolchains,
343 root_path: relative_path,
344 ..
345 }) = toolchains
346 {
347 let toolchains = toolchains
348 .toolchains
349 .into_iter()
350 .map(|toolchain| {
351 let path = PathBuf::from(toolchain.path.to_string());
352 proto::Toolchain {
353 name: toolchain.name.to_string(),
354 path: path.to_string_lossy().into_owned(),
355 raw_json: toolchain.as_json.to_string(),
356 }
357 })
358 .collect::<Vec<_>>();
359 (toolchains, relative_path)
360 } else {
361 (vec![], Arc::from(RelPath::empty()))
362 };
363
364 Ok(proto::ListToolchainsResponse {
365 has_values,
366 toolchains,
367 groups,
368 relative_worktree_path: Some(relative_path.as_unix_str().to_owned()),
369 })
370 }
371
372 async fn handle_resolve_toolchain(
373 this: Entity<Self>,
374 envelope: TypedEnvelope<proto::ResolveToolchain>,
375 mut cx: AsyncApp,
376 ) -> Result<proto::ResolveToolchainResponse> {
377 let toolchain = this
378 .update(&mut cx, |this, cx| {
379 let language_name = LanguageName::from_proto(envelope.payload.language_name);
380 let path = PathBuf::from(envelope.payload.abs_path);
381 this.resolve_toolchain(path, language_name, cx)
382 })
383 .await;
384 let response = match toolchain {
385 Ok(toolchain) => {
386 let toolchain = proto::Toolchain {
387 name: toolchain.name.to_string(),
388 path: toolchain.path.to_string(),
389 raw_json: toolchain.as_json.to_string(),
390 };
391 ResolveResponsePayload::Toolchain(toolchain)
392 }
393 Err(e) => ResolveResponsePayload::Error(e.to_string()),
394 };
395 Ok(ResolveToolchainResponse {
396 response: Some(response),
397 })
398 }
399
400 pub fn as_language_toolchain_store(&self) -> Arc<dyn LanguageToolchainStore> {
401 match &self.mode {
402 ToolchainStoreInner::Local(local) => Arc::new(LocalStore(local.downgrade())),
403 ToolchainStoreInner::Remote(remote) => Arc::new(RemoteStore(remote.downgrade())),
404 }
405 }
406 pub fn as_local_store(&self) -> Option<&Entity<LocalToolchainStore>> {
407 match &self.mode {
408 ToolchainStoreInner::Local(local) => Some(local),
409 ToolchainStoreInner::Remote(_) => None,
410 }
411 }
412}
413
414pub struct LocalToolchainStore {
415 languages: Arc<LanguageRegistry>,
416 worktree_store: Entity<WorktreeStore>,
417 project_environment: Entity<ProjectEnvironment>,
418 active_toolchains: BTreeMap<(WorktreeId, LanguageName), BTreeMap<Arc<RelPath>, Toolchain>>,
419 manifest_tree: Entity<ManifestTree>,
420}
421
422#[async_trait(?Send)]
423impl language::LocalLanguageToolchainStore for LocalStore {
424 fn active_toolchain(
425 self: Arc<Self>,
426 worktree_id: WorktreeId,
427 path: &Arc<RelPath>,
428 language_name: LanguageName,
429 cx: &mut AsyncApp,
430 ) -> Option<Toolchain> {
431 self.0
432 .update(cx, |this, _| {
433 this.active_toolchain(worktree_id, path, language_name)
434 })
435 .ok()?
436 }
437}
438
439#[async_trait(?Send)]
440impl language::LanguageToolchainStore for RemoteStore {
441 async fn active_toolchain(
442 self: Arc<Self>,
443 worktree_id: WorktreeId,
444 path: Arc<RelPath>,
445 language_name: LanguageName,
446 cx: &mut AsyncApp,
447 ) -> Option<Toolchain> {
448 self.0
449 .update(cx, |this, cx| {
450 this.active_toolchain(ProjectPath { worktree_id, path }, language_name, cx)
451 })
452 .ok()?
453 .await
454 }
455}
456
457pub struct EmptyToolchainStore;
458impl language::LocalLanguageToolchainStore for EmptyToolchainStore {
459 fn active_toolchain(
460 self: Arc<Self>,
461 _: WorktreeId,
462 _: &Arc<RelPath>,
463 _: LanguageName,
464 _: &mut AsyncApp,
465 ) -> Option<Toolchain> {
466 None
467 }
468}
469pub(crate) struct LocalStore(WeakEntity<LocalToolchainStore>);
470struct RemoteStore(WeakEntity<RemoteToolchainStore>);
471
472#[derive(Clone)]
473pub enum ToolchainStoreEvent {
474 ToolchainActivated,
475 CustomToolchainsModified,
476}
477
478impl EventEmitter<ToolchainStoreEvent> for LocalToolchainStore {}
479
480impl LocalToolchainStore {
481 pub(crate) fn activate_toolchain(
482 &self,
483 path: ProjectPath,
484 toolchain: Toolchain,
485 cx: &mut Context<Self>,
486 ) -> Task<Option<()>> {
487 cx.spawn(async move |this, cx| {
488 this.update(cx, |this, cx| {
489 this.active_toolchains
490 .entry((path.worktree_id, toolchain.language_name.clone()))
491 .or_default()
492 .insert(path.path, toolchain.clone());
493 cx.emit(ToolchainStoreEvent::ToolchainActivated);
494 })
495 .ok();
496 Some(())
497 })
498 }
499 pub(crate) fn list_toolchains(
500 &mut self,
501 path: ProjectPath,
502 language_name: LanguageName,
503 cx: &mut Context<Self>,
504 ) -> Task<Option<(ToolchainList, Arc<RelPath>)>> {
505 let registry = self.languages.clone();
506
507 let manifest_tree = self.manifest_tree.downgrade();
508
509 let environment = self.project_environment.clone();
510 cx.spawn(async move |this, cx| {
511 let language = cx
512 .background_spawn(registry.language_for_name(language_name.as_ref()))
513 .await
514 .ok()?;
515 let toolchains = language.toolchain_lister()?;
516 let manifest_name = toolchains.meta().manifest_name;
517 let (snapshot, worktree) = this
518 .update(cx, |this, cx| {
519 this.worktree_store
520 .read(cx)
521 .worktree_for_id(path.worktree_id, cx)
522 .map(|worktree| (worktree.read(cx).snapshot(), worktree))
523 })
524 .ok()
525 .flatten()?;
526 let worktree_id = snapshot.id();
527 let worktree_root = snapshot.abs_path().to_path_buf();
528 let delegate =
529 Arc::from(ManifestQueryDelegate::new(snapshot)) as Arc<dyn ManifestDelegate>;
530 let relative_path = manifest_tree
531 .update(cx, |this, cx| {
532 this.root_for_path(&path, &manifest_name, &delegate, cx)
533 })
534 .ok()?
535 .unwrap_or_else(|| ProjectPath {
536 path: Arc::from(RelPath::empty()),
537 worktree_id,
538 });
539 let abs_path = worktree.update(cx, |this, _| this.absolutize(&relative_path.path));
540
541 let project_env = environment
542 .update(cx, |environment, cx| {
543 environment.local_directory_environment(
544 &Shell::System,
545 abs_path.as_path().into(),
546 cx,
547 )
548 })
549 .await;
550
551 cx.background_spawn(async move {
552 Some((
553 toolchains
554 .list(worktree_root, relative_path.path.clone(), project_env)
555 .await,
556 relative_path.path,
557 ))
558 })
559 .await
560 })
561 }
562 pub(crate) fn active_toolchain(
563 &self,
564 worktree_id: WorktreeId,
565 relative_path: &Arc<RelPath>,
566 language_name: LanguageName,
567 ) -> Option<Toolchain> {
568 let ancestors = relative_path.ancestors();
569
570 self.active_toolchains
571 .get(&(worktree_id, language_name))
572 .and_then(|paths| {
573 ancestors
574 .into_iter()
575 .find_map(|root_path| paths.get(root_path))
576 })
577 .cloned()
578 }
579
580 fn resolve_toolchain(
581 &self,
582 path: PathBuf,
583 language_name: LanguageName,
584 cx: &mut Context<Self>,
585 ) -> Task<Result<Toolchain>> {
586 let registry = self.languages.clone();
587 let environment = self.project_environment.clone();
588 cx.spawn(async move |_, cx| {
589 let language = cx
590 .background_spawn(registry.language_for_name(&language_name.0))
591 .await
592 .with_context(|| format!("Language {} not found", language_name.0))?;
593 let toolchain_lister = language.toolchain_lister().with_context(|| {
594 format!("Language {} does not support toolchains", language_name.0)
595 })?;
596
597 let project_env = environment
598 .update(cx, |environment, cx| {
599 environment.local_directory_environment(
600 &Shell::System,
601 path.as_path().into(),
602 cx,
603 )
604 })
605 .await;
606 cx.background_spawn(async move { toolchain_lister.resolve(path, project_env).await })
607 .await
608 })
609 }
610}
611
612impl EventEmitter<ToolchainStoreEvent> for RemoteToolchainStore {}
613struct RemoteToolchainStore {
614 client: AnyProtoClient,
615 project_id: u64,
616}
617
618impl RemoteToolchainStore {
619 pub(crate) fn activate_toolchain(
620 &self,
621 project_path: ProjectPath,
622 toolchain: Toolchain,
623 cx: &mut Context<Self>,
624 ) -> Task<Option<()>> {
625 let project_id = self.project_id;
626 let client = self.client.clone();
627 cx.spawn(async move |this, cx| {
628 let did_activate = cx
629 .background_spawn(async move {
630 let path = PathBuf::from(toolchain.path.to_string());
631 let _ = client
632 .request(proto::ActivateToolchain {
633 project_id,
634 worktree_id: project_path.worktree_id.to_proto(),
635 language_name: toolchain.language_name.into(),
636 toolchain: Some(proto::Toolchain {
637 name: toolchain.name.into(),
638 path: path.to_string_lossy().into_owned(),
639 raw_json: toolchain.as_json.to_string(),
640 }),
641 path: Some(project_path.path.as_unix_str().to_owned()),
642 })
643 .await
644 .log_err()?;
645 Some(())
646 })
647 .await;
648 did_activate.and_then(|_| {
649 this.update(cx, |_, cx| {
650 cx.emit(ToolchainStoreEvent::ToolchainActivated);
651 })
652 .ok()
653 })
654 })
655 }
656
657 pub(crate) fn list_toolchains(
658 &self,
659 path: ProjectPath,
660 language_name: LanguageName,
661 cx: &App,
662 ) -> Task<Option<(ToolchainList, Arc<RelPath>)>> {
663 let project_id = self.project_id;
664 let client = self.client.clone();
665 cx.background_spawn(async move {
666 let response = client
667 .request(proto::ListToolchains {
668 project_id,
669 worktree_id: path.worktree_id.to_proto(),
670 language_name: language_name.clone().into(),
671 path: Some(path.path.as_unix_str().to_owned()),
672 })
673 .await
674 .log_err()?;
675 if !response.has_values {
676 return None;
677 }
678 let toolchains = response
679 .toolchains
680 .into_iter()
681 .filter_map(|toolchain| {
682 Some(Toolchain {
683 language_name: language_name.clone(),
684 name: toolchain.name.into(),
685 path: toolchain.path.into(),
686 as_json: serde_json::Value::from_str(&toolchain.raw_json).ok()?,
687 })
688 })
689 .collect();
690 let groups = response
691 .groups
692 .into_iter()
693 .filter_map(|group| {
694 Some((usize::try_from(group.start_index).ok()?, group.name.into()))
695 })
696 .collect();
697 let relative_path = RelPath::from_unix_str(
698 response
699 .relative_worktree_path
700 .as_deref()
701 .unwrap_or_default(),
702 )
703 .log_err()?;
704 Some((
705 ToolchainList {
706 toolchains,
707 default: None,
708 groups,
709 },
710 relative_path.into(),
711 ))
712 })
713 }
714 pub(crate) fn active_toolchain(
715 &self,
716 path: ProjectPath,
717 language_name: LanguageName,
718 cx: &App,
719 ) -> Task<Option<Toolchain>> {
720 let project_id = self.project_id;
721 let client = self.client.clone();
722 cx.background_spawn(async move {
723 let response = client
724 .request(proto::ActiveToolchain {
725 project_id,
726 worktree_id: path.worktree_id.to_proto(),
727 language_name: language_name.clone().into(),
728 path: Some(path.path.as_unix_str().to_owned()),
729 })
730 .await
731 .log_err()?;
732
733 response.toolchain.and_then(|toolchain| {
734 Some(Toolchain {
735 language_name: language_name.clone(),
736 name: toolchain.name.into(),
737 path: toolchain.path.into(),
738 as_json: serde_json::Value::from_str(&toolchain.raw_json).ok()?,
739 })
740 })
741 })
742 }
743
744 fn resolve_toolchain(
745 &self,
746 abs_path: PathBuf,
747 language_name: LanguageName,
748 cx: &mut Context<Self>,
749 ) -> Task<Result<Toolchain>> {
750 let project_id = self.project_id;
751 let client = self.client.clone();
752 cx.background_spawn(async move {
753 let response: proto::ResolveToolchainResponse = client
754 .request(proto::ResolveToolchain {
755 project_id,
756 language_name: language_name.clone().into(),
757 abs_path: abs_path.to_string_lossy().into_owned(),
758 })
759 .await?;
760
761 let response = response
762 .response
763 .context("Failed to resolve toolchain via RPC")?;
764 use proto::resolve_toolchain_response::Response;
765 match response {
766 Response::Toolchain(toolchain) => Ok(Toolchain {
767 language_name: language_name.clone(),
768 name: toolchain.name.into(),
769 path: toolchain.path.into(),
770 as_json: serde_json::Value::from_str(&toolchain.raw_json)
771 .context("Deserializing ResolveToolchain LSP response")?,
772 }),
773 Response::Error(error) => {
774 anyhow::bail!("{error}");
775 }
776 }
777 })
778 }
779}
780