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