lib

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

commit 107e8a4f7248720cff7e87d3f865f84d644ef7f0
parent 7840ad9ce4bf788a4b87b00611a50bbc5fe0d5c3
Author: triesap <tyson@radroots.org>
Date:   Sun,  4 Jan 2026 12:52:38 +0000

listing: Ignore \"null\" sentinel values in tag encoding


- Treat case-insensitive \"null\" strings as empty in clean_value
- Prevent emitting tags with \"null\" values during listing tag build
- Add regression test covering listing fields, location, and image URL
- Apply clean_value fix consistently across `radroots-events-codec` and trade codec

Diffstat:
Mevents-codec/src/listing/tags.rs | 2+-
Mevents-codec/tests/listing.rs | 29+++++++++++++++++++++++++++++
Mevents/bindings/ts/src/types.ts | 2++
Mtrade/bindings/ts/src/types.ts | 4++++
Mtrade/src/listing/codec.rs | 2+-
5 files changed, 37 insertions(+), 2 deletions(-)

diff --git a/events-codec/src/listing/tags.rs b/events-codec/src/listing/tags.rs @@ -566,7 +566,7 @@ fn push_tag_value(tags: &mut Vec<Vec<String>>, key: &str, value: &str) { fn clean_value(value: &str) -> Option<String> { let trimmed = value.trim(); - if trimmed.is_empty() { + if trimmed.is_empty() || trimmed.eq_ignore_ascii_case("null") { None } else { Some(trimmed.to_string()) diff --git a/events-codec/tests/listing.rs b/events-codec/tests/listing.rs @@ -368,3 +368,32 @@ fn listing_tags_full_includes_status_tag() { && t.get(1).map(|s| s.as_str()) == Some("active") })); } + +#[test] +fn listing_build_tags_ignores_null_strings() { + let mut listing = sample_listing_full("listing-1"); + listing.product.summary = Some("null".to_string()); + listing.product.process = Some("null".to_string()); + listing.product.lot = Some("null".to_string()); + listing.product.location = Some("null".to_string()); + listing.product.profile = Some("null".to_string()); + listing.product.year = Some("null".to_string()); + listing.location = Some(RadrootsListingLocation { + primary: "Moyobamba".to_string(), + city: Some("null".to_string()), + region: Some("San Martin".to_string()), + country: Some("null".to_string()), + lat: Some(-6.0346), + lng: Some(-76.9714), + geohash: None, + }); + listing.images = Some(vec![RadrootsListingImage { + url: "null".to_string(), + size: None, + }]); + + let tags = listing_build_tags(&listing).unwrap(); + assert!(!tags + .iter() + .any(|tag| tag.iter().any(|value| value == "null"))); +} diff --git a/events/bindings/ts/src/types.ts b/events/bindings/ts/src/types.ts @@ -1,3 +1,5 @@ +import type { RadrootsCoreDecimal, RadrootsCoreDiscount, RadrootsCoreDiscountValue, RadrootsCoreMoney, RadrootsCorePercent, RadrootsCoreQuantity, RadrootsCoreQuantityPrice, RadrootsCoreUnit } from "@radroots/core-bindings"; + // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. export type JobFeedbackStatus = "payment_required" | "processing" | "error" | "success" | "partial"; diff --git a/trade/bindings/ts/src/types.ts b/trade/bindings/ts/src/types.ts @@ -1,3 +1,7 @@ +import type { RadrootsListingImage, RadrootsNostrEventPtr, RadrootsPlotRef, RadrootsResourceAreaRef } from "@radroots/events-bindings"; + +import type { RadrootsCoreCurrency, RadrootsCoreDecimal, RadrootsCoreDiscount, RadrootsCoreDiscountValue, RadrootsCoreMoney, RadrootsCoreQuantity, RadrootsCoreQuantityPrice, RadrootsCoreUnit } from "@radroots/core-bindings"; + // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. export type RadrootsListing = { d_tag: string, farm: RadrootsListingFarmRef, product: RadrootsListingProduct, primary_bin_id: string, bins: Array<RadrootsListingBin>, resource_area?: RadrootsResourceAreaRef | null, plot?: RadrootsPlotRef | null, discounts?: RadrootsCoreDiscount[] | null, inventory_available?: RadrootsCoreDecimal | null, availability?: RadrootsListingAvailability | null, delivery_method?: RadrootsListingDeliveryMethod | null, location?: RadrootsListingLocation | null, images?: RadrootsListingImage[] | null, }; diff --git a/trade/src/listing/codec.rs b/trade/src/listing/codec.rs @@ -680,7 +680,7 @@ mod tests { fn clean_value(value: &str) -> Option<String> { let trimmed = value.trim(); - if trimmed.is_empty() { + if trimmed.is_empty() || trimmed.eq_ignore_ascii_case("null") { None } else { Some(trimmed.to_string())