phpactor.rs

 1use std::fs;
 2
 3use zed_extension_api::{self as zed, LanguageServerId, Result};
 4
 5pub struct Phpactor {
 6    cached_binary_path: Option<String>,
 7}
 8
 9impl Phpactor {
10    pub const LANGUAGE_SERVER_ID: &'static str = "phpactor";
11
12    pub fn new() -> Self {
13        Self {
14            cached_binary_path: None,
15        }
16    }
17
18    pub fn language_server_binary_path(
19        &mut self,
20        language_server_id: &LanguageServerId,
21        worktree: &zed::Worktree,
22    ) -> Result<String> {
23        if let Some(path) = worktree.which("phpactor") {
24            return Ok(path);
25        }
26
27        if let Some(path) = &self.cached_binary_path {
28            if fs::metadata(path).map_or(false, |stat| stat.is_file()) {
29                return Ok(path.clone());
30            }
31        }
32
33        zed::set_language_server_installation_status(
34            language_server_id,
35            &zed::LanguageServerInstallationStatus::CheckingForUpdate,
36        );
37        let release = zed::latest_github_release(
38            "phpactor/phpactor",
39            zed::GithubReleaseOptions {
40                require_assets: true,
41                pre_release: false,
42            },
43        )?;
44
45        let asset_name = "phpactor.phar";
46        let asset = release
47            .assets
48            .iter()
49            .find(|asset| asset.name == asset_name)
50            .ok_or_else(|| format!("no asset found matching {:?}", asset_name))?;
51
52        let version_dir = format!("phpactor-{}", release.version);
53        fs::create_dir_all(&version_dir).map_err(|e| format!("failed to create directory: {e}"))?;
54
55        let binary_path = format!("{version_dir}/phpactor.phar");
56
57        if !fs::metadata(&binary_path).map_or(false, |stat| stat.is_file()) {
58            zed::set_language_server_installation_status(
59                language_server_id,
60                &zed::LanguageServerInstallationStatus::Downloading,
61            );
62
63            zed::download_file(
64                &asset.download_url,
65                &binary_path,
66                zed::DownloadedFileType::Uncompressed,
67            )
68            .map_err(|e| format!("failed to download file: {e}"))?;
69
70            zed::make_file_executable(&binary_path)?;
71
72            let entries =
73                fs::read_dir(".").map_err(|e| format!("failed to list working directory {e}"))?;
74            for entry in entries {
75                let entry = entry.map_err(|e| format!("failed to load directory entry {e}"))?;
76                if entry.file_name().to_str() != Some(&version_dir) {
77                    fs::remove_dir_all(entry.path()).ok();
78                }
79            }
80        }
81
82        self.cached_binary_path = Some(binary_path.clone());
83        Ok(binary_path)
84    }
85}