lua.rs

  1use std::{any::Any, env::consts, path::PathBuf, sync::Arc};
  2
  3use anyhow::{anyhow, bail, Result};
  4use async_compression::futures::bufread::GzipDecoder;
  5use async_tar::Archive;
  6use async_trait::async_trait;
  7use client::http::HttpClient;
  8use futures::{io::BufReader, StreamExt};
  9use language::LanguageServerName;
 10use smol::fs;
 11use util::{async_iife, ResultExt};
 12
 13use super::installation::{latest_github_release, GitHubLspBinaryVersion};
 14
 15#[derive(Copy, Clone)]
 16pub struct LuaLspAdapter;
 17
 18#[async_trait]
 19impl super::LspAdapter for LuaLspAdapter {
 20    async fn name(&self) -> LanguageServerName {
 21        LanguageServerName("lua-language-server".into())
 22    }
 23
 24    async fn server_args(&self) -> Vec<String> {
 25        vec![
 26            "--logpath=~/lua-language-server.log".into(),
 27            "--loglevel=trace".into(),
 28        ]
 29    }
 30
 31    async fn fetch_latest_server_version(
 32        &self,
 33        http: Arc<dyn HttpClient>,
 34    ) -> Result<Box<dyn 'static + Send + Any>> {
 35        let release = latest_github_release("LuaLS/lua-language-server", http).await?;
 36        let version = release.name.clone();
 37        let platform = match consts::ARCH {
 38            "x86_64" => "x64",
 39            "aarch64" => "arm64",
 40            other => bail!("Running on unsupported platform: {other}"),
 41        };
 42        let asset_name = format!("lua-language-server-{version}-darwin-{platform}.tar.gz");
 43        let asset = release
 44            .assets
 45            .iter()
 46            .find(|asset| asset.name == asset_name)
 47            .ok_or_else(|| anyhow!("no asset found matching {:?}", asset_name))?;
 48        let version = GitHubLspBinaryVersion {
 49            name: release.name.clone(),
 50            url: asset.browser_download_url.clone(),
 51        };
 52        Ok(Box::new(version) as Box<_>)
 53    }
 54
 55    async fn fetch_server_binary(
 56        &self,
 57        version: Box<dyn 'static + Send + Any>,
 58        http: Arc<dyn HttpClient>,
 59        container_dir: PathBuf,
 60    ) -> Result<PathBuf> {
 61        let version = version.downcast::<GitHubLspBinaryVersion>().unwrap();
 62
 63        let binary_path = container_dir.join("bin/lua-language-server");
 64
 65        if fs::metadata(&binary_path).await.is_err() {
 66            let mut response = http
 67                .get(&version.url, Default::default(), true)
 68                .await
 69                .map_err(|err| anyhow!("error downloading release: {}", err))?;
 70            let decompressed_bytes = GzipDecoder::new(BufReader::new(response.body_mut()));
 71            let archive = Archive::new(decompressed_bytes);
 72            archive.unpack(container_dir).await?;
 73        }
 74
 75        fs::set_permissions(
 76            &binary_path,
 77            <fs::Permissions as fs::unix::PermissionsExt>::from_mode(0o755),
 78        )
 79        .await?;
 80        Ok(binary_path)
 81    }
 82
 83    async fn cached_server_binary(&self, container_dir: PathBuf) -> Option<PathBuf> {
 84        async_iife!({
 85            let mut last_binary_path = None;
 86            let mut entries = fs::read_dir(&container_dir).await?;
 87            while let Some(entry) = entries.next().await {
 88                let entry = entry?;
 89                if entry.file_type().await?.is_file()
 90                    && entry
 91                        .file_name()
 92                        .to_str()
 93                        .map_or(false, |name| name == "lua-language-server")
 94                {
 95                    last_binary_path = Some(entry.path());
 96                }
 97            }
 98
 99            if let Some(path) = last_binary_path {
100                Ok(path)
101            } else {
102                Err(anyhow!("no cached binary"))
103            }
104        })
105        .await
106        .log_err()
107    }
108}