toml.rs

  1use anyhow::{bail, Context, Result};
  2use async_compression::futures::bufread::GzipDecoder;
  3use async_trait::async_trait;
  4use futures::{io::BufReader, StreamExt};
  5use language::{LanguageServerName, LspAdapter, LspAdapterDelegate};
  6use lsp::LanguageServerBinary;
  7use smol::fs::{self, File};
  8use std::{any::Any, path::PathBuf};
  9use util::async_maybe;
 10use util::github::latest_github_release;
 11use util::{github::GitHubLspBinaryVersion, ResultExt};
 12
 13pub struct TaploLspAdapter;
 14
 15#[async_trait]
 16impl LspAdapter for TaploLspAdapter {
 17    fn name(&self) -> LanguageServerName {
 18        LanguageServerName("taplo-ls".into())
 19    }
 20
 21    fn short_name(&self) -> &'static str {
 22        "taplo-ls"
 23    }
 24
 25    async fn fetch_latest_server_version(
 26        &self,
 27        delegate: &dyn LspAdapterDelegate,
 28    ) -> Result<Box<dyn 'static + Send + Any>> {
 29        let release =
 30            latest_github_release("tamasfe/taplo", true, false, delegate.http_client()).await?;
 31        let asset_name = format!(
 32            "taplo-full-{os}-{arch}.gz",
 33            os = match std::env::consts::OS {
 34                "macos" => "darwin",
 35                "linux" => "linux",
 36                "windows" => "windows",
 37                other => bail!("Running on unsupported os: {other}"),
 38            },
 39            arch = std::env::consts::ARCH
 40        );
 41
 42        let asset = release
 43            .assets
 44            .iter()
 45            .find(|asset| asset.name == asset_name)
 46            .context(format!("no asset found matching {asset_name:?}"))?;
 47
 48        Ok(Box::new(GitHubLspBinaryVersion {
 49            name: release.tag_name,
 50            url: asset.browser_download_url.clone(),
 51        }))
 52    }
 53
 54    async fn fetch_server_binary(
 55        &self,
 56        version: Box<dyn 'static + Send + Any>,
 57        container_dir: PathBuf,
 58        delegate: &dyn LspAdapterDelegate,
 59    ) -> Result<LanguageServerBinary> {
 60        let version = version.downcast::<GitHubLspBinaryVersion>().unwrap();
 61        let binary_path = container_dir.join("taplo");
 62
 63        if fs::metadata(&binary_path).await.is_err() {
 64            let mut response = delegate
 65                .http_client()
 66                .get(&version.url, Default::default(), true)
 67                .await
 68                .context("error downloading release")?;
 69
 70            let decompressed_bytes = GzipDecoder::new(BufReader::new(response.body_mut()));
 71            let mut file = File::create(&binary_path).await?;
 72
 73            futures::io::copy(decompressed_bytes, &mut file).await?;
 74
 75            // todo("windows")
 76            #[cfg(not(windows))]
 77            {
 78                fs::set_permissions(
 79                    &binary_path,
 80                    <fs::Permissions as fs::unix::PermissionsExt>::from_mode(0o755),
 81                )
 82                .await?;
 83            }
 84        }
 85
 86        Ok(LanguageServerBinary {
 87            path: binary_path,
 88            env: None,
 89            arguments: vec!["lsp".into(), "stdio".into()],
 90        })
 91    }
 92
 93    async fn cached_server_binary(
 94        &self,
 95        container_dir: PathBuf,
 96        _: &dyn LspAdapterDelegate,
 97    ) -> Option<LanguageServerBinary> {
 98        get_cached_server_binary(container_dir).await
 99    }
100
101    async fn installation_test_binary(
102        &self,
103        container_dir: PathBuf,
104    ) -> Option<LanguageServerBinary> {
105        get_cached_server_binary(container_dir)
106            .await
107            .map(|mut binary| {
108                binary.arguments = vec!["--help".into()];
109                binary
110            })
111    }
112}
113
114async fn get_cached_server_binary(container_dir: PathBuf) -> Option<LanguageServerBinary> {
115    async_maybe!({
116        let mut last = None;
117        let mut entries = fs::read_dir(&container_dir).await?;
118        while let Some(entry) = entries.next().await {
119            last = Some(entry?.path());
120        }
121
122        anyhow::Ok(LanguageServerBinary {
123            path: last.context("no cached binary")?,
124            env: None,
125            arguments: Default::default(),
126        })
127    })
128    .await
129    .log_err()
130}