Skip to main content

embacle_server/runner/
mod.rs

1// ABOUTME: Runner management layer bridging REST handlers to embacle LlmProvider instances
2// ABOUTME: Provides factory creation, provider type parsing, and multiplex fan-out engine
3//
4// SPDX-License-Identifier: Apache-2.0
5// Copyright (c) 2026 dravr.ai
6
7pub mod factory;
8pub mod multiplex;
9
10use embacle::config::CliRunnerType;
11
12/// All provider types supported by embacle, in discovery priority order
13pub const ALL_PROVIDERS: &[CliRunnerType] = &[
14    CliRunnerType::ClaudeCode,
15    CliRunnerType::Copilot,
16    CliRunnerType::CursorAgent,
17    CliRunnerType::OpenCode,
18    CliRunnerType::GeminiCli,
19    CliRunnerType::CodexCli,
20];
21
22/// Parse a provider name string into a `CliRunnerType`
23///
24/// Accepts multiple naming conventions: `snake_case`, kebab-case, and
25/// short forms for flexible input.
26pub fn parse_runner_type(s: &str) -> Option<CliRunnerType> {
27    match s.to_lowercase().as_str() {
28        "claude_code" | "claude" | "claudecode" => Some(CliRunnerType::ClaudeCode),
29        "copilot" => Some(CliRunnerType::Copilot),
30        "cursor_agent" | "cursoragent" | "cursor-agent" => Some(CliRunnerType::CursorAgent),
31        "opencode" | "open_code" => Some(CliRunnerType::OpenCode),
32        "gemini" | "gemini_cli" | "geminicli" | "gemini-cli" => Some(CliRunnerType::GeminiCli),
33        "codex" | "codex_cli" | "codexcli" | "codex-cli" => Some(CliRunnerType::CodexCli),
34        _ => None,
35    }
36}
37
38/// Format the list of valid provider names for error messages
39pub const fn valid_provider_names() -> &'static str {
40    "claude_code, copilot, cursor_agent, opencode, gemini_cli, codex_cli"
41}
42
43#[cfg(test)]
44mod tests {
45    use super::*;
46
47    #[test]
48    fn parse_snake_case_variants() {
49        assert_eq!(
50            parse_runner_type("claude_code"),
51            Some(CliRunnerType::ClaudeCode)
52        );
53        assert_eq!(parse_runner_type("copilot"), Some(CliRunnerType::Copilot));
54        assert_eq!(
55            parse_runner_type("cursor_agent"),
56            Some(CliRunnerType::CursorAgent)
57        );
58        assert_eq!(parse_runner_type("opencode"), Some(CliRunnerType::OpenCode));
59        assert_eq!(
60            parse_runner_type("gemini_cli"),
61            Some(CliRunnerType::GeminiCli)
62        );
63        assert_eq!(
64            parse_runner_type("codex_cli"),
65            Some(CliRunnerType::CodexCli)
66        );
67    }
68
69    #[test]
70    fn parse_short_forms() {
71        assert_eq!(parse_runner_type("claude"), Some(CliRunnerType::ClaudeCode));
72        assert_eq!(
73            parse_runner_type("cursor-agent"),
74            Some(CliRunnerType::CursorAgent)
75        );
76        assert_eq!(parse_runner_type("gemini"), Some(CliRunnerType::GeminiCli));
77        assert_eq!(parse_runner_type("codex"), Some(CliRunnerType::CodexCli));
78    }
79
80    #[test]
81    fn parse_case_insensitive() {
82        assert_eq!(parse_runner_type("COPILOT"), Some(CliRunnerType::Copilot));
83        assert_eq!(
84            parse_runner_type("Claude_Code"),
85            Some(CliRunnerType::ClaudeCode)
86        );
87    }
88
89    #[test]
90    fn parse_unknown_returns_none() {
91        assert_eq!(parse_runner_type("gpt4"), None);
92        assert_eq!(parse_runner_type(""), None);
93    }
94
95    #[test]
96    fn all_providers_has_six_entries() {
97        assert_eq!(ALL_PROVIDERS.len(), 6);
98    }
99}