use self::process::{Process, StderrMode, spawn_process};
use self::transport::{Shared, reader_loop};
use super::{Capabilities, ProviderResult};
use parking_lot::Mutex;
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
use std::thread::JoinHandle;
use std::time::Duration;
mod handshake;
mod process;
mod restart;
mod transport;
mod wire;
#[cfg(test)]
mod tests;
pub const DEFAULT_TIMEOUT: Duration = Duration::from_millis(30_000);
pub struct StdioProvider {
name: String,
icon: Option<String>,
capabilities: Capabilities,
process: Mutex<Process>,
shared: Arc<Shared>,
current_id: AtomicU64,
timeout: Duration,
command: Vec<String>,
env: Vec<(String, String)>,
stderr_mode: StderrMode,
reader: Mutex<Option<JoinHandle<()>>>,
notice: Mutex<Option<String>>,
closed: AtomicBool,
cache_bytes: u64,
cache_dir: Option<std::path::PathBuf>,
cache_used: parking_lot::Mutex<Option<u64>>,
}
impl Drop for StdioProvider {
fn drop(&mut self) {
self.closed.store(true, Ordering::Release);
let process = self.process.get_mut();
let _ = process.child.kill();
let _ = process.child.wait();
if let Some(handle) = self.reader.lock().take() {
let _ = handle.join();
}
}
}
impl StdioProvider {
pub fn spawn(command: &[String], timeout: Duration) -> ProviderResult<Self> {
Self::spawn_inner(command, timeout, &[], StderrMode::Null)
}
pub fn spawn_with_stderr(
command: &[String],
timeout: Duration,
inherit_stderr: bool,
) -> ProviderResult<Self> {
Self::spawn_with_cache(command, timeout, inherit_stderr, 0, None)
}
pub fn spawn_with_cache(
command: &[String],
timeout: Duration,
inherit_stderr: bool,
cache_bytes: u64,
cache_dir: Option<std::path::PathBuf>,
) -> ProviderResult<Self> {
let mode = if inherit_stderr {
StderrMode::Inherit
} else {
StderrMode::Null
};
let mut provider = Self::spawn_inner(command, timeout, &[], mode)?;
provider.cache_bytes = cache_bytes;
provider.cache_dir = cache_dir;
let reply = provider.handshake()?;
provider.initialize_from(reply)
}
#[cfg(test)]
fn spawn_with_env(
command: &[String],
timeout: Duration,
env: &[(&str, &str)],
) -> ProviderResult<Self> {
Self::spawn_inner(command, timeout, env, StderrMode::Null)
}
fn spawn_inner(
command: &[String],
timeout: Duration,
env: &[(&str, &str)],
stderr_mode: StderrMode,
) -> ProviderResult<Self> {
let (process, stdout) = spawn_process(command, env, stderr_mode)?;
let shared = Arc::new(Shared::default());
let reader = std::thread::spawn({
let shared = Arc::clone(&shared);
move || reader_loop(stdout, shared)
});
let provider = StdioProvider {
name: "stdio".into(),
icon: None,
capabilities: Capabilities {
orgs: true,
code_search: true,
file_search: true,
},
process: Mutex::new(process),
shared,
current_id: AtomicU64::new(0),
timeout,
command: command.to_vec(),
env: env
.iter()
.map(|(k, v)| (k.to_string(), v.to_string()))
.collect(),
stderr_mode,
reader: Mutex::new(Some(reader)),
notice: Mutex::new(None),
closed: AtomicBool::new(false),
cache_bytes: 0,
cache_dir: None,
cache_used: parking_lot::Mutex::new(None),
};
let reply = provider.handshake()?;
provider.initialize_from(reply)
}
}