hyperchad_router

Crates.iohyperchad_router
lib.rshyperchad_router
version0.1.4
created_at2025-05-07 15:34:56.515758+00
updated_at2025-07-21 20:04:31.917937+00
descriptionHyperChad Router package
homepage
repositoryhttps://github.com/MoosicBox/MoosicBox
max_upload_size
id1664030
size92,994
Braden Steffaniak (BSteffaniak)

documentation

README

HyperChad Router

Async routing system for HyperChad applications with request handling and navigation.

Overview

The HyperChad Router package provides:

  • Async Routing: Full async request routing and handling
  • Route Matching: Flexible route path matching with literals and prefixes
  • Request Processing: Comprehensive request parsing and handling
  • Form Support: Multipart form and JSON body parsing
  • Client Detection: OS and client information detection
  • Navigation System: Programmatic navigation and content delivery

Features

Route Matching

  • Literal Routes: Exact path matching
  • Multiple Literals: Match against multiple possible paths
  • Prefix Matching: Match paths with specific prefixes
  • Flexible Patterns: Support for various route patterns

Request Handling

  • HTTP Methods: Support for GET, POST, PUT, DELETE, PATCH
  • Query Parameters: Automatic query string parsing
  • Headers: Complete header access and manipulation
  • Cookies: Cookie parsing and management
  • Body Parsing: JSON and form data parsing

Form Processing

  • Multipart Forms: Complete multipart form support
  • File Uploads: File upload handling with base64 encoding
  • JSON Bodies: JSON request body parsing
  • URL Encoded: URL-encoded form data support
  • Content-Type Detection: Automatic content type handling

Client Information

  • OS Detection: Automatic operating system detection
  • Client Info: Structured client information
  • Request Context: Rich request context and metadata

Installation

Add this to your Cargo.toml:

[dependencies]
hyperchad_router = { path = "../hyperchad/router" }

# Enable additional features
hyperchad_router = {
    path = "../hyperchad/router",
    features = ["serde", "form", "static-routes"]
}

Usage

Basic Router Setup

use hyperchad_router::{Router, RouteRequest, RoutePath};
use hyperchad_renderer::Content;

// Create router
let router = Router::new()
    .with_route("/", |_req| async {
        Some(Content::Html("<h1>Home</h1>".to_string()))
    })
    .with_route("/about", |_req| async {
        Some(Content::Html("<h1>About</h1>".to_string()))
    });

// Navigate to route
let content = router.navigate("/").await?;

Route Patterns

use hyperchad_router::RoutePath;

// Literal route
let home_route = RoutePath::Literal("/".to_string());

// Multiple literals
let api_routes = RoutePath::Literals(vec![
    "/api/v1".to_string(),
    "/api/v2".to_string(),
]);

// Prefix matching
let static_route = RoutePath::LiteralPrefix("/static/".to_string());

// From string slice arrays
let routes: RoutePath = &["/api", "/v1", "/users"][..].into();

Request Information

use hyperchad_router::{RouteRequest, RequestInfo, ClientInfo, ClientOs};

// Create request with client info
let client_info = ClientInfo {
    os: ClientOs {
        name: "Windows".to_string(),
    },
};

let request = RouteRequest::from(("/api/users", client_info));

// Access request properties
println!("Path: {}", request.path);
println!("Method: {:?}", request.method);
println!("OS: {}", request.info.client.os.name);

Form Handling

use serde::Deserialize;

#[derive(Deserialize)]
struct LoginForm {
    username: String,
    password: String,
}

let router = Router::new()
    .with_route_result("/login", |req| async move {
        if req.method == Method::Post {
            let form: LoginForm = req.parse_form()?;
            // Process login
            Ok(Some(Content::Html("Login successful".to_string())))
        } else {
            Ok(Some(Content::Html(r#"
                <form method="post">
                    <input name="username" type="text" required>
                    <input name="password" type="password" required>
                    <button type="submit">Login</button>
                </form>
            "#.to_string())))
        }
    });

JSON Body Parsing

use serde::{Deserialize, Serialize};

#[derive(Deserialize)]
struct ApiRequest {
    name: String,
    email: String,
}

#[derive(Serialize)]
struct ApiResponse {
    id: u32,
    message: String,
}

let router = Router::new()
    .with_route_result("/api/users", |req| async move {
        let user_data: ApiRequest = req.parse_body()?;

        // Process user creation
        let response = ApiResponse {
            id: 123,
            message: "User created".to_string(),
        };

        Ok(Some(Content::Json(serde_json::to_string(&response)?)))
    });

Static Routes

// Static routes (compiled at build time)
let router = Router::new()
    .with_static_route("/static/css/style.css", |_req| async {
        Some(Content::Css(include_str!("../static/style.css").to_string()))
    })
    .with_static_route("/static/js/app.js", |_req| async {
        Some(Content::JavaScript(include_str!("../static/app.js").to_string()))
    });

Navigation and Content Delivery

// Spawn navigation in background
let handle = router.navigate_spawn("/api/data");

// Wait for navigation result
match handle.await {
    Ok(Ok(())) => println!("Navigation successful"),
    Ok(Err(e)) => println!("Navigation error: {}", e),
    Err(e) => println!("Task error: {}", e),
}

// Send navigation result to receiver
router.navigate_send("/dashboard").await?;

// Wait for content on receiver
if let Some(content) = router.wait_for_navigation().await {
    // Handle received content
}

Error Handling

use hyperchad_router::{NavigateError, ParseError};

match router.navigate("/api/endpoint").await {
    Ok(Some(content)) => {
        // Handle successful navigation
    }
    Ok(None) => {
        // Route returned no content
    }
    Err(NavigateError::InvalidPath) => {
        // Invalid path provided
    }
    Err(NavigateError::Handler(e)) => {
        // Handler returned an error
    }
    Err(NavigateError::Sender) => {
        // Channel sender error
    }
}

Route Types

RoutePath Variants

  • Literal(String): Exact string match
  • Literals(Vec): Match any of multiple strings
  • LiteralPrefix(String): Match strings with specific prefix

Content Types

  • Html: HTML content
  • Json: JSON responses
  • Css: CSS stylesheets
  • JavaScript: JavaScript files
  • Custom: Custom content types

Client Information

ClientInfo Structure

pub struct ClientInfo {
    pub os: ClientOs,
}

pub struct ClientOs {
    pub name: String,  // "Windows", "macOS", "Linux", etc.
}

Default Client Detection

Automatic OS detection using the os_info crate provides default client information.

Feature Flags

  • serde: Enable JSON and form parsing
  • form: Enable multipart form support
  • static-routes: Enable static route compilation

Dependencies

  • Tokio: Async runtime and task management
  • Futures: Future utilities
  • Bytes: Efficient byte handling
  • Flume: Channel communication
  • QString: Query string parsing
  • Serde: Optional serialization support
  • Mime Multipart: Optional form parsing

Integration

This package is designed for:

  • Web Applications: Full-featured web application routing
  • API Servers: RESTful API endpoint handling
  • Form Processing: Web form and file upload handling
  • SPA Routing: Single-page application routing
  • Content Management: Dynamic content delivery systems
Commit count: 5735

cargo fmt