dto.rs (6250B)
1 use dto_bindgen_core::{ 2 BackendId, DescribeCtx, Dto, FieldDef, FieldPresence, IdentName, RootDescriptor, RustTypeId, 3 SourceSpan, StructDef, TargetFieldNames, TargetOverride, TypeDef, TypeRef, WireFieldNames, 4 }; 5 6 use crate::{ 7 checkpoint::{RadrootsEventsIndexedIndexCheckpoint, RadrootsEventsIndexedShardCheckpoint}, 8 manifest::{RadrootsEventsIndexedManifest, RadrootsEventsIndexedShardMetadata}, 9 types::RadrootsEventsIndexedIdRange, 10 }; 11 12 pub fn dto_roots() -> [RootDescriptor; 5] { 13 [ 14 RootDescriptor::new::<RadrootsEventsIndexedIdRange>(), 15 RootDescriptor::new::<RadrootsEventsIndexedShardMetadata>(), 16 RootDescriptor::new::<RadrootsEventsIndexedManifest>(), 17 RootDescriptor::new::<RadrootsEventsIndexedShardCheckpoint>(), 18 RootDescriptor::new::<RadrootsEventsIndexedIndexCheckpoint>(), 19 ] 20 } 21 22 impl Dto for RadrootsEventsIndexedShardCheckpoint { 23 fn describe(ctx: &mut DescribeCtx) -> TypeRef { 24 let def = StructDef::new( 25 "RadrootsEventsIndexedShardCheckpoint", 26 "RadrootsEventsIndexedShardCheckpoint", 27 span("crates/events_indexed/src/checkpoint.rs", 10), 28 ) 29 .with_field(field( 30 "shard_id", 31 "shard_id", 32 shard_id_ref(), 33 "crates/events_indexed/src/checkpoint.rs", 34 11, 35 )) 36 .with_field(field( 37 "last_created_at", 38 "last_created_at", 39 u32::describe(ctx), 40 "crates/events_indexed/src/checkpoint.rs", 41 16, 42 )) 43 .with_field(optional_nullable_field( 44 "last_event_id", 45 "last_event_id", 46 <Option<String> as Dto>::describe(ctx), 47 "crates/events_indexed/src/checkpoint.rs", 48 17, 49 )) 50 .with_field(optional_nullable_field( 51 "cursor", 52 "cursor", 53 <Option<String> as Dto>::describe(ctx), 54 "crates/events_indexed/src/checkpoint.rs", 55 18, 56 )); 57 register( 58 ctx, 59 "RadrootsEventsIndexedShardCheckpoint", 60 TypeDef::Struct(def), 61 ) 62 } 63 } 64 65 impl Dto for RadrootsEventsIndexedIndexCheckpoint { 66 fn describe(ctx: &mut DescribeCtx) -> TypeRef { 67 let def = StructDef::new( 68 "RadrootsEventsIndexedIndexCheckpoint", 69 "RadrootsEventsIndexedIndexCheckpoint", 70 span("crates/events_indexed/src/checkpoint.rs", 24), 71 ) 72 .with_field(field( 73 "generated_at", 74 "generated_at", 75 u32::describe(ctx), 76 "crates/events_indexed/src/checkpoint.rs", 77 30, 78 )) 79 .with_field(field( 80 "shards", 81 "shards", 82 <Vec<RadrootsEventsIndexedShardCheckpoint> as Dto>::describe(ctx), 83 "crates/events_indexed/src/checkpoint.rs", 84 31, 85 )); 86 register( 87 ctx, 88 "RadrootsEventsIndexedIndexCheckpoint", 89 TypeDef::Struct(def), 90 ) 91 } 92 } 93 94 fn register(ctx: &mut DescribeCtx, rust_ident: &str, type_def: TypeDef) -> TypeRef { 95 ctx.register_type( 96 RustTypeId::new("radroots_events_indexed", rust_ident), 97 type_def, 98 ) 99 } 100 101 fn shard_id_ref() -> TypeRef { 102 TypeRef::Override(TargetOverride::new( 103 BackendId::TypeScript, 104 "RadrootsEventsIndexedShardId", 105 )) 106 } 107 108 fn optional_nullable_field( 109 rust_name: &str, 110 wire_name: &str, 111 ty: TypeRef, 112 file: &str, 113 line: u32, 114 ) -> FieldDef { 115 field(rust_name, wire_name, ty, file, line).with_presence(FieldPresence::optional_nullable()) 116 } 117 118 fn field(rust_name: &str, wire_name: &str, ty: TypeRef, file: &str, line: u32) -> FieldDef { 119 FieldDef::new( 120 IdentName::new(rust_name), 121 WireFieldNames::same(wire_name), 122 TargetFieldNames::new(wire_name, rust_name), 123 ty, 124 span(file, line), 125 ) 126 } 127 128 fn span(file: &str, line: u32) -> SourceSpan { 129 SourceSpan::new(file, line, 1) 130 } 131 132 #[cfg(test)] 133 mod tests { 134 use dto_bindgen_core::{FieldDef, Primitive, StructDef, TypeDef, TypeRef, build_registry}; 135 136 use super::dto_roots; 137 138 #[test] 139 fn indexed_descriptor_roots_build_registry() { 140 let registry = build_registry(dto_roots()); 141 142 assert!(!registry.has_errors()); 143 assert_eq!(registry.roots.len(), dto_roots().len()); 144 assert!(registry.types_by_id.values().any( 145 |def| matches!(def, TypeDef::Struct(def) if def.export_name == "RadrootsEventsIndexedManifest") 146 )); 147 } 148 149 #[test] 150 fn custom_epoch_second_fields_render_as_numbers() { 151 let registry = build_registry(dto_roots()); 152 let shard_checkpoint = find_struct(®istry, "RadrootsEventsIndexedShardCheckpoint"); 153 let index_checkpoint = find_struct(®istry, "RadrootsEventsIndexedIndexCheckpoint"); 154 155 assert_eq!( 156 find_field(shard_checkpoint, "last_created_at").ty, 157 TypeRef::Primitive(Primitive::U32) 158 ); 159 assert_eq!( 160 find_field(index_checkpoint, "generated_at").ty, 161 TypeRef::Primitive(Primitive::U32) 162 ); 163 } 164 165 #[test] 166 fn optional_checkpoint_fields_are_optional_nullable() { 167 let registry = build_registry(dto_roots()); 168 let checkpoint = find_struct(®istry, "RadrootsEventsIndexedShardCheckpoint"); 169 170 for field_name in ["last_event_id", "cursor"] { 171 let field = find_field(checkpoint, field_name); 172 173 assert!(field.presence.nullable); 174 assert!(!field.presence.required_on_deserialize); 175 } 176 } 177 178 fn find_struct<'a>( 179 registry: &'a dto_bindgen_core::Registry, 180 export_name: &str, 181 ) -> &'a StructDef { 182 registry 183 .types_by_id 184 .values() 185 .find_map(|def| match def { 186 TypeDef::Struct(def) if def.export_name == export_name => Some(def), 187 _ => None, 188 }) 189 .expect("descriptor struct") 190 } 191 192 fn find_field<'a>(def: &'a StructDef, typescript_name: &str) -> &'a FieldDef { 193 def.fields 194 .iter() 195 .find(|field| field.target.typescript == typescript_name) 196 .expect("descriptor field") 197 } 198 }