lib.rs

  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}