pub trait Resource {
// Required methods
fn name(&self) -> &str;
fn resource_type(&self) -> ResourceType;
fn description(&self) -> Option<&str>;
fn validate(&self) -> Result<()>;
fn install(&self, target: &Path, profile: Option<&str>) -> Result<()>;
fn install_path(&self, base: &Path) -> PathBuf;
fn metadata(&self) -> Result<Value>;
fn as_any(&self) -> &dyn Any;
}Expand description
Base trait defining the interface for all AGPM resources
This trait provides a common interface for different types of resources (agents, snippets) managed by AGPM. It abstracts the core operations that can be performed on any resource, including validation, installation, and metadata access.
§Design Principles
- Type Safety: Each resource has a specific
ResourceType - Validation: Resources can validate their own structure and dependencies
- Installation: Resources know how to install themselves to target locations
- Metadata: Resources provide structured metadata for tooling and display
- Flexibility: Resources can be profiled or configured during installation
§Implementation Requirements
Implementors of this trait should:
- Provide meaningful error messages in validation failures
- Support atomic installation operations (no partial installs on failure)
- Generate deterministic installation paths
- Include rich metadata for resource discovery and management
§Examples
§Basic Resource Usage Pattern
use agpm_cli::core::{Resource, ResourceType};
use anyhow::Result;
use std::path::Path;
fn process_resource(resource: &dyn Resource) -> Result<()> {
// Get basic information
println!("Processing resource: {}", resource.name());
println!("Type: {}", resource.resource_type());
if let Some(description) = resource.description() {
println!("Description: {}", description);
}
// Validate the resource
resource.validate()?;
// Install to default location
let target = Path::new("./resources");
let install_path = resource.install_path(target);
resource.install(&install_path, None)?;
Ok(())
}§Metadata Extraction
use agpm_cli::core::Resource;
use anyhow::Result;
fn extract_metadata(resource: &dyn Resource) -> Result<()> {
let metadata = resource.metadata()?;
// Metadata is JSON Value for flexibility
if let Some(version) = metadata.get("version") {
println!("Resource version: {}", version);
}
if let Some(tags) = metadata.get("tags").and_then(|t| t.as_array()) {
println!("Tags: {:?}", tags);
}
Ok(())
}§Trait Object Usage
The trait is object-safe and can be used as a trait object:
use agpm_cli::core::Resource;
use std::any::Any;
fn handle_resource(resource: Box<dyn Resource>) {
println!("Handling resource: {}", resource.name());
// Can be downcasted to concrete types if needed
let any = resource.as_any();
// ... downcasting logic
}Required Methods§
Sourcefn name(&self) -> &str
fn name(&self) -> &str
Get the unique name identifier for this resource
The name is used to identify the resource in manifests, lockfiles, and CLI operations. It should be unique within a project’s namespace.
§Returns
A string slice containing the resource name
§Examples
use agpm_cli::core::Resource;
fn print_resource_info(resource: &dyn Resource) {
println!("Resource name: {}", resource.name());
}Sourcefn resource_type(&self) -> ResourceType
fn resource_type(&self) -> ResourceType
Get the resource type classification
Returns the ResourceType enum value that identifies what kind of
resource this is (Agent, Snippet, etc.).
§Returns
The ResourceType for this resource
§Examples
use agpm_cli::core::{Resource, ResourceType};
fn categorize_resource(resource: &dyn Resource) {
match resource.resource_type() {
ResourceType::Agent => println!("This is an AI agent"),
ResourceType::Snippet => println!("This is a code snippet"),
ResourceType::Command => println!("This is a Claude Code command"),
ResourceType::McpServer => println!("This is an MCP server"),
ResourceType::Script => println!("This is an executable script"),
ResourceType::Hook => println!("This is a hook configuration"),
ResourceType::Skill => println!("This is a Claude Skill"),
}
}Sourcefn description(&self) -> Option<&str>
fn description(&self) -> Option<&str>
Get the human-readable description of this resource
Returns an optional description that explains what the resource does or how it should be used. This is typically displayed in resource listings and help text.
§Returns
Some(description)if the resource has a descriptionNoneif no description is available
§Examples
use agpm_cli::core::Resource;
fn show_resource_details(resource: &dyn Resource) {
println!("Name: {}", resource.name());
if let Some(desc) = resource.description() {
println!("Description: {}", desc);
} else {
println!("No description available");
}
}Sourcefn validate(&self) -> Result<()>
fn validate(&self) -> Result<()>
Validate the resource structure and content
Performs comprehensive validation of the resource including:
- File structure integrity
- Content format validation
- Dependency constraint checking
- Metadata consistency
§Returns
Ok(())if the resource is validErr(error)with detailed validation failure information
§Examples
use agpm_cli::core::Resource;
use anyhow::Result;
fn validate_before_install(resource: &dyn Resource) -> Result<()> {
resource.validate()
.map_err(|e| anyhow::anyhow!("Resource validation failed: {}", e))?;
println!("Resource {} is valid", resource.name());
Ok(())
}Sourcefn install(&self, target: &Path, profile: Option<&str>) -> Result<()>
fn install(&self, target: &Path, profile: Option<&str>) -> Result<()>
Install the resource to the specified target path
Performs the actual installation of the resource files to the target location. This operation should be atomic - either it succeeds completely or fails without making any changes.
§Arguments
target- The directory path where the resource should be installedprofile- Optional profile name for customized installation (may be unused)
§Returns
Ok(())if installation succeedsErr(error)if installation fails with detailed error information
§Examples
use agpm_cli::core::Resource;
use std::path::Path;
use anyhow::Result;
fn install_resource(resource: &dyn Resource) -> Result<()> {
let target = Path::new("./installed-resources");
// Validate first
resource.validate()?;
// Install without profile
resource.install(target, None)?;
println!("Successfully installed {}", resource.name());
Ok(())
}Sourcefn install_path(&self, base: &Path) -> PathBuf
fn install_path(&self, base: &Path) -> PathBuf
Calculate the installation path for this resource
Determines where this resource would be installed relative to a base directory. This is used for path planning and conflict detection.
§Arguments
base- The base directory for installation
§Returns
The full path where this resource would be installed
§Examples
use agpm_cli::core::Resource;
use std::path::Path;
fn check_install_location(resource: &dyn Resource) {
let base = Path::new("/project/resources");
let install_path = resource.install_path(base);
println!("{} would be installed to: {}",
resource.name(),
install_path.display()
);
}Sourcefn metadata(&self) -> Result<Value>
fn metadata(&self) -> Result<Value>
Get structured metadata for this resource as JSON
Returns resource metadata in a flexible JSON format that can include version information, tags, author details, and other custom fields. This metadata is used for resource discovery, filtering, and display.
§Returns
Ok(json_value)containing the metadataErr(error)if metadata cannot be generated or parsed
§Metadata Structure
While flexible, metadata typically includes:
name: Resource nametype: Resource typeversion: Version informationdescription: Human-readable descriptiontags: Array of classification tagsdependencies: Dependency information
§Examples
use agpm_cli::core::Resource;
use anyhow::Result;
fn show_resource_metadata(resource: &dyn Resource) -> Result<()> {
let metadata = resource.metadata()?;
if let Some(version) = metadata.get("version") {
println!("Version: {}", version);
}
if let Some(tags) = metadata.get("tags").and_then(|t| t.as_array()) {
print!("Tags: ");
for tag in tags {
print!("{} ", tag.as_str().unwrap_or("?"));
}
println!();
}
Ok(())
}Sourcefn as_any(&self) -> &dyn Any
fn as_any(&self) -> &dyn Any
Get a reference to this resource as std::any::Any for downcasting
This method enables downcasting from the Resource trait object to
concrete resource implementations when needed for type-specific operations.
§Returns
A reference to this resource as std::any::Any
§Examples
use agpm_cli::core::Resource;
use std::any::Any;
// Hypothetical concrete resource type
struct MyAgent {
name: String,
// ... other fields
}
fn try_downcast_to_agent(resource: &dyn Resource) {
let any = resource.as_any();
if let Some(agent) = any.downcast_ref::<MyAgent>() {
println!("Successfully downcasted to MyAgent: {}", agent.name);
} else {
println!("Resource is not a MyAgent type");
}
}