ryo-executor 0.1.0

[experimental] Mutation execution engine for RYO - parallel execution, conflict detection, workspace management
Documentation
//! Executor trait: Action を実行するインターフェース

use super::context::ExecutionContext;
use crate::decider::{Action, ActionKind, ActionResult};
use std::fmt;

/// Executor のエラー型
#[derive(Debug)]
pub enum ExecutorError {
    /// サポートされていない ActionKind
    UnsupportedAction(ActionKind),
    /// コマンド実行エラー
    CommandFailed(String),
    /// ファイルが見つからない
    FileNotFound(String),
    /// タイムアウト
    Timeout,
    /// その他のエラー
    Other(String),
}

impl fmt::Display for ExecutorError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::UnsupportedAction(kind) => write!(f, "Unsupported action: {:?}", kind),
            Self::CommandFailed(msg) => write!(f, "Command failed: {}", msg),
            Self::FileNotFound(path) => write!(f, "File not found: {}", path),
            Self::Timeout => write!(f, "Execution timeout"),
            Self::Other(msg) => write!(f, "{}", msg),
        }
    }
}

impl std::error::Error for ExecutorError {}

/// Executor trait: Action を実行して ActionResult を返す
pub trait Executor: Send + Sync {
    /// Action を実行
    fn execute(
        &self,
        action: &Action,
        ctx: &ExecutionContext,
    ) -> Result<ActionResult, ExecutorError>;

    /// このExecutorがサポートする ActionKind の一覧
    fn supported_kinds(&self) -> &[ActionKind];

    /// このActionを実行可能か
    fn can_execute(&self, action: &Action) -> bool {
        self.supported_kinds().contains(&action.kind)
    }

    /// 名前(デバッグ用)
    fn name(&self) -> &'static str {
        "Executor"
    }
}

/// 複数の Executor をチェーンする
pub struct CompositeExecutor {
    executors: Vec<Box<dyn Executor>>,
}

impl CompositeExecutor {
    /// 新しい CompositeExecutor を作成
    pub fn new() -> Self {
        Self {
            executors: Vec::new(),
        }
    }

    /// Executor を追加
    pub fn with<E: Executor + 'static>(mut self, executor: E) -> Self {
        self.executors.push(Box::new(executor));
        self
    }
}

impl Default for CompositeExecutor {
    fn default() -> Self {
        Self::new()
    }
}

impl Executor for CompositeExecutor {
    fn execute(
        &self,
        action: &Action,
        ctx: &ExecutionContext,
    ) -> Result<ActionResult, ExecutorError> {
        // 最初に処理可能な Executor を使う
        for executor in &self.executors {
            if executor.can_execute(action) {
                return executor.execute(action, ctx);
            }
        }
        Err(ExecutorError::UnsupportedAction(action.kind))
    }

    fn supported_kinds(&self) -> &[ActionKind] {
        // 全 Executor のサポートを集約(簡略化のため空を返す)
        &[]
    }

    fn can_execute(&self, action: &Action) -> bool {
        self.executors.iter().any(|e| e.can_execute(action))
    }

    fn name(&self) -> &'static str {
        "CompositeExecutor"
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_executor_error_display() {
        let err = ExecutorError::UnsupportedAction(ActionKind::Read);
        assert!(err.to_string().contains("Unsupported"));

        let err = ExecutorError::FileNotFound("test.rs".to_string());
        assert!(err.to_string().contains("test.rs"));
    }
}