re_dataframe_ui 0.31.3

Rich table widget over DataFusion.
Documentation
use std::sync::Arc;

use arrow::array::{ArrayRef, RecordBatch, RecordBatchOptions};
use arrow::datatypes::{DataType, Field, Fields, Schema};
use egui::vec2;
use egui_kittest::{Harness, SnapshotResults};
use re_dataframe_ui::column_header_tooltip_ui;
use re_log_types::{EntityPath, Timeline};
use re_sorbet::{
    BatchType, ComponentColumnDescriptor, IndexColumnDescriptor, RowIdColumnDescriptor, SorbetBatch,
};
use re_types_core::reflection::generic_placeholder_for_datatype;
use re_types_core::{ArchetypeName, ComponentIdentifier, ComponentType};

#[test]
fn test_column_header_tooltips() {
    let mut snapshot_results = SnapshotResults::new();
    let fields_and_descriptions = test_fields();

    let fields = Fields::from(
        fields_and_descriptions
            .iter()
            .map(|(field, _)| field.clone())
            .collect::<Vec<_>>(),
    );

    let data = fields_and_descriptions
        .iter()
        .map(|(field, _)| Arc::new(generic_placeholder_for_datatype(field.data_type())) as ArrayRef)
        .collect::<Vec<_>>();

    let record_batch = RecordBatch::try_new_with_options(
        Arc::new(Schema::new_with_metadata(
            fields.clone(),
            Default::default(),
        )),
        data,
        &RecordBatchOptions::default(),
    )
    .expect("Failed to create test RecordBatch");

    let sorbet_batch = SorbetBatch::try_from_record_batch(&record_batch, BatchType::Dataframe)
        .expect("Failed to create test SorbetBatch");

    let descriptions = fields_and_descriptions
        .iter()
        .map(|(_, name)| *name)
        .collect::<Vec<_>>();

    for (desc, field, migrated_field, description) in itertools::izip!(
        sorbet_batch.sorbet_schema().columns.clone(),
        &fields,
        sorbet_batch.fields(),
        descriptions
    ) {
        for show_extras in [false, true] {
            let mut harness = Harness::builder()
                .with_size(vec2(600.0, 600.0))
                .build_ui(|ui| {
                    re_ui::apply_style_and_install_loaders(ui.ctx());

                    column_header_tooltip_ui(
                        ui,
                        &(&desc).into(),
                        field.as_ref(),
                        migrated_field.as_ref(),
                        show_extras,
                    );
                });

            harness.run();
            harness.snapshot(format!(
                "header_tooltip_{description}{}",
                if show_extras { "_with_extras" } else { "" }
            ));

            snapshot_results.extend_harness(&mut harness);
        }
    }
}

fn test_fields() -> Vec<(Field, &'static str)> {
    let component_column_desc = ComponentColumnDescriptor {
        store_datatype: DataType::Int64,
        component_type: Some(ComponentType::new("rerun.components.Null")),
        entity_path: EntityPath::from("/some/path"),
        archetype: Some(ArchetypeName::new("ArchetypeName")),
        component: ComponentIdentifier::from("component_identifier"),
        is_static: false,
        is_tombstone: false,
        is_semantically_empty: false,
    };

    vec![
        (
            RowIdColumnDescriptor::from_sorted(true).to_arrow_field(),
            "row_id_column",
        ),
        (
            IndexColumnDescriptor::from(Timeline::new_duration("duration_timeline"))
                .to_arrow_field(),
            "index_column",
        ),
        (
            component_column_desc.to_arrow_field(BatchType::Chunk),
            "chunk_component_column",
        ),
        (
            component_column_desc.to_arrow_field(BatchType::Dataframe),
            "dataframe_component_column",
        ),
        (
            Field::new("simple_field", DataType::Int64, false),
            "raw_field",
        ),
        (
            Field::new("simple_field", DataType::Int64, true),
            "row_field_nullable",
        ),
        (
            Field::new("user_metadata", DataType::Float32, false)
                .with_metadata(std::iter::once(("hello".to_owned(), "world".to_owned())).collect()),
            "raw_field_user_metadata",
        ),
        (
            Field::new("user_metadata", DataType::Float32, false).with_metadata(
                [
                    ("hello".to_owned(), "world".to_owned()),
                    (
                        re_sorbet::metadata::SORBET_ENTITY_PATH.to_owned(),
                        "/entity/path".to_owned(),
                    ),
                ]
                .into_iter()
                .collect(),
            ),
            "raw_field_user_and_sorbet_metadata",
        ),
    ]
}