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