Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/facett

facett-pipeline/examples/pipeline_demo.rs

//! **pipeline_demo** — the glow-up, alive. A multi-stage Spark-style pipeline DAG
//! that **animates** (marching-ants flow along edges, per-stage status glow:
//! running pulses, done holds, failed blooms) and is **fully theme-driven** —
//! every colour comes from the active [`Theme`], switchable live.
//!
//! Pure egui on the **glow** backend (no wgpu / no custom shader).
//!
//! Run: `cargo run -p facett-pipeline --example pipeline_demo`

use eframe::egui;
use egui::Vec2;
use facett_core::{Facet, Theme, set_theme};
use facett_pipeline::{JobProgress, PipelineView, Status};

/// Build a fresh 6-stage ETL pipeline (rebuilt each frame so the demo can swap
/// speed/animation without threading state through).
fn pipeline(speed: f32, animated: bool) -> PipelineView {
    // raw → clean → join ↘            (join also reads dim)
    //                      agg → out
    //        dim ─────────↗
    let nodes = vec![
        ("raw.events".to_string(), Status::Done),
        ("clean".to_string(), Status::Done),
        ("dim.users".to_string(), Status::Done),
        ("join".to_string(), Status::Running),
        ("agg.daily".to_string(), Status::Running),
        ("gold.out".to_string(), Status::Idle),
        ("audit".to_string(), Status::Failed),
    ];
    // (reads_idx, target_idx): the left node reads from the right.
    let edges = vec![
        (1, 0), // clean reads raw
        (3, 1), // join reads clean
        (3, 2), // join reads dim
        (4, 3), // agg reads join
        (5, 4), // out reads agg
        (6, 4), // audit reads agg
    ];
    let throughput = vec![
        Some("48k rows/s".to_string()),
        Some("31k/s".to_string()),
        Some("dim".to_string()),
        Some("12k/s".to_string()),
        None,
        Some("err".to_string()),
    ];
    PipelineView::new("ETL", nodes, edges)
        .with_progress(JobProgress {
            fraction: 0.62,
            tasks_done: 124,
            tasks_total: 200,
            rows: 1_240_000,
            bytes_read: 48 * 1024 * 1024,
            elapsed_s: 12.5,
            inflight: 3,
        })
        .with_throughput(throughput)
        .with_speed(speed)
        .with_animation(animated)
}

struct App {
    theme_idx: usize,
    speed: f32,
    animated: bool,
}

impl Default for App {
    fn default() -> Self {
        Self { theme_idx: 3, speed: 0.6, animated: true } // start cyberpunk-neon
    }
}

impl eframe::App for App {
    fn ui(&mut self, root: &mut egui::Ui, _frame: &mut eframe::Frame) {
        let ctx = root.ctx().clone();
        set_theme(&ctx, Theme::ALL[self.theme_idx]());

        egui::Panel::top("controls").show(root, |ui| {
            ui.horizontal_wrapped(|ui| {
                ui.label("Palette:");
                for (i, ctor) in Theme::ALL.iter().enumerate() {
                    ui.selectable_value(&mut self.theme_idx, i, ctor().name);
                }
            });
            ui.horizontal(|ui| {
                ui.checkbox(&mut self.animated, "Animate flow + glow");
                ui.add(egui::Slider::new(&mut self.speed, 0.0..=3.0).text("flow speed"));
            });
        });

        egui::CentralPanel::default().show(root, |ui| {
            // Rebuild + render the pipeline (cheap; layout is deterministic).
            egui::ScrollArea::both().show(ui, |ui| {
                pipeline(self.speed, self.animated).ui(ui);
            });
        });
    }
}

fn main() -> eframe::Result {
    let options = eframe::NativeOptions {
        viewport: egui::ViewportBuilder::default().with_inner_size(Vec2::new(980.0, 640.0)),
        ..Default::default()
    };
    eframe::run_native("facett · pipeline_demo", options, Box::new(|_cc| Ok(Box::<App>::default())))
}