lib.rs

  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}