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];
19
20/// Parse a provider name string into a `CliRunnerType`
21///
22/// Accepts multiple naming conventions: `snake_case`, kebab-case, and
23/// short forms for flexible input.
24pub fn parse_runner_type(s: &str) -> Option<CliRunnerType> {
25    match s.to_lowercase().as_str() {
26        "claude_code" | "claude" | "claudecode" => Some(CliRunnerType::ClaudeCode),
27        "copilot" => Some(CliRunnerType::Copilot),
28        "cursor_agent" | "cursoragent" | "cursor-agent" => Some(CliRunnerType::CursorAgent),
29        "opencode" | "open_code" => Some(CliRunnerType::OpenCode),
30        _ => None,
31    }
32}
33
34/// Format the list of valid provider names for error messages
35pub const fn valid_provider_names() -> &'static str {
36    "claude_code, copilot, cursor_agent, opencode"
37}
38
39#[cfg(test)]
40mod tests {
41    use super::*;
42
43    #[test]
44    fn parse_snake_case_variants() {
45        assert_eq!(
46            parse_runner_type("claude_code"),
47            Some(CliRunnerType::ClaudeCode)
48        );
49        assert_eq!(parse_runner_type("copilot"), Some(CliRunnerType::Copilot));
50        assert_eq!(
51            parse_runner_type("cursor_agent"),
52            Some(CliRunnerType::CursorAgent)
53        );
54        assert_eq!(parse_runner_type("opencode"), Some(CliRunnerType::OpenCode));
55    }
56
57    #[test]
58    fn parse_short_forms() {
59        assert_eq!(parse_runner_type("claude"), Some(CliRunnerType::ClaudeCode));
60        assert_eq!(
61            parse_runner_type("cursor-agent"),
62            Some(CliRunnerType::CursorAgent)
63        );
64    }
65
66    #[test]
67    fn parse_case_insensitive() {
68        assert_eq!(parse_runner_type("COPILOT"), Some(CliRunnerType::Copilot));
69        assert_eq!(
70            parse_runner_type("Claude_Code"),
71            Some(CliRunnerType::ClaudeCode)
72        );
73    }
74
75    #[test]
76    fn parse_unknown_returns_none() {
77        assert_eq!(parse_runner_type("gpt4"), None);
78        assert_eq!(parse_runner_type(""), None);
79    }
80
81    #[test]
82    fn all_providers_has_four_entries() {
83        assert_eq!(ALL_PROVIDERS.len(), 4);
84    }
85}