1use plugin::prelude::*;
2use std::fs;
3
4#[import]
5fn command(string: String) -> Option<String>;
6
7#[bind]
8pub fn name(_: ()) -> &'static str {
9 println!("huh, let me see...");
10 Command::new("sh")
11 .arg("-c")
12 .arg("echo hello")
13 .output()
14 .expect("failed to execute process");
15 "vscode-json-languageserver"
16}
17
18#[bind]
19pub fn server_args(_: ()) -> Vec<String> {
20 vec!["--stdio".into()]
21}
22
23#[bind]
24fn fetch_latest_server_version() -> Option<String> {
25 #[derive(Deserialize)]
26 struct NpmInfo {
27 versions: Vec<String>,
28 }
29
30 let output = command("npm info vscode-json-languageserver --json")?;
31 if !output.status.success() {
32 return None;
33 }
34
35 let mut info: NpmInfo = serde_json::from_slice(&output.stdout)?;
36 info.versions.pop()
37}
38
39#[bind]
40pub fn fetch_server_binary(version: String) -> Option<PathBuf> {
41 let version_dir = container_dir.join(version.as_str());
42 fs::create_dir_all(&version_dir)
43 .await
44 .context("failed to create version directory")?;
45 let binary_path = version_dir.join(Self::BIN_PATH);
46
47 if fs::metadata(&binary_path).await.is_err() {
48 let output = smol::process::Command::new("npm")
49 .current_dir(&version_dir)
50 .arg("install")
51 .arg(format!("vscode-json-languageserver@{}", version))
52 .output()
53 .await
54 .context("failed to run npm install")?;
55 if !output.status.success() {
56 Err(anyhow!("failed to install vscode-json-languageserver"))?;
57 }
58
59 if let Some(mut entries) = fs::read_dir(&container_dir).await.log_err() {
60 while let Some(entry) = entries.next().await {
61 if let Some(entry) = entry.log_err() {
62 let entry_path = entry.path();
63 if entry_path.as_path() != version_dir {
64 fs::remove_dir_all(&entry_path).await.log_err();
65 }
66 }
67 }
68 }
69 }
70
71 Ok(binary_path)
72}
73
74#[bind]
75pub fn cached_server_binary(container_dir: PathBuf) -> Option<PathBuf> {
76 let mut last_version_dir = None;
77 let mut entries = fs::read_dir(&container_dir).await?;
78 while let Some(entry) = entries.next().await {
79 let entry = entry?;
80 if entry.file_type().await?.is_dir() {
81 last_version_dir = Some(entry.path());
82 }
83 }
84 let last_version_dir = last_version_dir.ok_or_else(|| anyhow!("no cached binary"))?;
85 let bin_path = last_version_dir.join(Self::BIN_PATH);
86 if bin_path.exists() {
87 Ok(bin_path)
88 } else {
89 Err(anyhow!(
90 "missing executable in directory {:?}",
91 last_version_dir
92 ))
93 }
94}
95
96#[bind]
97pub fn initialization_options(_: ()) -> Option<serde_json::Value> {
98 Some(json!({
99 "provideFormatter": true
100 }))
101}
102
103#[bind]
104fn id_for_language(name: String) -> Option<String> {
105 if name == "JSON" {
106 Some("jsonc".into())
107 } else {
108 None
109 }
110}