1use plugin::prelude::*;
2use serde_json::json;
3use std::fs;
4use std::path::PathBuf;
5
6// #[import]
7// fn command(string: String) -> Option<String>;
8
9// TODO: some sort of macro to generate ABI bindings
10extern "C" {
11 pub fn hello(item: u32) -> u32;
12 pub fn bye(item: u32) -> u32;
13}
14
15// #[bind]
16// pub async fn name(u32) -> u32 {
17
18// }
19
20const BIN_PATH: &'static str =
21 "node_modules/vscode-json-languageserver/bin/vscode-json-languageserver";
22
23#[bind]
24pub fn name() -> &'static str {
25 // let number = unsafe { hello(27) };
26 // println!("got: {}", number);
27 // let number = unsafe { bye(28) };
28 // println!("got: {}", number);
29 "vscode-json-languageserver"
30}
31
32#[bind]
33pub fn server_args() -> Vec<String> {
34 vec!["--stdio".into()]
35}
36
37#[bind]
38pub fn fetch_latest_server_version() -> Option<String> {
39 #[derive(Deserialize)]
40 struct NpmInfo {
41 versions: Vec<String>,
42 }
43
44 let output = command("npm info vscode-json-languageserver --json")?;
45 if !output.status.success() {
46 return None;
47 }
48
49 let mut info: NpmInfo = serde_json::from_slice(&output.stdout)?;
50 info.versions.pop()
51}
52
53#[bind]
54pub fn fetch_server_binary(container_dir: PathBuf, version: String) -> Result<PathBuf, String> {
55 let version_dir = container_dir.join(version.as_str());
56 fs::create_dir_all(&version_dir)
57 .or_or_else(|| "failed to create version directory".to_string())?;
58 let binary_path = version_dir.join(Self::BIN_PATH);
59
60 if fs::metadata(&binary_path).await.is_err() {
61 let output = command(format!(
62 "npm install vscode-json-languageserver@{}",
63 version
64 ));
65 if !output.status.success() {
66 Err(anyhow!("failed to install vscode-json-languageserver"))?;
67 }
68
69 if let Some(mut entries) = fs::read_dir(&container_dir).await.log_err() {
70 while let Some(entry) = entries.next().await {
71 if let Some(entry) = entry.log_err() {
72 let entry_path = entry.path();
73 if entry_path.as_path() != version_dir {
74 fs::remove_dir_all(&entry_path).await.log_err();
75 }
76 }
77 }
78 }
79 }
80
81 Ok(binary_path)
82}
83
84#[bind]
85pub fn cached_server_binary(container_dir: PathBuf) -> Option<PathBuf> {
86 let mut last_version_dir = None;
87 let mut entries = fs::read_dir(&container_dir).ok()?;
88
89 while let Some(entry) = entries.next() {
90 let entry = entry.ok()?;
91 if entry.file_type().ok()?.is_dir() {
92 last_version_dir = Some(entry.path());
93 }
94 }
95
96 let last_version_dir = last_version_dir?;
97 let bin_path = last_version_dir.join(BIN_PATH);
98 if bin_path.exists() {
99 Some(bin_path)
100 } else {
101 None
102 }
103}
104
105#[bind]
106pub fn initialization_options() -> Option<String> {
107 Some("{ \"provideFormatter\": true }".to_string())
108}
109
110#[bind]
111pub fn id_for_language(name: String) -> Option<String> {
112 if name == "JSON" {
113 Some("jsonc".into())
114 } else {
115 None
116 }
117}