use std::path::PathBuf;
use anyhow::Result;
use clap::Parser;
use nornir::viz::UrdrThreadsApp;
#[derive(Parser)]
#[command(name = "urdr-threads")]
#[command(about = "Time-travel visualizer for the Urðr warehouse", long_about = None)]
struct Cli {
#[arg(long, default_value = "/tmp/nornir-demo-warehouse")]
warehouse: PathBuf,
#[arg(long, default_value = "")]
workspace: String,
#[arg(long)]
config: Option<PathBuf>,
}
fn main() -> Result<()> {
let cli = Cli::parse();
let (workspace_root, repos) = match cli.config.as_deref() {
Some(p) => match nornir::config::load_explicit(p) {
Ok(loaded) => (
loaded.workspace_root,
loaded.nornir.repo.keys().cloned().collect(),
),
Err(_) => (PathBuf::new(), Vec::new()),
},
None => match std::env::current_dir()
.ok()
.and_then(|cwd| nornir::config::discover(&cwd).ok())
{
Some(loaded) => (
loaded.workspace_root,
loaded.nornir.repo.keys().cloned().collect(),
),
None => (PathBuf::new(), Vec::new()),
},
};
let native_options = eframe::NativeOptions {
viewport: eframe::egui::ViewportBuilder::default()
.with_inner_size([1200.0, 700.0])
.with_title("Urðr Threads — nornir time machine"),
..Default::default()
};
let server = std::env::var("NORNIR_SERVER").ok().filter(|s| !s.is_empty());
let workspace = std::env::var("NORNIR_WORKSPACE")
.ok()
.filter(|s| !s.is_empty())
.unwrap_or(cli.workspace);
eframe::run_native(
"Urðr Threads",
native_options,
Box::new(move |_cc| {
let app = match server {
Some(endpoint) => {
let token = std::env::var("NORNIR_SERVER_TOKEN").unwrap_or_default();
UrdrThreadsApp::with_remote(endpoint, token, workspace, workspace_root, repos)
}
None => UrdrThreadsApp::with_repos(cli.warehouse, workspace, workspace_root, repos),
};
Ok(Box::new(app))
}),
)
.map_err(|e| anyhow::anyhow!("eframe error: {e}"))?;
Ok(())
}