1use proc_macro2::{Delimiter, Group, TokenStream, TokenTree};
5
6use crate::{generate::compile_error, key_conformance::expand_tests};
7
8pub struct KeyField {
9 pub name: String,
10 pub column: KeyColumn,
11 pub ty: String,
12}
13
14#[derive(Clone, Copy)]
15pub enum KeyColumn {
16 U8,
17 U16,
18 U32,
19 U64,
20 U128,
21 RowNumber,
22 GroupId,
23 Blob16,
24 TableId,
25 ColumnId,
26 ObjectId,
27 StorageId,
28 FlowId,
29 FlowEdgeId,
30 OperatorId,
31 HandlerId,
32 NamespaceId,
33 SumTypeId,
34 SequenceId,
35 EpochSeconds,
36 IdentityId,
37 ViewId,
38 SeriesId,
39 SinkId,
40 SourceId,
41 QueueId,
42 RingBufferId,
43 BindingId,
44 DictionaryId,
45 ProcedureId,
46 DateTime,
47 Partition,
48 ColumnPropertyId,
49 RowShapeFingerprint,
50 RelationshipId,
51 MigrationId,
52 MigrationEventId,
53 PrimaryKeyId,
54 ColumnBlockId,
55 IndexId,
56 OptionU8,
57 ReprU8,
58 Str,
59 DisplayStr,
60 EscapedBytes,
61 KeyspaceId,
62 RawTail,
63 TaggedIndexId,
64 VarintU128,
65 SortRun,
66 AscRowNumber,
67}
68
69impl KeyColumn {
70 fn width(self) -> usize {
71 match self {
72 KeyColumn::U8 | KeyColumn::ReprU8 | KeyColumn::KeyspaceId => 1,
73 KeyColumn::Str
74 | KeyColumn::DisplayStr
75 | KeyColumn::EscapedBytes
76 | KeyColumn::RawTail
77 | KeyColumn::SortRun => 16,
78 KeyColumn::TaggedIndexId => 9,
79 KeyColumn::VarintU128 => 17,
80 KeyColumn::AscRowNumber => 8,
81 KeyColumn::U16 => 2,
82 KeyColumn::U32 => 4,
83 KeyColumn::U64
84 | KeyColumn::RowNumber
85 | KeyColumn::TableId
86 | KeyColumn::ColumnId
87 | KeyColumn::FlowId
88 | KeyColumn::FlowEdgeId
89 | KeyColumn::OperatorId
90 | KeyColumn::HandlerId
91 | KeyColumn::NamespaceId
92 | KeyColumn::SumTypeId
93 | KeyColumn::SequenceId
94 | KeyColumn::EpochSeconds
95 | KeyColumn::ViewId
96 | KeyColumn::SeriesId
97 | KeyColumn::SinkId
98 | KeyColumn::SourceId
99 | KeyColumn::QueueId
100 | KeyColumn::RingBufferId
101 | KeyColumn::BindingId
102 | KeyColumn::DictionaryId
103 | KeyColumn::ProcedureId
104 | KeyColumn::DateTime
105 | KeyColumn::ColumnPropertyId
106 | KeyColumn::RowShapeFingerprint
107 | KeyColumn::RelationshipId
108 | KeyColumn::MigrationId
109 | KeyColumn::MigrationEventId
110 | KeyColumn::ColumnBlockId
111 | KeyColumn::PrimaryKeyId
112 | KeyColumn::IndexId => 8,
113 KeyColumn::ObjectId | KeyColumn::StorageId => 9,
114 KeyColumn::OptionU8 => 2,
115 KeyColumn::U128 | KeyColumn::Blob16 | KeyColumn::Partition | KeyColumn::IdentityId => 16,
116 KeyColumn::GroupId => 24,
117 }
118 }
119
120 fn field_exprs(self, field: &str) -> Vec<String> {
121 let u = |width: &str, expr: String| vec![format!("Field::UDesc(Width::{width}, {expr} as u128)")];
122 let cow = |expr: String| format!("::std::borrow::Cow::Borrowed({expr})");
123 match self {
124 KeyColumn::Str => vec![format!(
125 "Field::BytesDesc(ByteEncoding::Escaped, {})",
126 cow(format!("self.{field}.as_ref().as_bytes()"))
127 )],
128 KeyColumn::DisplayStr => vec![format!(
129 "Field::BytesDesc(ByteEncoding::Escaped, ::std::borrow::Cow::Owned(self.{field}.to_string().into_bytes()))"
130 )],
131 KeyColumn::EscapedBytes => vec![format!(
132 "Field::BytesDesc(ByteEncoding::Escaped, {})",
133 cow(format!("self.{field}.as_slice()"))
134 )],
135 KeyColumn::KeyspaceId => u("U8", format!("self.{field}.0")),
136 KeyColumn::RawTail => {
137 vec![format!(
138 "Field::RawAsc(RawEncoding::Verbatim, {})",
139 cow(format!("self.{field}.as_slice()"))
140 )]
141 }
142 KeyColumn::TaggedIndexId => vec![
143 format!("Field::UAsc(Width::U8, index_tag(&self.{field}) as u128)"),
144 format!("Field::UDesc(Width::U64, self.{field}.as_u64() as u128)"),
145 ],
146 KeyColumn::VarintU128 => u("Varint", format!("self.{field}")),
147 KeyColumn::SortRun => {
148 vec![format!(
149 "Field::RawAsc(RawEncoding::SortRun, {})",
150 cow(format!("self.{field}.as_slice()"))
151 )]
152 }
153 KeyColumn::AscRowNumber => vec![format!("Field::UAsc(Width::U64, self.{field}.0.0 as u128)")],
154 KeyColumn::U8 => u("U8", format!("self.{field}")),
155 KeyColumn::U16 => u("U16", format!("self.{field}")),
156 KeyColumn::U32 => u("U32", format!("self.{field}")),
157 KeyColumn::U64 => u("U64", format!("self.{field}")),
158 KeyColumn::U128 => u("U128", format!("self.{field}")),
159 KeyColumn::ReprU8 => u("U8", format!("self.{field} as u8")),
160 KeyColumn::RowNumber => u("U64", format!("self.{field}.0")),
161 KeyColumn::Partition => u("U128", format!("self.{field}.0")),
162 KeyColumn::ProcedureId => u("U64", format!("*self.{field}")),
163 KeyColumn::EpochSeconds => u("U64", format!("self.{field}.seconds()")),
164 KeyColumn::DateTime => u("U64", format!("self.{field}.to_order()")),
165 KeyColumn::RowShapeFingerprint | KeyColumn::IndexId => {
166 u("U64", format!("self.{field}.as_u64()"))
167 }
168 KeyColumn::GroupId => vec![format!(
169 "Field::BytesDesc(ByteEncoding::Fixed, ::std::borrow::Cow::Borrowed(self.{field}.as_bytes()))"
170 )],
171 KeyColumn::IdentityId => vec![format!(
172 "Field::BytesDesc(ByteEncoding::Escaped, ::std::borrow::Cow::Borrowed(self.{field}.as_bytes()))"
173 )],
174 KeyColumn::Blob16 => vec![format!(
175 "Field::RawAsc(RawEncoding::Verbatim, ::std::borrow::Cow::Borrowed(&self.{field}))"
176 )],
177 KeyColumn::ObjectId | KeyColumn::StorageId => vec![
178 format!("Field::UAsc(Width::U8, self.{field}.type_tag() as u128)"),
179 format!("Field::UDesc(Width::U64, self.{field}.as_u64() as u128)"),
180 ],
181 KeyColumn::OptionU8 => vec![
182 format!("Field::UDesc(Width::U8, self.{field}.is_some() as u128)"),
183 format!("Field::UDesc(Width::U8, self.{field}.unwrap_or(0u8) as u128)"),
184 ],
185 KeyColumn::TableId
186 | KeyColumn::ColumnId
187 | KeyColumn::FlowId
188 | KeyColumn::FlowEdgeId
189 | KeyColumn::OperatorId
190 | KeyColumn::HandlerId
191 | KeyColumn::NamespaceId
192 | KeyColumn::SumTypeId
193 | KeyColumn::SequenceId
194 | KeyColumn::ViewId
195 | KeyColumn::SeriesId
196 | KeyColumn::SinkId
197 | KeyColumn::SourceId
198 | KeyColumn::QueueId
199 | KeyColumn::RingBufferId
200 | KeyColumn::BindingId
201 | KeyColumn::DictionaryId
202 | KeyColumn::ColumnPropertyId
203 | KeyColumn::RelationshipId
204 | KeyColumn::MigrationId
205 | KeyColumn::MigrationEventId
206 | KeyColumn::ColumnBlockId
207 | KeyColumn::PrimaryKeyId => u("U64", format!("self.{field}.0")),
208 }
209 }
210
211 fn encode_stmt(self, field: &str) -> String {
212 match self {
213 KeyColumn::Str => format!("serializer.extend_str(&self.{field});"),
214 KeyColumn::DisplayStr => format!("serializer.extend_str(self.{field}.to_string());"),
215 KeyColumn::EscapedBytes => format!("serializer.extend_bytes(&self.{field});"),
216 KeyColumn::KeyspaceId => format!("serializer.extend_u8(self.{field}.0);"),
217 KeyColumn::RawTail => format!("serializer.extend_raw(self.{field}.as_slice());"),
218 KeyColumn::TaggedIndexId => format!("serializer.extend_index_id(self.{field});"),
219 KeyColumn::VarintU128 => format!("serializer.extend_u128_varint(self.{field});"),
220 KeyColumn::SortRun => format!("extend_sort_run(&mut serializer, &self.{field});"),
221 KeyColumn::AscRowNumber => format!("serializer.extend_raw(&self.{field}.0.0.to_be_bytes());"),
222 KeyColumn::U8 => format!("serializer.extend_u8(self.{field});"),
223 KeyColumn::ReprU8 => format!("serializer.extend_u8(self.{field} as u8);"),
224 KeyColumn::U16 => format!("serializer.extend_u16(self.{field});"),
225 KeyColumn::U32 => format!("serializer.extend_u32(self.{field});"),
226 KeyColumn::U64 => format!("serializer.extend_u64(self.{field});"),
227 KeyColumn::U128 => format!("serializer.extend_u128(self.{field});"),
228 KeyColumn::RowNumber => format!("serializer.extend_u64(self.{field}.0);"),
229 KeyColumn::GroupId => format!("serializer.extend_fixed(*self.{field}.as_bytes());"),
230 KeyColumn::Blob16 => format!("serializer.extend_raw(&self.{field});"),
231 KeyColumn::IdentityId => format!("serializer.extend_identity_id(&self.{field});"),
232 KeyColumn::TableId
233 | KeyColumn::ColumnId
234 | KeyColumn::FlowId
235 | KeyColumn::FlowEdgeId
236 | KeyColumn::OperatorId
237 | KeyColumn::HandlerId
238 | KeyColumn::NamespaceId
239 | KeyColumn::SumTypeId
240 | KeyColumn::SequenceId
241 | KeyColumn::ViewId
242 | KeyColumn::SeriesId
243 | KeyColumn::SinkId
244 | KeyColumn::SourceId
245 | KeyColumn::QueueId
246 | KeyColumn::RingBufferId
247 | KeyColumn::BindingId
248 | KeyColumn::DictionaryId
249 | KeyColumn::ColumnPropertyId
250 | KeyColumn::RelationshipId
251 | KeyColumn::MigrationId
252 | KeyColumn::MigrationEventId
253 | KeyColumn::ColumnBlockId
254 | KeyColumn::PrimaryKeyId => format!("serializer.extend_u64(self.{field}.0);"),
255 KeyColumn::ProcedureId => format!("serializer.extend_u64(*self.{field});"),
256 KeyColumn::EpochSeconds => format!("serializer.extend_u64(self.{field}.seconds());"),
257 KeyColumn::DateTime => format!("serializer.extend_datetime(&self.{field});"),
258 KeyColumn::RowShapeFingerprint => format!("serializer.extend_u64(self.{field}.as_u64());"),
259 KeyColumn::Partition => format!("serializer.extend_u128(self.{field}.0);"),
260 KeyColumn::IndexId => format!("serializer.extend_u64(self.{field}.as_u64());"),
261 KeyColumn::ObjectId | KeyColumn::StorageId => {
262 format!("serializer.extend_object_id(self.{field});")
263 }
264 KeyColumn::OptionU8 => format!(
265 "match self.{field} {{ Some(v) => {{ serializer.extend_u8(1u8).extend_u8(v); }} \
266 None => {{ serializer.extend_u8(0u8).extend_u8(0u8); }} }}"
267 ),
268 }
269 }
270
271 fn decode_expr(self, field_type: &str) -> String {
272 match self {
273 KeyColumn::Str => format!("{field_type}(de.read_str().ok()?)"),
274 KeyColumn::DisplayStr => {
275 format!("<{field_type} as ::core::str::FromStr>::from_str(&de.read_str().ok()?).ok()?")
276 }
277 KeyColumn::EscapedBytes => format!("{field_type}::new(de.read_bytes().ok()?)"),
278 KeyColumn::KeyspaceId => "KeyspaceId(de.read_u8().ok()?)".to_string(),
279 KeyColumn::RawTail if field_type == "Vec<u8>" => {
280 "de.read_raw(de.remaining()).ok()?.to_vec()".to_string()
281 }
282 KeyColumn::RawTail => format!("{field_type}::new(de.read_raw(de.remaining()).ok()?)"),
283 KeyColumn::TaggedIndexId => "de.read_index_id().ok()?".to_string(),
284 KeyColumn::VarintU128 => "de.read_u128_varint().ok()?".to_string(),
285 KeyColumn::SortRun => "read_sort_run(&mut de)?".to_string(),
286 KeyColumn::AscRowNumber => "read_row_tail(&mut de)?".to_string(),
287 KeyColumn::U8 => "de.read_u8().ok()?".to_string(),
288 KeyColumn::ReprU8 => format!("{field_type}::try_from(de.read_u8().ok()?).ok()?"),
289 KeyColumn::U16 => "de.read_u16().ok()?".to_string(),
290 KeyColumn::U32 => "de.read_u32().ok()?".to_string(),
291 KeyColumn::U64 => "de.read_u64().ok()?".to_string(),
292 KeyColumn::U128 => "de.read_u128().ok()?".to_string(),
293 KeyColumn::RowNumber => "RowNumber(de.read_u64().ok()?)".to_string(),
294 KeyColumn::GroupId => "GroupId::from_bytes(de.read_fixed().ok()?)".to_string(),
295 KeyColumn::Blob16 => "{ let bytes = de.read_raw(16).ok()?; let mut buf = [0u8; 16]; \
296 buf.copy_from_slice(bytes); buf }"
297 .to_string(),
298 KeyColumn::IdentityId => "de.read_identity_id().ok()?".to_string(),
299 KeyColumn::TableId
300 | KeyColumn::ColumnId
301 | KeyColumn::FlowId
302 | KeyColumn::FlowEdgeId
303 | KeyColumn::OperatorId
304 | KeyColumn::HandlerId
305 | KeyColumn::NamespaceId
306 | KeyColumn::SumTypeId
307 | KeyColumn::SequenceId
308 | KeyColumn::ViewId
309 | KeyColumn::SeriesId
310 | KeyColumn::SinkId
311 | KeyColumn::SourceId
312 | KeyColumn::QueueId
313 | KeyColumn::RingBufferId
314 | KeyColumn::BindingId
315 | KeyColumn::DictionaryId
316 | KeyColumn::ColumnPropertyId
317 | KeyColumn::RelationshipId
318 | KeyColumn::MigrationId
319 | KeyColumn::MigrationEventId
320 | KeyColumn::ColumnBlockId
321 | KeyColumn::PrimaryKeyId => format!("{field_type}(de.read_u64().ok()?)"),
322 KeyColumn::ProcedureId => "ProcedureId::from_raw(de.read_u64().ok()?)".to_string(),
323 KeyColumn::EpochSeconds => "EpochSeconds::new(de.read_u64().ok()?)".to_string(),
324 KeyColumn::DateTime => "de.read_datetime().ok()?".to_string(),
325 KeyColumn::RowShapeFingerprint => "RowShapeFingerprint::new(de.read_u64().ok()?)".to_string(),
326 KeyColumn::Partition => "Partition(de.read_u128().ok()?)".to_string(),
327 KeyColumn::IndexId => "IndexId::Primary(PrimaryKeyId(de.read_u64().ok()?))".to_string(),
328 KeyColumn::ObjectId => "de.read_object_id().ok()?".to_string(),
329 KeyColumn::StorageId => "StorageId::from_object(de.read_object_id().ok()?)?".to_string(),
330 KeyColumn::OptionU8 => "match de.read_u8().ok()? { 1u8 => Some(de.read_u8().ok()?), 0u8 => { \
331 de.read_u8().ok()?; None }, _ => return None }"
332 .to_string(),
333 }
334 }
335}
336
337pub fn derive_key(input: TokenStream) -> TokenStream {
338 let tokens: Vec<TokenTree> = input.into_iter().collect();
339 let mut iter = tokens.iter().peekable();
340
341 let mut tag: Option<String> = None;
342 while let Some(TokenTree::Punct(p)) = iter.peek() {
343 if p.as_char() != '#' {
344 break;
345 }
346 iter.next();
347 match iter.next() {
348 Some(TokenTree::Group(g)) if g.delimiter() == Delimiter::Bracket => {
349 if let Some(found) = parse_key_attribute(g) {
350 tag = Some(found);
351 }
352 }
353 _ => return compile_error("expected an attribute after '#'"),
354 }
355 }
356
357 let tag = match tag {
358 Some(tag) => tag,
359 None => return compile_error("KeyCodec requires #[key(tag = Variant)] naming its KeyTag"),
360 };
361
362 if let Some(TokenTree::Ident(i)) = iter.peek()
363 && *i == "pub"
364 {
365 iter.next();
366 if let Some(TokenTree::Group(g)) = iter.peek()
367 && g.delimiter() == Delimiter::Parenthesis
368 {
369 iter.next();
370 }
371 }
372
373 match iter.next() {
374 Some(TokenTree::Ident(i)) if *i == "struct" => {}
375 _ => return compile_error("KeyCodec can only be derived for structs"),
376 }
377
378 let name = match iter.next() {
379 Some(TokenTree::Ident(i)) => i.to_string(),
380 _ => return compile_error("expected struct name"),
381 };
382
383 let body = match iter.next() {
384 Some(TokenTree::Group(g)) if g.delimiter() == Delimiter::Brace => g.clone(),
385 Some(TokenTree::Punct(p)) if p.as_char() == '<' => {
386 return compile_error("KeyCodec cannot be derived for a generic struct");
387 }
388 Some(TokenTree::Group(g)) if g.delimiter() == Delimiter::Parenthesis => {
389 return compile_error("KeyCodec requires named fields, so a tuple struct has no field order");
390 }
391 _ => return compile_error("expected struct body"),
392 };
393
394 let fields = match parse_fields(&body) {
395 Ok(fields) => fields,
396 Err(err) => return err,
397 };
398
399 expand(&name, &tag, &fields)
400}
401
402fn parse_key_attribute(group: &Group) -> Option<String> {
403 let tokens: Vec<TokenTree> = group.stream().into_iter().collect();
404 let mut iter = tokens.iter();
405
406 match iter.next() {
407 Some(TokenTree::Ident(i)) if *i == "key" => {}
408 _ => return None,
409 }
410
411 let inner = match iter.next() {
412 Some(TokenTree::Group(g)) if g.delimiter() == Delimiter::Parenthesis => g.clone(),
413 _ => return None,
414 };
415
416 let inner_tokens: Vec<TokenTree> = inner.stream().into_iter().collect();
417 match inner_tokens.as_slice() {
418 [TokenTree::Ident(tag_ident), TokenTree::Punct(eq), TokenTree::Ident(variant)]
419 if *tag_ident == "tag" && eq.as_char() == '=' =>
420 {
421 Some(variant.to_string())
422 }
423 _ => None,
424 }
425}
426
427fn parse_fields(body: &Group) -> Result<Vec<KeyField>, TokenStream> {
428 let tokens: Vec<TokenTree> = body.stream().into_iter().collect();
429 let mut iter = tokens.iter().peekable();
430 let mut fields = Vec::new();
431
432 while iter.peek().is_some() {
433 let mut attribute: Option<FieldAttribute> = None;
434 while let Some(TokenTree::Punct(p)) = iter.peek() {
435 if p.as_char() == '#' {
436 iter.next();
437 if let Some(TokenTree::Group(g)) = iter.peek() {
438 if g.delimiter() == Delimiter::Bracket
439 && let Some(found) = field_attribute(g)
440 {
441 attribute = Some(found);
442 }
443 iter.next();
444 }
445 } else {
446 break;
447 }
448 }
449 if iter.peek().is_none() {
450 break;
451 }
452
453 if let Some(TokenTree::Ident(i)) = iter.peek()
454 && *i == "pub"
455 {
456 iter.next();
457 if let Some(TokenTree::Group(g)) = iter.peek()
458 && g.delimiter() == Delimiter::Parenthesis
459 {
460 iter.next();
461 }
462 }
463
464 let field_name = match iter.next() {
465 Some(TokenTree::Ident(i)) => i.to_string(),
466 _ => return Err(compile_error("expected field name")),
467 };
468
469 match iter.next() {
470 Some(TokenTree::Punct(p)) if p.as_char() == ':' => {}
471 _ => return Err(compile_error("expected ':' after field name")),
472 }
473
474 let mut ty_tokens: Vec<TokenTree> = Vec::new();
475 loop {
476 match iter.peek() {
477 Some(TokenTree::Punct(p)) if p.as_char() == ',' => {
478 iter.next();
479 break;
480 }
481 Some(t) => {
482 ty_tokens.push((*t).clone());
483 iter.next();
484 }
485 None => break,
486 }
487 }
488
489 if ty_tokens.is_empty() {
490 return Err(compile_error(&format!("field '{}' has no type", field_name)));
491 }
492
493 let ty = render(&ty_tokens);
494 let column = match attribute {
495 Some(FieldAttribute::ReprU8) => KeyColumn::ReprU8,
496 Some(FieldAttribute::Display) => KeyColumn::DisplayStr,
497 Some(FieldAttribute::Tagged) if ty.ends_with("IndexId") => KeyColumn::TaggedIndexId,
498 Some(FieldAttribute::Varint) if ty.ends_with("u128") => KeyColumn::VarintU128,
499 Some(FieldAttribute::Tagged) | Some(FieldAttribute::Varint) => {
500 return Err(compile_error(&format!(
501 "field '{}' has type '{}', which its key attribute does not apply to",
502 field_name, ty
503 )));
504 }
505 None => match column_type(&ty_tokens) {
506 Some(column) => column,
507 None => {
508 return Err(compile_error(&format!(
509 "field '{}' has type '{}', which has no flat key column type",
510 field_name, ty
511 )));
512 }
513 },
514 };
515
516 if fields.last().is_some_and(|last: &KeyField| matches!(last.column, KeyColumn::RawTail)) {
517 return Err(compile_error(&format!(
518 "field '{}' follows a raw tail, which must be the last field because it takes the rest of the key",
519 field_name
520 )));
521 }
522
523 fields.push(KeyField {
524 name: field_name,
525 column,
526 ty,
527 });
528 }
529
530 Ok(fields)
531}
532
533#[derive(Clone, Copy)]
534enum FieldAttribute {
535 ReprU8,
536 Display,
537 Tagged,
538 Varint,
539}
540
541fn field_attribute(group: &Group) -> Option<FieldAttribute> {
542 let tokens: Vec<TokenTree> = group.stream().into_iter().collect();
543 let mut iter = tokens.iter();
544
545 match iter.next() {
546 Some(TokenTree::Ident(i)) if *i == "key" => {}
547 _ => return None,
548 }
549
550 let inner = match iter.next() {
551 Some(TokenTree::Group(g)) if g.delimiter() == Delimiter::Parenthesis => g.clone(),
552 _ => return None,
553 };
554
555 let inner_tokens: Vec<TokenTree> = inner.stream().into_iter().collect();
556 match inner_tokens.as_slice() {
557 [TokenTree::Ident(repr), TokenTree::Punct(eq), TokenTree::Ident(width)]
558 if *repr == "repr" && eq.as_char() == '=' && *width == "u8" =>
559 {
560 Some(FieldAttribute::ReprU8)
561 }
562 [TokenTree::Ident(word)] if *word == "display" => Some(FieldAttribute::Display),
563 [TokenTree::Ident(word)] if *word == "tagged" => Some(FieldAttribute::Tagged),
564 [TokenTree::Ident(word)] if *word == "varint" => Some(FieldAttribute::Varint),
565 _ => None,
566 }
567}
568
569fn column_type(tokens: &[TokenTree]) -> Option<KeyColumn> {
570 if let [TokenTree::Group(group)] = tokens {
571 return (group.delimiter() == Delimiter::Bracket && is_byte_array_16(group))
572 .then_some(KeyColumn::Blob16);
573 }
574
575 if is_option_u8(tokens) {
576 return Some(KeyColumn::OptionU8);
577 }
578
579 if is_generic_of(tokens, "Vec", "u8") {
580 return Some(KeyColumn::RawTail);
581 }
582
583 if is_generic_of(strip_path(tokens), "Asc", "RowNumber") {
584 return Some(KeyColumn::AscRowNumber);
585 }
586
587 let unqualified = strip_path(tokens);
588 if unqualified.len() != 1 {
589 return None;
590 }
591
592 let head = match unqualified.first() {
593 Some(TokenTree::Ident(i)) => i.to_string(),
594 _ => return None,
595 };
596
597 match head.as_str() {
598 "u8" => Some(KeyColumn::U8),
599 "u16" => Some(KeyColumn::U16),
600 "u32" => Some(KeyColumn::U32),
601 "u64" => Some(KeyColumn::U64),
602 "u128" => Some(KeyColumn::U128),
603 "RowNumber" => Some(KeyColumn::RowNumber),
604 "GroupId" => Some(KeyColumn::GroupId),
605 "TableId" => Some(KeyColumn::TableId),
606 "ColumnId" => Some(KeyColumn::ColumnId),
607 "ObjectId" => Some(KeyColumn::ObjectId),
608 "StorageId" => Some(KeyColumn::StorageId),
609 "QueueId" => Some(KeyColumn::QueueId),
610 "RingBufferId" => Some(KeyColumn::RingBufferId),
611 "SeriesId" => Some(KeyColumn::SeriesId),
612 "DateTime" => Some(KeyColumn::DateTime),
613 "Partition" => Some(KeyColumn::Partition),
614 "FlowId" => Some(KeyColumn::FlowId),
615 "FlowEdgeId" => Some(KeyColumn::FlowEdgeId),
616 "OperatorId" => Some(KeyColumn::OperatorId),
617 "HandlerId" => Some(KeyColumn::HandlerId),
618 "NamespaceId" => Some(KeyColumn::NamespaceId),
619 "SumTypeId" => Some(KeyColumn::SumTypeId),
620 "SequenceId" => Some(KeyColumn::SequenceId),
621 "EpochSeconds" => Some(KeyColumn::EpochSeconds),
622 "IdentityId" => Some(KeyColumn::IdentityId),
623 "AuthenticationId" | "IdentityAttributeId" | "RoleId" | "PolicyId" | "TokenId" => Some(KeyColumn::U64),
624 "ViewId" => Some(KeyColumn::ViewId),
625 "SinkId" => Some(KeyColumn::SinkId),
626 "SourceId" => Some(KeyColumn::SourceId),
627 "BindingId" => Some(KeyColumn::BindingId),
628 "DictionaryId" => Some(KeyColumn::DictionaryId),
629 "ProcedureId" => Some(KeyColumn::ProcedureId),
630 "ColumnPropertyId" => Some(KeyColumn::ColumnPropertyId),
631 "RowShapeFingerprint" => Some(KeyColumn::RowShapeFingerprint),
632 "IndexId" => Some(KeyColumn::IndexId),
633 "RelationshipId" => Some(KeyColumn::RelationshipId),
634 "MigrationId" => Some(KeyColumn::MigrationId),
635 "MigrationEventId" => Some(KeyColumn::MigrationEventId),
636 "PrimaryKeyId" => Some(KeyColumn::PrimaryKeyId),
637 "ColumnBlockId" => Some(KeyColumn::ColumnBlockId),
638 "CdcConsumerId" => Some(KeyColumn::Str),
639 "EncodedKey" => Some(KeyColumn::EscapedBytes),
640 "KeyspaceId" => Some(KeyColumn::KeyspaceId),
641 "EncodedIndexKey" => Some(KeyColumn::RawTail),
642 "SortRun" => Some(KeyColumn::SortRun),
643 _ => None,
644 }
645}
646
647fn is_generic_of(tokens: &[TokenTree], outer: &str, inner: &str) -> bool {
648 matches!(
649 tokens,
650 [TokenTree::Ident(o), TokenTree::Punct(lt), TokenTree::Ident(u), TokenTree::Punct(gt)]
651 if *o == outer && lt.as_char() == '<' && *u == inner && gt.as_char() == '>'
652 )
653}
654
655fn is_option_u8(tokens: &[TokenTree]) -> bool {
656 matches!(
657 tokens,
658 [TokenTree::Ident(o), TokenTree::Punct(lt), TokenTree::Ident(u), TokenTree::Punct(gt)]
659 if *o == "Option" && lt.as_char() == '<' && *u == "u8" && gt.as_char() == '>'
660 )
661}
662
663fn is_byte_array_16(group: &Group) -> bool {
664 let tokens: Vec<TokenTree> = group.stream().into_iter().collect();
665 match tokens.as_slice() {
666 [TokenTree::Ident(element), TokenTree::Punct(semi), TokenTree::Literal(len)] => {
667 *element == "u8" && semi.as_char() == ';' && len.to_string() == "16"
668 }
669 _ => false,
670 }
671}
672
673fn strip_path(tokens: &[TokenTree]) -> &[TokenTree] {
674 let mut at = 0;
675 if is_colon(tokens.first()) && is_colon(tokens.get(1)) {
676 at = 2;
677 }
678 while matches!(tokens.get(at), Some(TokenTree::Ident(_)))
679 && is_colon(tokens.get(at + 1))
680 && is_colon(tokens.get(at + 2))
681 {
682 at += 3;
683 }
684 &tokens[at..]
685}
686
687fn is_colon(token: Option<&TokenTree>) -> bool {
688 matches!(token, Some(TokenTree::Punct(p)) if p.as_char() == ':')
689}
690
691fn render(tokens: &[TokenTree]) -> String {
692 tokens.iter().map(|token| token.to_string()).collect()
693}
694
695fn expand(name: &str, tag: &str, fields: &[KeyField]) -> TokenStream {
696 let capacity: usize = 1 + fields.iter().map(|f| f.column.width()).sum::<usize>();
697
698 let mut encode_body = String::new();
699 for field in fields {
700 encode_body.push_str("\t\t");
701 encode_body.push_str(&field.column.encode_stmt(&field.name));
702 encode_body.push('\n');
703 }
704
705 let mut decode_body = String::new();
706 for field in fields {
707 decode_body.push_str(&format!("\t\t\t{}: {},\n", field.name, field.column.decode_expr(&field.ty)));
708 }
709
710 let mut out = String::new();
711 out.push_str(&format!("impl {name} {{\n"));
712 out.push_str(&format!("\tpub const TAG: KeyTag = KeyTag::{tag};\n\n"));
713 out.push_str("\tpub fn encode(&self) -> EncodedKey {\n");
714 out.push_str(&format!(
715 "\t\tlet mut serializer = ::reifydb_codec::key::serializer::KeySerializer::with_capacity({capacity});\n"
716 ));
717 out.push_str("\t\tserializer.extend_u8(Self::TAG as u8);\n");
718 out.push_str(&encode_body);
719 out.push_str("\t\tserializer.to_encoded_key()\n\t}\n\n");
720 out.push_str("\tpub fn decode(key: &EncodedKey) -> Option<Self> {\n");
721 out.push_str(
722 "\t\tlet mut de = ::reifydb_codec::key::deserializer::KeyDeserializer::from_bytes(key.as_slice());\n",
723 );
724 out.push_str("\t\tlet found: KeyTag = de.read_u8().ok()?.try_into().ok()?;\n");
725 out.push_str("\t\tif found != Self::TAG {\n\t\t\treturn None;\n\t\t}\n");
726 out.push_str(&format!("\t\tlet decoded = Self {{\n{decode_body}\t\t}};\n"));
727 out.push_str("\t\tif !de.is_empty() {\n\t\t\treturn None;\n\t\t}\n");
728 out.push_str("\t\tSome(decoded)\n\t}\n}\n\n");
729
730 out.push_str(&format!("#[automatically_derived]\nimpl KeyFields for {name} {{\n"));
731 out.push_str("\tfn fields(&self) -> ::smallvec::SmallVec<[Field<'_>; 6]> {\n");
732 out.push_str("\t\t::smallvec::smallvec![\n");
733 for field in fields {
734 for expr in field.column.field_exprs(&field.name) {
735 out.push_str(&format!("\t\t\t{expr},\n"));
736 }
737 }
738 out.push_str("\t\t]\n\t}\n}\n\n");
739 out.push_str(&format!("#[automatically_derived]\nimpl {name} {{\n"));
740 out.push_str("\tpub fn cmp_fields(&self, other: &Self) -> ::core::cmp::Ordering {\n");
741 for field in fields {
742 for expr in field.column.field_exprs(&field.name) {
743 let theirs = expr.replace("self.", "other.");
744 out.push_str(&format!(
745 "\t\tmatch Ord::cmp(&{expr}, &{theirs}) {{\n\t\t\t::core::cmp::Ordering::Equal => {{}}\n\t\t\tordering => return ordering,\n\t\t}}\n"
746 ));
747 }
748 }
749 out.push_str("\t\t::core::cmp::Ordering::Equal\n\t}\n}\n\n");
750 out.push_str(&expand_tests(name, fields));
751
752 out.parse().expect("derived key impl must be valid Rust")
753}
754
755#[cfg(test)]
756mod tests {
757 use super::derive_key;
758
759 fn expand(source: &str) -> String {
760 derive_key(source.parse().unwrap()).to_string()
761 }
762
763 #[test]
764 fn missing_attribute_is_rejected() {
765 let out = expand("struct RowKey { row: u64 }");
766 assert!(out.contains("compile_error"), "{out}");
767 assert!(out.contains("key"), "{out}");
768 }
769
770 #[test]
771 fn a_well_formed_struct_expands_without_an_error() {
772 let out = expand("#[key(tag = Row)] struct RowKey { table: u64, row: RowNumber }");
773 assert!(!out.contains("compile_error"), "{out}");
774 assert!(out.contains("impl RowKey { pub const TAG"), "{out}");
775 assert!(out.contains("KeyTag :: Row"), "{out}");
776 }
777
778 #[test]
779 fn fields_are_encoded_in_declaration_order() {
780 let out = expand("#[key(tag = Row)] struct RowKey { table: u64, row: RowNumber }");
781 let table = out.find("self . table").expect("table field encoded");
782 let row = out.find("self . row").expect("row field encoded");
783 assert!(table < row, "{out}");
784 }
785
786 #[test]
787 fn a_repr_u8_field_encodes_the_discriminant_and_decodes_through_try_from() {
788 let out = expand("#[key(tag = SystemVersion)] struct K { #[key(repr = u8)] version: SystemVersion }");
791 assert!(!out.contains("compile_error"), "{out}");
792 assert!(out.contains("extend_u8 (self . version as u8)"), "{out}");
793 assert!(out.contains("SystemVersion :: try_from"), "{out}");
794 }
795
796 #[test]
797 fn a_repr_u8_field_still_rejects_a_type_the_attribute_was_not_put_on() {
798 let out = expand("#[key(tag = SystemVersion)] struct K { version: SystemVersion }");
799 assert!(out.contains("compile_error"), "{out}");
800 }
801
802 #[test]
803 fn an_unrecognised_field_type_is_rejected() {
804 let out = expand("#[key(tag = Row)] struct RowKey { at: String }");
805 assert!(out.contains("compile_error"), "{out}");
806 assert!(out.contains("at"), "{out}");
807 assert!(out.contains("String"), "{out}");
808 }
809
810 #[test]
811 fn a_tuple_struct_is_rejected() {
812 let out = expand("#[key(tag = Row)] struct RowKey(u64);");
813 assert!(out.contains("compile_error"), "{out}");
814 }
815
816 #[test]
817 fn a_generic_struct_is_rejected() {
818 let out = expand("#[key(tag = Row)] struct RowKey<T> { row: T }");
819 assert!(out.contains("compile_error"), "{out}");
820 }
821
822 #[test]
823 fn a_byte_array_field_maps_to_blob16() {
824 let out = expand("#[key(tag = Row)] struct RowKey { blob: [u8; 16] }");
825 assert!(!out.contains("compile_error"), "{out}");
826 assert!(out.contains("extend_raw"));
827 }
828
829 #[test]
830 fn a_byte_array_of_the_wrong_width_is_rejected() {
831 let out = expand("#[key(tag = Row)] struct RowKey { blob: [u8; 32] }");
832 assert!(out.contains("compile_error"), "{out}");
833 }
834
835 #[test]
836 fn table_id_and_column_id_encode_via_their_wrapped_u64() {
837 let out = expand("#[key(tag = Table)] struct TableKey { table: TableId, column: ColumnId }");
838 assert!(!out.contains("compile_error"), "{out}");
839 assert!(out.contains("self . table . 0"), "{out}");
840 assert!(out.contains("TableId (de . read_u64 () . ok () ?)"), "{out}");
841 assert!(out.contains("ColumnId (de . read_u64 () . ok () ?)"), "{out}");
842 }
843
844 #[test]
845 fn an_option_u8_field_encodes_as_a_fixed_two_byte_presence_and_value_pair() {
846 let out = expand("#[key(tag = Row)] struct RowKey { tag: Option<u8> }");
847 assert!(!out.contains("compile_error"), "{out}");
848 assert!(out.contains("with_capacity (3)"), "{out}: width must stay fixed at 2 bytes for None and Some");
849 assert!(out.contains("Some (v) => { serializer . extend_u8 (1u8) . extend_u8 (v) ; }"), "{out}");
850 assert!(out.contains("None => { serializer . extend_u8 (0u8) . extend_u8 (0u8) ; }"), "{out}");
851 assert!(out.contains("1u8 => Some (de . read_u8 () . ok () ?)"), "{out}");
852 assert!(out.contains("0u8 => { de . read_u8 () . ok () ?; None }"), "{out}");
853 }
854
855 #[test]
856 fn object_id_and_storage_id_encode_via_the_catalog_ext() {
857 let out = expand("#[key(tag = Row)] struct RowKey { storage: StorageId, object: ObjectId }");
858 assert!(!out.contains("compile_error"), "{out}");
859 assert!(out.contains("extend_object_id (self . storage)"), "{out}");
860 assert!(out.contains("extend_object_id (self . object)"), "{out}");
861 assert!(out.contains("StorageId :: from_object (de . read_object_id () . ok () ?) ?"), "{out}");
862 assert!(out.contains("de . read_object_id () . ok () ?"), "{out}");
863 }
864}