df-derive-macros 0.3.0

Procedural derive macro implementation for df-derive.
Documentation
use quote::ToTokens;

pub fn unannotated_cow_bytes<S: ToTokens + ?Sized>(
    span: &S,
    field_display_name: &str,
    can_add_as_binary: bool,
) -> syn::Error {
    let message = if can_add_as_binary {
        format!(
            "field `{field_display_name}` uses `Cow<'_, [u8]>` without `as_binary`; \
             add `#[df_derive(as_binary)]` to encode it as Binary, or use \
             `Vec<u8>` if you want the default `List(UInt8)` representation"
        )
    } else {
        format!(
            "field `{field_display_name}` contains `Cow<'_, [u8]>` in a tuple element; \
             tuple elements cannot be annotated with `as_binary`, so use `Vec<u8>` for \
             the default `List(UInt8)` representation or hoist the bytes into a named \
             struct field with `#[df_derive(as_binary)]`"
        )
    };
    syn::Error::new_spanned(span, message)
}

pub fn unannotated_borrowed_bytes<S: ToTokens + ?Sized>(
    span: &S,
    field_display_name: &str,
    can_add_as_binary: bool,
) -> syn::Error {
    let message = if can_add_as_binary {
        format!(
            "field `{field_display_name}` uses `&[u8]` without `as_binary`; \
             add `#[df_derive(as_binary)]` to encode it as Binary, or use \
             `Vec<u8>` if you want the default `List(UInt8)` representation"
        )
    } else {
        format!(
            "field `{field_display_name}` contains `&[u8]` in a tuple element; \
             tuple elements cannot be annotated with `as_binary`, so use `Vec<u8>` \
             for the default `List(UInt8)` representation or hoist the bytes into \
             a named struct field with `#[df_derive(as_binary)]`"
        )
    };
    syn::Error::new_spanned(span, message)
}

pub fn borrowed_slice<S: ToTokens + ?Sized>(span: &S, field_display_name: &str) -> syn::Error {
    syn::Error::new_spanned(
        span,
        format!(
            "field `{field_display_name}` uses `&[T]`, but df-derive only \
             supports `&[u8]` with `#[df_derive(as_binary)]`; use `Vec<T>` \
             for list columns"
        ),
    )
}

pub fn cow_slice<S: ToTokens + ?Sized>(span: &S, field_display_name: &str) -> syn::Error {
    syn::Error::new_spanned(
        span,
        format!(
            "field `{field_display_name}` uses `Cow<'_, [T]>`, but df-derive only \
             supports `Cow<'_, [u8]>` with `#[df_derive(as_binary)]`; use `Vec<T>` \
             for list columns"
        ),
    )
}

pub fn as_str_wrong_base<S: ToTokens + ?Sized>(span: &S, field_display_name: &str) -> syn::Error {
    syn::Error::new_spanned(
        span,
        format!(
            "field `{field_display_name}` has `as_str` but its base type does not implement \
             `AsRef<str>`; `as_str` only applies to `String`, `&str`, `Cow<'_, str>`, \
             custom struct types, or generic type parameters — drop the attribute or \
             change the field type"
        ),
    )
}

pub fn as_string_std_duration<S: ToTokens + ?Sized>(
    span: &S,
    field_display_name: &str,
) -> syn::Error {
    syn::Error::new_spanned(
        span,
        format!(
            "field `{field_display_name}` has `as_string`, but \
             `std::time::Duration` and `core::time::Duration` do not implement \
             `Display`; drop `as_string` to encode a Duration column, or wrap \
             the value in a custom type that implements `Display`"
        ),
    )
}

pub fn as_string_bytes<S: ToTokens + ?Sized>(span: &S, field_display_name: &str) -> syn::Error {
    syn::Error::new_spanned(
        span,
        format!(
            "field `{field_display_name}` has `as_string`, but byte slices \
             (`&[u8]`/`Cow<'_, [u8]>`) do not implement `Display`; use \
             `#[df_derive(as_binary)]` for a Binary column, use `Vec<u8>` \
             for a `List(UInt8)` column, or wrap the value in a custom type \
             that implements `Display`"
        ),
    )
}

pub fn as_string_slice<S: ToTokens + ?Sized>(span: &S, field_display_name: &str) -> syn::Error {
    syn::Error::new_spanned(
        span,
        format!(
            "field `{field_display_name}` has `as_string`, but borrowed slices \
             (`&[T]`/`Cow<'_, [T]>`) do not implement `Display`; use `Vec<T>` \
             for list columns, or wrap the value in a custom type that \
             implements `Display`"
        ),
    )
}

pub fn as_string_tuple<S: ToTokens + ?Sized>(span: &S, field_display_name: &str) -> syn::Error {
    syn::Error::new_spanned(
        span,
        format!(
            "field `{field_display_name}` has `as_string` but its type is a tuple; \
             field-level attributes do not apply to multi-column tuple fields. \
             Hoist the tuple into a named struct that derives `ToDataFrame` if \
             you need per-element attributes."
        ),
    )
}

pub fn decimal_wrong_base<S: ToTokens + ?Sized>(span: &S, field_display_name: &str) -> syn::Error {
    syn::Error::new_spanned(
        span,
        format!(
            "field `{field_display_name}` has `decimal(...)` but its base type is not \
             a decimal backend candidate; `decimal(...)` applies to types named \
             `Decimal`, custom struct types, or generic type parameters that \
             implement `Decimal128Encode`"
        ),
    )
}

pub fn time_unit_naive_date<S: ToTokens + ?Sized>(
    span: &S,
    field_display_name: &str,
) -> syn::Error {
    syn::Error::new_spanned(
        span,
        format!(
            "field `{field_display_name}` has `time_unit = \"...\"` but its base type is \
             `chrono::NaiveDate`, which has a fixed encoding (i32 days since 1970-01-01) \
             and offers no unit choice — remove the attribute"
        ),
    )
}

pub fn time_unit_naive_time<S: ToTokens + ?Sized>(
    span: &S,
    field_display_name: &str,
) -> syn::Error {
    syn::Error::new_spanned(
        span,
        format!(
            "field `{field_display_name}` has `time_unit = \"...\"` but its base type is \
             `chrono::NaiveTime`, which has a fixed encoding (i64 nanoseconds since \
             midnight) and offers no unit choice — remove the attribute"
        ),
    )
}

pub fn time_unit_wrong_base<S: ToTokens + ?Sized>(
    span: &S,
    field_display_name: &str,
) -> syn::Error {
    syn::Error::new_spanned(
        span,
        format!(
            "field `{field_display_name}` has `time_unit = \"...\"` but its base type is \
             not `chrono::DateTime<Tz>`, `chrono::NaiveDateTime`, \
             `std::time::Duration`, `core::time::Duration`, or \
             `chrono::Duration`; remove the attribute or change the field type"
        ),
    )
}