lib.rs

  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}