Practical difference between protocols and function pattern matching?

I’m writing an RTMP server that needs to handle all the different RTMP message and command types that come down the pipe. Each message has a different overall format (and subformats in some cases), and completely different data, and what happens (and what I respond with) is different depending on both the message that I receive and the current state of the server handling that specific connection.

To make the process testable the general flow flow is:

Received packet → extract header and packet data → parse data into message based on header → process message.

Structs seemed like the best way to describe what data is contained by an individual message (and what’s not contained), and provide optimal pattern matching (especially for serializing messages to send over the wire, since the format of each message in binary form is different).

Not sure if that helps explain correctly why I’m using structs or if I’m just missing something that’s more “Elixir-like” (most of my experience is in C#, with minimal F# and Rust playing).