bctx_forge/substrate/
mod.rs1pub mod local;
2pub mod null;
3
4use anyhow::Result;
5
6#[derive(Debug, Clone)]
7pub struct SubstrateCommand {
8 pub program: String,
9 pub args: Vec<String>,
10 pub working_dir: Option<String>,
11 pub timeout_secs: u64,
12 pub capture_stderr: bool,
13 pub env: Vec<(String, String)>,
14}
15
16impl SubstrateCommand {
17 pub fn new(program: impl Into<String>) -> Self {
18 Self {
19 program: program.into(),
20 args: Vec::new(),
21 working_dir: None,
22 timeout_secs: 30,
23 capture_stderr: true,
24 env: Vec::new(),
25 }
26 }
27
28 pub fn args(mut self, args: impl IntoIterator<Item = impl Into<String>>) -> Self {
29 self.args = args.into_iter().map(Into::into).collect();
30 self
31 }
32
33 pub fn cwd(mut self, dir: impl Into<String>) -> Self {
34 self.working_dir = Some(dir.into());
35 self
36 }
37
38 pub fn timeout(mut self, secs: u64) -> Self {
39 self.timeout_secs = secs;
40 self
41 }
42}
43
44#[derive(Debug, Clone)]
45pub struct SubstrateResult {
46 pub stdout: Vec<u8>,
47 pub stderr: Vec<u8>,
48 pub exit_code: i32,
49 pub duration_ms: u64,
50}
51
52impl SubstrateResult {
53 pub fn stdout_str(&self) -> String {
54 String::from_utf8_lossy(&self.stdout).into_owned()
55 }
56
57 pub fn stderr_str(&self) -> String {
58 String::from_utf8_lossy(&self.stderr).into_owned()
59 }
60
61 pub fn succeeded(&self) -> bool {
62 self.exit_code == 0
63 }
64}
65
66#[derive(Debug, thiserror::Error)]
67pub enum SubstrateError {
68 #[error("command not found: {0}")]
69 NotFound(String),
70 #[error("execution timed out after {0}s")]
71 Timeout(u64),
72 #[error("io error: {0}")]
73 Io(#[from] std::io::Error),
74 #[error("spawn failed: {0}")]
75 Spawn(String),
76}
77
78pub trait Substrate: Send + Sync {
79 fn execute(&self, cmd: SubstrateCommand) -> Result<SubstrateResult, SubstrateError>;
80 fn is_available(&self) -> bool;
81}