ryo_executor/executor/
traits.rs1use super::context::ExecutionContext;
4use crate::decider::{Action, ActionKind, ActionResult};
5use std::fmt;
6
7#[derive(Debug)]
9pub enum ExecutorError {
10 UnsupportedAction(ActionKind),
12 CommandFailed(String),
14 FileNotFound(String),
16 Timeout,
18 Other(String),
20}
21
22impl fmt::Display for ExecutorError {
23 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
24 match self {
25 Self::UnsupportedAction(kind) => write!(f, "Unsupported action: {:?}", kind),
26 Self::CommandFailed(msg) => write!(f, "Command failed: {}", msg),
27 Self::FileNotFound(path) => write!(f, "File not found: {}", path),
28 Self::Timeout => write!(f, "Execution timeout"),
29 Self::Other(msg) => write!(f, "{}", msg),
30 }
31 }
32}
33
34impl std::error::Error for ExecutorError {}
35
36pub trait Executor: Send + Sync {
38 fn execute(
40 &self,
41 action: &Action,
42 ctx: &ExecutionContext,
43 ) -> Result<ActionResult, ExecutorError>;
44
45 fn supported_kinds(&self) -> &[ActionKind];
47
48 fn can_execute(&self, action: &Action) -> bool {
50 self.supported_kinds().contains(&action.kind)
51 }
52
53 fn name(&self) -> &'static str {
55 "Executor"
56 }
57}
58
59pub struct CompositeExecutor {
61 executors: Vec<Box<dyn Executor>>,
62}
63
64impl CompositeExecutor {
65 pub fn new() -> Self {
67 Self {
68 executors: Vec::new(),
69 }
70 }
71
72 pub fn with<E: Executor + 'static>(mut self, executor: E) -> Self {
74 self.executors.push(Box::new(executor));
75 self
76 }
77}
78
79impl Default for CompositeExecutor {
80 fn default() -> Self {
81 Self::new()
82 }
83}
84
85impl Executor for CompositeExecutor {
86 fn execute(
87 &self,
88 action: &Action,
89 ctx: &ExecutionContext,
90 ) -> Result<ActionResult, ExecutorError> {
91 for executor in &self.executors {
93 if executor.can_execute(action) {
94 return executor.execute(action, ctx);
95 }
96 }
97 Err(ExecutorError::UnsupportedAction(action.kind))
98 }
99
100 fn supported_kinds(&self) -> &[ActionKind] {
101 &[]
103 }
104
105 fn can_execute(&self, action: &Action) -> bool {
106 self.executors.iter().any(|e| e.can_execute(action))
107 }
108
109 fn name(&self) -> &'static str {
110 "CompositeExecutor"
111 }
112}
113
114#[cfg(test)]
115mod tests {
116 use super::*;
117
118 #[test]
119 fn test_executor_error_display() {
120 let err = ExecutorError::UnsupportedAction(ActionKind::Read);
121 assert!(err.to_string().contains("Unsupported"));
122
123 let err = ExecutorError::FileNotFound("test.rs".to_string());
124 assert!(err.to_string().contains("test.rs"));
125 }
126}