use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
#[non_exhaustive]
#[serde(rename_all = "snake_case")]
pub enum Harness {
ClaudeCode,
Pi,
Codex,
Amp,
Qwen,
Gemini,
#[default]
#[serde(other)]
Unknown,
}
impl Harness {
#[must_use]
pub fn short_tag(&self) -> &'static str {
match self {
Self::ClaudeCode => "claude",
Self::Pi => "pi",
Self::Codex => "codex",
Self::Amp => "amp",
Self::Qwen => "qwen",
Self::Gemini => "gemini",
Self::Unknown => "?",
}
}
}
impl fmt::Display for Harness {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(match self {
Self::ClaudeCode => "Claude Code",
Self::Pi => "pi",
Self::Codex => "Codex CLI",
Self::Amp => "Amp",
Self::Qwen => "Qwen Code",
Self::Gemini => "Gemini CLI",
Self::Unknown => "unknown",
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn harness_serializes_snake_case() {
assert_eq!(
serde_json::to_string(&Harness::ClaudeCode).unwrap(),
"\"claude_code\""
);
assert_eq!(serde_json::to_string(&Harness::Pi).unwrap(), "\"pi\"");
assert_eq!(serde_json::to_string(&Harness::Codex).unwrap(), "\"codex\"");
assert_eq!(serde_json::to_string(&Harness::Amp).unwrap(), "\"amp\"");
assert_eq!(serde_json::to_string(&Harness::Qwen).unwrap(), "\"qwen\"");
assert_eq!(
serde_json::to_string(&Harness::Gemini).unwrap(),
"\"gemini\""
);
assert_eq!(
serde_json::to_string(&Harness::Unknown).unwrap(),
"\"unknown\""
);
}
#[test]
fn harness_default_is_unknown() {
assert_eq!(Harness::default(), Harness::Unknown);
}
#[test]
fn short_tag_matches_display_intent() {
assert_eq!(Harness::ClaudeCode.short_tag(), "claude");
assert_eq!(Harness::Pi.short_tag(), "pi");
assert_eq!(Harness::Codex.short_tag(), "codex");
assert_eq!(Harness::Amp.short_tag(), "amp");
assert_eq!(Harness::Qwen.short_tag(), "qwen");
assert_eq!(Harness::Gemini.short_tag(), "gemini");
assert_eq!(Harness::Unknown.short_tag(), "?");
}
}