1use anyhow::{anyhow, Context, Result};
2use async_compression::futures::bufread::GzipDecoder;
3use async_tar::Archive;
4use async_trait::async_trait;
5use futures::{io::BufReader, StreamExt};
6use language::{LanguageServerName, LspAdapterDelegate};
7use lsp::LanguageServerBinary;
8use smol::fs;
9use std::env::consts::ARCH;
10use std::ffi::OsString;
11use std::{any::Any, path::PathBuf};
12use util::async_maybe;
13use util::github::latest_github_release;
14use util::{github::GitHubLspBinaryVersion, ResultExt};
15
16pub struct OmniSharpAdapter;
17
18#[async_trait]
19impl super::LspAdapter for OmniSharpAdapter {
20 fn name(&self) -> LanguageServerName {
21 LanguageServerName("OmniSharp".into())
22 }
23
24 async fn fetch_latest_server_version(
25 &self,
26 delegate: &dyn LspAdapterDelegate,
27 ) -> Result<Box<dyn 'static + Send + Any>> {
28 let mapped_arch = match ARCH {
29 "aarch64" => Some("arm64"),
30 "x86_64" => Some("x64"),
31 _ => None,
32 };
33
34 match mapped_arch {
35 None => Ok(Box::new(())),
36 Some(arch) => {
37 let release = latest_github_release(
38 "OmniSharp/omnisharp-roslyn",
39 true,
40 false,
41 delegate.http_client(),
42 )
43 .await?;
44 let asset_name = format!("omnisharp-osx-{}-net6.0.tar.gz", arch);
45 let asset = release
46 .assets
47 .iter()
48 .find(|asset| asset.name == asset_name)
49 .ok_or_else(|| anyhow!("no asset found matching {:?}", asset_name))?;
50 let version = GitHubLspBinaryVersion {
51 name: release.tag_name,
52 url: asset.browser_download_url.clone(),
53 };
54
55 Ok(Box::new(version) as Box<_>)
56 }
57 }
58 }
59
60 async fn fetch_server_binary(
61 &self,
62 version: Box<dyn 'static + Send + Any>,
63 container_dir: PathBuf,
64 delegate: &dyn LspAdapterDelegate,
65 ) -> Result<LanguageServerBinary> {
66 let version = version.downcast::<GitHubLspBinaryVersion>().unwrap();
67 let binary_path = container_dir.join("omnisharp");
68
69 if fs::metadata(&binary_path).await.is_err() {
70 let mut response = delegate
71 .http_client()
72 .get(&version.url, Default::default(), true)
73 .await
74 .context("error downloading release")?;
75 let decompressed_bytes = GzipDecoder::new(BufReader::new(response.body_mut()));
76 let archive = Archive::new(decompressed_bytes);
77 archive.unpack(container_dir).await?;
78 }
79
80 // todo("windows")
81 #[cfg(not(windows))]
82 {
83 fs::set_permissions(
84 &binary_path,
85 <fs::Permissions as fs::unix::PermissionsExt>::from_mode(0o755),
86 )
87 .await?;
88 }
89 Ok(LanguageServerBinary {
90 path: binary_path,
91 env: None,
92 arguments: server_binary_arguments(),
93 })
94 }
95
96 async fn cached_server_binary(
97 &self,
98 container_dir: PathBuf,
99 _: &dyn LspAdapterDelegate,
100 ) -> Option<LanguageServerBinary> {
101 get_cached_server_binary(container_dir).await
102 }
103
104 async fn installation_test_binary(
105 &self,
106 container_dir: PathBuf,
107 ) -> Option<LanguageServerBinary> {
108 get_cached_server_binary(container_dir)
109 .await
110 .map(|mut binary| {
111 binary.arguments = vec!["--help".into()];
112 binary
113 })
114 }
115}
116
117async fn get_cached_server_binary(container_dir: PathBuf) -> Option<LanguageServerBinary> {
118 async_maybe!({
119 let mut last_binary_path = None;
120 let mut entries = fs::read_dir(&container_dir).await?;
121 while let Some(entry) = entries.next().await {
122 let entry = entry?;
123 if entry.file_type().await?.is_file()
124 && entry
125 .file_name()
126 .to_str()
127 .map_or(false, |name| name == "omnisharp")
128 {
129 last_binary_path = Some(entry.path());
130 }
131 }
132
133 if let Some(path) = last_binary_path {
134 Ok(LanguageServerBinary {
135 path,
136 env: None,
137 arguments: server_binary_arguments(),
138 })
139 } else {
140 Err(anyhow!("no cached binary"))
141 }
142 })
143 .await
144 .log_err()
145}
146
147fn server_binary_arguments() -> Vec<OsString> {
148 vec!["-lsp".into()]
149}