Files
banks2ff/AGENTS.md
2025-11-22 15:04:04 +00:00

6.1 KiB

Banks2FF Development Guide

Project Purpose

Banks2FF is a Rust CLI tool that synchronizes bank transactions from GoCardless Bank Account Data API to Firefly III personal finance manager. It implements a hexagonal architecture for clean separation of concerns and comprehensive testing.

🚨 CRITICAL: Financial Data Security

ABSOLUTE REQUIREMENT: Financial Data Masking

NEVER expose, log, or display raw financial information including:

  • Transaction amounts
  • Account balances
  • IBANs or account numbers
  • Transaction descriptions
  • Personal identifiers
  • API keys or tokens

Compliance Protocol for Debugging

When debugging financial data issues:

  1. Create Anonymized Test Scripts: Write small, focused scripts that extract only the necessary data structure information
  2. Use Mock Data: Replace real financial values with placeholder data
  3. Validate Structure, Not Values: Focus on data structure integrity, not actual financial content
  4. Sanitize All Outputs: Ensure any debugging output masks sensitive information
// ✅ GOOD: Structure validation with mock data
fn validate_transaction_structure() {
    let mock_tx = BankTransaction {
        amount: Decimal::new(12345, 2), // Mock amount
        currency: "EUR".to_string(),
        // ... other fields with mock data
    };
    // Validate structure only
}

// ❌ BAD: Exposing real financial data
fn debug_real_transactions(transactions: Vec<BankTransaction>) {
    for tx in transactions {
        println!("Real amount: {}", tx.amount); // SECURITY VIOLATION
    }
}

Rust Development Best Practices

Error Handling

  • Use thiserror for domain-specific error types in core modules
  • Use anyhow for application-level error context and propagation
  • Never use panic! in production code - handle errors gracefully
  • Implement From traits for error type conversions
// Core domain errors
#[derive(Error, Debug)]
pub enum SyncError {
    #[error("Failed to fetch transactions from source: {0}")]
    SourceError(#[from] anyhow::Error),
    #[error("Failed to store transaction: {0}")]
    DestinationError(#[from] anyhow::Error),
}

Async Programming

  • Use tokio as the async runtime (workspace dependency)
  • Prefer async-trait for trait methods that need to be async
  • Handle cancellation properly with select! or tokio::time::timeout
  • Use ? operator for error propagation in async functions

Testing Strategy

  • Unit Tests: Test pure functions and business logic in isolation
  • Integration Tests: Test adapter implementations with wiremock
  • Mock External Dependencies: Use mockall for trait-based testing
  • Test Fixtures: Store sample JSON responses in tests/fixtures/
#[cfg(test)]
mod tests {
    use super::*;
    use mockall::predicate::*;
    
    #[tokio::test]
    async fn test_sync_with_mock_source() {
        let mut mock_source = MockTransactionSource::new();
        // Setup mock expectations
        // Test core logic
    }
}

Code Organization

  • Workspace Dependencies: Define common dependencies in root Cargo.toml
  • Feature Flags: Use features for optional functionality
  • Module Structure: Keep modules focused and single-responsibility
  • Public API: Minimize public surface area; prefer internal modules

Dependencies and Patterns

Key Workspace Dependencies:

  • tokio: Async runtime with full features
  • reqwest: HTTP client with JSON support
  • serde/serde_json: Serialization/deserialization
  • chrono: Date/time handling with serde support
  • rust_decimal: Precise decimal arithmetic for financial data
  • tracing/tracing-subscriber: Structured logging
  • clap: CLI argument parsing with derive macros
  • anyhow/thiserror: Error handling
  • async-trait: Async trait support
  • wiremock: HTTP mocking for tests
  • mockall: Runtime mocking for tests

Development Workflow

1. Code Development

  • Write code in appropriate modules following the hexagonal architecture
  • Keep core business logic separate from external integrations
  • Use workspace dependencies consistently

2. Testing

  • Write tests alongside code in #[cfg(test)] modules
  • Test both happy path and error conditions
  • Use mock objects for external dependencies
  • Ensure all tests pass: cargo test --workspace

3. Code Quality

  • Follow Rust idioms and conventions
  • Use cargo fmt for formatting
  • Use cargo clippy for linting
  • Ensure documentation for public APIs

4. Commit Standards

  • Commit both code and tests together
  • Write clear, descriptive commit messages
  • Ensure the workspace compiles: cargo build --workspace

Project Structure Guidelines

Core Module (banks2ff/src/core/)

  • models.rs: Domain entities (BankTransaction, Account)
  • ports.rs: Trait definitions (TransactionSource, TransactionDestination)
  • sync.rs: Business logic orchestration

Adapters Module (banks2ff/src/adapters/)

  • gocardless/: GoCardless API integration
  • firefly/: Firefly III API integration
  • Each adapter implements the appropriate port trait

Client Libraries

  • gocardless-client/: Standalone GoCardless API wrapper
  • firefly-client/: Standalone Firefly III API wrapper
  • Both use reqwest for HTTP communication

Security Considerations

  • Never log sensitive data: Use tracing filters to exclude financial information
  • Environment variables: Store credentials in .env file (never in code)
  • Input validation: Validate all external data before processing
  • Error messages: Don't expose sensitive information in error messages

Performance Considerations

  • Caching: Use caching to reduce API calls (see GoCardlessAdapter)
  • Rate Limiting: Handle 429 responses gracefully
  • Batch Processing: Process transactions in reasonable batches
  • Async Concurrency: Use tokio for concurrent operations where appropriate

Observability

  • Structured Logging: Use tracing with spans for operations
  • Error Context: Provide context in error messages for debugging
  • Metrics: Consider adding metrics for sync operations
  • Log Levels: Use appropriate log levels (debug, info, warn, error)