df-derive 0.3.0

Derive fast conversions from Rust structs into Polars DataFrames.
Documentation
//! Generic structs (v0.3.0).
//!
//! `#[derive(ToDataFrame)]` accepts type parameters, default type parameters,
//! and multiple generics. The macro injects `ToDataFrame + Columnar`
//! bounds on every type parameter, so any concrete instantiation must satisfy
//! those traits. The unit type `()` can be used as a payload to contribute
//! zero columns.
//!
//! Uses the default `df-derive` facade runtime, including the unit-payload impls.

use df_derive::ToDataFrame;
use df_derive::dataframe::{ToDataFrame as _, ToDataFrameVec as _};

#[derive(ToDataFrame, Clone)]
struct Meta {
    timestamp: i64,
    note: String,
}

// No explicit `T: Clone` bounds below — `#[derive(Clone)]` adds its own, and
// the derive macro auto-injects `ToDataFrame + Columnar` on every type
// parameter for the impl blocks it generates.

#[derive(ToDataFrame, Clone)]
struct Wrapper<T> {
    id: u32,
    payload: T,
}

#[derive(ToDataFrame, Clone)]
struct DefaultMeta<M = ()> {
    value: i32,
    meta: M,
}

#[derive(ToDataFrame, Clone)]
struct Pair<A, B> {
    name: String,
    left: A,
    right: B,
}

#[derive(ToDataFrame, Clone)]
struct OptWrapper<T> {
    id: u32,
    payload: Option<T>,
}

#[derive(ToDataFrame, Clone)]
struct VecWrapper<T> {
    id: u32,
    payload: Vec<T>,
}

fn main() -> polars::prelude::PolarsResult<()> {
    // 1. Generic over a nested struct. Schema flattens to `id`, plus
    //    `payload.timestamp` and `payload.note` from `Meta`.
    let row = Wrapper {
        id: 1,
        payload: Meta {
            timestamp: 1_700_000_000,
            note: "hello".into(),
        },
    };
    println!("Wrapper<Meta> (single):\n{}\n", row.to_dataframe()?);

    let rows = vec![
        Wrapper {
            id: 1,
            payload: Meta {
                timestamp: 1_700_000_000,
                note: "first".into(),
            },
        },
        Wrapper {
            id: 2,
            payload: Meta {
                timestamp: 1_700_000_100,
                note: "second".into(),
            },
        },
    ];
    println!(
        "Wrapper<Meta> (batch):\n{}\n",
        rows.as_slice().to_dataframe()?
    );

    // 2. Unit payload. `Wrapper<()>` and `DefaultMeta` (default M = ())
    //    contribute no extra columns — useful for "tagged" rows that carry no
    //    extra payload but share a schema with richer instantiations.
    let unit = Wrapper { id: 7, payload: () };
    println!("Wrapper<()> (unit payload):\n{}\n", unit.to_dataframe()?);

    let default_meta: DefaultMeta = DefaultMeta {
        value: 42,
        meta: (),
    };
    println!(
        "DefaultMeta (M defaults to ()):\n{}\n",
        default_meta.to_dataframe()?
    );

    // 3. Multiple generic parameters. Each parameter gets the standard bound
    //    set (`ToDataFrame + Columnar`) injected by the macro.
    let pair = Pair {
        name: "trade-1".into(),
        left: Meta {
            timestamp: 1,
            note: "buy".into(),
        },
        right: Meta {
            timestamp: 2,
            note: "sell".into(),
        },
    };
    println!("Pair<Meta, Meta>:\n{}\n", pair.to_dataframe()?);

    // 4. Option<T> and Vec<T> for generic T. Both go through the bulk
    //    columnar path (gather/scatter for Option, flatten + slice for Vec)
    //    when emitted by the derive.
    let opts = vec![
        OptWrapper {
            id: 1,
            payload: Some(Meta {
                timestamp: 10,
                note: "present".into(),
            }),
        },
        OptWrapper {
            id: 2,
            payload: None,
        },
    ];
    println!(
        "OptWrapper<Meta> (batch):\n{}\n",
        opts.as_slice().to_dataframe()?
    );

    let vecs = vec![
        VecWrapper {
            id: 1,
            payload: vec![
                Meta {
                    timestamp: 1,
                    note: "a".into(),
                },
                Meta {
                    timestamp: 2,
                    note: "b".into(),
                },
            ],
        },
        VecWrapper {
            id: 2,
            payload: vec![],
        },
    ];
    println!(
        "VecWrapper<Meta> (batch):\n{}\n",
        vecs.as_slice().to_dataframe()?
    );

    Ok(())
}