sift-query-derive 0.1.0

Derive macros for SiftQuery entities
Documentation
use super::FieldConfig;
use proc_macro2::TokenStream as TokenStream2;
use quote::quote;
use std::collections::{BTreeMap, BTreeSet};

pub(crate) fn parse_order_list(
    input: Option<&str>,
    label: &str,
    fields: &BTreeMap<String, &FieldConfig>,
    span: proc_macro2::Span,
) -> syn::Result<Vec<(u16, bool)>> {
    let Some(input) = input else {
        return Ok(Vec::new());
    };
    if input.is_empty() {
        return Err(syn::Error::new(span, format!("{label} cannot be empty")));
    }
    let mut output = Vec::new();
    let mut seen = BTreeSet::new();
    for item in input.split(',') {
        let (name, direction) = item.split_once(':').ok_or_else(|| {
            syn::Error::new(span, format!("{label} item {item} has no direction"))
        })?;
        let field = fields.get(name).ok_or_else(|| {
            syn::Error::new(span, format!("{label} references unknown field {name}"))
        })?;
        if !field.sortable {
            return Err(syn::Error::new(
                span,
                format!("{label} field {name} is not sortable"),
            ));
        }
        if !seen.insert(name) {
            return Err(syn::Error::new(
                span,
                format!("{label} contains duplicate field {name}"),
            ));
        }
        let ascending = match direction {
            "asc" => true,
            "desc" => false,
            _ => {
                return Err(syn::Error::new(
                    span,
                    format!("{label} has invalid direction {direction}"),
                ));
            }
        };
        output.push((field.index, ascending));
    }
    Ok(output)
}

pub(crate) fn order_tokens(order: &[(u16, bool)]) -> Vec<TokenStream2> {
    order
        .iter()
        .map(|(field, ascending)| {
            let direction = if *ascending {
                quote!(::sift_query::__private::SortOrder::Asc)
            } else {
                quote!(::sift_query::__private::SortOrder::Desc)
            };
            quote! {
                ::sift_query::__private::OrderMeta {
                    field: ::sift_query::__private::FieldId(#field),
                    direction: #direction,
                }
            }
        })
        .collect()
}