Rustelo/crates/foundation/FOUNDATION_INTEGRATION_GUIDE.md
Jesús Pérez 0d0297423e
Some checks failed
CI/CD Pipeline / Test Suite (push) Has been cancelled
CI/CD Pipeline / Security Audit (push) Has been cancelled
CI/CD Pipeline / Performance Benchmarks (push) Has been cancelled
Rust CI / Security Audit (push) Has been cancelled
Rust CI / Check + Test + Lint (nightly) (push) Has been cancelled
Rust CI / Check + Test + Lint (stable) (push) Has been cancelled
CI/CD Pipeline / Build Docker Image (push) Has been cancelled
CI/CD Pipeline / Deploy to Staging (push) Has been cancelled
CI/CD Pipeline / Deploy to Production (push) Has been cancelled
CI/CD Pipeline / Cleanup (push) Has been cancelled
chore: fix with CI and pre-commit
2026-02-08 20:37:49 +00:00

22 KiB

Rustelo Foundation Integration Guide

Overview

The Rustelo Foundation provides a complete ecosystem of library crates for building modern web applications. This guide shows how all foundation crates work together to create powerful, maintainable applications.

Foundation Architecture

Application Layer (Your Implementation)
├── main.rs (imports and uses foundation libraries)
├── build.rs (uses foundation build utilities)
└── Cargo.toml (depends on foundation crates)

Foundation Library Layer
├── server/          # Server-side library with importable main functions
├── client/          # Client-side library with app mounting functions  
├── components/      # Reusable UI component library
├── pages/           # Page generation and template system
├── core-lib/        # Shared utilities and business logic
└── core-types/      # Shared type definitions

Complete Application Example

1. Application Structure

my-rustelo-app/
├── Cargo.toml           # Workspace and dependencies
├── build.rs             # Build-time page generation
├── src/
│   ├── main.rs          # Application entry point
│   └── lib.rs           # Application library code
├── content/             # Markdown content and configuration
├── templates/           # Page templates
└── public/             # Static assets

2. Cargo.toml - Foundation Dependencies

[package]
name = "my-rustelo-app"
version = "0.1.0"
edition = "2021"

[dependencies]
# Foundation crates
server = { path = "path/to/rustelo/crates/foundation/crates/server" }
client = { path = "path/to/rustelo/crates/foundation/crates/client" }
components = { path = "path/to/rustelo/crates/foundation/crates/components" }
pages = { path = "path/to/rustelo/crates/foundation/crates/pages" }
core-lib = { path = "path/to/rustelo/crates/foundation/crates/core-lib" }
core-types = { path = "path/to/rustelo/crates/foundation/crates/core-types" }

# Leptos framework
leptos = { version = "0.8", features = ["ssr", "hydrate"] }
leptos_router = "0.8"
leptos_axum = "0.8"

# Additional dependencies
tokio = { version = "1.0", features = ["full"] }
axum = "0.8"

[features]
default = []
ssr = ["leptos/ssr", "server/ssr", "pages/ssr", "components/ssr"]
hydrate = ["leptos/hydrate", "client/hydrate", "pages/hydrate", "components/hydrate"]

[[bin]]
name = "server"
path = "src/main.rs"
required-features = ["ssr"]

[lib]
name = "my_rustelo_app"
crate-type = ["cdylib", "rlib"]

3. build.rs - Foundation Build Integration

//! Build script using foundation build utilities

use std::error::Error;

fn main() -> Result<(), Box<dyn Error>> {
    // Use server foundation build utilities
    server::build::build_foundation()?;
    
    // Use pages foundation for page generation
    pages::build::generate_pages_from_content("content/")?;
    
    // Use core-lib for configuration processing
    core_lib::build::process_configuration("config/")?;
    
    // Set up rerun conditions
    println!("cargo:rerun-if-changed=content/");
    println!("cargo:rerun-if-changed=templates/");
    println!("cargo:rerun-if-changed=config/");
    
    Ok(())
}

4. src/main.rs - Complete Application Integration

//! Complete Rustelo application using all foundation crates

use server::{run_server_with_config, ServerConfig};
use client::{create_base_app, ClientConfig};
use components::{
    navigation::{BrandHeader, Footer},
    content::{UnifiedContentCard, ContentManager},
    theme::{ThemeProvider, ThemeConfig},
};
use pages::{
    HomePage, AboutPage, ContactPage,
    content::{ContentIndexPage, ContentCategoryPage},
    PostViewerPage,
};
use core_lib::{
    config::{load_app_config, AppConfig},
    i18n::{setup_translations, TranslationManager},
    content::{ContentService, MarkdownProcessor},
};
use core_types::{
    ContentItem, Route, User, AppError,
    config::{DatabaseConfig, I18nConfig},
};

use leptos::*;
use leptos_router::*;

#[tokio::main]
async fn main() -> Result<(), AppError> {
    // 1. Load application configuration using core-lib
    let app_config: AppConfig = load_app_config("config/app.toml")?;
    
    // 2. Setup internationalization using core-lib
    let translation_manager = setup_translations(&app_config.i18n)?;
    
    // 3. Initialize content service using core-lib
    let content_service = ContentService::new(&app_config.content_path)?;
    
    // 4. Configure server using server foundation
    let server_config = ServerConfig::builder()
        .from_app_config(&app_config)
        .content_service(content_service.clone())
        .translation_manager(translation_manager.clone())
        .enable_features(["content", "auth", "i18n"])
        .build();
    
    // 5. Start server using foundation main function
    run_server_with_config(server_config).await?;
    
    Ok(())
}

/// Application component integrating all foundation components
#[component]
pub fn App() -> impl IntoView {
    // Load app configuration
    let app_config = use_context::<AppConfig>().unwrap();
    
    // Setup theme configuration
    let theme_config = ThemeConfig::builder()
        .from_app_config(&app_config)
        .build();
    
    view! {
        // Theme provider from components foundation
        <ThemeProvider config=theme_config>
            // Router setup
            <Router>
                <div class="min-h-screen flex flex-col">
                    // Header using components foundation
                    <AppHeader />
                    
                    // Main content with routing using pages foundation
                    <main class="flex-1">
                        <Routes>
                            // Home page from pages foundation
                            <Route path="/" view=HomePage />
                            
                            // Static pages from pages foundation
                            <Route path="/about" view=AboutPage />
                            <Route path="/contact" view=ContactPage />
                            
                            // Content pages using pages foundation
                            <Route path="/blog" view=|| {
                                ContentIndexPage {
                                    content_type: "blog".to_string(),
                                    layout: "grid".to_string(),
                                    per_page: Some(10),
                                }
                            } />
                            
                            <Route path="/blog/:category" view=ContentCategoryPage />
                            <Route path="/blog/post/:slug" view=|| {
                                PostViewerPage {
                                    content_type: "blog".to_string(),
                                    show_related: true,
                                    enable_comments: true,
                                }
                            } />
                            
                            // Portfolio section
                            <Route path="/portfolio" view=|| {
                                ContentIndexPage {
                                    content_type: "portfolio".to_string(),
                                    layout: "cards".to_string(),
                                    per_page: Some(12),
                                }
                            } />
                            
                            // 404 page
                            <Route path="/*any" view=pages::NotFoundPage />
                        </Routes>
                    </main>
                    
                    // Footer using components foundation
                    <AppFooter />
                </div>
            </Router>
        </ThemeProvider>
    }
}

/// Application header component
#[component]
fn AppHeader() -> impl IntoView {
    let app_config = use_context::<AppConfig>().unwrap();
    
    view! {
        <BrandHeader 
            brand_name=app_config.site_name.clone()
            logo_url=app_config.logo_url.clone()
        >
            // Navigation using components foundation
            <components::navigation::NavMenu orientation="horizontal">
                <components::ui::SpaLink href="/" active_class="text-blue-600">
                    "Home"
                </components::ui::SpaLink>
                <components::ui::SpaLink href="/about" active_class="text-blue-600">
                    "About"
                </components::ui::SpaLink>
                <components::ui::SpaLink href="/blog" active_class="text-blue-600">
                    "Blog"
                </components::ui::SpaLink>
                <components::ui::SpaLink href="/portfolio" active_class="text-blue-600">
                    "Portfolio"
                </components::ui::SpaLink>
                <components::ui::SpaLink href="/contact" active_class="text-blue-600">
                    "Contact"
                </components::ui::SpaLink>
            </components::navigation::NavMenu>
            
            // Language selector if i18n enabled
            {if app_config.i18n.enabled {
                view! {
                    <components::navigation::LanguageSelector
                        current_lang=app_config.i18n.default_language.clone()
                        available_langs=app_config.i18n.supported_languages.clone()
                    />
                }.into_view()
            } else {
                view! {}.into_view()
            }}
        </BrandHeader>
    }
}

/// Application footer component
#[component]
fn AppFooter() -> impl IntoView {
    let app_config = use_context::<AppConfig>().unwrap();
    
    view! {
        <Footer copyright=format!("© 2024 {}", app_config.site_name)>
            <div class="grid md:grid-cols-3 gap-8">
                // Site links
                <div>
                    <h4 class="font-semibold mb-4">"Site"</h4>
                    <div class="space-y-2">
                        <components::ui::SpaLink href="/about" class="block text-gray-600 hover:text-gray-800">
                            "About"
                        </components::ui::SpaLink>
                        <components::ui::SpaLink href="/blog" class="block text-gray-600 hover:text-gray-800">
                            "Blog"
                        </components::ui::SpaLink>
                        <components::ui::SpaLink href="/portfolio" class="block text-gray-600 hover:text-gray-800">
                            "Portfolio"
                        </components::ui::SpaLink>
                    </div>
                </div>
                
                // Content links
                <div>
                    <h4 class="font-semibold mb-4">"Content"</h4>
                    <div class="space-y-2">
                        <components::ui::SpaLink href="/blog/category/tutorials" class="block text-gray-600 hover:text-gray-800">
                            "Tutorials"
                        </components::ui::SpaLink>
                        <components::ui::SpaLink href="/blog/category/news" class="block text-gray-600 hover:text-gray-800">
                            "News"
                        </components::ui::SpaLink>
                        <components::ui::SpaLink href="/portfolio" class="block text-gray-600 hover:text-gray-800">
                            "Work"
                        </components::ui::SpaLink>
                    </div>
                </div>
                
                // Legal links
                <div>
                    <h4 class="font-semibold mb-4">"Legal"</h4>
                    <div class="space-y-2">
                        <components::ui::SpaLink href="/privacy" class="block text-gray-600 hover:text-gray-800">
                            "Privacy Policy"
                        </components::ui::SpaLink>
                        <components::ui::SpaLink href="/terms" class="block text-gray-600 hover:text-gray-800">
                            "Terms of Service"
                        </components::ui::SpaLink>
                    </div>
                </div>
            </div>
        </Footer>
    }
}

/// Client-side hydration using client foundation
#[cfg(feature = "hydrate")]
#[wasm_bindgen::prelude::wasm_bindgen]
pub fn hydrate() {
    use client::{hydrate_app_with_config, ClientConfig};
    
    let client_config = ClientConfig::builder()
        .mount_selector("#app")
        .enable_hydration(true)
        .enable_router(true)
        .build();
    
    hydrate_app_with_config(|| view! { <App /> }, client_config);
}

/// Server-side rendering setup
#[cfg(feature = "ssr")]
pub fn render_app() -> String {
    leptos::ssr::render_to_string(|| view! { <App /> })
}

Cross-Crate Communication Patterns

1. Configuration Flow

// core-lib loads and validates configuration
let app_config = core_lib::config::load_app_config("config/app.toml")?;

// server uses configuration for setup
let server_config = server::ServerConfig::from_app_config(&app_config);

// components use configuration for theming
let theme_config = components::theme::ThemeConfig::from_app_config(&app_config);

// pages use configuration for content processing
let page_config = pages::PageConfig::from_app_config(&app_config);

2. Content Processing Pipeline

// core-lib processes raw content
let processor = core_lib::content::MarkdownProcessor::new(&app_config.content_path);
let content_items = processor.process_all().await?;

// pages generates pages from processed content
let page_generator = pages::PageGenerator::new()
    .with_content_items(content_items)
    .with_templates_from_config(&app_config);

// components display the content
let content_display = components::content::ContentManager::new()
    .with_content_source(content_items)
    .with_layout("grid");

3. Type Safety Across Crates

// core-types defines shared types
use core_types::{ContentItem, User, Route, AppError};

// All crates use the same types for consistency
fn process_content(item: ContentItem) -> Result<ContentItem, AppError> {
    // Processing logic
}

fn render_user_profile(user: User) -> impl IntoView {
    // Rendering logic
}

fn handle_route(route: Route) -> Result<(), AppError> {
    // Routing logic
}

Build System Integration

1. Multi-Stage Build Process

// build.rs orchestrates all foundation build utilities
fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Stage 1: Core configuration processing
    core_lib::build::process_configuration()?;
    
    // Stage 2: Content processing and validation
    core_lib::build::process_content()?;
    
    // Stage 3: Page generation
    pages::build::generate_pages()?;
    
    // Stage 4: Route generation
    server::build::generate_routes()?;
    
    // Stage 5: Asset processing
    process_static_assets()?;
    
    Ok(())
}

2. Feature Flag Coordination

# Features are coordinated across all foundation crates
[features]
default = []

# Core features
auth = ["server/auth", "core-lib/auth", "core-types/auth"]
i18n = ["server/i18n", "client/i18n", "pages/i18n", "core-lib/i18n"]
content-db = ["server/content-db", "pages/content-db", "core-lib/content-db"]

# Rendering features  
ssr = ["server/ssr", "pages/ssr", "components/ssr"]
hydrate = ["client/hydrate", "pages/hydrate", "components/hydrate"]

# Development features
dev-tools = ["server/dev-tools", "client/dev-tools"]

Advanced Integration Patterns

1. Plugin Architecture

// Define plugin traits in core-types
pub trait ContentProcessor {
    fn process(&self, content: &str) -> Result<String, ProcessingError>;
}

// Implement plugins in various crates
impl ContentProcessor for pages::MarkdownProcessor {
    fn process(&self, content: &str) -> Result<String, ProcessingError> {
        self.process_markdown(content)
    }
}

// Register and use plugins through core-lib
let processor_registry = core_lib::plugins::ProcessorRegistry::new()
    .register("markdown", Box::new(pages::MarkdownProcessor::new()))
    .register("handlebars", Box::new(templates::HandlebarsProcessor::new()));

2. Event System

// Define events in core-types
#[derive(Debug, Clone)]
pub enum AppEvent {
    ContentUpdated(ContentItem),
    UserAuthenticated(User),
    RouteChanged(Route),
    ThemeChanged(String),
}

// Emit events from various crates
// In pages crate
self.event_bus.emit(AppEvent::ContentUpdated(content_item));

// In server crate
self.event_bus.emit(AppEvent::UserAuthenticated(user));

// Listen to events in components
self.event_bus.subscribe(|event| match event {
    AppEvent::ThemeChanged(theme) => update_theme(theme),
    AppEvent::ContentUpdated(item) => refresh_content_display(item),
    _ => {}
});

3. State Management Integration

// Global state defined in core-types
#[derive(Debug, Clone)]
pub struct AppState {
    pub user: Option<User>,
    pub theme: Theme,
    pub language: Language,
    pub content_cache: HashMap<String, ContentItem>,
}

// State management in core-lib
pub struct StateManager {
    state: RwSignal<AppState>,
    event_bus: EventBus,
}

impl StateManager {
    pub fn update_user(&self, user: Option<User>) {
        self.state.update(|state| state.user = user);
        if let Some(user) = user {
            self.event_bus.emit(AppEvent::UserAuthenticated(user));
        }
    }
}

// Use state in components
#[component]
pub fn UserProfile() -> impl IntoView {
    let state = use_context::<StateManager>().unwrap();
    let user = create_memo(move |_| state.get_user());
    
    view! {
        <Show when=move || user.get().is_some()>
            // Render user profile
        </Show>
    }
}

Testing Integration

1. Cross-Crate Testing

// Integration tests that span multiple crates
#[cfg(test)]
mod integration_tests {
    use super::*;
    
    #[tokio::test]
    async fn test_full_content_pipeline() {
        // Use core-lib to load configuration
        let config = core_lib::config::load_test_config().await.unwrap();
        
        // Use pages to process content
        let content = pages::process_test_content(&config).await.unwrap();
        
        // Use components to render content
        let rendered = components::render_test_content(content).await.unwrap();
        
        // Verify the full pipeline
        assert!(rendered.contains("expected content"));
    }
    
    #[test]
    fn test_type_consistency() {
        // Verify types work across crate boundaries
        let content_item = core_types::ContentItem::new("test", "Test Content");
        let processed = core_lib::content::process_item(content_item.clone()).unwrap();
        let rendered = components::content::render_item(&processed);
        
        assert_eq!(processed.id, content_item.id);
    }
}

2. End-to-End Testing

// E2E tests using all foundation crates together
#[cfg(test)]
mod e2e_tests {
    use super::*;
    
    #[tokio::test]
    async fn test_complete_application() {
        // Start server using server foundation
        let server_handle = server::test_utils::start_test_server().await;
        
        // Generate test content using pages foundation
        pages::test_utils::generate_test_pages().await.unwrap();
        
        // Test client-side functionality
        let client_test = client::test_utils::TestClient::new()
            .navigate_to("/")
            .expect_content("Welcome")
            .navigate_to("/blog")
            .expect_content("Blog Posts")
            .run()
            .await;
            
        assert!(client_test.passed());
        
        // Cleanup
        server_handle.stop().await;
    }
}

Deployment Integration

1. Production Build

#!/bin/bash
# Build script using all foundation capabilities

# Build server with all features
cargo build --release --features "ssr,auth,content-db,i18n,metrics"

# Build client for WebAssembly
wasm-pack build --target web --features "hydrate,router,i18n"

# Generate static pages
cargo run --bin page-generator --features "generation"

# Process and optimize assets
cargo run --bin asset-processor

# Create deployment package
tar -czf rustelo-app.tar.gz target/release/server pkg/ generated/ public/

2. Docker Integration

# Multi-stage Docker build using foundation crates
FROM rust:1.70 as builder

# Copy foundation source
COPY rustelo/crates/foundation /app/foundation
COPY . /app/src

WORKDIR /app/src

# Build with all production features
RUN cargo build --release --features "production"

# Build client WASM
RUN wasm-pack build --target web --features "hydrate,router,i18n"

FROM nginx:alpine

# Copy server binary
COPY --from=builder /app/src/target/release/server /usr/local/bin/

# Copy static assets and generated pages
COPY --from=builder /app/src/pkg /var/www/html/pkg
COPY --from=builder /app/src/generated /var/www/html/generated
COPY --from=builder /app/src/public /var/www/html/public

# Copy nginx configuration
COPY nginx.conf /etc/nginx/nginx.conf

# Start script that runs both server and nginx
COPY start.sh /start.sh
RUN chmod +x /start.sh

CMD ["/start.sh"]

Best Practices for Foundation Integration

1. Dependency Management

  • Use workspace dependencies for version consistency
  • Enable only the features you need
  • Use path dependencies during development
  • Switch to published crates for production

2. Configuration Management

  • Centralize configuration in core-lib
  • Use type-safe configuration structs from core-types
  • Validate configuration at startup
  • Support environment-specific configs

3. Error Handling

  • Define common errors in core-types
  • Implement From traits for error conversion
  • Use Result types consistently across crates
  • Provide meaningful error contexts

4. Performance Optimization

  • Use foundation build utilities for optimization
  • Enable appropriate feature flags
  • Leverage foundation caching mechanisms
  • Profile across crate boundaries

5. Testing Strategy

  • Test individual crates in isolation
  • Write integration tests for crate interactions
  • Use foundation test utilities
  • Implement E2E testing for complete flows

The Rustelo Foundation provides a complete, integrated development experience where each crate is designed to work seamlessly with the others while maintaining clear separation of concerns and reusability.