use std::sync::Arc;
use std::time::Duration;
use ciborium::Value as CborValue;
use indexmap::IndexMap;
use tempfile::TempDir;
use vantage_cli_util::render_records;
use vantage_core::Result;
use vantage_dataset::prelude::ReadableValueSet;
use vantage_diorama::{ChangeEvent, Dio, Lens, SortDir, TableScenery};
use vantage_faker::{LiveFolderConfig, LiveFolderSim};
use vantage_types::Record;
const VIEWPORT: usize = 30;
fn render_pane(title: &str, hint: &str, scenery: &Arc<dyn TableScenery>) {
println!("┌─ {title} ─┐");
if hint.is_empty() {
println!("│ (not opened yet)");
println!("└──────────────────────────────────────────────────────────────");
return;
}
let total = scenery.row_count();
let shown = total.min(VIEWPORT);
println!("│ {hint} · showing {shown} of {total} rows");
let mut records: IndexMap<String, Record<CborValue>> = IndexMap::new();
for i in 0..shown {
if let Some(row) = scenery.row(i) {
let id = match row.record.get("name") {
Some(CborValue::Text(s)) => s.clone(),
_ => i.to_string(),
};
records.insert(id, row.record.clone());
}
}
if records.is_empty() {
println!("│ (no rows yet this tick)");
} else {
render_records(&records, Some("name"));
}
println!();
}
async fn open_scenery(dio: &vantage_diorama::Dio) -> Result<Arc<dyn TableScenery>> {
let scenery: Arc<dyn TableScenery> = dio
.table_scenery()
.sort("modified", SortDir::Desc)
.open()
.await?;
scenery.set_viewport(0..VIEWPORT);
Ok(scenery)
}
#[tokio::main]
async fn main() -> Result<()> {
let tmp = TempDir::new().expect("tempdir");
let lens = Arc::new(
Lens::new()
.cache_at(tmp.path().join("cache.redb"))
.on_start(|dio| {
let dio = dio.clone();
async move {
let rows = dio.master().list_values().await?;
dio.cache().insert_values(rows).await
}
})
.on_refresh(|dio| {
let dio = dio.clone();
async move {
let rows = dio.master().list_values().await?;
dio.cache().clear().await?;
dio.cache().insert_values(rows).await
}
})
.on_event(|dio, evt| {
let dio = dio.clone();
async move {
if matches!(evt, ChangeEvent::Invalidated) {
dio.refresh().await?;
}
Ok(())
}
})
.build()
.expect("build lens"),
);
let sim = LiveFolderSim::new(LiveFolderConfig {
backfill: Duration::from_secs(2 * 3600),
chunk_threshold: 5 * 1024 * 1024 * 1024,
error_pct_per_sec: 0.1,
..LiveFolderConfig::default()
});
let today = {
let secs = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map(|d| d.as_secs())
.unwrap_or(0);
let days = secs / 86400;
let z = days as i64 + 719468;
let era = z.div_euclid(146097);
let doe = (z - era * 146097) as u64;
let yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365;
let y = yoe as i64 + era * 400;
let doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
let mp = (5 * doy + 2) / 153;
let d = doy - (153 * mp + 2) / 5 + 1;
let m = if mp < 10 { mp + 3 } else { mp - 9 };
let y = if m <= 2 { y + 1 } else { y };
format!("{y:04}-{m:02}-{d:02}")
};
let (ymd_vista, events) = sim.listing_vista("ymd", &today);
let ymd_dio = lens.make_dio(ymd_vista).await?;
let mut rx = events.subscribe();
let error_logs_dio = match find_child_row(&ymd_dio, "error_logs").await? {
Some(row) => Some(ymd_dio.get_ref("subdir", &row).await?),
None => None,
};
let events_dio = match find_child_row(&ymd_dio, "events").await? {
Some(row) => Some(ymd_dio.get_ref("subdir", &row).await?),
None => None,
};
let ymd_scenery = open_scenery(&ymd_dio).await?;
let error_logs_scenery = match &error_logs_dio {
Some(d) => Some(open_scenery(d).await?),
None => None,
};
let events_scenery = match &events_dio {
Some(d) => Some(open_scenery(d).await?),
None => None,
};
let dios_for_forward: Vec<Option<Dio>> = vec![
Some(ymd_dio.clone()),
error_logs_dio.clone(),
events_dio.clone(),
];
tokio::spawn(async move {
while let Ok(evt) = rx.recv().await {
for dio in dios_for_forward.iter().flatten() {
let _ = dio.handle_event(evt.clone()).await;
}
}
});
println!("vantage-faker · scenery_folder_cli · Ctrl-C to quit\n");
println!("Opening 3 sceneries via Dio::get_ref(\"subdir\", row):");
println!(" • ymd = listing of {today}/");
println!(
" • errors = {}",
match &error_logs_dio {
Some(_) => "get_ref(\"subdir\", error_logs_row)",
None => "(no error_logs folder yet)",
}
);
println!(
" • events = {}",
match &events_dio {
Some(_) => "get_ref(\"subdir\", events_row)",
None => "(no events folder yet)",
}
);
println!();
loop {
print!("\x1B[2J\x1B[H");
println!("vantage-faker · scenery_folder_cli · Ctrl-C to quit\n");
render_pane(
&format!("ymd: {today}/"),
" Dio over listing_vista(\"ymd\", today)",
&ymd_scenery,
);
match &error_logs_scenery {
Some(s) => render_pane(
"error_logs (via get_ref subdir)",
" Dio::get_ref(\"subdir\", error_logs_row)",
s,
),
None => render_pane("error_logs", "", &ymd_scenery),
}
match &events_scenery {
Some(s) => render_pane(
"events (via get_ref subdir)",
" Dio::get_ref(\"subdir\", events_row)",
s,
),
None => render_pane("events", "", &ymd_scenery),
}
tokio::time::sleep(Duration::from_secs(1)).await;
}
}
async fn find_child_row(dio: &Dio, name: &str) -> Result<Option<Record<CborValue>>> {
let rows = dio.master().list_values().await?;
Ok(rows
.into_iter()
.find(|(_, r)| r.get("name").and_then(|v| v.as_text()) == Some(name))
.map(|(_, r)| r))
}