lib

Core libraries for Radroots
git clone https://radroots.dev/git/lib.git
Log | Files | Refs | README | LICENSE

commit 3fe5f98493474202efdf85b81e72d43388e8c1b5
parent c811285120427795aa83806d75e260dd3e7de520
Author: triesap <tyson@radroots.org>
Date:   Wed, 24 Jun 2026 07:05:43 +0000

dto: encode events optional field policy

- mark event Option fields as optional nullable in source descriptors
- cover listing product and event reference descriptor presence
- keep the RCLD-04 events migration on the DTO-only lib branch
- validate radroots_events dto descriptor tests

Diffstat:
Mcrates/events/src/dto.rs | 61+++++++++++++++++++++++++++++++++++++++++++++++++------------
1 file changed, 49 insertions(+), 12 deletions(-)

diff --git a/crates/events/src/dto.rs b/crates/events/src/dto.rs @@ -106,14 +106,14 @@ impl Dto for RadrootsNostrEventRef { "crates/events/src/lib.rs", 67, )) - .with_field(field( + .with_field(optional_nullable_field( "d_tag", "d_tag", <Option<String> as Dto>::describe(ctx), "crates/events/src/lib.rs", 68, )) - .with_field(field( + .with_field(optional_nullable_field( "relays", "relays", <Option<Vec<String>> as Dto>::describe(ctx), @@ -138,7 +138,7 @@ impl Dto for RadrootsNostrEventPtr { "crates/events/src/lib.rs", 75, )) - .with_field(field( + .with_field(optional_nullable_field( "relays", "relays", <Option<String> as Dto>::describe(ctx), @@ -177,42 +177,42 @@ impl Dto for RadrootsListingProduct { "crates/events/src/listing.rs", 84, )) - .with_field(nullable_field( + .with_field(optional_nullable_field( "summary", "summary", <Option<String> as Dto>::describe(ctx), "crates/events/src/listing.rs", 85, )) - .with_field(nullable_field( + .with_field(optional_nullable_field( "process", "process", <Option<String> as Dto>::describe(ctx), "crates/events/src/listing.rs", 86, )) - .with_field(nullable_field( + .with_field(optional_nullable_field( "lot", "lot", <Option<String> as Dto>::describe(ctx), "crates/events/src/listing.rs", 87, )) - .with_field(nullable_field( + .with_field(optional_nullable_field( "location", "location", <Option<String> as Dto>::describe(ctx), "crates/events/src/listing.rs", 88, )) - .with_field(nullable_field( + .with_field(optional_nullable_field( "profile", "profile", <Option<String> as Dto>::describe(ctx), "crates/events/src/listing.rs", 89, )) - .with_field(nullable_field( + .with_field(optional_nullable_field( "year", "year", <Option<String> as Dto>::describe(ctx), @@ -263,7 +263,7 @@ impl Dto for RadrootsListingImage { "crates/events/src/listing.rs", 127, )) - .with_field(nullable_field( + .with_field(optional_nullable_field( "size", "size", TypeRef::option(size), @@ -278,14 +278,14 @@ fn register(ctx: &mut DescribeCtx, rust_ident: &str, type_def: TypeDef) -> TypeR ctx.register_type(RustTypeId::new("radroots_events", rust_ident), type_def) } -fn nullable_field( +fn optional_nullable_field( rust_name: &str, wire_name: &str, ty: TypeRef, file: &str, line: u32, ) -> FieldDef { - field(rust_name, wire_name, ty, file, line).with_presence(FieldPresence::nullable_required()) + field(rust_name, wire_name, ty, file, line).with_presence(FieldPresence::optional_nullable()) } fn field(rust_name: &str, wire_name: &str, ty: TypeRef, file: &str, line: u32) -> FieldDef { @@ -324,4 +324,41 @@ mod tests { .any(|def| matches!(def, TypeDef::Struct(def) if def.export_name == "RadrootsListingImageSize")) ); } + + #[test] + fn option_fields_are_optional_nullable() { + let registry = build_registry(dto_roots()); + + let product = registry + .types_by_id + .values() + .find_map(|def| match def { + TypeDef::Struct(def) if def.export_name == "RadrootsListingProduct" => Some(def), + _ => None, + }) + .expect("listing product descriptor exists"); + let summary = product + .fields + .iter() + .find(|field| field.rust_name.as_str() == "summary") + .expect("summary field exists"); + assert!(!summary.presence.required_on_deserialize); + assert!(summary.presence.nullable); + + let event_ref = registry + .types_by_id + .values() + .find_map(|def| match def { + TypeDef::Struct(def) if def.export_name == "RadrootsNostrEventRef" => Some(def), + _ => None, + }) + .expect("event ref descriptor exists"); + let d_tag = event_ref + .fields + .iter() + .find(|field| field.rust_name.as_str() == "d_tag") + .expect("d_tag field exists"); + assert!(!d_tag.presence.required_on_deserialize); + assert!(d_tag.presence.nullable); + } }