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