github-bot-sdk 0.2.0

A comprehensive Rust SDK for GitHub App integration with authentication, webhooks, and API client
Documentation
# Code Standards


## Code Organization


### Workspace Structure


Currently organized as a Cargo workspace. See Cargo.toml for current crate structure.

### Module Structure


Organize code into logical modules following clean architecture principles:

```
src/
├── lib.rs              # Public API and module declarations
├── error.rs            # Error types and handling
├── error_tests.rs      # Error tests
├── module1.rs          # Module 1 public interface (use for single-file modules)
├── module1/
│   ├── mod.rs          # Module public interface (use for multi-file modules)
│   ├── mod_tests.rs    # Module-level tests
│   ├── types.rs        # Domain types
│   ├── types_tests.rs  # Type tests
│   └── implementation.rs
├── module2.rs          # Module 2 public interface
└── module2/
    └── ...
```

**File vs Directory Modules**:

- Single file `module.rs`: Module fits in one file, no submodules needed
- Directory `module/mod.rs`: Module requires multiple files or has submodules

### Naming Conventions


- **Module names**: lowercase with underscores (`auth_provider`, `queue_client`)
- **Type names**: PascalCase (`GitHubAppId`, `InstallationToken`)
- **Function names**: snake_case (`get_installation_token`, `is_expired`)
- **Test files**: `<source_file>_tests.rs`
- **Test functions**: `test_<what_is_being_tested>`

## Documentation


### Rustdoc Requirements


All public APIs must have rustdoc comments:

```rust
/// Brief one-line summary of what this does.
///
/// More detailed explanation of the functionality, including:
/// - Key behaviors
/// - Important constraints
/// - Edge cases
///
/// # Examples
///
/// ```rust
/// use crate::MyType;
///
/// let instance = MyType::new(42);
/// assert_eq!(instance.value(), 42);
/// ```
///
/// # Errors
///
/// Returns `ErrorType` if:
/// - Condition 1
/// - Condition 2
///
/// # Panics
///
/// Documents any conditions that cause panics.
pub fn public_api() -> Result<(), ErrorType> {
    // Implementation...
}
```

### Test Documentation


Test functions should have doc comments explaining what they verify:

```rust
/// Verify that expired tokens are correctly identified.
///
/// Creates a token that expired 5 minutes ago and verifies
/// that `is_expired()` returns true.
#[test]

fn test_token_expiration_detection() {
    // Test implementation...
}
```

## Error Handling


### Error Type Guidelines


1. Use `thiserror` for error type derivation
2. Implement retry classification (`is_transient()`, `should_retry()`)
3. Include sufficient context for debugging
4. Never expose secrets in error messages
5. Use `.context()` to build error chains with additional context

```rust
#[derive(Debug, Error)]

pub enum MyError {
    #[error("Operation failed: {context}")]
    OperationFailed { context: String },

    #[error("Resource not found: {id}")]
    NotFound { id: String },
}

impl MyError {
    pub fn is_transient(&self) -> bool {
        match self {
            Self::OperationFailed { .. } => true,
            Self::NotFound { .. } => false,
        }
    }
}
```

## Rust-Specific Conventions


### Type Safety


- Use newtype pattern for domain identifiers
- Leverage type system to prevent invalid states
- Use `#[must_use]` for types that shouldn't be ignored

```rust
/// GitHub App identifier.
///
/// This is a newtype wrapper to prevent mixing up different ID types.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]

#[must_use]

pub struct GitHubAppId(u64);
```

### Async/Await


- All I/O operations must be async
- Use `#[async_trait]` for async trait methods
- Document cancellation behavior
- Ensure proper resource cleanup
- Use `tokio::spawn` for concurrent tasks; avoid blocking operations in async context
- Implement timeouts with `tokio::time::timeout` for operations that may hang
- Use `tokio::select!` for cancellation patterns

### Trait Design


- Use traits for abstraction boundaries and testability
- Prefer trait objects (`dyn Trait`) for runtime polymorphism
- Use generic bounds (`T: Trait`) for compile-time polymorphism
- Document trait contracts thoroughly, including preconditions and postconditions
- Implement common traits (`Clone`, `Debug`, `Send`, `Sync`) when appropriate

### Performance Considerations


- Prefer borrowing (`&T`) over cloning when possible
- Use `Arc<T>` for shared ownership across threads
- Use `Cow<'_, T>` when data may or may not need to be owned
- Avoid unnecessary allocations in hot paths
- Profile before optimizing; measure impact of changes

### Security


- Never log secrets or tokens
- Implement `Debug` carefully for sensitive types
- Use constant-time comparison for security-critical operations
- Zero sensitive memory on drop when possible
- Validate and sanitize all external inputs (webhooks, API responses, user data)
- Use type system to distinguish validated vs unvalidated data

```rust
impl std::fmt::Debug for SecretToken {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("SecretToken")
            .field("token", &"<REDACTED>")
            .finish()
    }
}
```

### Logging and Observability


- Use structured logging with appropriate levels (trace, debug, info, warn, error)
- **Critical**: Never log secrets, tokens, or sensitive data in any form
- Log operation start/end for auditing
- Include correlation IDs for distributed tracing
- Use `Debug` trait carefully on types containing sensitive data