gleam.rs

  1use std::any::Any;
  2use std::env::consts;
  3use std::ffi::OsString;
  4use std::path::PathBuf;
  5
  6use anyhow::{anyhow, bail, Result};
  7use async_compression::futures::bufread::GzipDecoder;
  8use async_tar::Archive;
  9use async_trait::async_trait;
 10use futures::io::BufReader;
 11use futures::StreamExt;
 12use language::{LanguageServerName, LspAdapter, LspAdapterDelegate};
 13use lsp::LanguageServerBinary;
 14use smol::fs;
 15use util::github::{latest_github_release, GitHubLspBinaryVersion};
 16use util::{async_maybe, ResultExt};
 17
 18fn server_binary_arguments() -> Vec<OsString> {
 19    vec!["lsp".into()]
 20}
 21
 22pub struct GleamLspAdapter;
 23
 24#[async_trait]
 25impl LspAdapter for GleamLspAdapter {
 26    fn name(&self) -> LanguageServerName {
 27        LanguageServerName("gleam".into())
 28    }
 29
 30    fn short_name(&self) -> &'static str {
 31        "gleam"
 32    }
 33
 34    async fn fetch_latest_server_version(
 35        &self,
 36        delegate: &dyn LspAdapterDelegate,
 37    ) -> Result<Box<dyn 'static + Send + Any>> {
 38        let release =
 39            latest_github_release("gleam-lang/gleam", true, false, delegate.http_client()).await?;
 40        let asset_name = format!(
 41            "gleam-{version}-{arch}-{os}.tar.gz",
 42            version = release.tag_name,
 43            arch = std::env::consts::ARCH,
 44            os = match consts::OS {
 45                "macos" => "apple-darwin",
 46                "linux" => "unknown-linux-musl",
 47                "windows" => "pc-windows-msvc",
 48                other => bail!("Running on unsupported os: {other}"),
 49            },
 50        );
 51        let asset = release
 52            .assets
 53            .iter()
 54            .find(|asset| asset.name == asset_name)
 55            .ok_or_else(|| anyhow!("no asset found matching {:?}", asset_name))?;
 56        Ok(Box::new(GitHubLspBinaryVersion {
 57            name: release.tag_name,
 58            url: asset.browser_download_url.clone(),
 59        }))
 60    }
 61
 62    async fn fetch_server_binary(
 63        &self,
 64        version: Box<dyn 'static + Send + Any>,
 65        container_dir: PathBuf,
 66        delegate: &dyn LspAdapterDelegate,
 67    ) -> Result<LanguageServerBinary> {
 68        let version = version.downcast::<GitHubLspBinaryVersion>().unwrap();
 69        let binary_path = container_dir.join("gleam");
 70
 71        if fs::metadata(&binary_path).await.is_err() {
 72            let mut response = delegate
 73                .http_client()
 74                .get(&version.url, Default::default(), true)
 75                .await
 76                .map_err(|err| anyhow!("error downloading release: {}", err))?;
 77            let decompressed_bytes = GzipDecoder::new(BufReader::new(response.body_mut()));
 78            let archive = Archive::new(decompressed_bytes);
 79            archive.unpack(container_dir).await?;
 80        }
 81
 82        Ok(LanguageServerBinary {
 83            path: binary_path,
 84            env: None,
 85            arguments: server_binary_arguments(),
 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!["--version".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.ok_or_else(|| anyhow!("no cached binary"))?,
120            env: None,
121            arguments: server_binary_arguments(),
122        })
123    })
124    .await
125    .log_err()
126}