use std::path::Path;
use rudb_common::{Error, Result, Value};
use rudb_functions::device_card_fields;
use rudb_io::device::{Card, card};
use rudb_plan::{Expr, Plan, Slice};
use crate::metadata::{Metadata, text};
pub(crate) fn device_card(
plan: &Plan,
args: Slice,
index: u32,
columns: Slice,
) -> Result<Metadata> {
let values: Vec<&Value> = plan
.expr_list(args)
.iter()
.map(|argument| match plan.expr(*argument) {
Expr::Constant(reference) => Ok(plan.value(*reference)),
_ => Err(Error::not_implemented(
"rudb_device_card() given an argument that is not a constant",
)),
})
.collect::<Result<_>>()?;
let path = match values.first() {
Some(Value::Varchar(path)) => path.clone(),
_ => return Err(Error::invalid_input("rudb_device_card() needs the path of a directory")),
};
let iterations = match values.get(1) {
None => None,
Some(Value::BigInt(n)) => {
Some(u32::try_from(*n).ok().filter(|n| *n > 0).ok_or_else(|| {
Error::invalid_input(format!("rudb_device_card() cannot run {n} iterations"))
})?)
}
Some(other) => {
return Err(Error::internal(format!(
"an iteration count bound as BIGINT arrived as {other}"
)));
}
};
let measured = card(Path::new(&path), iterations)?;
Metadata::new("rudb_device_card", &device_card_fields(), &rows(&measured), plan, index, columns)
}
#[expect(
clippy::cast_precision_loss,
reason = "nanosecond latencies and byte rates on a real device are far under 2^53"
)]
fn rows(card: &Card) -> Vec<Vec<Value>> {
let micros = |ns: u64| Value::Double(ns as f64 / 1_000.0);
let count = |n: u64| Value::BigInt(i64::try_from(n).unwrap_or(i64::MAX));
card.probes
.iter()
.enumerate()
.map(|(at, probe)| {
vec![
text(&card.path.display().to_string()),
text(&card.device),
text(&card.filesystem),
text(probe.call.name()),
Value::Boolean(at == 0),
micros(probe.p50_4k_ns),
micros(probe.p99_4k_ns),
micros(probe.p50_64k_ns),
micros(probe.p99_64k_ns),
Value::Boolean(probe.plausible),
Value::Double(card.write_bytes_per_s as f64 / f64::from(1 << 20)),
count(card.syncs_per_s[0]),
count(card.syncs_per_s[1]),
count(card.syncs_per_s[2]),
count(card.syncs_per_s[3]),
Value::Double(card.scaling),
text(card.plp.name()),
Value::Boolean(card.memory_backed),
Value::Integer(i32::try_from(card.lanes).unwrap_or(i32::MAX)),
Value::Integer(i32::try_from(card.iterations).unwrap_or(i32::MAX)),
]
})
.collect()
}