compo 0.1.0

Compo is a general-purpose declarative and reactive component framework designed for single-threaded asynchronous execution, offering high performance and safety guarantees. Suitable for GUI scenarios or other similar non-GUI component systems.
Documentation
# Compo - Declarative and Reactive Component Framework


[中文文档](README-zh-CN.md)

---

Compo is a general-purpose declarative and reactive component framework designed for single-threaded asynchronous
execution, offering high performance and safety guarantees.
It does not include any pre-implemented components but provides a `#[component]` macro and essential type exports,
completely independent of third-party libraries.
Suitable for GUI scenarios or other similar non-GUI component systems.

## Features


- **Concise Syntax**: Eliminates 99% of unnecessary boilerplate code, making it simple to use.
- **Declarative Components**: Easily define components using the `#[component]` macro.
- **Reactive Rendering**: Automatically re-renders child components when dependent variables change.
- **Minimal Trait Dependencies**: No `Send`/`Sync` or `'static` constraints, and no cross-thread synchronization
  mechanisms.
- **Single-Threaded Async**: Efficiently runs in a single-threaded environment, ideal for high-performance scenarios.
- **No Third-Party Dependencies**: Fully standalone, with no external libraries.
- **Safety Guarantees**: Strict type checking and runtime safety mechanisms.

## Quick Start


### Installation


Add Compo to your `Cargo.toml`:

```shell
cargo add compo
```

### Example Code


Here's a simple example demonstrating how to define and use components:

```rust
use compo::prelude::*;

fn main() {
    run(app);
}

#[component]

async fn app() {
    #[render] // Rendering is deferred to the next polling
    row {};
    #[render] // Component parameters can be omitted (using default values)
    button {};
    
    let tick_listener = Default::default(); // Create an event listener
    #[render]
    countdown {
        on_tick: tick_listener, // If a child component emits an event, the listener can receive it
    };
    while let Some(i) = tick_listener.listen().await.as_ref() {
        // Receive events from the countdown component
        println!("{} seconds.", i);
    }
    
    println!("Hello, app!");
}

#[component]

async fn row() {
    let mut text = "Hello";
    #[render] // Renders a component, re-renders child component if dependent variables change
    button {
        text: text,
        width: 32,
    };
    text = "world"; // Will re-render the button component
}

#[component]

async fn button(#[default = "hello"] text: &str, #[doc = "width"] width: u32) {
    #[field]
    // Fields are automatically added to internal struct (not exposed), values can be changed but won't trigger re-render of dependent child components
    let id: i32 = 0;
    println!("{}, {}", text, id);
    *id = 1; // Field lifetime is same as run function, so value can be reused across multiple renders
}

#[component]

async fn countdown(#[event] on_tick: Option<u32>) {
    // Emit events for the parent component to receive
    for i in (0..10).rev() {
        let _ = on_tick.emit(Some(i));
        sleep(Duration::from_secs(1)).await;
    }
    let _ = on_tick.emit(None);
}
```

### Running the Example


When you run this example with `cargo run --example basic`, you'll see the following output:

```
hello, 0
9 seconds.
world, 0
world, 1
8 seconds.
7 seconds.
6 seconds.
5 seconds.
4 seconds.
3 seconds.
2 seconds.
1 seconds.
0 seconds.
Hello, app!
```

This output demonstrates:

1. The button component first renders with default text "hello" and id=0
2. The countdown component starts emitting events with countdown values from 9 to 0
3. The button component re-renders with text "world" and id=0, then again with id=1
4. The app component continues receiving countdown events
5. After the countdown completes, the app component prints "Hello, app!"

## API Documentation


### `#[component]` Macro


Used to define components. Components must be asynchronous functions and support rendering child components and reactive
updates.

### `#[render]` Attribute


Marks child components for rendering. If dependent variables change, the child component will re-render.

### `#[field]` Attribute


Defines internal fields for components, with lifetimes matching the `run` function.

### `#[event]` Attribute


Marks a component parameter as an event emitter. This allows child components to send events to their parent components. The parameter should be of type `Option<T>`, where `T` is the type of data to be emitted. Events can be emitted using the `.emit()` method and received by the parent component using the `.listen().await` method.

## Contributing


Issues and Pull Requests are welcome!

## License


Apache-2.0