lib.rs

  1use plugin::prelude::*;
  2use std::fs;
  3
  4#[import]
  5fn command(string: String) -> Option<String>;
  6
  7#[bind]
  8pub fn name(_: ()) -> &'static str {
  9    println!("huh, let me see...");
 10    Command::new("sh")
 11        .arg("-c")
 12        .arg("echo hello")
 13        .output()
 14        .expect("failed to execute process");
 15    "vscode-json-languageserver"
 16}
 17
 18#[bind]
 19pub fn server_args(_: ()) -> Vec<String> {
 20    vec!["--stdio".into()]
 21}
 22
 23#[bind]
 24fn fetch_latest_server_version() -> Option<String> {
 25    #[derive(Deserialize)]
 26    struct NpmInfo {
 27        versions: Vec<String>,
 28    }
 29
 30    let output = command("npm info vscode-json-languageserver --json")?;
 31    if !output.status.success() {
 32        return None;
 33    }
 34
 35    let mut info: NpmInfo = serde_json::from_slice(&output.stdout)?;
 36    info.versions.pop()
 37}
 38
 39#[bind]
 40pub fn fetch_server_binary(version: String) -> Option<PathBuf> {
 41    let version_dir = container_dir.join(version.as_str());
 42    fs::create_dir_all(&version_dir)
 43        .await
 44        .context("failed to create version directory")?;
 45    let binary_path = version_dir.join(Self::BIN_PATH);
 46
 47    if fs::metadata(&binary_path).await.is_err() {
 48        let output = smol::process::Command::new("npm")
 49            .current_dir(&version_dir)
 50            .arg("install")
 51            .arg(format!("vscode-json-languageserver@{}", version))
 52            .output()
 53            .await
 54            .context("failed to run npm install")?;
 55        if !output.status.success() {
 56            Err(anyhow!("failed to install vscode-json-languageserver"))?;
 57        }
 58
 59        if let Some(mut entries) = fs::read_dir(&container_dir).await.log_err() {
 60            while let Some(entry) = entries.next().await {
 61                if let Some(entry) = entry.log_err() {
 62                    let entry_path = entry.path();
 63                    if entry_path.as_path() != version_dir {
 64                        fs::remove_dir_all(&entry_path).await.log_err();
 65                    }
 66                }
 67            }
 68        }
 69    }
 70
 71    Ok(binary_path)
 72}
 73
 74#[bind]
 75pub fn cached_server_binary(container_dir: PathBuf) -> Option<PathBuf> {
 76    let mut last_version_dir = None;
 77    let mut entries = fs::read_dir(&container_dir).await?;
 78    while let Some(entry) = entries.next().await {
 79        let entry = entry?;
 80        if entry.file_type().await?.is_dir() {
 81            last_version_dir = Some(entry.path());
 82        }
 83    }
 84    let last_version_dir = last_version_dir.ok_or_else(|| anyhow!("no cached binary"))?;
 85    let bin_path = last_version_dir.join(Self::BIN_PATH);
 86    if bin_path.exists() {
 87        Ok(bin_path)
 88    } else {
 89        Err(anyhow!(
 90            "missing executable in directory {:?}",
 91            last_version_dir
 92        ))
 93    }
 94}
 95
 96#[bind]
 97pub fn initialization_options(_: ()) -> Option<serde_json::Value> {
 98    Some(json!({
 99        "provideFormatter": true
100    }))
101}
102
103#[bind]
104fn id_for_language(name: String) -> Option<String> {
105    if name == "JSON" {
106        Some("jsonc".into())
107    } else {
108        None
109    }
110}