use std::path::{Path, PathBuf};
use std::process::{Child, Command, Stdio};
use std::sync::{Arc, OnceLock};
use anyhow::{Context, Result, bail};
use lsp_types::notification::Notification;
use scarb_metadata::{Metadata, MetadataCommand};
use tracing::{error, warn};
use crate::env_config;
use crate::lsp::ext::ScarbMetadataFailed;
use crate::server::client::Notifier;
pub const SCARB_TOML: &str = "Scarb.toml";
#[derive(Clone)]
pub struct ScarbToolchain {
scarb_path_cell: Arc<OnceLock<Option<PathBuf>>>,
notifier: Notifier,
is_silent: bool,
}
impl ScarbToolchain {
pub fn new(notifier: Notifier) -> Self {
ScarbToolchain { scarb_path_cell: Default::default(), notifier, is_silent: false }
}
fn discover(&self) -> Option<&Path> {
self.scarb_path_cell
.get_or_init(|| {
let path = env_config::scarb_path();
if path.is_none() {
if self.is_silent {
warn!("attempt to use scarb without SCARB env being set");
} else {
error!("attempt to use scarb without SCARB env being set");
self.notifier.notify::<ScarbPathMissing>(());
}
}
path
})
.as_ref()
.map(PathBuf::as_path)
}
pub fn silent(&self) -> Self {
if self.is_silent {
self.clone()
} else {
Self {
scarb_path_cell: match self.scarb_path_cell.get() {
Some(_) => self.scarb_path_cell.clone(),
None => Default::default(),
},
notifier: self.notifier.clone(),
is_silent: true,
}
}
}
#[tracing::instrument(skip(self))]
pub fn metadata(&self, manifest: &Path) -> Result<Metadata> {
let Some(scarb_path) = self.discover() else {
bail!("could not find scarb executable");
};
if !self.is_silent {
self.notifier.notify::<ScarbResolvingStart>(());
}
let result = MetadataCommand::new()
.scarb_path(scarb_path)
.manifest_path(manifest)
.inherit_stderr()
.exec()
.context("failed to execute: scarb metadata");
if !self.is_silent {
self.notifier.notify::<ScarbResolvingFinish>(());
if result.is_err() {
self.notifier.notify::<ScarbMetadataFailed>(());
}
}
result
}
pub fn proc_macro_server(&self) -> Result<Child> {
let Some(scarb_path) = self.discover() else { bail!("failed to get scarb path") };
let proc_macro_server = Command::new(scarb_path)
.arg("--quiet") .arg("proc-macro-server")
.envs(std::env::var("RUST_BACKTRACE").map(|value| ("RUST_BACKTRACE", value)))
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::inherit())
.spawn()?;
Ok(proc_macro_server)
}
}
#[derive(Debug)]
struct ScarbPathMissing {}
impl Notification for ScarbPathMissing {
type Params = ();
const METHOD: &'static str = "scarb/could-not-find-scarb-executable";
}
#[derive(Debug)]
struct ScarbResolvingStart {}
impl Notification for ScarbResolvingStart {
type Params = ();
const METHOD: &'static str = "scarb/resolving-start";
}
#[derive(Debug)]
struct ScarbResolvingFinish {}
impl Notification for ScarbResolvingFinish {
type Params = ();
const METHOD: &'static str = "scarb/resolving-finish";
}