use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[cfg_attr(feature = "schema_gen", derive(schemars::JsonSchema))]
#[serde(rename_all = "snake_case")]
pub enum Surface {
Cli,
Mcp,
Skill,
Plugin,
}
impl Surface {
pub const fn as_str(&self) -> &'static str {
match self {
Self::Cli => "cli",
Self::Mcp => "mcp",
Self::Skill => "skill",
Self::Plugin => "plugin",
}
}
pub fn parse(s: &str) -> Option<Self> {
match s {
"cli" => Some(Self::Cli),
"mcp" => Some(Self::Mcp),
"skill" => Some(Self::Skill),
"plugin" => Some(Self::Plugin),
_ => None,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn as_str_round_trips_via_parse() {
for s in [Surface::Cli, Surface::Mcp, Surface::Skill, Surface::Plugin] {
assert_eq!(Surface::parse(s.as_str()), Some(s));
}
}
#[test]
fn as_str_uses_lowercase_tokens() {
assert_eq!(Surface::Cli.as_str(), "cli");
assert_eq!(Surface::Mcp.as_str(), "mcp");
assert_eq!(Surface::Skill.as_str(), "skill");
assert_eq!(Surface::Plugin.as_str(), "plugin");
}
#[test]
fn serde_round_trips_for_all_variants() {
for s in [Surface::Cli, Surface::Mcp, Surface::Skill, Surface::Plugin] {
let j = serde_json::to_string(&s).expect("serialize");
let back: Surface = serde_json::from_str(&j).expect("deserialize");
assert_eq!(s, back);
}
}
#[test]
fn serde_wire_format_is_lowercase_token() {
assert_eq!(serde_json::to_string(&Surface::Cli).unwrap(), "\"cli\"");
assert_eq!(serde_json::to_string(&Surface::Mcp).unwrap(), "\"mcp\"");
assert_eq!(serde_json::to_string(&Surface::Skill).unwrap(), "\"skill\"");
assert_eq!(
serde_json::to_string(&Surface::Plugin).unwrap(),
"\"plugin\""
);
}
#[test]
fn all_four_variants_distinct() {
let all = [Surface::Cli, Surface::Mcp, Surface::Skill, Surface::Plugin];
assert_eq!(all.len(), 4);
for (i, a) in all.iter().enumerate() {
for (j, b) in all.iter().enumerate() {
if i == j {
assert_eq!(a, b);
} else {
assert_ne!(a, b);
}
}
}
}
#[test]
fn parse_returns_none_for_unknown_token() {
assert_eq!(Surface::parse(""), None);
assert_eq!(Surface::parse("CLI"), None);
assert_eq!(Surface::parse("cli "), None);
assert_eq!(Surface::parse("agent"), None);
}
}