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 ColumnSnapshotId,
55 IndexId,
56 OptionU8,
57 ReprU8,
58}
59
60impl KeyColumn {
61 fn width(self) -> usize {
62 match self {
63 KeyColumn::U8 | KeyColumn::ReprU8 => 1,
64 KeyColumn::U16 => 2,
65 KeyColumn::U32 => 4,
66 KeyColumn::U64
67 | KeyColumn::RowNumber
68 | KeyColumn::TableId
69 | KeyColumn::ColumnId
70 | KeyColumn::FlowId
71 | KeyColumn::FlowEdgeId
72 | KeyColumn::OperatorId
73 | KeyColumn::HandlerId
74 | KeyColumn::NamespaceId
75 | KeyColumn::SumTypeId
76 | KeyColumn::SequenceId
77 | KeyColumn::EpochSeconds
78 | KeyColumn::ViewId
79 | KeyColumn::SeriesId
80 | KeyColumn::SinkId
81 | KeyColumn::SourceId
82 | KeyColumn::QueueId
83 | KeyColumn::RingBufferId
84 | KeyColumn::BindingId
85 | KeyColumn::DictionaryId
86 | KeyColumn::ProcedureId
87 | KeyColumn::DateTime
88 | KeyColumn::ColumnPropertyId
89 | KeyColumn::RowShapeFingerprint
90 | KeyColumn::RelationshipId
91 | KeyColumn::MigrationId
92 | KeyColumn::MigrationEventId
93 | KeyColumn::ColumnSnapshotId
94 | KeyColumn::PrimaryKeyId
95 | KeyColumn::IndexId => 8,
96 KeyColumn::ObjectId | KeyColumn::StorageId => 9,
97 KeyColumn::OptionU8 => 2,
98 KeyColumn::U128 | KeyColumn::Blob16 | KeyColumn::Partition | KeyColumn::IdentityId => 16,
99 KeyColumn::GroupId => 24,
100 }
101 }
102
103 fn field_exprs(self, field: &str) -> Vec<String> {
104 let u = |width: &str, expr: String| vec![format!("Field::UDesc(Width::{width}, {expr} as u128)")];
105 match self {
106 KeyColumn::U8 => u("U8", format!("self.{field}")),
107 KeyColumn::U16 => u("U16", format!("self.{field}")),
108 KeyColumn::U32 => u("U32", format!("self.{field}")),
109 KeyColumn::U64 => u("U64", format!("self.{field}")),
110 KeyColumn::U128 => u("U128", format!("self.{field}")),
111 KeyColumn::ReprU8 => u("U8", format!("self.{field} as u8")),
112 KeyColumn::RowNumber => u("U64", format!("self.{field}.0")),
113 KeyColumn::Partition => u("U128", format!("self.{field}.0")),
114 KeyColumn::ProcedureId => u("U64", format!("*self.{field}")),
115 KeyColumn::EpochSeconds => u("U64", format!("self.{field}.seconds()")),
116 KeyColumn::DateTime => u("U64", format!("self.{field}.to_nanos()")),
117 KeyColumn::RowShapeFingerprint | KeyColumn::IndexId => {
118 u("U64", format!("self.{field}.as_u64()"))
119 }
120 KeyColumn::GroupId => vec![format!(
121 "Field::BytesDesc(ByteEncoding::Fixed, ::std::borrow::Cow::Borrowed(self.{field}.as_bytes()))"
122 )],
123 KeyColumn::IdentityId => vec![format!(
124 "Field::BytesDesc(ByteEncoding::Escaped, ::std::borrow::Cow::Borrowed(self.{field}.as_bytes()))"
125 )],
126 KeyColumn::Blob16 => vec![format!(
127 "Field::RawAsc(RawEncoding::Verbatim, ::std::borrow::Cow::Borrowed(&self.{field}))"
128 )],
129 KeyColumn::ObjectId | KeyColumn::StorageId => vec![
130 format!("Field::UAsc(Width::U8, self.{field}.type_tag() as u128)"),
131 format!("Field::UDesc(Width::U64, self.{field}.as_u64() as u128)"),
132 ],
133 KeyColumn::OptionU8 => vec![
134 format!("Field::UDesc(Width::U8, self.{field}.is_some() as u128)"),
135 format!("Field::UDesc(Width::U8, self.{field}.unwrap_or(0u8) as u128)"),
136 ],
137 KeyColumn::TableId
138 | KeyColumn::ColumnId
139 | KeyColumn::FlowId
140 | KeyColumn::FlowEdgeId
141 | KeyColumn::OperatorId
142 | KeyColumn::HandlerId
143 | KeyColumn::NamespaceId
144 | KeyColumn::SumTypeId
145 | KeyColumn::SequenceId
146 | KeyColumn::ViewId
147 | KeyColumn::SeriesId
148 | KeyColumn::SinkId
149 | KeyColumn::SourceId
150 | KeyColumn::QueueId
151 | KeyColumn::RingBufferId
152 | KeyColumn::BindingId
153 | KeyColumn::DictionaryId
154 | KeyColumn::ColumnPropertyId
155 | KeyColumn::RelationshipId
156 | KeyColumn::MigrationId
157 | KeyColumn::MigrationEventId
158 | KeyColumn::ColumnSnapshotId
159 | KeyColumn::PrimaryKeyId => u("U64", format!("self.{field}.0")),
160 }
161 }
162
163 fn encode_stmt(self, field: &str) -> String {
164 match self {
165 KeyColumn::U8 => format!("serializer.extend_u8(self.{field});"),
166 KeyColumn::ReprU8 => format!("serializer.extend_u8(self.{field} as u8);"),
167 KeyColumn::U16 => format!("serializer.extend_u16(self.{field});"),
168 KeyColumn::U32 => format!("serializer.extend_u32(self.{field});"),
169 KeyColumn::U64 => format!("serializer.extend_u64(self.{field});"),
170 KeyColumn::U128 => format!("serializer.extend_u128(self.{field});"),
171 KeyColumn::RowNumber => format!("serializer.extend_u64(self.{field}.0);"),
172 KeyColumn::GroupId => format!("serializer.extend_fixed(*self.{field}.as_bytes());"),
173 KeyColumn::Blob16 => format!("serializer.extend_raw(&self.{field});"),
174 KeyColumn::IdentityId => format!("serializer.extend_identity_id(&self.{field});"),
175 KeyColumn::TableId
176 | KeyColumn::ColumnId
177 | KeyColumn::FlowId
178 | KeyColumn::FlowEdgeId
179 | KeyColumn::OperatorId
180 | KeyColumn::HandlerId
181 | KeyColumn::NamespaceId
182 | KeyColumn::SumTypeId
183 | KeyColumn::SequenceId
184 | KeyColumn::ViewId
185 | KeyColumn::SeriesId
186 | KeyColumn::SinkId
187 | KeyColumn::SourceId
188 | KeyColumn::QueueId
189 | KeyColumn::RingBufferId
190 | KeyColumn::BindingId
191 | KeyColumn::DictionaryId
192 | KeyColumn::ColumnPropertyId
193 | KeyColumn::RelationshipId
194 | KeyColumn::MigrationId
195 | KeyColumn::MigrationEventId
196 | KeyColumn::ColumnSnapshotId
197 | KeyColumn::PrimaryKeyId => format!("serializer.extend_u64(self.{field}.0);"),
198 KeyColumn::ProcedureId => format!("serializer.extend_u64(*self.{field});"),
199 KeyColumn::EpochSeconds => format!("serializer.extend_u64(self.{field}.seconds());"),
200 KeyColumn::DateTime => format!("serializer.extend_datetime(&self.{field});"),
201 KeyColumn::RowShapeFingerprint => format!("serializer.extend_u64(self.{field}.as_u64());"),
202 KeyColumn::Partition => format!("serializer.extend_u128(self.{field}.0);"),
203 KeyColumn::IndexId => format!("serializer.extend_u64(self.{field}.as_u64());"),
204 KeyColumn::ObjectId | KeyColumn::StorageId => {
205 format!("serializer.extend_object_id(self.{field});")
206 }
207 KeyColumn::OptionU8 => format!(
208 "match self.{field} {{ Some(v) => {{ serializer.extend_u8(1u8).extend_u8(v); }} \
209 None => {{ serializer.extend_u8(0u8).extend_u8(0u8); }} }}"
210 ),
211 }
212 }
213
214 fn decode_expr(self, field_type: &str) -> String {
215 match self {
216 KeyColumn::U8 => "de.read_u8().ok()?".to_string(),
217 KeyColumn::ReprU8 => format!("{field_type}::try_from(de.read_u8().ok()?).ok()?"),
218 KeyColumn::U16 => "de.read_u16().ok()?".to_string(),
219 KeyColumn::U32 => "de.read_u32().ok()?".to_string(),
220 KeyColumn::U64 => "de.read_u64().ok()?".to_string(),
221 KeyColumn::U128 => "de.read_u128().ok()?".to_string(),
222 KeyColumn::RowNumber => "RowNumber(de.read_u64().ok()?)".to_string(),
223 KeyColumn::GroupId => "GroupId::from_bytes(de.read_fixed().ok()?)".to_string(),
224 KeyColumn::Blob16 => "{ let bytes = de.read_raw(16).ok()?; let mut buf = [0u8; 16]; \
225 buf.copy_from_slice(bytes); buf }"
226 .to_string(),
227 KeyColumn::IdentityId => "de.read_identity_id().ok()?".to_string(),
228 KeyColumn::TableId
229 | KeyColumn::ColumnId
230 | KeyColumn::FlowId
231 | KeyColumn::FlowEdgeId
232 | KeyColumn::OperatorId
233 | KeyColumn::HandlerId
234 | KeyColumn::NamespaceId
235 | KeyColumn::SumTypeId
236 | KeyColumn::SequenceId
237 | KeyColumn::ViewId
238 | KeyColumn::SeriesId
239 | KeyColumn::SinkId
240 | KeyColumn::SourceId
241 | KeyColumn::QueueId
242 | KeyColumn::RingBufferId
243 | KeyColumn::BindingId
244 | KeyColumn::DictionaryId
245 | KeyColumn::ColumnPropertyId
246 | KeyColumn::RelationshipId
247 | KeyColumn::MigrationId
248 | KeyColumn::MigrationEventId
249 | KeyColumn::ColumnSnapshotId
250 | KeyColumn::PrimaryKeyId => format!("{field_type}(de.read_u64().ok()?)"),
251 KeyColumn::ProcedureId => "ProcedureId::from_raw(de.read_u64().ok()?)".to_string(),
252 KeyColumn::EpochSeconds => "EpochSeconds::new(de.read_u64().ok()?)".to_string(),
253 KeyColumn::DateTime => "de.read_datetime().ok()?".to_string(),
254 KeyColumn::RowShapeFingerprint => "RowShapeFingerprint::new(de.read_u64().ok()?)".to_string(),
255 KeyColumn::Partition => "Partition(de.read_u128().ok()?)".to_string(),
256 KeyColumn::IndexId => "IndexId::Primary(PrimaryKeyId(de.read_u64().ok()?))".to_string(),
257 KeyColumn::ObjectId => "de.read_object_id().ok()?".to_string(),
258 KeyColumn::StorageId => "StorageId::from_object(de.read_object_id().ok()?)?".to_string(),
259 KeyColumn::OptionU8 => "match de.read_u8().ok()? { 1u8 => Some(de.read_u8().ok()?), 0u8 => { \
260 de.read_u8().ok()?; None }, _ => return None }"
261 .to_string(),
262 }
263 }
264}
265
266pub fn derive_key(input: TokenStream) -> TokenStream {
267 let tokens: Vec<TokenTree> = input.into_iter().collect();
268 let mut iter = tokens.iter().peekable();
269
270 let mut tag: Option<String> = None;
271 while let Some(TokenTree::Punct(p)) = iter.peek() {
272 if p.as_char() != '#' {
273 break;
274 }
275 iter.next();
276 match iter.next() {
277 Some(TokenTree::Group(g)) if g.delimiter() == Delimiter::Bracket => {
278 if let Some(found) = parse_key_attribute(g) {
279 tag = Some(found);
280 }
281 }
282 _ => return compile_error("expected an attribute after '#'"),
283 }
284 }
285
286 let tag = match tag {
287 Some(tag) => tag,
288 None => return compile_error("KeyCodec requires #[key(tag = Variant)] naming its KeyTag"),
289 };
290
291 if let Some(TokenTree::Ident(i)) = iter.peek()
292 && *i == "pub"
293 {
294 iter.next();
295 if let Some(TokenTree::Group(g)) = iter.peek()
296 && g.delimiter() == Delimiter::Parenthesis
297 {
298 iter.next();
299 }
300 }
301
302 match iter.next() {
303 Some(TokenTree::Ident(i)) if *i == "struct" => {}
304 _ => return compile_error("KeyCodec can only be derived for structs"),
305 }
306
307 let name = match iter.next() {
308 Some(TokenTree::Ident(i)) => i.to_string(),
309 _ => return compile_error("expected struct name"),
310 };
311
312 let body = match iter.next() {
313 Some(TokenTree::Group(g)) if g.delimiter() == Delimiter::Brace => g.clone(),
314 Some(TokenTree::Punct(p)) if p.as_char() == '<' => {
315 return compile_error("KeyCodec cannot be derived for a generic struct");
316 }
317 Some(TokenTree::Group(g)) if g.delimiter() == Delimiter::Parenthesis => {
318 return compile_error("KeyCodec requires named fields, so a tuple struct has no field order");
319 }
320 _ => return compile_error("expected struct body"),
321 };
322
323 let fields = match parse_fields(&body) {
324 Ok(fields) => fields,
325 Err(err) => return err,
326 };
327
328 expand(&name, &tag, &fields)
329}
330
331fn parse_key_attribute(group: &Group) -> Option<String> {
332 let tokens: Vec<TokenTree> = group.stream().into_iter().collect();
333 let mut iter = tokens.iter();
334
335 match iter.next() {
336 Some(TokenTree::Ident(i)) if *i == "key" => {}
337 _ => return None,
338 }
339
340 let inner = match iter.next() {
341 Some(TokenTree::Group(g)) if g.delimiter() == Delimiter::Parenthesis => g.clone(),
342 _ => return None,
343 };
344
345 let inner_tokens: Vec<TokenTree> = inner.stream().into_iter().collect();
346 match inner_tokens.as_slice() {
347 [TokenTree::Ident(tag_ident), TokenTree::Punct(eq), TokenTree::Ident(variant)]
348 if *tag_ident == "tag" && eq.as_char() == '=' =>
349 {
350 Some(variant.to_string())
351 }
352 _ => None,
353 }
354}
355
356fn parse_fields(body: &Group) -> Result<Vec<KeyField>, TokenStream> {
357 let tokens: Vec<TokenTree> = body.stream().into_iter().collect();
358 let mut iter = tokens.iter().peekable();
359 let mut fields = Vec::new();
360
361 while iter.peek().is_some() {
362 let mut repr_u8 = false;
363 while let Some(TokenTree::Punct(p)) = iter.peek() {
364 if p.as_char() == '#' {
365 iter.next();
366 if let Some(TokenTree::Group(g)) = iter.peek() {
367 if g.delimiter() == Delimiter::Bracket && is_repr_u8_attribute(g) {
368 repr_u8 = true;
369 }
370 iter.next();
371 }
372 } else {
373 break;
374 }
375 }
376 if iter.peek().is_none() {
377 break;
378 }
379
380 if let Some(TokenTree::Ident(i)) = iter.peek()
381 && *i == "pub"
382 {
383 iter.next();
384 if let Some(TokenTree::Group(g)) = iter.peek()
385 && g.delimiter() == Delimiter::Parenthesis
386 {
387 iter.next();
388 }
389 }
390
391 let field_name = match iter.next() {
392 Some(TokenTree::Ident(i)) => i.to_string(),
393 _ => return Err(compile_error("expected field name")),
394 };
395
396 match iter.next() {
397 Some(TokenTree::Punct(p)) if p.as_char() == ':' => {}
398 _ => return Err(compile_error("expected ':' after field name")),
399 }
400
401 let mut ty_tokens: Vec<TokenTree> = Vec::new();
402 loop {
403 match iter.peek() {
404 Some(TokenTree::Punct(p)) if p.as_char() == ',' => {
405 iter.next();
406 break;
407 }
408 Some(t) => {
409 ty_tokens.push((*t).clone());
410 iter.next();
411 }
412 None => break,
413 }
414 }
415
416 if ty_tokens.is_empty() {
417 return Err(compile_error(&format!("field '{}' has no type", field_name)));
418 }
419
420 let ty = render(&ty_tokens);
421 let column = if repr_u8 {
422 KeyColumn::ReprU8
423 } else {
424 match column_type(&ty_tokens) {
425 Some(column) => column,
426 None => {
427 return Err(compile_error(&format!(
428 "field '{}' has type '{}', which has no flat key column type",
429 field_name, ty
430 )));
431 }
432 }
433 };
434
435 fields.push(KeyField {
436 name: field_name,
437 column,
438 ty,
439 });
440 }
441
442 Ok(fields)
443}
444
445fn is_repr_u8_attribute(group: &Group) -> bool {
446 let tokens: Vec<TokenTree> = group.stream().into_iter().collect();
447 let mut iter = tokens.iter();
448
449 match iter.next() {
450 Some(TokenTree::Ident(i)) if *i == "key" => {}
451 _ => return false,
452 }
453
454 let inner = match iter.next() {
455 Some(TokenTree::Group(g)) if g.delimiter() == Delimiter::Parenthesis => g.clone(),
456 _ => return false,
457 };
458
459 let inner_tokens: Vec<TokenTree> = inner.stream().into_iter().collect();
460 matches!(
461 inner_tokens.as_slice(),
462 [TokenTree::Ident(repr), TokenTree::Punct(eq), TokenTree::Ident(width)]
463 if *repr == "repr" && eq.as_char() == '=' && *width == "u8"
464 )
465}
466
467fn column_type(tokens: &[TokenTree]) -> Option<KeyColumn> {
468 if let [TokenTree::Group(group)] = tokens {
469 return (group.delimiter() == Delimiter::Bracket && is_byte_array_16(group))
470 .then_some(KeyColumn::Blob16);
471 }
472
473 if is_option_u8(tokens) {
474 return Some(KeyColumn::OptionU8);
475 }
476
477 let unqualified = strip_path(tokens);
478 if unqualified.len() != 1 {
479 return None;
480 }
481
482 let head = match unqualified.first() {
483 Some(TokenTree::Ident(i)) => i.to_string(),
484 _ => return None,
485 };
486
487 match head.as_str() {
488 "u8" => Some(KeyColumn::U8),
489 "u16" => Some(KeyColumn::U16),
490 "u32" => Some(KeyColumn::U32),
491 "u64" => Some(KeyColumn::U64),
492 "u128" => Some(KeyColumn::U128),
493 "RowNumber" => Some(KeyColumn::RowNumber),
494 "GroupId" => Some(KeyColumn::GroupId),
495 "TableId" => Some(KeyColumn::TableId),
496 "ColumnId" => Some(KeyColumn::ColumnId),
497 "ObjectId" => Some(KeyColumn::ObjectId),
498 "StorageId" => Some(KeyColumn::StorageId),
499 "QueueId" => Some(KeyColumn::QueueId),
500 "RingBufferId" => Some(KeyColumn::RingBufferId),
501 "SeriesId" => Some(KeyColumn::SeriesId),
502 "DateTime" => Some(KeyColumn::DateTime),
503 "Partition" => Some(KeyColumn::Partition),
504 "FlowId" => Some(KeyColumn::FlowId),
505 "FlowEdgeId" => Some(KeyColumn::FlowEdgeId),
506 "OperatorId" => Some(KeyColumn::OperatorId),
507 "HandlerId" => Some(KeyColumn::HandlerId),
508 "NamespaceId" => Some(KeyColumn::NamespaceId),
509 "SumTypeId" => Some(KeyColumn::SumTypeId),
510 "SequenceId" => Some(KeyColumn::SequenceId),
511 "EpochSeconds" => Some(KeyColumn::EpochSeconds),
512 "IdentityId" => Some(KeyColumn::IdentityId),
513 "AuthenticationId" | "IdentityAttributeId" | "RoleId" | "PolicyId" | "TokenId" => Some(KeyColumn::U64),
514 "ViewId" => Some(KeyColumn::ViewId),
515 "SinkId" => Some(KeyColumn::SinkId),
516 "SourceId" => Some(KeyColumn::SourceId),
517 "BindingId" => Some(KeyColumn::BindingId),
518 "DictionaryId" => Some(KeyColumn::DictionaryId),
519 "ProcedureId" => Some(KeyColumn::ProcedureId),
520 "ColumnPropertyId" => Some(KeyColumn::ColumnPropertyId),
521 "RowShapeFingerprint" => Some(KeyColumn::RowShapeFingerprint),
522 "IndexId" => Some(KeyColumn::IndexId),
523 "RelationshipId" => Some(KeyColumn::RelationshipId),
524 "MigrationId" => Some(KeyColumn::MigrationId),
525 "MigrationEventId" => Some(KeyColumn::MigrationEventId),
526 "PrimaryKeyId" => Some(KeyColumn::PrimaryKeyId),
527 "ColumnSnapshotId" => Some(KeyColumn::ColumnSnapshotId),
528 _ => None,
529 }
530}
531
532fn is_option_u8(tokens: &[TokenTree]) -> bool {
533 matches!(
534 tokens,
535 [TokenTree::Ident(o), TokenTree::Punct(lt), TokenTree::Ident(u), TokenTree::Punct(gt)]
536 if *o == "Option" && lt.as_char() == '<' && *u == "u8" && gt.as_char() == '>'
537 )
538}
539
540fn is_byte_array_16(group: &Group) -> bool {
541 let tokens: Vec<TokenTree> = group.stream().into_iter().collect();
542 match tokens.as_slice() {
543 [TokenTree::Ident(element), TokenTree::Punct(semi), TokenTree::Literal(len)] => {
544 *element == "u8" && semi.as_char() == ';' && len.to_string() == "16"
545 }
546 _ => false,
547 }
548}
549
550fn strip_path(tokens: &[TokenTree]) -> &[TokenTree] {
551 let mut at = 0;
552 if is_colon(tokens.first()) && is_colon(tokens.get(1)) {
553 at = 2;
554 }
555 while matches!(tokens.get(at), Some(TokenTree::Ident(_)))
556 && is_colon(tokens.get(at + 1))
557 && is_colon(tokens.get(at + 2))
558 {
559 at += 3;
560 }
561 &tokens[at..]
562}
563
564fn is_colon(token: Option<&TokenTree>) -> bool {
565 matches!(token, Some(TokenTree::Punct(p)) if p.as_char() == ':')
566}
567
568fn render(tokens: &[TokenTree]) -> String {
569 tokens.iter().map(|token| token.to_string()).collect()
570}
571
572fn expand(name: &str, tag: &str, fields: &[KeyField]) -> TokenStream {
573 let capacity: usize = 1 + fields.iter().map(|f| f.column.width()).sum::<usize>();
574
575 let mut encode_body = String::new();
576 for field in fields {
577 encode_body.push_str("\t\t");
578 encode_body.push_str(&field.column.encode_stmt(&field.name));
579 encode_body.push('\n');
580 }
581
582 let mut decode_body = String::new();
583 for field in fields {
584 decode_body.push_str(&format!("\t\t\t{}: {},\n", field.name, field.column.decode_expr(&field.ty)));
585 }
586
587 let mut out = String::new();
588 out.push_str(&format!("impl {name} {{\n"));
589 out.push_str(&format!("\tpub const TAG: KeyTag = KeyTag::{tag};\n\n"));
590 out.push_str("\tpub fn encode(&self) -> EncodedKey {\n");
591 out.push_str(&format!(
592 "\t\tlet mut serializer = ::reifydb_codec::key::serializer::KeySerializer::with_capacity({capacity});\n"
593 ));
594 out.push_str("\t\tserializer.extend_u8(Self::TAG as u8);\n");
595 out.push_str(&encode_body);
596 out.push_str("\t\tserializer.to_encoded_key()\n\t}\n\n");
597 out.push_str("\tpub fn decode(key: &EncodedKey) -> Option<Self> {\n");
598 out.push_str(
599 "\t\tlet mut de = ::reifydb_codec::key::deserializer::KeyDeserializer::from_bytes(key.as_slice());\n",
600 );
601 out.push_str("\t\tlet found: KeyTag = de.read_u8().ok()?.try_into().ok()?;\n");
602 out.push_str("\t\tif found != Self::TAG {\n\t\t\treturn None;\n\t\t}\n");
603 out.push_str(&format!("\t\tlet decoded = Self {{\n{decode_body}\t\t}};\n"));
604 out.push_str("\t\tif !de.is_empty() {\n\t\t\treturn None;\n\t\t}\n");
605 out.push_str("\t\tSome(decoded)\n\t}\n}\n\n");
606
607 out.push_str(&format!("#[automatically_derived]\nimpl KeyFields for {name} {{\n"));
608 out.push_str("\tfn fields(&self) -> ::smallvec::SmallVec<[Field<'_>; 6]> {\n");
609 out.push_str("\t\t::smallvec::smallvec![\n");
610 for field in fields {
611 for expr in field.column.field_exprs(&field.name) {
612 out.push_str(&format!("\t\t\t{expr},\n"));
613 }
614 }
615 out.push_str("\t\t]\n\t}\n}\n\n");
616 out.push_str(&expand_tests(name, fields));
617
618 out.parse().expect("derived key impl must be valid Rust")
619}
620
621#[cfg(test)]
622mod tests {
623 use super::derive_key;
624
625 fn expand(source: &str) -> String {
626 derive_key(source.parse().unwrap()).to_string()
627 }
628
629 #[test]
630 fn missing_attribute_is_rejected() {
631 let out = expand("struct RowKey { row: u64 }");
632 assert!(out.contains("compile_error"), "{out}");
633 assert!(out.contains("key"), "{out}");
634 }
635
636 #[test]
637 fn a_well_formed_struct_expands_without_an_error() {
638 let out = expand("#[key(tag = Row)] struct RowKey { table: u64, row: RowNumber }");
639 assert!(!out.contains("compile_error"), "{out}");
640 assert!(out.contains("impl RowKey { pub const TAG"), "{out}");
641 assert!(out.contains("KeyTag :: Row"), "{out}");
642 }
643
644 #[test]
645 fn fields_are_encoded_in_declaration_order() {
646 let out = expand("#[key(tag = Row)] struct RowKey { table: u64, row: RowNumber }");
647 let table = out.find("self . table").expect("table field encoded");
648 let row = out.find("self . row").expect("row field encoded");
649 assert!(table < row, "{out}");
650 }
651
652 #[test]
653 fn a_repr_u8_field_encodes_the_discriminant_and_decodes_through_try_from() {
654 let out = expand("#[key(tag = SystemVersion)] struct K { #[key(repr = u8)] version: SystemVersion }");
657 assert!(!out.contains("compile_error"), "{out}");
658 assert!(out.contains("extend_u8 (self . version as u8)"), "{out}");
659 assert!(out.contains("SystemVersion :: try_from"), "{out}");
660 }
661
662 #[test]
663 fn a_repr_u8_field_still_rejects_a_type_the_attribute_was_not_put_on() {
664 let out = expand("#[key(tag = SystemVersion)] struct K { version: SystemVersion }");
665 assert!(out.contains("compile_error"), "{out}");
666 }
667
668 #[test]
669 fn an_unrecognised_field_type_is_rejected() {
670 let out = expand("#[key(tag = Row)] struct RowKey { at: String }");
671 assert!(out.contains("compile_error"), "{out}");
672 assert!(out.contains("at"), "{out}");
673 assert!(out.contains("String"), "{out}");
674 }
675
676 #[test]
677 fn a_tuple_struct_is_rejected() {
678 let out = expand("#[key(tag = Row)] struct RowKey(u64);");
679 assert!(out.contains("compile_error"), "{out}");
680 }
681
682 #[test]
683 fn a_generic_struct_is_rejected() {
684 let out = expand("#[key(tag = Row)] struct RowKey<T> { row: T }");
685 assert!(out.contains("compile_error"), "{out}");
686 }
687
688 #[test]
689 fn a_byte_array_field_maps_to_blob16() {
690 let out = expand("#[key(tag = Row)] struct RowKey { blob: [u8; 16] }");
691 assert!(!out.contains("compile_error"), "{out}");
692 assert!(out.contains("extend_raw"));
693 }
694
695 #[test]
696 fn a_byte_array_of_the_wrong_width_is_rejected() {
697 let out = expand("#[key(tag = Row)] struct RowKey { blob: [u8; 32] }");
698 assert!(out.contains("compile_error"), "{out}");
699 }
700
701 #[test]
702 fn table_id_and_column_id_encode_via_their_wrapped_u64() {
703 let out = expand("#[key(tag = Table)] struct TableKey { table: TableId, column: ColumnId }");
704 assert!(!out.contains("compile_error"), "{out}");
705 assert!(out.contains("self . table . 0"), "{out}");
706 assert!(out.contains("TableId (de . read_u64 () . ok () ?)"), "{out}");
707 assert!(out.contains("ColumnId (de . read_u64 () . ok () ?)"), "{out}");
708 }
709
710 #[test]
711 fn an_option_u8_field_encodes_as_a_fixed_two_byte_presence_and_value_pair() {
712 let out = expand("#[key(tag = Row)] struct RowKey { tag: Option<u8> }");
713 assert!(!out.contains("compile_error"), "{out}");
714 assert!(out.contains("with_capacity (3)"), "{out}: width must stay fixed at 2 bytes for None and Some");
715 assert!(out.contains("Some (v) => { serializer . extend_u8 (1u8) . extend_u8 (v) ; }"), "{out}");
716 assert!(out.contains("None => { serializer . extend_u8 (0u8) . extend_u8 (0u8) ; }"), "{out}");
717 assert!(out.contains("1u8 => Some (de . read_u8 () . ok () ?)"), "{out}");
718 assert!(out.contains("0u8 => { de . read_u8 () . ok () ?; None }"), "{out}");
719 }
720
721 #[test]
722 fn object_id_and_storage_id_encode_via_the_catalog_ext() {
723 let out = expand("#[key(tag = Row)] struct RowKey { storage: StorageId, object: ObjectId }");
724 assert!(!out.contains("compile_error"), "{out}");
725 assert!(out.contains("extend_object_id (self . storage)"), "{out}");
726 assert!(out.contains("extend_object_id (self . object)"), "{out}");
727 assert!(out.contains("StorageId :: from_object (de . read_object_id () . ok () ?) ?"), "{out}");
728 assert!(out.contains("de . read_object_id () . ok () ?"), "{out}");
729 }
730}