1use plugin::prelude::*;
2use serde::Deserialize;
3use serde_json::json;
4use std::fs;
5use std::path::PathBuf;
6
7// #[import]
8// fn my_command(string: &str) -> Option<String>;
9
10// #[no_mangle]
11// // TODO: switch len from usize to u32?
12// pub extern "C" fn #outer_fn_name(ptr: *const u8, len: usize) -> *const ::plugin::__Buffer {
13// // setup
14// let buffer = ::plugin::__Buffer { ptr, len };
15// let data = unsafe { buffer.to_vec() };
16
17// // operation
18// let data: #ty = match ::plugin::bincode::deserialize(&data) {
19// Ok(d) => d,
20// Err(e) => panic!("Data passed to function not deserializable."),
21// };
22// let result = #inner_fn_name(#args);
23// let new_data: Result<Vec<u8>, _> = ::plugin::bincode::serialize(&result);
24// let new_data = new_data.unwrap();
25
26// // teardown
27// let new_buffer = unsafe { ::plugin::__Buffer::from_vec(new_data) };
28// return new_buffer.leak_to_heap();
29// }
30
31extern "C" {
32 fn __command(ptr: *const u8, len: usize) -> *mut ::plugin::__Buffer;
33}
34
35#[no_mangle]
36fn command(string: &str) -> Option<String> {
37 dbg!("executing command: {}", string);
38 // serialize data
39 let data = string;
40 let data = ::plugin::bincode::serialize(&data).unwrap();
41 let buffer = unsafe { ::plugin::__Buffer::from_vec(data) };
42 let ptr = buffer.ptr;
43 let len = buffer.len;
44 // leak data to heap
45 buffer.leak_to_heap();
46 // call extern function
47 let result = unsafe { __command(ptr, len) };
48 // get result
49 let new_buffer = unsafe { Box::from_raw(result) }; // convert into box
50 let new_data = unsafe { new_buffer.to_vec() };
51
52 // deserialize data
53 let new_data: Option<String> = match ::plugin::bincode::deserialize(&new_data) {
54 Ok(d) => d,
55 Err(e) => panic!("Data returned from function not deserializable."),
56 };
57 return new_data;
58}
59
60// TODO: some sort of macro to generate ABI bindings
61extern "C" {
62 pub fn hello(item: u32) -> u32;
63 pub fn bye(item: u32) -> u32;
64}
65
66// #[bind]
67// pub async fn name(u32) -> u32 {
68
69// }
70
71// #[no_mangle]
72// pub extern "C" fn very_unique_name_of_course() -> impl std::future::Future<Output = u32> {
73// async move {
74// std::fs::read_to_string("heck.txt").unwrap().len() as u32
75// }
76// }
77
78const BIN_PATH: &'static str =
79 "node_modules/vscode-json-languageserver/bin/vscode-json-languageserver";
80
81#[export]
82pub fn name() -> &'static str {
83 // let number = unsafe { hello(27) };
84 // let number = unsafe { bye(28) };
85 // println!("got: {}", number);
86 "vscode-json-languageserver"
87}
88
89#[export]
90pub fn server_args() -> Vec<String> {
91 vec!["--stdio".into()]
92}
93
94#[export]
95pub fn fetch_latest_server_version() -> Option<String> {
96 #[derive(Deserialize)]
97 struct NpmInfo {
98 versions: Vec<String>,
99 }
100
101 // TODO: command returns error code
102 let output = command("npm info vscode-json-languageserver --json")?;
103 // if !output.is_ok() {
104 // return None;
105 // }
106
107 let mut info: NpmInfo = serde_json::from_str(&output).ok()?;
108 info.versions.pop()
109}
110
111#[export]
112pub fn fetch_server_binary(container_dir: PathBuf, version: String) -> Result<PathBuf, String> {
113 let version_dir = container_dir.join(version.as_str());
114 fs::create_dir_all(&version_dir)
115 .map_err(|_| "failed to create version directory".to_string())?;
116 let binary_path = version_dir.join(BIN_PATH);
117
118 if fs::metadata(&binary_path).is_err() {
119 let output = command(&format!(
120 "npm install vscode-json-languageserver@{}",
121 version
122 ));
123 if output.is_none() {
124 return Err("failed to install vscode-json-languageserver".to_string());
125 }
126
127 if let Some(mut entries) = fs::read_dir(&container_dir).ok() {
128 while let Some(entry) = entries.next() {
129 if let Some(entry) = entry.ok() {
130 let entry_path = entry.path();
131 if entry_path.as_path() != version_dir {
132 fs::remove_dir_all(&entry_path).ok();
133 }
134 }
135 }
136 }
137 }
138
139 Ok(binary_path)
140}
141
142#[export]
143pub fn cached_server_binary(container_dir: PathBuf) -> Option<PathBuf> {
144 let mut last_version_dir = None;
145 let mut entries = fs::read_dir(&container_dir).ok()?;
146
147 while let Some(entry) = entries.next() {
148 let entry = entry.ok()?;
149 if entry.file_type().ok()?.is_dir() {
150 last_version_dir = Some(entry.path());
151 }
152 }
153
154 let last_version_dir = last_version_dir?;
155 let bin_path = last_version_dir.join(BIN_PATH);
156 if bin_path.exists() {
157 Some(bin_path)
158 } else {
159 None
160 }
161}
162
163#[export]
164pub fn label_for_completion(label: String) -> Option<String> {
165 None
166}
167
168#[export]
169pub fn initialization_options() -> Option<String> {
170 Some("{ \"provideFormatter\": true }".to_string())
171}
172
173#[export]
174pub fn id_for_language(name: String) -> Option<String> {
175 if name == "JSON" {
176 Some("jsonc".into())
177 } else {
178 None
179 }
180}