criware_utf_macros/lib.rs
1//! The procedural macros offered by `criware-utf`.
2//!
3//! Please do not use this on its own. Use the full `criware-utf` crate.
4//!
5
6extern crate proc_macro;
7use proc_macro::TokenStream;
8
9pub(crate) type Result<T> = syn::Result<T>;
10
11macro_rules! syn_error {
12 ($span:expr, $message:expr) => {
13 return Err(syn::Error::new($span, $message))
14 };
15}
16
17mod utf_table;
18mod utils;
19
20/**
21Macro for arranging a table and implementing useful `Table` behavior
22
23# General Output Structure
24
25This attribute macro accepts a struct definition, and outputs one or more new
26struct definitions, along with an implementation of the `Table` trait.
27
28For example, if the struct is named `ImportantTable`. The final struct
29definition may look something like this:
30
31```no_run
32# struct ImportantTableConstants {};
33# struct ImportantTableRow {};
34struct ImportantTable {
35 constants: ImportantTableConstants,
36 rows: Vec<ImportantTableRow>,
37 write_context: criware_utf::WriteContext
38}
39```
40
41The macro would also define `ImportantTableConstants` and `ImportantTableRow`
42as part of its output, containing the constant or row fields respectively. They
43are only defined when they are needed.
44
45- If there are no constant columns, `ImportantTableConstants` and the `constants`
46field are not included
47- If there are no rowed columns, `ImportantTableRow` and the `rows` field are
48not included
49- If there are no optional rowed columns, the `write_context` field is not
50included
51
52## Input/Output Examples
53
54```no_run
55# use criware_utf_derive::utf_table;
56// Input
57#[utf_table]
58struct NuTable {
59 id: i64,
60 blob: Vec<u8>,
61 #[constant]
62 comment: String
63}
64
65// Output
66struct NuTableConstants {
67 comment: String
68}
69struct NuTableRow {
70 id: i64,
71 blob: Vec<u8>
72}
73struct NuTable {
74 constants: NuTableConstants,
75 rows: Vec<NuTableRow>
76}
77impl Table for NuTable {/** ... */}
78```
79
80```no_run
81# use criware_utf_derive::utf_table;
82// Input
83#[utf_table(constants = FileTableInfo, row = File)]
84struct FileTable {
85 id: u64,
86 name: String,
87 #[column_name = "Crc"]
88 #[optional]
89 crc32: u32
90 #[constant]
91 version: String
92 #[constant]
93 #[optional]
94 table_metadata: Vec<u8>
95}
96
97// Output
98struct FileTableInfo {
99 version: String,
100 table_metadata: Option<Vec<u8>>
101}
102struct File {
103 id: u64,
104 name: String,
105 crc32: Option<u32>
106}
107struct FileTable {
108 constants: FileTableInfo,
109 rows: Vec<File>,
110 write_context: WriteContext
111}
112impl Table for FileTable {/** ... */}
113```
114
115# Attribute Options
116
117This section outlines the optional configuration options that can be included
118with the `#[utf_table]` attribute.
119
120These options can be chained together by separating them with a comma. Specifying
121any of these options multiple times will cause a compile error.
122
123```no_run
124# use criware_utf_derive::utf_table;
125#[utf_table(table_name = "TABLE", constants = TableStuff)]
126# struct Table {}
127```
128
129## `table_name`
130
131By default, the macro assumes the name of the table is the name of the struct.
132This can be overwritten by specifying a string literal. The generated read procedure
133will check that the name of the table matches, so specifying this is important.
134
135```no_run
136# use criware_utf_derive::utf_table;
137#[utf_table(table_name = "ActualTableName")]
138# struct Table {}
139```
140
141## `constants`
142
143If a constant struct is generated, by default its name will be the *name of the
144input struct* + "Constants". This name can be overwritten by specifying a type name.
145If there is no constant struct generated, this option does nothing.
146
147```no_run
148# use criware_utf_derive::utf_table;
149#[utf_table(constants = TConsts)]
150# struct Table {}
151```
152
153## `row`
154
155If a row struct is generated, by default its name will be the *name of the
156input struct* + "Row". This name can be overwritten by specifying a type name.
157If there is no row struct generated, this option does nothing.
158
159```no_run
160# use criware_utf_derive::utf_table;
161#[utf_table(row = TRow)]
162# struct Table {}
163```
164
165# Field Options
166
167This section outlines the optional configuration options for each field within
168the input struct.
169
170Multiple may be used on the same field, but duplicate or conflicting options
171will result in a compile error.
172
173```no_run
174# #[criware_utf_derive::utf_table]
175# struct Table {
176#[optional]
177#[constant]
178some_value: i64
179# }
180```
181
182## `#[column_name = "{name}"]`
183
184By default, the in-table column name associated with a field is an **upper
185camel case** conversion of the field name (e.g. "some_column" => "SomeColumn").
186This can be overwritten by passing a string literal.
187
188```no_run
189# #[criware_utf_derive::utf_table]
190# struct Table {
191#[column_name = "TheActualColumnName"]
192some_value: i64
193# }
194```
195
196## `#[constant]`
197
198By default, columns are rowed. This marks the column as constant instead.
199
200If this is used in the presence of `#[rowed]`, there will be a compile error.
201
202```no_run
203# #[criware_utf_derive::utf_table]
204# struct Table {
205#[constant]
206some_value: i64
207# }
208```
209
210## `#[rowed]`
211
212Since columns are rowed by default, using this attribute is superfluous.
213Nevertheless, this can be manually specified if it helps make the form of each
214column clear.
215
216If this is used in the presence of `#[constant]`, there will be a compile error.
217
218```no_run
219# #[criware_utf_derive::utf_table]
220# struct Table {
221#[rowed]
222some_value: i64
223# }
224```
225
226## `#[optional]`
227
228This marks a column as optional.
229
230In the constant or row struct output, the field will have type `Option<T>`
231instead of `T` (where `T` is the original type specified in the struct definition).
232In the table read procedure, the column storage method may be zero or
233constant/rowed (depending on whether the column is marked as constant or rowed).
234
235In the table write procedure, the storage method that is used for the column
236is dependent on the column's associated value(s). If the values are all `None`,
237the storage method will be zero. If the values are all `Some`, the storage
238method will be constant/rowed. **There cannot be a mix of `Some` and `None`**.
239If there is, the write procedure will error out.
240
241Furthermore, if the column is rowed and there are *no rows*, the storage method
242will depend on other factors.
243
244If the table was read, it will simply copy the storage method it was originally
245read as.
246
247If the table is created from scratch, the storage method will default to
248zero. This behavior can be overwritten with configuration options on
249the `#[optional]` attribute.
250
251### `#[optional(include)]`
252
253This will make the column constant/rowed instead of zero
254
255### `#[optional(exclude)]`
256
257This does nothing, but can make the write procedure's behavior more clear to
258readers.
259*/
260#[proc_macro_attribute]
261pub fn utf_table(attr: TokenStream, item: TokenStream) -> TokenStream {
262 match utf_table::parse(attr.into(), item.into()) {
263 Ok(value) => value.into(),
264 Err(error) => error.to_compile_error().into(),
265 }
266}