Claude Code Plugins

Community-maintained marketplace

Feedback

rust-code-organization

@gar-ai/mallorn
1
0

Structure Rust code using modules, traits, builders, and newtypes. Use when designing APIs, abstracting behavior, or preventing type confusion.

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name rust-code-organization
description Structure Rust code using modules, traits, builders, and newtypes. Use when designing APIs, abstracting behavior, or preventing type confusion.

Code Organization

Patterns for structuring maintainable Rust code.

Module Structure

Organize by feature/domain, not by type:

src/
├── lib.rs              # Public API exports
├── error.rs            # Error types
├── config.rs           # Configuration
├── db/
│   ├── mod.rs          # Module exports
│   ├── repo.rs         # Repository implementation
│   └── models.rs       # Database models
├── pipeline/
│   ├── mod.rs
│   ├── processor.rs
│   └── prefetch.rs
└── scheduler/
    ├── mod.rs
    ├── manager.rs
    └── queue.rs

Module Visibility

// mod.rs - Control what's public
mod internal;           // Private to this module
pub mod public;         // Public to external crates
pub(crate) mod shared;  // Public within crate only
pub(super) mod parent;  // Public to parent module only

// Re-export for clean API
pub use self::repo::VideoRepository;
pub use self::models::{VideoRecord, VideoStatus};

Traits for Abstraction

// Define behavior as trait
pub trait Repository {
    type Record;
    type Error;

    async fn get(&self, id: &str) -> Result<Self::Record, Self::Error>;
    async fn save(&self, record: &Self::Record) -> Result<(), Self::Error>;
}

// Business logic depends on trait, not concrete type
pub async fn process<R: Repository>(repo: &R, id: &str) -> Result<()> {
    let record = repo.get(id).await?;
    // Process...
    repo.save(&record).await
}

// Easy to mock in tests
#[cfg(test)]
struct MockRepository;

#[cfg(test)]
impl Repository for MockRepository {
    // Test implementation
}

Builder Pattern

pub struct Config {
    host: String,
    port: u16,
    timeout: Duration,
}

#[derive(Default)]
pub struct ConfigBuilder {
    host: Option<String>,
    port: Option<u16>,
    timeout: Option<Duration>,
}

impl ConfigBuilder {
    pub fn new() -> Self {
        Self::default()
    }

    pub fn host(mut self, host: impl Into<String>) -> Self {
        self.host = Some(host.into());
        self
    }

    pub fn port(mut self, port: u16) -> Self {
        self.port = Some(port);
        self
    }

    pub fn timeout(mut self, timeout: Duration) -> Self {
        self.timeout = Some(timeout);
        self
    }

    pub fn build(self) -> Result<Config, ConfigError> {
        Ok(Config {
            host: self.host.ok_or(ConfigError::MissingHost)?,
            port: self.port.unwrap_or(8080),
            timeout: self.timeout.unwrap_or(Duration::from_secs(30)),
        })
    }
}

// Usage
let config = ConfigBuilder::new()
    .host("localhost")
    .port(3000)
    .build()?;

Newtype Pattern

Prevent mixing up types that have the same underlying representation:

// Without newtype - easy to mix up!
fn process_user(user_id: Uuid, order_id: Uuid) { ... }
process_user(order_id, user_id);  // Compiles but wrong!

// With newtype - compile-time safety
pub struct UserId(Uuid);
pub struct OrderId(Uuid);

fn process_user(user_id: UserId, order_id: OrderId) { ... }
process_user(order_id, user_id);  // Compile error!

// Derive common traits
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct VideoId(pub i64);

impl VideoId {
    pub fn new(id: i64) -> Self {
        Self(id)
    }

    pub fn inner(&self) -> i64 {
        self.0
    }
}

// Implement Display for logging
impl std::fmt::Display for VideoId {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "video:{}", self.0)
    }
}

Function Composition

fn process_user_data(data: &str) -> Result<User> {
    parse_user_data(data)
        .and_then(validate_user)
        .map(transform_user)
}

// With ? operator for early return
fn process_video(path: &Path) -> Result<Video> {
    let data = read_file(path)?;
    let parsed = parse_video(&data)?;
    let validated = validate_video(parsed)?;
    Ok(transform_video(validated))
}

Struct Update Syntax

#[derive(Clone)]
pub struct Options {
    pub timeout: Duration,
    pub retries: u32,
    pub verbose: bool,
}

impl Default for Options {
    fn default() -> Self {
        Self {
            timeout: Duration::from_secs(30),
            retries: 3,
            verbose: false,
        }
    }
}

// Create with partial overrides
let options = Options {
    timeout: Duration::from_secs(60),
    ..Default::default()
};

Extension Traits

// Add methods to external types
pub trait StringExt {
    fn truncate_to(&self, max_len: usize) -> &str;
}

impl StringExt for str {
    fn truncate_to(&self, max_len: usize) -> &str {
        if self.len() <= max_len {
            self
        } else {
            &self[..max_len]
        }
    }
}

// Usage
let short = long_string.truncate_to(100);

Guidelines

  • Organize modules by domain, not by type
  • Use pub(crate) for internal APIs
  • Define traits for testable abstractions
  • Use builders for complex configuration
  • Use newtypes to prevent type confusion
  • Prefer composition over inheritance
  • Keep public API surface minimal

Examples

See hercules-local-algo/src/ for production module organization.