csv-codegen 0.1.1

A Rust procedural macro that transforms CSV data into safe, zero-cost code. Generate match arms, loops, and nested queries directly from CSV files, ensuring type safety and deterministic code generation.
Documentation

csv_codegen

A Rust procedural macro for generating code from CSV data at compile time. Transform CSV files into Rust constants, functions, structs, and other code using a flexible templating syntax.

Features

  • Compile-time CSV processing - CSV files are read and processed during compilation
  • Template-based code generation - Use a simple template syntax to generate any Rust code
  • Field transformations - Convert CSV data to valid Rust identifiers, constants, types, and literals
  • Filtering support - Include/exclude rows based on conditions
  • Pivoting - Transform columns into key-value pairs for more flexible data structures
  • Type-safe literals - Generate properly typed numeric literals (42_f64, 10_u32, etc.)

Installation

Add this to your Cargo.toml:

[dependencies]
csv_codegen = "0.1"

Quick Start

Given a CSV file products.csv:

name,price,category
apple,1.20,fruit
carrot,0.80,vegetable
banana,0.90,fruit

Generate constants:

use csv_codegen::csv_template;

csv_template!("products.csv", #for()(
    pub const #CONST({name}_PRICE): f64 = #({price}_f64);
));

// Generates:
// pub const APPLE_PRICE: f64 = 1.20_f64;
// pub const CARROT_PRICE: f64 = 0.80_f64;
// pub const BANANA_PRICE: f64 = 0.90_f64;

assert_eq!(APPLE_PRICE, 1.20);

Syntax

csv_template!(
    "path/to/file.csv",
    [pivot(column_range, key_column, value_column),]
    #for([condition])(
        template_code
    )
)

Arguments

  • CSV path: Relative path to the CSV file from the crate root
  • pivot() (optional): Transforms specified columns into key-value pairs
  • #for(): Iterates over CSV rows, with optional filtering condition

Field Substitution

Identifier Transformations

  • #ident(expression) - Converts to snake_case identifier
  • #CONST(expression) - Converts to SCREAMING_SNAKE_CASE constant
  • #Type(expression) - Converts to PascalCase type name
// "Green Apple" becomes:
#ident({name})     // green_apple
#CONST({name})     // GREEN_APPLE  
#Type({name})      // GreenApple

Literal Formatting

  • #({field}_suffix) - Creates typed literals
  • #("{field}") - Creates string literals
#({price}_f64)     // 1.20_f64
#({count}_u32)     // 42_u32
#("{name}")        // "apple"

Filtering

Filter rows using conditions in #for():

csv_template!("products.csv", #for(category == "fruit")(
    pub fn #ident(get_{name}_price)() -> f64 {
        #({price}_f64)
    }
));

// Only generates functions for fruits:
// pub fn get_apple_price() -> f64 { 1.20_f64 }
// pub fn get_banana_price() -> f64 { 0.90_f64 }

Supported operators:

  • == - equality
  • != - inequality

Pivoting

Transform columns into key-value pairs:

// sales.csv: product,q1_sales,q2_sales,q3_sales,q4_sales
//           widget,100,150,120,200

csv_template!("sales.csv", 
    pivot("q1_sales"..="q4_sales", quarter, amount),
    #for()(
        match (product, quarter) {
            #for()(
                (#("{product}"), #("{quarter}")) => #({amount}_u32),
            )
            _ => 0,
        }
    )
);

This transforms each row into multiple rows with quarter/amount pairs.

Examples

Generate Match Arms

csv_template!("products.csv", #for()(
    fn get_price(name: &str) -> Option<f64> {
        match name {
            #for(price != "")(
                #("{name}") => Some(#({price}_f64)),
            )
            _ => None,
        }
    }
));

Generate Structs with Constants

csv_template!("products.csv", #for()(
    pub struct #Type({name}Product);
    
    impl #Type({name}Product) {
        pub const PRICE: f64 = #({price}_f64);
        pub const CATEGORY: &'static str = #("{category}");
    }
));

Pivoting Example

csv_template!("sales.csv", 
    pivot("q1_sales"..="q4_sales", quarter, sales),
    #for()(
        struct #Type({product}Product);
        impl #Type({product}Product) {
            #for()(
                pub const #CONST({quarter}): u32 = #({sales}_u32);
            )
        }
    )
);

// Usage:
assert_eq!(WidgetProduct::Q1_SALES, 100);
assert_eq!(WidgetProduct::Q4_SALES, 200);

Use Cases

  • Configuration from CSV - Generate constants and enums from configuration data
  • Test data - Create test fixtures from CSV files
  • Code tables - Transform lookup tables into efficient match statements
  • Translations - Create internationalization constants from CSV files

Limitations

  • CSV files are read at compile time - changes require recompilation
  • Field names must be valid when converted to Rust identifiers
  • Empty cells are treated as empty strings
  • Only basic filtering conditions are supported (==, !=)

License

Licensed under Apache License, Version 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)