use std::path::{Path, PathBuf};
use crate::serena_ls_cmd::{
TS_LS_ID, TS_LS_PACKAGE, TS_LS_PIN, TYPESCRIPT_PIN, VUE, VUE_LS_ID, VUE_LS_PIN,
};
pub const LS_RESOURCES_REL: [&str; 2] = ["language_servers", "static"];
pub const VUE_LS_CLASS: &str = "VueLanguageServer";
pub const VUE_LSP_DIR: &str = "vue-lsp";
pub const MARKER_FILE: &str = ".installed_version";
pub const VUE_SHIM_REL: [&str; 3] = ["node_modules", ".bin", "vue-language-server"];
pub const TS_SHIM_REL: [&str; 3] = ["node_modules", ".bin", TS_LS_PACKAGE];
pub const VUE_VERSION_KEY: &str = "vue_language_server_version";
pub const TYPESCRIPT_VERSION_KEY: &str = "typescript_version";
pub const TS_LS_VERSION_KEY: &str = "typescript_language_server_version";
pub fn version_settings() -> [(&'static str, &'static str, &'static str); 3] {
[
(VUE_LS_ID, VUE_VERSION_KEY, VUE_LS_PIN),
(TS_LS_ID, TYPESCRIPT_VERSION_KEY, TYPESCRIPT_PIN),
(TS_LS_ID, TS_LS_VERSION_KEY, TS_LS_PIN),
]
}
pub fn marker() -> String {
format!("{VUE_LS_PIN}_{TYPESCRIPT_PIN}_{TS_LS_PIN}")
}
pub fn resources_parent(serena_home: &Path) -> PathBuf {
let mut p = serena_home.to_path_buf();
p.extend(LS_RESOURCES_REL);
p.join(VUE_LS_CLASS)
}
pub fn resources_link(serena_home: &Path) -> PathBuf {
resources_parent(serena_home).join(VUE_LSP_DIR)
}
pub fn vue_shim(dir: &Path) -> PathBuf {
let mut p = dir.to_path_buf();
p.extend(VUE_SHIM_REL);
p
}
pub fn ts_shim(dir: &Path) -> PathBuf {
let mut p = dir.to_path_buf();
p.extend(TS_SHIM_REL);
p
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum LinkState {
Absent,
Linked,
}
pub fn check_link(serena_home: &Path, tools_dir: &Path) -> Result<LinkState, String> {
let link = resources_link(serena_home);
let meta = match std::fs::symlink_metadata(&link) {
Ok(m) => m,
Err(e) if e.kind() == std::io::ErrorKind::NotFound => return Ok(LinkState::Absent),
Err(e) => return Err(format!("{}: {e}", link.display())),
};
if !meta.file_type().is_symlink() {
return Err(format!(
"{} is not MUR's link (serena's own download, or replaced)",
link.display()
));
}
let want = VUE.dir(tools_dir);
let real = link
.canonicalize()
.map_err(|e| format!("{} ({e})", link.display()))?;
let want_real = want
.canonicalize()
.map_err(|e| format!("{} ({e})", want.display()))?;
if real != want_real {
return Err(format!(
"{} -> {}, not {}",
link.display(),
real.display(),
want.display()
));
}
for shim in [vue_shim(&real), ts_shim(&real)] {
if !shim.exists() {
return Err(format!("{} is missing", shim.display()));
}
}
let marker_path = real.join(MARKER_FILE);
match std::fs::read_to_string(&marker_path) {
Ok(s) if s.trim() == marker() => Ok(LinkState::Linked),
Ok(s) => Err(format!(
"{} reads {:?}, expected {:?}",
marker_path.display(),
s.trim(),
marker()
)),
Err(e) => Err(format!("{} ({e})", marker_path.display())),
}
}
#[cfg(all(test, unix))]
#[path = "serena_vue_tests.rs"]
mod tests;