# 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`:
```toml
[dependencies]
csv_codegen = "0.1"
```
## Quick Start
Given a CSV file `products.csv`:
```csv
name,price,category
apple,1.20,fruit
carrot,0.80,vegetable
banana,0.90,fruit
```
Generate constants:
```rust
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
```rust
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
```rust
// "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
```rust
#({price}_f64) // 1.20_f64
#({count}_u32) // 42_u32
#("{name}") // "apple"
```
## Filtering
Filter rows using conditions in `#for()`:
```rust
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:
```rust
// 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
```rust
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
```rust
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
```rust
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](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)