Resource

Trait Resource 

Source
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§

Source

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());
}
Source

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"),
    }
}
Source

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 description
  • None if 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");
    }
}
Source

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 valid
  • Err(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(())
}
Source

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 installed
  • profile - Optional profile name for customized installation (may be unused)
§Returns
  • Ok(()) if installation succeeds
  • Err(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(())
}
Source

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()
    );
}
Source

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 metadata
  • Err(error) if metadata cannot be generated or parsed
§Metadata Structure

While flexible, metadata typically includes:

  • name: Resource name
  • type: Resource type
  • version: Version information
  • description: Human-readable description
  • tags: Array of classification tags
  • dependencies: 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(())
}
Source

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");
    }
}

Implementors§