use self::process::{Process, StderrMode, spawn_process};
use self::reader::spawn_reader;
use self::routing::Shared;
use parking_lot::Mutex;
use rootle_provider::{Capabilities, ProviderResult};
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
use std::thread::JoinHandle;
use std::time::Duration;
mod handshake;
mod process;
mod reader;
mod restart;
mod routing;
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>>,
failure_noticed: Mutex<bool>,
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();
process.terminate();
if let Some(handle) = self.reader.lock().take() {
let _ = handle.join();
}
}
}
#[derive(Default)]
struct SpawnCache {
bytes: u64,
directory: Option<std::path::PathBuf>,
}
impl StdioProvider {
pub fn spawn(command: &[String], timeout: Duration) -> ProviderResult<Self> {
Self::spawn_inner(
command,
timeout,
&[],
StderrMode::Null,
SpawnCache::default(),
)
}
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
};
Self::spawn_inner(
command,
timeout,
&[],
mode,
SpawnCache {
bytes: cache_bytes,
directory: cache_dir,
},
)
}
pub fn spawn_with_env(
command: &[String],
timeout: Duration,
env: &[(&str, &str)],
) -> ProviderResult<Self> {
Self::spawn_inner(
command,
timeout,
env,
StderrMode::Null,
SpawnCache::default(),
)
}
fn spawn_inner(
command: &[String],
timeout: Duration,
env: &[(&str, &str)],
stderr_mode: StderrMode,
cache: SpawnCache,
) -> ProviderResult<Self> {
let (process, stdout) = spawn_process(command, env, stderr_mode)?;
let shared = Arc::new(Shared::default());
let session = shared.routing.lock().session;
let reader = spawn_reader(stdout, Arc::clone(&shared), session)?;
let provider = StdioProvider {
name: "stdio".into(),
icon: None,
capabilities: Capabilities {
commit: false,
orgs: true,
code_search: true,
file_search: true,
refs: false,
log: false,
blame: false,
},
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),
failure_noticed: Mutex::new(false),
closed: AtomicBool::new(false),
cache_bytes: cache.bytes,
cache_dir: cache.directory,
cache_used: parking_lot::Mutex::new(None),
};
let reply = provider.handshake()?;
provider.initialize_from(reply)
}
}