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

nordisk/knut

crates/knut-popviz/src/main.rs

//! `knut-popviz` — a desktop egui driver for the population bulk-loader (feature
//! `gui`). Watch the 12-label graph fill FalkorDB live, per label, with
//! throughput — and the same state is written to `$KNUT_POPVIZ_STATE` every
//! ~300 ms so the GUI can be robot-tested headlessly (see `lib.rs`).
//!
//! ```bash
//! cargo run -p knut-popviz --features gui -- --url redis://127.0.0.1:6379 --graph pop --scale 0.001 --drop
//! ```
//!
//! ## Flags
//!
//! - `--url <redis://…>` — FalkorDB to load into (default `redis://127.0.0.1:6379`).
//! - `--graph <name>` / `--scale <f64>` / `--drop` — graph name, size, wipe-first.
//! - `--ingest <auto|bulk|unwind>` — FalkorDB lane (default `auto`): native
//!   `GRAPH.BULK` fast lane on an empty/`--drop`ped graph, parallel-`UNWIND`
//!   otherwise. `bulk`/`unwind` pin the lane (the parity test uses both).
//! - `--tls-cert <pem> --tls-key <pem> [--tls-ca <pem>]` — mutual TLS, for a FalkorDB that
//!   takes nothing else (`--url rediss://host:6379`). With `tls-auth-clients-user CN` on the
//!   server the certificate's CN is the user; no password. `--tls-ca` verifies the server
//!   (default: the system roots).
//! - `--autostart` / `KNUT_POPVIZ_AUTOSTART` — kick off the *load* on the first
//!   frame (for headless robot tests). Does **not** touch the DB.
//! - `--start-db` / `KNUT_FALKORDB_AUTOSTART` — if `--url` is unreachable, bring
//!   up a FalkorDB container from Rust (testcontainers → Podman, LAW 7), wait
//!   until ready, and connect to its mapped port. Opt-in so a user with their own
//!   DB is never surprised. Image from `KNUT_FALKORDB_IMAGE`
//!   (default `localhost/knut-falkordb-valkey9:latest` — valkey-9, never stock redis).

use knut_popviz::app::PopvizApp;
use knut_popviz::{IngestMode, LoadParams, Target};

fn main() -> eframe::Result<()> {
    let args: Vec<String> = std::env::args().collect();
    // `--version`/`-V` — the suite identity `knut-popviz v<ver> (<git-sha>)`,
    // stamped pure-fs by build.rs. popviz hand-rolls its arg parsing (no clap),
    // so this is checked before anything else and exits.
    if args.iter().any(|a| a == "--version" || a == "-V") {
        println!("knut-popviz {}", knut_popviz::VERSION_LINE);
        return Ok(());
    }
    let opt = |k: &str, d: &str| {
        args.windows(2)
            .find(|w| w[0] == k)
            .map(|w| w[1].clone())
            .unwrap_or_else(|| d.into())
    };
    let target = match opt("--target", "falkor").as_str() {
        "iceberg" => Target::Iceberg {
            warehouse: opt("--warehouse", "/tmp/knut_popviz_warehouse"),
        },
        _ => Target::Falkor,
    };
    let mut params = LoadParams {
        url: opt("--url", "redis://127.0.0.1:6379"),
        graph: opt("--graph", "pop"),
        scale: opt("--scale", "0.001").parse().unwrap_or(0.001),
        drop: args.iter().any(|a| a == "--drop"),
        target,
        // `--ingest auto|bulk|unwind` (default auto): an empty/`--drop`ped graph
        // takes the native GRAPH.BULK fast lane, else the parallel-UNWIND path.
        ingest: IngestMode::from_str_lenient(&opt("--ingest", "auto")),
        tls: None,
    };
    // `--tls-cert` + `--tls-key` together or not at all: half an identity is a mistake to
    // stop on, not a plain connect to fall back to.
    let (cert, key) = (opt("--tls-cert", ""), opt("--tls-key", ""));
    if !cert.is_empty() || !key.is_empty() {
        if cert.is_empty() || key.is_empty() {
            eprintln!("knut-popviz: --tls-cert and --tls-key go together");
            std::process::exit(2);
        }
        let ca = opt("--tls-ca", "");
        let ca = (!ca.is_empty()).then(|| std::path::PathBuf::from(ca));
        match graphar_falkordb::FalkorTls::from_files(cert.as_ref(), key.as_ref(), ca.as_deref()) {
            Ok(id) => params.tls = Some(id),
            Err(e) => {
                eprintln!("knut-popviz: {e}");
                std::process::exit(2);
            }
        }
    }

    // FalkorDB autostart (opt-in via `--start-db` / `KNUT_FALKORDB_AUTOSTART`):
    // when the configured `--url` is unreachable, bring up a FalkorDB container
    // from Rust (testcontainers → Podman, LAW 7), wait until ready, and rewrite
    // the URL to the container's mapped port. The guard must outlive the app, so
    // it's held until `run_native` returns. Distinct from the pre-existing
    // `--autostart` flag, which kicks off the *load* on the first frame.
    let want_db_autostart =
        std::env::var("KNUT_FALKORDB_AUTOSTART").is_ok() || args.iter().any(|a| a == "--start-db");
    let _db_guard = if want_db_autostart && matches!(params.target, Target::Falkor) {
        let rt = tokio::runtime::Builder::new_current_thread()
            .enable_all()
            .build()
            .expect("tokio runtime for FalkorDB autostart");
        match rt.block_on(graphar_falkordb::falkordb_container::ensure_falkordb(
            &params.url,
        )) {
            Ok((url, guard)) => {
                if guard.is_some() {
                    eprintln!("knut-popviz: started FalkorDB container at {url}");
                }
                params.url = url;
                guard
            }
            Err(e) => {
                eprintln!("knut-popviz: FalkorDB autostart failed: {e}");
                None
            }
        }
    } else {
        None
    };

    eframe::run_native(
        "knut-popviz",
        eframe::NativeOptions::default(),
        Box::new(|_cc| Ok(Box::new(PopvizApp::new(params)))),
    )
}