Leniently parsing data into strictly typed form while staying interoperable with the horrible 3rd party implementations out there

⚓ Rust    📅 2026-05-07    👤 surdeus    👁️ 1      

surdeus

Background: I'm building on a Upnp library from the ground up (for reasons ...) and am looking for some advice on parsing invalid messages while maintaining interoperability with all the non-conform implementations out there ...

The problem: There is a spec which clearly defines mandatory & optional fields, value formats and invariants. I've been running a listener to grab real-life messages and have yet to find a single one which actually conforms to the spec.

Example: Upnp defines a specific Uri format which begins: uuid:<device-uuid>:: (V2 repeated states that <device-uuid> must be a UUID; V1 didn't it just included the term & reference spec in the glossary ...) So I have a message from a speaker which decided to use uuid:MANUF-SERIALNUMBER:: :face_with_spiral_eyes: :face_vomiting:

The Question: So how do I meaningfully parse & work with flaky, non-conform inputs and stay inter-operable? This feels like it's a specific case of a general problem ...

Ideas:

  • Stringly type everything (so instead of parsing the Uri into a struct including uuid: Uuid use uuid: String, or just do away with the struct altogether and store the whole uri as a string!) - negates my "reasons"
  • wrap all kinds of stuff in an enum Lenient<T> with variants Valid(T) and Invalid(String) - not sure how this would affect overall performance. I'd rather have some general understanding before going off on a long experiment with benchmarks and significant code changes, just to see ...
  • a trait Lentient<T> offering fn get_lenient(&header, key) which then constructs viable default values, or more likely often works in combo with the enum above.
  • just store valid values, wrap everything in an Option and let downstream worry about validating they have enough info to work with. (Not my style and I'd probably be better just using something that's already out there instead of doing this)

Or is there an obvious, and well-known solution that I'm just not connecting with this case right now? (Although usually in those cases I work it out just as I finish asking for help ...)

3 posts - 2 participants

Read full topic

🏷️ Rust_feed