pub mod local;
pub mod null;
use anyhow::Result;
#[derive(Debug, Clone)]
pub struct SubstrateCommand {
pub program: String,
pub args: Vec<String>,
pub working_dir: Option<String>,
pub timeout_secs: u64,
pub capture_stderr: bool,
pub env: Vec<(String, String)>,
}
impl SubstrateCommand {
pub fn new(program: impl Into<String>) -> Self {
Self {
program: program.into(),
args: Vec::new(),
working_dir: None,
timeout_secs: 30,
capture_stderr: true,
env: Vec::new(),
}
}
pub fn args(mut self, args: impl IntoIterator<Item = impl Into<String>>) -> Self {
self.args = args.into_iter().map(Into::into).collect();
self
}
pub fn cwd(mut self, dir: impl Into<String>) -> Self {
self.working_dir = Some(dir.into());
self
}
pub fn timeout(mut self, secs: u64) -> Self {
self.timeout_secs = secs;
self
}
}
#[derive(Debug, Clone)]
pub struct SubstrateResult {
pub stdout: Vec<u8>,
pub stderr: Vec<u8>,
pub exit_code: i32,
pub duration_ms: u64,
}
impl SubstrateResult {
pub fn stdout_str(&self) -> String {
String::from_utf8_lossy(&self.stdout).into_owned()
}
pub fn stderr_str(&self) -> String {
String::from_utf8_lossy(&self.stderr).into_owned()
}
pub fn succeeded(&self) -> bool {
self.exit_code == 0
}
}
#[derive(Debug, thiserror::Error)]
pub enum SubstrateError {
#[error("command not found: {0}")]
NotFound(String),
#[error("execution timed out after {0}s")]
Timeout(u64),
#[error("io error: {0}")]
Io(#[from] std::io::Error),
#[error("spawn failed: {0}")]
Spawn(String),
}
pub trait Substrate: Send + Sync {
fn execute(&self, cmd: SubstrateCommand) -> Result<SubstrateResult, SubstrateError>;
fn is_available(&self) -> bool;
}