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 workspace_from_path(p: &std::path::Path) -> Option<String> {
p.components()
.filter_map(|c| c.as_os_str().to_str())
.find_map(|s| s.strip_prefix("workspace_").map(str::to_string))
.filter(|s| !s.is_empty())
}
fn main() -> Result<()> {
let cli = Cli::parse();
let loaded = match cli.config.as_deref() {
Some(p) => nornir::config::load_explicit(p).ok(),
None => std::env::current_dir()
.ok()
.and_then(|cwd| nornir::config::discover(&cwd).ok()),
};
let (workspace_root, repos, config_workspace) = match loaded {
Some(loaded) => {
let config_ws = workspace_from_path(&loaded.config_path)
.or_else(|| workspace_from_path(std::path::Path::new(&loaded.nornir.storage.local_path)));
(
loaded.workspace_root,
loaded.nornir.repo.keys().cloned().collect(),
config_ws,
)
}
None => (PathBuf::new(), Vec::new(), None),
};
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| {
cc.egui_ctx.set_visuals(eframe::egui::Visuals::dark());
let app = match server {
Some(endpoint) => {
let token = std::env::var("NORNIR_SERVER_TOKEN").unwrap_or_default();
UrdrThreadsApp::with_remote_preferring(
endpoint, token, workspace, config_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(())
}