Skip to main content

Coding Standards

ClawDesk follows idiomatic Rust conventions with project-specific patterns. All code must pass cargo clippy --workspace and cargo fmt --check before merge.

Rust Edition & Toolchain

  • Edition: 2024
  • Minimum Rust version: 1.91
  • License: MIT
  • Author: Sushanth

Error Handling

Use thiserror for library error types and anyhow for application-level (binary) code.

Library Crates

use thiserror::Error;

#[derive(Debug, Error)]
pub enum ChannelError {
#[error("connection failed: {0}")]
ConnectionFailed(String),

#[error("rate limited, retry after {retry_after_secs}s")]
RateLimited { retry_after_secs: u64 },

#[error("authentication failed")]
AuthFailed,

#[error(transparent)]
Storage(#[from] StorageError),
}

Binary Crates

use anyhow::{Context, Result};

fn main() -> Result<()> {
let config = load_config()
.context("failed to load configuration")?;
Ok(())
}
warning

Never use unwrap() or expect() in library code. Use ? propagation or explicit error handling. Panics via unwrap are only acceptable in tests and main().

Naming Conventions

ItemConventionExample
Cratesclawdesk-{name} kebab-caseclawdesk-agents
Modulessnake_caserate_limit, context_guard
TypesPascalCaseSessionStore, ChannelError
TraitsPascalCase, noun/adjectiveChannel, Streaming, Threaded
Functionssnake_case, verb-firstsend_message(), build_context()
ConstantsSCREAMING_SNAKE_CASEMAX_RETRIES, DEFAULT_TIMEOUT
Type parametersSingle uppercase letter or descriptiveT, S: SessionStore

Module Structure

Each crate follows a consistent layout:

clawdesk-{name}/
├── Cargo.toml
├── src/
│ ├── lib.rs # Public API re-exports
│ ├── error.rs # Crate-specific error types
│ ├── {module}.rs # Feature modules
│ └── tests/ # Integration tests (if needed)
└── tests/
└── integration.rs # Cross-module integration tests

lib.rs Pattern

//! # clawdesk-channel
//!
//! Channel abstraction layer for ClawDesk.
//! Defines the core `Channel` trait and optional Layer 1 capabilities.

mod error;
mod health;
mod rate_limit;
mod registry;

pub use error::ChannelError;
pub use health::HealthCheck;
pub use rate_limit::RateLimiter;
pub use registry::ChannelRegistry;

Dependency Rules

ClawDesk uses a hexagonal architecture with strict dependency direction:

  1. clawdesk-types — Shared types, depended on by everything
  2. Domain crates (domain, channel, agents) — Depend only on types
  3. Infrastructure crates (storage, sochdb, providers) — Implement domain traits
  4. Application crates (gateway, cli, tauri) — Wire everything together
danger

Domain crates must never depend on infrastructure crates. Use trait-based dependency inversion.

Async Patterns

  • Use tokio as the async runtime (multi-threaded)
  • Prefer async fn over manual Future implementations
  • Use async-trait for async trait methods
  • Cancellation safety: document whether functions are cancel-safe
use async_trait::async_trait;

#[async_trait]
pub trait SessionStore: Send + Sync {
async fn get(&self, id: &SessionId) -> Result<Option<Session>, StorageError>;
async fn save(&self, session: &Session) -> Result<(), StorageError>;
}

Documentation

  • All public items must have doc comments (///)
  • Module-level docs use //!
  • Include # Examples sections for non-trivial APIs
  • Use # Errors sections to document error conditions
/// Sends a message through the specified channel.
///
/// # Arguments
///
/// * `channel` - The target channel identifier
/// * `message` - The message payload to deliver
///
/// # Errors
///
/// Returns [`ChannelError::ConnectionFailed`] if the channel is unreachable.
/// Returns [`ChannelError::RateLimited`] if the send rate exceeds limits.
pub async fn send_message(
channel: &ChannelId,
message: &Message,
) -> Result<(), ChannelError> {
// ...
}

Clippy Lints

The workspace enables these clippy lint groups:

[workspace.lints.clippy]
pedantic = "warn"
nursery = "warn"
unwrap_used = "deny"
expect_used = "deny"

Common lint fixes:

LintFix
clippy::module_name_repetitionsAllow with #[allow()] where clarity requires it
clippy::missing_errors_docAdd # Errors doc section
clippy::must_use_candidateAdd #[must_use] or suppress if appropriate

Formatting

Run cargo fmt before every commit. The project uses default rustfmt settings with one override:

# rustfmt.toml
max_width = 100
use_field_init_shorthand = true

Imports

Order imports by group, separated by blank lines:

// 1. Standard library
use std::collections::HashMap;
use std::sync::Arc;

// 2. External crates
use serde::{Deserialize, Serialize};
use tokio::sync::RwLock;

// 3. Workspace crates
use clawdesk_types::{ChannelId, Message};

// 4. Local modules
use crate::registry::ChannelRegistry;

Release Profile

The workspace release profile is optimized for production:

[profile.release]
lto = "fat"
strip = true
panic = "abort"

This produces smaller, faster binaries but increases compile times.