Backend Shared Crate in Rust
Find a file
Ben Slingo 86d3ab3974 Refactor church-core for modularity, maintainability, and cross-platform support
Major restructuring to follow DRY/KISS principles with proper separation of concerns.

Structural Changes:
- Rename models/ → types/ for better semantic clarity
- Add api/ module with trait-based architecture (ApiClient trait)
- Add validation/ module for centralized business logic
- Create proper feature flags: native, wasm, ios, server

Code Cleanup (-1,633 lines, ~16% reduction):
- Delete uniffi_wrapper_backup.rs (1,756 lines of old code)
- Delete redundant ffi.rs (11 lines)
- Consolidate UniFFI wrappers to minimal passthrough
- Update all imports from models:: to types::
- Make tokio and moka optional dependencies

UniFFI Improvements:
- Organize church_core.udl with clear sections and comments
- Simplify build.rs with better error handling
- Preserve all 58+ iOS functions (zero breaking changes)

Configuration Updates:
- Update default API URL: https://rockvilletollandsda.church/api
- Update all references (config, sermons, tests)

Documentation:
- Update README with new architecture
- Add Recent Improvements section
- Update feature flag documentation
- Add iOS/Swift usage examples
2025-10-24 00:48:24 -04:00
bindings/ios/ChurchCore.xcframework Fix compilation errors and complete modular refactoring 2025-08-30 16:49:35 -04:00
RTSDA Initial commit: Church Core Rust library 2025-08-16 19:25:01 -04:00
src Refactor church-core for modularity, maintainability, and cross-platform support 2025-10-24 00:48:24 -04:00
tests Refactor church-core for modularity, maintainability, and cross-platform support 2025-10-24 00:48:24 -04:00
.gitignore Clean up repository structure 2025-08-16 19:28:17 -04:00
build.rs Refactor church-core for modularity, maintainability, and cross-platform support 2025-10-24 00:48:24 -04:00
Cargo.toml Refactor church-core for modularity, maintainability, and cross-platform support 2025-10-24 00:48:24 -04:00
church_core_architecture.md Refactor church-core for modularity, maintainability, and cross-platform support 2025-10-24 00:48:24 -04:00
Makefile Initial commit: Church Core Rust library 2025-08-16 19:25:01 -04:00
README.md Refactor church-core for modularity, maintainability, and cross-platform support 2025-10-24 00:48:24 -04:00

Church Core

A shared Rust crate providing unified API access and data models for church applications across multiple platforms (iOS, Android, Web, Desktop).

Overview

Church Core centralizes all church application logic, API communication, and data management into a single, well-tested Rust crate. This enables client applications to become "dumb display devices" while ensuring consistency across platforms.

Features

  • Unified API Client: Single interface for all church APIs
  • Consistent Data Models: Shared types for events, bulletins, sermons, etc.
  • Cross-Platform Support: Native bindings for iOS, Android, WASM, and FFI
  • Built-in Caching: Automatic response caching with TTL
  • Offline Support: Graceful offline operation with cached data
  • Authentication: PocketBase and Jellyfin integration
  • Error Handling: Comprehensive error types with retry logic
  • Type Safety: Rust's type system prevents API mismatches

Architecture

church-core/
├── src/
│   ├── types/           # Pure data models (platform-agnostic)
│   │   ├── event.rs     # Event models
│   │   ├── bulletin.rs  # Bulletin models
│   │   ├── sermon.rs    # Sermon models
│   │   ├── contact.rs   # Contact models
│   │   ├── auth.rs      # Authentication models
│   │   ├── bible.rs     # Bible verse models
│   │   ├── config.rs    # Configuration models
│   │   ├── streaming.rs # Livestream models
│   │   └── ...          # Other type definitions
│   │
│   ├── api/             # API client (trait-based, platform abstraction)
│   │   ├── mod.rs       # ApiClient trait definition
│   │   ├── native.rs    # Native/iOS implementation (reqwest)
│   │   ├── wasm.rs      # WASM implementation (future)
│   │   └── endpoints.rs # URL builders
│   │
│   ├── validation/      # Business logic validation
│   │   ├── common.rs    # Email, phone validation
│   │   ├── contact.rs   # Contact form validation
│   │   └── event.rs     # Event validation
│   │
│   ├── client/          # Legacy HTTP client (being deprecated)
│   │   ├── events.rs    # Event operations
│   │   ├── bulletins.rs # Bulletin operations
│   │   ├── sermons.rs   # Sermon operations
│   │   └── ...
│   │
│   ├── uniffi/          # iOS UniFFI bindings
│   │   ├── events.rs    # Event functions
│   │   ├── bible.rs     # Bible functions
│   │   ├── config.rs    # Config functions
│   │   └── ...          # Other iOS bindings
│   │
│   ├── auth/            # Authentication modules
│   ├── cache/           # Caching system
│   ├── utils/           # Utility functions
│   ├── config.rs        # Configuration
│   ├── error.rs         # Error types
│   └── lib.rs           # Main entry point
│
├── church_core.udl      # UniFFI interface definition
├── build.rs             # Build script (UniFFI scaffolding)
└── README.md

Quick Start

Basic Usage

use church_core::{ChurchApiClient, ChurchCoreConfig};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Create client with default configuration
    let config = ChurchCoreConfig::default();
    let client = ChurchApiClient::new(config)?;
    
    // Get upcoming events
    let events = client.get_upcoming_events(Some(10)).await?;
    for event in events {
        println!("{}: {}", event.title, event.start_time);
    }
    
    // Get current bulletin
    if let Some(bulletin) = client.get_current_bulletin().await? {
        println!("Current bulletin: {}", bulletin.title);
    }
    
    // Submit contact form
    let contact = ContactForm::new(
        "John Doe".to_string(),
        "john@example.com".to_string(),
        "Prayer Request".to_string(),
        "Please pray for my family.".to_string()
    );
    let submission_id = client.submit_contact_form(contact).await?;
    println!("Contact form submitted: {}", submission_id);
    
    Ok(())
}

Custom Configuration

use church_core::{ChurchApiClient, ChurchCoreConfig};
use std::time::Duration;

let config = ChurchCoreConfig::new()
    .with_base_url("https://api.mychurch.org/api")
    .with_cache_ttl(Duration::from_secs(600))
    .with_timeout(Duration::from_secs(15))
    .with_retry_attempts(5)
    .with_offline_mode(true);

let client = ChurchApiClient::new(config)?;

Data Models

Event

use church_core::{Event, EventCategory, NewEvent};
use chrono::{DateTime, Utc};

// Create a new event
let new_event = NewEvent {
    title: "Bible Study".to_string(),
    description: "Weekly Bible study group".to_string(),
    start_time: Utc::now(),
    end_time: Utc::now() + chrono::Duration::hours(2),
    location: "Fellowship Hall".to_string(),
    location_url: Some("https://maps.google.com/...".to_string()),
    category: EventCategory::Education,
    is_featured: true,
    // ... other fields
};

let event_id = client.create_event(new_event).await?;

Bulletin

use church_core::{Bulletin, NewBulletin};
use chrono::NaiveDate;

// Get current bulletin
if let Some(bulletin) = client.get_current_bulletin().await? {
    println!("Sabbath School: {}", bulletin.sabbath_school);
    println!("Divine Worship: {}", bulletin.divine_worship);
    
    // Check for announcements
    for announcement in bulletin.active_announcements() {
        println!("📢 {}: {}", announcement.title, announcement.content);
    }
}

Contact Form

use church_core::{ContactForm, ContactCategory, VisitorInfo};

let contact = ContactForm::new(
    "Jane Smith".to_string(),
    "jane@example.com".to_string(),
    "New Visitor".to_string(),
    "I'm interested in learning more about your church.".to_string()
)
.with_category(ContactCategory::Visitor)
.with_phone("555-123-4567".to_string())
.with_visitor_info(VisitorInfo {
    is_first_time: true,
    wants_follow_up: true,
    wants_newsletter: true,
    // ... other fields
});

let submission_id = client.submit_contact_form(contact).await?;

Authentication

PocketBase Authentication

use church_core::auth::{LoginRequest, AuthToken};

// Authenticate with PocketBase
let login = LoginRequest {
    identity: "admin@church.org".to_string(),
    password: "secure_password".to_string(),
};

// Note: Authentication methods would be implemented in auth module
// let token = client.authenticate_pocketbase(login).await?;
// client.set_auth_token(token).await;

Caching

The client automatically caches responses with configurable TTL:

// Cache is automatically used
let events = client.get_upcoming_events(None).await?; // Fetches from API
let events = client.get_upcoming_events(None).await?; // Returns from cache

// Manual cache management
client.clear_cache().await;
let (cache_size, max_size) = client.get_cache_stats().await;

Error Handling

use church_core::{ChurchApiError, Result};

match client.get_event("invalid-id").await {
    Ok(Some(event)) => println!("Found event: {}", event.title),
    Ok(None) => println!("Event not found"),
    Err(ChurchApiError::NotFound) => println!("Event does not exist"),
    Err(ChurchApiError::Network(_)) => println!("Network error - check connection"),
    Err(ChurchApiError::Auth(_)) => println!("Authentication required"),
    Err(e) => println!("Other error: {}", e),
}

Platform Integration

iOS (Swift via UniFFI)

import church_core

// Fetch events (returns JSON string)
let eventsJSON = fetch_events_json()

// Fetch bulletins
let bulletinsJSON = fetch_bulletins_json()

// Submit contact form
let result = submit_contact_v2_json(
    name: "John Doe",
    email: "john@example.com",
    subject: "Question",
    message: "Hello!",
    phone: "555-1234"
)

// Validate email
if validate_email_address(email: "test@example.com") {
    print("Valid email")
}

// Get church config
let churchName = get_church_name()
let coordinates = get_coordinates()

Native Rust

use church_core::{ChurchApiClient, ChurchCoreConfig};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let config = ChurchCoreConfig::default();
    let client = ChurchApiClient::new(config)?;

    let events = client.get_upcoming_events(Some(10)).await?;
    println!("Found {} events", events.len());

    Ok(())
}

Web (WASM) - Coming Soon

WASM support is planned with the trait-based api::ApiClient architecture already in place.

Testing

Run the test binary to verify API connectivity:

cargo run --bin church-core-test

This will test:

  • Health check endpoint
  • Upcoming events retrieval
  • Current bulletin fetching
  • Configuration loading
  • Cache statistics

API Endpoints

The client interfaces with these standard endpoints:

  • GET /events/upcoming - Get upcoming events
  • GET /events/{id} - Get single event
  • POST /events - Create event
  • GET /bulletins/current - Get current bulletin
  • GET /bulletins - List bulletins
  • GET /config - Get church configuration
  • POST /contact - Submit contact form
  • GET /sermons - List sermons
  • GET /sermons/search - Search sermons

Recent Improvements (v0.2.0)

Codebase Restructuring

The codebase has been significantly restructured for better maintainability:

  • Renamed models/types/ - More accurate naming for pure data types
  • New api/ module - Trait-based architecture ready for WASM
  • New validation/ module - Centralized business logic validation
  • Cleaned up UniFFI - Removed 1,767 lines of redundant wrapper code
  • Better feature flags - Clear separation: native, wasm, ios
  • Optional dependencies - Only include what you need for each platform

Code Reduction

  • ~20% smaller codebase - Removed redundant wrappers and duplicated code
  • Modular organization - Clear separation of concerns
  • DRY/KISS principles - No duplication, simple design patterns

Architecture Benefits

  • Cross-platform ready - Trait-based design supports multiple targets
  • Zero breaking changes - All iOS functions preserved
  • Easy to extend - Clear module boundaries for new features
  • Better testing - Validation logic separated from API code

Development

Building for iOS (No Python Required!)

# Quick build using Makefile
make ios

# Or use the build script directly
./build_ios.sh

# Install dependencies first time
make install-deps

# Development build (faster)
make dev

# Clean build artifacts
make clean

Building Steps Explained

  1. Install iOS targets: Adds Rust toolchains for iOS devices and simulators
  2. Build static libraries: Creates .a files for each iOS architecture
  3. Create universal library: Combines all architectures using lipo
  4. Generate Swift bindings: Uses uniffi-bindgen to create Swift interfaces
  5. Copy to iOS project: Moves all files to your Xcode project

Generated files:

  • church_core.swift - Swift interface to your Rust code
  • church_coreFFI.h - C header file
  • church_coreFFI.modulemap - Module map for Swift
  • libchurch_core.a - Universal static library

Standard Rust Building

# Standard build
cargo build

# Build with WASM support
cargo build --features wasm

# Build with UniFFI support
cargo build --features uniffi

# Run tests
cargo test --features uniffi

Features

  • default: Native Rust client (includes native)
  • native: Native platform features (tokio, moka cache)
  • wasm: WebAssembly bindings (future)
  • ios: iOS bindings via UniFFI
  • server: Server-side usage (alias for native)

License

MIT License - see LICENSE file for details.

Contributing

  1. Fork the repository
  2. Create a feature branch
  3. Add tests for new functionality
  4. Ensure all tests pass
  5. Submit a pull request

Support

For issues and questions:

  • Check the API documentation
  • Review existing GitHub issues
  • Create a new issue with detailed information