buf.rs

  1use std::fs;
  2
  3use zed_extension_api::{
  4    self as zed, Architecture, DownloadedFileType, GithubReleaseOptions, Os, Result,
  5    settings::LspSettings,
  6};
  7
  8use crate::language_servers::util;
  9
 10pub(crate) struct BufLsp {
 11    cached_binary_path: Option<String>,
 12}
 13
 14impl BufLsp {
 15    pub(crate) const SERVER_NAME: &str = "buf";
 16
 17    pub(crate) fn new() -> Self {
 18        BufLsp {
 19            cached_binary_path: None,
 20        }
 21    }
 22
 23    pub(crate) fn language_server_binary(
 24        &mut self,
 25        worktree: &zed::Worktree,
 26    ) -> Result<zed::Command> {
 27        let binary_settings = LspSettings::for_worktree(Self::SERVER_NAME, worktree)
 28            .ok()
 29            .and_then(|lsp_settings| lsp_settings.binary);
 30
 31        let args = binary_settings
 32            .as_ref()
 33            .and_then(|binary_settings| binary_settings.arguments.clone())
 34            .unwrap_or_else(|| ["lsp", "serve"].map(ToOwned::to_owned).into());
 35
 36        if let Some(path) = binary_settings.and_then(|binary_settings| binary_settings.path) {
 37            return Ok(zed::Command {
 38                command: path,
 39                args,
 40                env: Default::default(),
 41            });
 42        } else if let Some(path) = self.cached_binary_path.clone() {
 43            return Ok(zed::Command {
 44                command: path,
 45                args,
 46                env: Default::default(),
 47            });
 48        } else if let Some(path) = worktree.which(Self::SERVER_NAME) {
 49            self.cached_binary_path = Some(path.clone());
 50            return Ok(zed::Command {
 51                command: path,
 52                args,
 53                env: Default::default(),
 54            });
 55        }
 56
 57        let latest_release = zed::latest_github_release(
 58            "bufbuild/buf",
 59            GithubReleaseOptions {
 60                require_assets: true,
 61                pre_release: false,
 62            },
 63        )?;
 64
 65        let (os, arch) = zed::current_platform();
 66
 67        let release_suffix = match (os, arch) {
 68            (Os::Mac, Architecture::Aarch64) => "Darwin-arm64",
 69            (Os::Mac, Architecture::X8664) => "Darwin-x86_64",
 70            (Os::Linux, Architecture::Aarch64) => "Linux-aarch64",
 71            (Os::Linux, Architecture::X8664) => "Linux-x86_64",
 72            (Os::Windows, Architecture::Aarch64) => "Windows-arm64.exe",
 73            (Os::Windows, Architecture::X8664) => "Windows-x86_64.exe",
 74            _ => {
 75                return Err("Platform and architecture not supported by buf CLI".to_string());
 76            }
 77        };
 78
 79        let release_name = format!("buf-{release_suffix}");
 80
 81        let version_dir = format!("{}-{}", Self::SERVER_NAME, latest_release.version);
 82        fs::create_dir_all(&version_dir).map_err(|_| "Could not create directory")?;
 83
 84        let binary_path = format!("{version_dir}/buf");
 85
 86        let download_target = latest_release
 87            .assets
 88            .into_iter()
 89            .find(|asset| asset.name == release_name)
 90            .ok_or_else(|| {
 91                format!(
 92                    "Could not find asset with name {} in buf CLI release",
 93                    &release_name
 94                )
 95            })?;
 96
 97        zed::download_file(
 98            &download_target.download_url,
 99            &binary_path,
100            DownloadedFileType::Uncompressed,
101        )?;
102        zed::make_file_executable(&binary_path)?;
103
104        util::remove_outdated_versions(Self::SERVER_NAME, &version_dir)?;
105
106        self.cached_binary_path = Some(binary_path.clone());
107
108        Ok(zed::Command {
109            command: binary_path,
110            args,
111            env: Default::default(),
112        })
113    }
114}