use serde::{Deserialize, Serialize};
#[derive(Debug, Eq, PartialEq, Clone, Hash, Serialize, Deserialize, Ord, PartialOrd)]
#[repr(transparent)]
pub struct Component(String);
impl schemars::JsonSchema for Component {
fn schema_name() -> std::borrow::Cow<'static, str> {
"Component".into()
}
fn json_schema(_generator: &mut schemars::SchemaGenerator) -> schemars::Schema {
schemars::json_schema!({
"type": "string",
"description": "Identifies a specific plugin and atomic functionality to execute. Use component name for builtins (e.g., 'eval') or path format for plugins (e.g., '/python/udf').",
"examples": ["/builtin/eval", "/mcpfs/list_files", "/python/udf"]
})
}
}
impl std::fmt::Display for Component {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
impl Component {
pub fn for_plugin(plugin: &str, component_path: &str) -> Self {
debug_assert!(
component_path.starts_with('/'),
"component_path must start with '/'"
);
Self(format!("/{plugin}{component_path}"))
}
pub fn from_string(input: impl Into<String>) -> Self {
Self(input.into())
}
pub fn path(&self) -> &str {
&self.0
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_component_new() {
let component = Component::for_plugin("mock", "/test");
assert_eq!(component.path(), "/mock/test");
}
#[test]
fn test_component_serialization() {
let component = Component::from_string("/mock/test");
let json = serde_json::to_string(&component).unwrap();
assert_eq!(json, "\"/mock/test\"");
}
#[test]
fn test_component_deserialization() {
let component: Component = serde_json::from_str("\"/mock/test\"").unwrap();
assert_eq!(component.path(), "/mock/test");
}
#[test]
fn test_new_plugin() {
let component = Component::for_plugin("mcp", "/tool/component");
assert_eq!(component.path(), "/mcp/tool/component");
}
}