Skip to main content

Crate bubbletea_macros

Crate bubbletea_macros 

Source
Expand description

§bubbletea-macros

Procedural macros for the bubbletea TUI framework.

This crate provides the #[derive(Model)] macro which reduces boilerplate when implementing the Model trait for your TUI applications.

§Role in charmed_rust

bubbletea-macros is an optional ergonomic layer for the core framework:

  • bubbletea re-exports the derive macro when the macros feature is enabled.
  • demo_showcase uses the derive macro for concise models in examples and tests.

§Quick Start

use bubbletea::{Cmd, Message, Model};

#[derive(Model)]
struct Counter {
    #[state]  // Marks fields for optimized change detection
    count: i32,
}

impl Counter {
    fn init(&self) -> Option<Cmd> {
        None
    }

    fn update(&mut self, msg: Message) -> Option<Cmd> {
        if let Some(&delta) = msg.downcast_ref::<i32>() {
            self.count += delta;
        }
        None
    }

    fn view(&self) -> String {
        format!("Count: {}", self.count)
    }
}

§How It Works

The #[derive(Model)] macro generates a Model trait implementation that delegates to inherent methods on your struct. You implement the init, update, and view methods directly on your struct, and the macro bridges them to the trait.

§Required Methods

Your struct must implement these inherent methods:

MethodSignaturePurpose
initfn init(&self) -> Option<Cmd>Initial command on startup
updatefn update(&mut self, msg: Message) -> Option<Cmd>Handle messages
viewfn view(&self) -> StringRender the UI

§State Tracking with #[state]

The #[state] attribute enables optimized re-rendering by tracking which fields trigger view updates. Fields marked with #[state] are monitored for changes, and only changes to these fields signal that a re-render is needed.

§Basic Usage

#[derive(Model)]
struct App {
    #[state]
    counter: i32,      // Changes trigger re-render

    cache: String,     // Not tracked (no re-render on change)
}

§Advanced State Options

#[derive(Model)]
struct App {
    // Custom equality function for floating-point comparison
    #[state(eq = "float_approx_eq")]
    progress: f64,

    // Excluded from change detection (internal bookkeeping)
    #[state(skip)]
    last_tick: std::time::Instant,

    // Debug logging when this field changes
    #[state(debug)]
    selected_index: usize,
}

fn float_approx_eq(a: &f64, b: &f64) -> bool {
    (a - b).abs() < 0.001
}

§State Options

OptionDescription
eq = "fn_name"Custom equality function fn(&T, &T) -> bool
skipExclude field from change detection
debugLog changes to this field (debug builds only)

§Generated Code

The macro generates:

  1. Model trait implementation - Delegates to your inherent methods
  2. State snapshot struct - For change detection (only if #[state] fields exist)
  3. Helper methods - __snapshot_state() and __state_changed()

§Generic Structs

The derive macro supports generic structs with type parameters:

#[derive(Model)]
struct Container<T: Clone + PartialEq + Send + 'static> {
    #[state]
    value: T,
}

§Error Messages

The macro provides helpful error messages for common mistakes:

  • Using #[derive(Model)] on enums, unions, or tuple structs
  • Fields marked #[state] that don’t implement Clone or PartialEq

§Migration from Manual Implementation

Before (manual):

impl Model for Counter {
    fn init(&self) -> Option<Cmd> { None }
    fn update(&mut self, msg: Message) -> Option<Cmd> {
        // handle message
        None
    }
    fn view(&self) -> String {
        format!("{}", self.count)
    }
}

After (derive):

#[derive(Model)]
struct Counter {
    #[state]
    count: i32,
}

impl Counter {
    fn init(&self) -> Option<Cmd> { None }
    fn update(&mut self, msg: Message) -> Option<Cmd> {
        // handle message
        None
    }
    fn view(&self) -> String {
        format!("{}", self.count)
    }
}

The benefit is automatic state change tracking and cleaner separation of your model logic from the trait boilerplate.

Derive Macros§

Model
Derive macro for implementing the Model trait.