mssql_derive/lib.rs
1// Proc macros operate on named structs where field.ident is always Some
2#![allow(clippy::unwrap_used)]
3
4//! # mssql-derive
5//!
6//! Procedural macros for SQL Server row mapping and parameter handling.
7//!
8//! This crate provides derive macros for automatically implementing
9//! row-to-struct mapping and struct-to-parameter conversion.
10//!
11//! ## Available Macros
12//!
13//! - `#[derive(FromRow)]` - Convert database rows to structs
14//! - `#[derive(ToParams)]` - Convert structs to query parameters
15//! - `#[derive(Tvp)]` - Table-valued parameter support
16//!
17//! ## Example
18//!
19//! ```rust,ignore
20//! use mssql_derive::{FromRow, ToParams};
21//!
22//! // Automatic row mapping
23//! #[derive(FromRow)]
24//! struct User {
25//! id: i32,
26//! #[mssql(rename = "user_name")]
27//! name: String,
28//! email: Option<String>,
29//! }
30//!
31//! // Automatic parameter conversion
32//! #[derive(ToParams)]
33//! struct NewUser {
34//! name: String,
35//! email: String,
36//! }
37//! ```
38
39#![warn(missing_docs)]
40
41use proc_macro::TokenStream;
42use syn::parse_macro_input;
43
44mod attributes;
45mod from_row;
46mod naming;
47mod to_params;
48mod tvp;
49
50/// Derive macro for implementing `FromRow` trait.
51///
52/// This macro generates code to convert a database row into a struct.
53///
54/// ## Attributes
55///
56/// ### Field Attributes
57///
58/// - `#[mssql(rename = "column_name")]` - Map field to a different column name
59/// - `#[mssql(skip)]` - Skip this field (must have a Default implementation)
60/// - `#[mssql(default)]` - Use Default if column is NULL or missing
61/// - `#[mssql(flatten)]` - Flatten a nested struct implementing FromRow
62///
63/// ### Struct Attributes
64///
65/// - `#[mssql(rename_all = "snake_case")]` - Apply naming convention to all fields
66///
67/// ## Example
68///
69/// ```rust,ignore
70/// #[derive(FromRow)]
71/// #[mssql(rename_all = "PascalCase")]
72/// struct User {
73/// id: i32,
74/// #[mssql(rename = "UserName")]
75/// name: String,
76/// #[mssql(default)]
77/// email: Option<String>,
78/// #[mssql(skip)]
79/// computed: String,
80/// }
81/// ```
82#[proc_macro_derive(FromRow, attributes(mssql))]
83pub fn derive_from_row(input: TokenStream) -> TokenStream {
84 let input = parse_macro_input!(input as syn::DeriveInput);
85 match from_row::impl_from_row(&input) {
86 Ok(tokens) => tokens.into(),
87 Err(err) => err.to_compile_error().into(),
88 }
89}
90
91/// Derive macro for implementing `ToParams` trait.
92///
93/// This macro generates code to convert a struct into query parameters.
94///
95/// ## Attributes
96///
97/// - `#[mssql(rename = "param_name")]` - Use a different parameter name
98/// - `#[mssql(skip)]` - Don't include this field as a parameter
99///
100/// ## Example
101///
102/// ```rust,ignore
103/// #[derive(ToParams)]
104/// struct NewUser {
105/// name: String,
106/// #[mssql(rename = "email_address")]
107/// email: String,
108/// #[mssql(skip)]
109/// internal_id: u64,
110/// }
111///
112/// let user = NewUser {
113/// name: "Alice".into(),
114/// email: "alice@example.com".into(),
115/// internal_id: 0,
116/// };
117///
118/// client.execute(
119/// "INSERT INTO users (name, email_address) VALUES (@name, @email_address)",
120/// &user.to_params()?,
121/// ).await?;
122/// ```
123#[proc_macro_derive(ToParams, attributes(mssql))]
124pub fn derive_to_params(input: TokenStream) -> TokenStream {
125 let input = parse_macro_input!(input as syn::DeriveInput);
126 match to_params::impl_to_params(&input) {
127 Ok(tokens) => tokens.into(),
128 Err(err) => err.to_compile_error().into(),
129 }
130}
131
132/// Derive macro for implementing `Tvp` trait (Table-Valued Parameters).
133///
134/// This macro generates code to use a struct as a table-valued parameter row.
135///
136/// ## Attributes
137///
138/// ### Struct Attributes (Required)
139///
140/// - `#[mssql(type_name = "schema.TypeName")]` - SQL Server TVP type name
141///
142/// ### Field Attributes
143///
144/// - `#[mssql(rename = "column_name")]` - Map field to a different column name
145/// - `#[mssql(skip)]` - Don't include this field in the TVP
146///
147/// ## Example
148///
149/// First, create the table type in SQL Server:
150///
151/// ```sql
152/// CREATE TYPE dbo.UserIdList AS TABLE (
153/// UserId INT NOT NULL
154/// );
155/// ```
156///
157/// Then derive the trait:
158///
159/// ```rust,ignore
160/// #[derive(Tvp)]
161/// #[mssql(type_name = "dbo.UserIdList")]
162/// struct UserId {
163/// #[mssql(rename = "UserId")]
164/// user_id: i32,
165/// }
166///
167/// let ids = vec![UserId { user_id: 1 }, UserId { user_id: 2 }];
168/// let tvp = TvpValue::new(&ids)?;
169/// ```
170#[proc_macro_derive(Tvp, attributes(mssql))]
171pub fn derive_tvp(input: TokenStream) -> TokenStream {
172 let input = parse_macro_input!(input as syn::DeriveInput);
173 match tvp::impl_tvp(&input) {
174 Ok(tokens) => tokens.into(),
175 Err(err) => err.to_compile_error().into(),
176 }
177}