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}