anyclaw_sdk_tool/
workspace_exec.rs1use std::future::Future;
2use std::path::Path;
3use std::pin::Pin;
4
5use anyclaw_sdk_types::WorkspaceScope;
6
7use crate::error::ToolSdkError;
8
9pub trait WorkspaceExec: Send + Sync + 'static {
20 fn scope(&self) -> WorkspaceScope;
22
23 fn read_file(&self, path: &Path) -> impl Future<Output = Result<Vec<u8>, ToolSdkError>> + Send;
27
28 fn write_file(
33 &self,
34 path: &Path,
35 contents: &[u8],
36 ) -> impl Future<Output = Result<(), ToolSdkError>> + Send;
37
38 fn exec(
42 &self,
43 command: &str,
44 args: &[&str],
45 ) -> impl Future<Output = Result<ExecOutput, ToolSdkError>> + Send;
46}
47
48pub trait DynWorkspaceExec: Send + Sync + 'static {
53 fn scope(&self) -> WorkspaceScope;
55
56 fn read_file<'a>(
58 &'a self,
59 path: &'a Path,
60 ) -> Pin<Box<dyn Future<Output = Result<Vec<u8>, ToolSdkError>> + Send + 'a>>;
61
62 fn write_file<'a>(
64 &'a self,
65 path: &'a Path,
66 contents: &'a [u8],
67 ) -> Pin<Box<dyn Future<Output = Result<(), ToolSdkError>> + Send + 'a>>;
68
69 fn exec<'a>(
71 &'a self,
72 command: &'a str,
73 args: &'a [&'a str],
74 ) -> Pin<Box<dyn Future<Output = Result<ExecOutput, ToolSdkError>> + Send + 'a>>;
75}
76
77impl<T: WorkspaceExec> DynWorkspaceExec for T {
78 fn scope(&self) -> WorkspaceScope {
79 WorkspaceExec::scope(self)
80 }
81
82 fn read_file<'a>(
83 &'a self,
84 path: &'a Path,
85 ) -> Pin<Box<dyn Future<Output = Result<Vec<u8>, ToolSdkError>> + Send + 'a>> {
86 Box::pin(WorkspaceExec::read_file(self, path))
87 }
88
89 fn write_file<'a>(
90 &'a self,
91 path: &'a Path,
92 contents: &'a [u8],
93 ) -> Pin<Box<dyn Future<Output = Result<(), ToolSdkError>> + Send + 'a>> {
94 Box::pin(WorkspaceExec::write_file(self, path, contents))
95 }
96
97 fn exec<'a>(
98 &'a self,
99 command: &'a str,
100 args: &'a [&'a str],
101 ) -> Pin<Box<dyn Future<Output = Result<ExecOutput, ToolSdkError>> + Send + 'a>> {
102 Box::pin(WorkspaceExec::exec(self, command, args))
103 }
104}
105
106#[derive(Debug, Clone)]
108pub struct ExecOutput {
109 pub exit_code: i32,
111 pub stdout: Vec<u8>,
113 pub stderr: Vec<u8>,
115}
116
117impl ExecOutput {
118 pub fn success(&self) -> bool {
120 self.exit_code == 0
121 }
122}
123
124#[cfg(test)]
125mod tests {
126 use rstest::rstest;
127
128 use super::*;
129
130 #[rstest]
131 fn when_exec_output_exit_zero_then_success() {
132 let output = ExecOutput {
133 exit_code: 0,
134 stdout: vec![],
135 stderr: vec![],
136 };
137 assert!(output.success());
138 }
139
140 #[rstest]
141 fn when_exec_output_exit_nonzero_then_not_success() {
142 let output = ExecOutput {
143 exit_code: 1,
144 stdout: vec![],
145 stderr: b"error".to_vec(),
146 };
147 assert!(!output.success());
148 }
149
150 #[rstest]
151 fn when_dyn_workspace_exec_used_as_trait_object_then_compiles() {
152 fn _accepts_dyn(_w: &dyn DynWorkspaceExec) {}
153 }
154}