Skip to content

revskill10/structural-rs

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

2 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Structural-RS πŸ¦€

Universal structural types for Rust - bringing TypeScript's structural typing, Ruby's expressiveness, and Elixir's concurrency to Rust.

Crates.io Documentation License

🌟 Features

  • πŸ”„ Structural Typing: TypeScript-like duck typing with compile-time safety
  • πŸ—ΊοΈ EnumMap: Efficient enum-indexed data structures with O(1) access
  • 🎭 Actor System: Elixir OTP-inspired actors with supervision trees
  • 🎯 Pattern Matching: Powerful pattern matching on structural data
  • πŸ“‘ Fault Tolerance: Supervisor trees for self-healing systems
  • ⚑ High Performance: Zero-cost abstractions with Rust's performance
  • 🧩 Modular Design: Use only what you need

πŸš€ Quick Start

Add to your Cargo.toml:

[dependencies]
structural = "0.1"

Basic Usage

use structural::prelude::*;

// Create structural data (like TypeScript objects)
let user_data = shape_map! {
    "name" => Shape::Str("Alice".to_string()),
    "age" => Shape::Num(30.0),
    "active" => Shape::Bool(true),
    "preferences" => shape_map! {
        "theme" => Shape::Str("dark".to_string()),
        "notifications" => Shape::Bool(true)
    }
};

// Duck typing for structural access
let duck = Duck::new(&user_data);
let name: String = duck.get("name")?;
let theme: String = duck.index(["preferences", "theme"])?.to()?;

println!("User {} prefers {} theme", name, theme);

EnumMap (Efficient enum-indexed data)

use structural::prelude::*;
use structural_derive::EnumVariants;

#[derive(EnumVariants)]
enum Color { Red, Green, Blue }

// Create enum-indexed map (like TypeScript mapped types)
let colors = construct_enummap::<Color, String, _>(|variant| {
    match variant {
        "Red" => "#FF0000".to_string(),
        "Green" => "#00FF00".to_string(), 
        "Blue" => "#0000FF".to_string(),
        _ => "#000000".to_string(),
    }
});

println!("Red color: {}", colors[&Color::Red]);

Pattern Matching (Elixir-like)

use structural::prelude::*;
use structural::pattern::*;

let api_response = shape_variant!("Ok", shape_map! {
    "data" => Shape::Str("Success!".to_string()),
    "timestamp" => Shape::Num(1642681800.0)
});

let pattern = variant("Ok").with_inner(
    map().field("data".to_string(), var("content"))
         .field("timestamp".to_string(), var("time"))
);

if pattern.matches(&api_response) {
    let bindings = pattern.extract(&api_response)?;
    let content: String = bindings.get_as("content")?;
    println!("Success: {}", content);
}

Actor System (Elixir OTP-like)

use structural::prelude::*;
use std::pin::Pin;

struct CounterActor { count: i32 }

impl Actor for CounterActor {
    fn handle(&mut self, envelope: Envelope, _ctx: &mut ActorContext) 
        -> Pin<Box<dyn Future<Output = Result<(), ActorError>> + Send + '_>>
    {
        Box::pin(async move {
            let duck = Duck::new(&envelope.message);
            match duck.try_get::<String>("action").as_deref() {
                Some("increment") => {
                    self.count += 1;
                    let response = shape_map! {
                        "count" => Shape::Num(self.count as f64)
                    };
                    envelope.reply(response)?;
                }
                _ => {}
            }
            Ok(())
        })
    }
}

#[tokio::main]
async fn main() -> Result<(), ActorError> {
    let system = ActorSystem::new("example".to_string());
    let counter = system.spawn_actor(
        Some("counter".to_string()), 
        CounterActor { count: 0 }
    ).await?;
    
    let msg = shape_map! { "action" => Shape::Str("increment".to_string()) };
    let response = counter.ask(msg, Duration::from_secs(1)).await?;
    
    let duck = Duck::new(&response);
    println!("Count: {}", duck.get::<f64>("count")?);
    
    system.stop_all().await?;
    Ok(())
}

Supervisor Trees (Fault tolerance)

use structural::prelude::*;
use structural_actor::*;

let supervisor = SupervisorBuilder::new()
    .strategy(SupervisorStrategy::OneForOne)
    .restart_intensity(3, Duration::from_secs(5))
    .child(
        ChildSpec::new("worker1".to_string()),
        || async { WorkerActor::new("worker-1") }
    )
    .child(
        ChildSpec::new("worker2".to_string()),
        || async { WorkerActor::new("worker-2") }
    )
    .build();

let system = ActorSystem::new("fault_tolerant".to_string());
let supervisor_ref = system.spawn_actor(Some("supervisor".to_string()), supervisor).await?;

// Children will be automatically restarted if they fail
// OneForOne strategy means only failed child gets restarted

πŸ—οΈ Architecture

Structural-RS is organized into modular crates:

  • structural-core: Core types (Shape, Duck, EnumMap, pattern matching)
  • structural-derive: Procedural macros (EnumVariants, Shape)
  • structural-actor: Actor system with supervision trees
  • structural: Main crate that re-exports everything

πŸ“š Examples

Comprehensive Showcase

Run the full feature showcase:

cargo run --bin structural_showcase --package comprehensive-example

This demonstrates:

  • Structural typing with complex nested data
  • EnumMap usage with different enum types
  • Pattern matching on various data structures
  • Actor system with message passing
  • Supervisor trees with fault tolerance

Chat Server Example

A real-world example implementing a chat server:

cargo run --bin chat_server --package comprehensive-example

Features:

  • WebSocket connections
  • Room management
  • Message broadcasting
  • Actor-based architecture
  • Fault-tolerant supervision

🎯 Use Cases

TypeScript Developers

  • Structural typing: Access object properties dynamically with type safety
  • Duck typing: "If it walks like a duck..." - structural compatibility
  • JSON handling: Parse and manipulate JSON with TypeScript-like syntax

Ruby Developers

  • Dynamic data: Flexible data structures with hash-like access
  • Expressiveness: Intuitive APIs for data manipulation
  • Metaprogramming: Dynamic behavior with compile-time safety

Elixir Developers

  • Actor model: Lightweight processes with message passing
  • Supervision trees: Fault-tolerant systems with automatic recovery
  • Pattern matching: Destructure data with powerful pattern syntax
  • OTP patterns: GenServer-like actors with lifecycle management

Systems Programming

  • Network protocols: Parse and handle protocol messages structurally
  • Configuration: Dynamic configuration with validation
  • APIs: Build HTTP APIs with structural request/response handling
  • Microservices: Actor-based service architecture

πŸ”¬ Advanced Features

Custom Derive Macros

use structural_derive::{EnumVariants, Shape};

#[derive(EnumVariants, Debug)]
enum Status { Active, Inactive, Pending }

#[derive(Shape, Debug)]
struct User {
    name: String,
    age: u32,
    status: Status,
}

// Automatically implements ToShape and FromShape
let user = User { 
    name: "Bob".to_string(), 
    age: 25, 
    status: Status::Active 
};

let shape = user.to_shape();
let duck = Duck::new(&shape);
println!("User name: {}", duck.get::<String>("name")?);

Pattern Matching DSL

use structural::pattern::*;

// Complex nested pattern matching
let pattern = map()
    .field("user".to_string(), 
        map().field("profile".to_string(),
            map().field("settings".to_string(),
                array().element(var("first_setting"))
                      .element(literal("dark_mode"))
            )
        )
    );

// Matches deeply nested structures
if pattern.matches(&complex_data) {
    let bindings = pattern.extract(&complex_data)?;
    // Access extracted variables
}

Typed Actors

struct TypedCounterActor { count: i32 }

#[derive(Debug)]
enum CounterMessage {
    Increment,
    Decrement, 
    GetCount,
}

impl ToShape for CounterMessage { /* ... */ }
impl FromShape for CounterMessage { /* ... */ }

impl TypedActor<CounterMessage> for TypedCounterActor {
    fn handle_message(&mut self, message: CounterMessage, ctx: &mut ActorContext) 
        -> Pin<Box<dyn Future<Output = Result<(), ActorError>> + Send + '_>>
    {
        Box::pin(async move {
            match message {
                CounterMessage::Increment => self.count += 1,
                CounterMessage::Decrement => self.count -= 1,
                CounterMessage::GetCount => {
                    // Send response...
                }
            }
            Ok(())
        })
    }
}

πŸ“Š Performance

  • Zero-cost abstractions: No runtime overhead for structural typing
  • Efficient EnumMap: O(1) access time, minimal memory overhead
  • Lightweight actors: Thousands of actors with minimal memory usage
  • Fast pattern matching: Compile-time optimized pattern matching
  • Async/await: Built on Tokio for high-performance async I/O

πŸ› οΈ Development

Building

# Build all crates
cargo build --workspace

# Run tests
cargo test --workspace

# Run examples
cargo run --example structural_showcase

Testing

# Unit tests
cargo test --lib

# Integration tests  
cargo test --test integration

# Property-based tests
cargo test --features proptest

🀝 Contributing

Contributions are welcome! Please see CONTRIBUTING.md for guidelines.

Areas for Contribution

  • Performance optimizations: Micro-benchmarks and optimizations
  • More pattern types: Additional pattern matching constructs
  • Protocol implementations: HTTP, WebSocket, etc.
  • Validation framework: Schema validation for shapes
  • Documentation: Examples, guides, and API docs

πŸ“„ License

Licensed under either of:

at your option.

πŸ™ Acknowledgments

Inspired by:

  • TypeScript: Structural typing and duck typing
  • Ruby: Dynamic expressiveness and metaprogramming
  • Elixir/OTP: Actor model and fault tolerance
  • Rust: Zero-cost abstractions and memory safety

About

Structural typing in Rust

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages