use std::cell::RefCell;
use crate::{
filtering::{matches_grid_row_prepared_filters, prepare_grid_column_filters},
grouping::build_grid_display_items,
models::{BuildGridPipelineContext, GridRow, PipelineResult},
pagination::{is_virtualization_enabled, paginate_grid_rows},
sorting::sort_grid_rows,
tree::{build_grid_rows, filter_and_flatten_grid_tree_rows, is_tree_enabled},
};
struct RowsCacheEntry {
data_ptr: usize,
data_len: usize,
options_ptr: usize,
hidden_ptr: usize,
hidden_len: usize,
expanded_ptr: usize,
expanded_len: usize,
row_size: usize,
rows: Vec<GridRow>,
}
thread_local! {
static ROWS_CACHE: RefCell<Option<RowsCacheEntry>> = const { RefCell::new(None) };
}
fn build_grid_rows_cached(context: &BuildGridPipelineContext) -> Vec<GridRow> {
let data_ptr = context.options.data.as_ptr() as usize;
let data_len = context.options.data.len();
let options_ptr = (&context.options as *const _) as usize;
let hidden_ptr = (&context.hidden_row_reasons as *const _) as usize;
let hidden_len = context.hidden_row_reasons.len();
let expanded_ptr = (&context.expanded_rows as *const _) as usize;
let expanded_len = context.expanded_rows.len();
let row_size = context.row_size;
let cached = ROWS_CACHE.with(|cache| {
let cache = cache.borrow();
cache.as_ref().and_then(|entry| {
if entry.data_ptr == data_ptr
&& entry.data_len == data_len
&& entry.options_ptr == options_ptr
&& entry.hidden_ptr == hidden_ptr
&& entry.hidden_len == hidden_len
&& entry.expanded_ptr == expanded_ptr
&& entry.expanded_len == expanded_len
&& entry.row_size == row_size
{
Some(entry.rows.clone())
} else {
None
}
})
});
if let Some(rows) = cached {
return rows;
}
let rows = build_grid_rows(
&context.options,
row_size,
&context.hidden_row_reasons,
&context.expanded_rows,
);
ROWS_CACHE.with(|cache| {
*cache.borrow_mut() = Some(RowsCacheEntry {
data_ptr,
data_len,
options_ptr,
hidden_ptr,
hidden_len,
expanded_ptr,
expanded_len,
row_size,
rows: rows.clone(),
});
});
rows
}
pub fn get_cached_grid_pipeline_rows(context: &BuildGridPipelineContext) -> Vec<GridRow> {
build_grid_rows_cached(context)
}
pub fn clear_grid_pipeline_rows_cache() {
ROWS_CACHE.with(|cache| {
*cache.borrow_mut() = None;
});
}
fn reset_filter_reasons(rows: &mut [GridRow]) {
for row in rows {
if row.invisible_reasons.is_empty() {
continue;
}
let had_filter = row
.invisible_reasons
.iter()
.any(|reason| reason.starts_with("filter:"));
if !had_filter {
continue;
}
row.invisible_reasons
.retain(|reason| !reason.starts_with("filter:"));
if row.invisible_reasons.is_empty() {
row.visible = true;
}
}
}
#[cfg(not(target_arch = "wasm32"))]
fn pipeline_started_at() -> std::time::Instant {
std::time::Instant::now()
}
#[cfg(target_arch = "wasm32")]
fn pipeline_started_at() {}
#[cfg(not(target_arch = "wasm32"))]
fn pipeline_elapsed_ms(started_at: std::time::Instant) -> f64 {
started_at.elapsed().as_secs_f64() * 1000.0
}
#[cfg(target_arch = "wasm32")]
fn pipeline_elapsed_ms(_: ()) -> f64 {
0.0
}
pub fn build_grid_pipeline(context: &BuildGridPipelineContext) -> PipelineResult {
let started_at = pipeline_started_at();
let mut rows = build_grid_rows_cached(context);
let visible_rows = if is_tree_enabled(&context.options) {
filter_and_flatten_grid_tree_rows(
&rows,
&context.columns,
&context.options,
&context.active_filters,
&context.expanded_tree_rows,
&context.sort_state,
)
} else {
let filtering_enabled = context.options.enable_filtering;
reset_filter_reasons(&mut rows);
let filtered = if !filtering_enabled {
rows.into_iter().filter(|row| row.visible).collect()
} else {
let prepared = prepare_grid_column_filters(&context.columns, &context.active_filters);
if prepared.is_empty() {
rows.into_iter().filter(|row| row.visible).collect()
} else {
rows.into_iter()
.filter_map(|row| {
let mut current = row.clone();
if !current.visible {
return None;
}
matches_grid_row_prepared_filters(&mut current, &prepared)
.then_some(current)
})
.collect::<Vec<_>>()
}
};
sort_grid_rows(
&filtered,
&context.columns,
&context.options,
&context.sort_state,
)
};
let total_items = if context.options.use_external_pagination {
context.options.total_items.unwrap_or(visible_rows.len())
} else {
visible_rows.len()
};
let paged_rows = paginate_grid_rows(
&visible_rows,
&context.options,
context.current_page,
context.page_size,
total_items,
);
let display_items = build_grid_display_items(
&paged_rows,
&context.columns,
&context.options,
&context.group_by_columns,
&context.collapsed_groups,
);
PipelineResult {
visible_rows: paged_rows,
virtualization_enabled: is_virtualization_enabled(&context.options, display_items.len()),
display_items,
pipeline_ms: pipeline_elapsed_ms(started_at),
total_items,
}
}