dto.rs (9120B)
1 use dto_bindgen_core::{ 2 BackendId, DescribeCtx, Dto, FieldDef, IdentName, RootDescriptor, RustTypeId, SourceSpan, 3 StructDef, TargetFieldNames, TargetOverride, TypeDef, TypeRef, WireFieldNames, 4 }; 5 6 use crate::listing::{ 7 model::{RadrootsTradeListingSubtotal, RadrootsTradeListingTotal}, 8 validation::RadrootsTradeListing, 9 }; 10 11 pub fn dto_roots() -> [RootDescriptor; 3] { 12 [ 13 RootDescriptor::new::<RadrootsTradeListing>(), 14 RootDescriptor::new::<RadrootsTradeListingSubtotal>(), 15 RootDescriptor::new::<RadrootsTradeListingTotal>(), 16 ] 17 } 18 19 impl Dto for RadrootsTradeListing { 20 fn describe(ctx: &mut DescribeCtx) -> TypeRef { 21 let def = StructDef::new( 22 "RadrootsTradeListing", 23 "RadrootsTradeListing", 24 span("crates/trade/src/listing/validation.rs", 25), 25 ) 26 .with_field(field( 27 "listing_id", 28 "listing_id", 29 String::describe(ctx), 30 "crates/trade/src/listing/validation.rs", 31 26, 32 )) 33 .with_field(field( 34 "listing_addr", 35 "listing_addr", 36 String::describe(ctx), 37 "crates/trade/src/listing/validation.rs", 38 27, 39 )) 40 .with_field(field( 41 "seller_pubkey", 42 "seller_pubkey", 43 String::describe(ctx), 44 "crates/trade/src/listing/validation.rs", 45 28, 46 )) 47 .with_field(field( 48 "title", 49 "title", 50 String::describe(ctx), 51 "crates/trade/src/listing/validation.rs", 52 29, 53 )) 54 .with_field(field( 55 "description", 56 "description", 57 String::describe(ctx), 58 "crates/trade/src/listing/validation.rs", 59 30, 60 )) 61 .with_field(field( 62 "product_type", 63 "product_type", 64 String::describe(ctx), 65 "crates/trade/src/listing/validation.rs", 66 31, 67 )) 68 .with_field(field( 69 "primary_bin_id", 70 "primary_bin_id", 71 String::describe(ctx), 72 "crates/trade/src/listing/validation.rs", 73 32, 74 )) 75 .with_field(field( 76 "bin_quantity", 77 "bin_quantity", 78 ts_ref("RadrootsCoreQuantity"), 79 "crates/trade/src/listing/validation.rs", 80 33, 81 )) 82 .with_field(field( 83 "unit", 84 "unit", 85 ts_ref("RadrootsCoreUnit"), 86 "crates/trade/src/listing/validation.rs", 87 34, 88 )) 89 .with_field(field( 90 "unit_price", 91 "unit_price", 92 ts_ref("RadrootsCoreMoney"), 93 "crates/trade/src/listing/validation.rs", 94 35, 95 )) 96 .with_field(field( 97 "inventory_available", 98 "inventory_available", 99 ts_ref("RadrootsCoreDecimal"), 100 "crates/trade/src/listing/validation.rs", 101 36, 102 )) 103 .with_field(field( 104 "availability", 105 "availability", 106 ts_ref("RadrootsListingAvailability"), 107 "crates/trade/src/listing/validation.rs", 108 37, 109 )) 110 .with_field(field( 111 "location", 112 "location", 113 ts_ref("RadrootsListingLocation"), 114 "crates/trade/src/listing/validation.rs", 115 38, 116 )) 117 .with_field(field( 118 "delivery_method", 119 "delivery_method", 120 ts_ref("RadrootsListingDeliveryMethod"), 121 "crates/trade/src/listing/validation.rs", 122 39, 123 )) 124 .with_field(field( 125 "listing", 126 "listing", 127 ts_ref("RadrootsListing"), 128 "crates/trade/src/listing/validation.rs", 129 40, 130 )); 131 register(ctx, "RadrootsTradeListing", TypeDef::Struct(def)) 132 } 133 } 134 135 impl Dto for RadrootsTradeListingSubtotal { 136 fn describe(ctx: &mut DescribeCtx) -> TypeRef { 137 trade_listing_total_like( 138 ctx, 139 "RadrootsTradeListingSubtotal", 140 "crates/trade/src/listing/model.rs", 141 3, 142 ) 143 } 144 } 145 146 impl Dto for RadrootsTradeListingTotal { 147 fn describe(ctx: &mut DescribeCtx) -> TypeRef { 148 trade_listing_total_like( 149 ctx, 150 "RadrootsTradeListingTotal", 151 "crates/trade/src/listing/model.rs", 152 12, 153 ) 154 } 155 } 156 157 fn trade_listing_total_like( 158 ctx: &mut DescribeCtx, 159 rust_ident: &str, 160 file: &str, 161 line: u32, 162 ) -> TypeRef { 163 let def = StructDef::new(rust_ident, rust_ident, span(file, line)) 164 .with_field(field( 165 "price_amount", 166 "price_amount", 167 ts_ref("RadrootsCoreMoney"), 168 file, 169 line + 1, 170 )) 171 .with_field(field( 172 "price_currency", 173 "price_currency", 174 ts_ref("RadrootsCoreCurrency"), 175 file, 176 line + 2, 177 )) 178 .with_field(field( 179 "quantity_amount", 180 "quantity_amount", 181 ts_ref("RadrootsCoreDecimal"), 182 file, 183 line + 3, 184 )) 185 .with_field(field( 186 "quantity_unit", 187 "quantity_unit", 188 ts_ref("RadrootsCoreUnit"), 189 file, 190 line + 4, 191 )); 192 register(ctx, rust_ident, TypeDef::Struct(def)) 193 } 194 195 fn register(ctx: &mut DescribeCtx, rust_ident: &str, type_def: TypeDef) -> TypeRef { 196 ctx.register_type(RustTypeId::new("radroots_trade", rust_ident), type_def) 197 } 198 199 fn ts_ref(target_type: &str) -> TypeRef { 200 TypeRef::Override(TargetOverride::new(BackendId::TypeScript, target_type)) 201 } 202 203 fn field(rust_name: &str, wire_name: &str, ty: TypeRef, file: &str, line: u32) -> FieldDef { 204 FieldDef::new( 205 IdentName::new(rust_name), 206 WireFieldNames::same(wire_name), 207 TargetFieldNames::new(wire_name, rust_name), 208 ty, 209 span(file, line), 210 ) 211 } 212 213 fn span(file: &str, line: u32) -> SourceSpan { 214 SourceSpan::new(file, line, 1) 215 } 216 217 #[cfg(test)] 218 mod tests { 219 use dto_bindgen_core::{BackendId, Registry, StructDef, TypeDef, TypeRef, build_registry}; 220 221 use super::dto_roots; 222 223 const TRADE_SOURCE_ROOTS: &[&str] = &[ 224 "RadrootsTradeListing", 225 "RadrootsTradeListingSubtotal", 226 "RadrootsTradeListingTotal", 227 ]; 228 229 #[test] 230 fn trade_descriptor_roots_build_registry() { 231 let registry = build_registry(dto_roots()); 232 233 assert!( 234 !registry.has_errors(), 235 "trade registry has diagnostics: {:?}", 236 registry.diagnostics 237 ); 238 assert_eq!(registry.roots.len(), dto_roots().len()); 239 } 240 241 #[test] 242 fn trade_source_roots_are_deterministic() { 243 let registry = build_registry(dto_roots()); 244 245 assert_eq!(root_export_names(®istry), TRADE_SOURCE_ROOTS); 246 } 247 248 #[test] 249 fn trade_source_fields_use_package_aliases_for_import_boundaries() { 250 let registry = build_registry(dto_roots()); 251 let listing = find_struct(®istry, "RadrootsTradeListing"); 252 let subtotal = find_struct(®istry, "RadrootsTradeListingSubtotal"); 253 254 assert_eq!( 255 typescript_override_target(field_ty(listing, "inventory_available")), 256 Some("RadrootsCoreDecimal") 257 ); 258 assert_eq!( 259 typescript_override_target(field_ty(listing, "listing")), 260 Some("RadrootsListing") 261 ); 262 assert_eq!( 263 typescript_override_target(field_ty(subtotal, "price_currency")), 264 Some("RadrootsCoreCurrency") 265 ); 266 } 267 268 fn root_export_names(registry: &Registry) -> Vec<&str> { 269 registry 270 .roots 271 .iter() 272 .map(|type_id| { 273 registry 274 .type_def(*type_id) 275 .map(type_export_name) 276 .expect("root type") 277 }) 278 .collect() 279 } 280 281 fn type_export_name(def: &TypeDef) -> &str { 282 match def { 283 TypeDef::Struct(def) => def.export_name.as_str(), 284 TypeDef::Enum(def) => def.export_name.as_str(), 285 } 286 } 287 288 fn find_struct<'a>(registry: &'a Registry, export_name: &str) -> &'a StructDef { 289 registry 290 .types_by_id 291 .values() 292 .find_map(|def| match def { 293 TypeDef::Struct(def) if def.export_name == export_name => Some(def), 294 _ => None, 295 }) 296 .expect("descriptor struct") 297 } 298 299 fn field_ty<'a>(def: &'a StructDef, field_name: &str) -> &'a TypeRef { 300 &def.fields 301 .iter() 302 .find(|field| field.target.typescript == field_name) 303 .expect("descriptor field") 304 .ty 305 } 306 307 fn typescript_override_target(ty: &TypeRef) -> Option<&str> { 308 match ty { 309 TypeRef::Override(target) if target.backend == BackendId::TypeScript => { 310 Some(target.target_type.as_str()) 311 } 312 _ => None, 313 } 314 } 315 }