Packet types and subtypes
View SourceAs we have seen, in the salute example, it's possible to define the same packet structure for multiple packet types, and use a field that contains a fixed value to differentiate between them.
Since this is a very common pattern in protocol specifications, some niceties
from Etiquette.Spec let us create them in an even more
concise manner.
If you read the packet documentation, there's an
available :of argument. This lets us inherit the structure of a previously
defined packet, and reuse it in a way where we only need to specify the
differences.
Let's take our "salute" specification from before:
defmodule SaluteSpec do
use Etiquette.Spec
packet "My Salute Packet", id: :salute do
field "Source Port", 8
field "Destination Port", 8
field "Packet Type", 2
field "Payload Type", 6
field "Payload", 8
end
packet "Hello Packet", id: :hello do
field "Source Port", 8
field "Destination Port", 8
field "Packet Type", 2, fixed: 0b00
field "Payload Type", 6
field "Payload", 8
end
packet "Goodbye Packet", id: :goodbye do
field "Source Port", 8
field "Destination Port", 8
field "Packet Type", 2, fixed: 0b11
field "Payload Type", 6
field "Payload", 8
end
endIn this case, it quickly becomes apparent that most fields are declared multiple
times. For reusing fields from an existing structure, let's use the :of
argument:
defmodule SaluteSpec do
use Etiquette.Spec
packet "My Salute Packet", id: :salute do
...
end
packet "Hello Packet", id: :hello, of: :salute do
...
end
packet "Goodbye Packet", id: :goodbye, of: :salute do
...
end
endNow that "Hello" and "Goodbye" are using the structure from "Salute", let's start removing fields, starting with "Source Port":
defmodule SaluteSpec do
use Etiquette.Spec
packet "My Salute Packet", id: :salute do
field "Source Port", 8
field "Destination Port", 8
field "Packet Type", 2
field "Payload Type", 6
field "Payload", 8
end
packet "Hello Packet", id: :hello, of: :salute do
field "Destination Port", 8
field "Packet Type", 2, fixed: 0b00
field "Payload Type", 6
field "Payload", 8
end
packet "Goodbye Packet", id: :goodbye, of: :salute do
field "Destination Port", 8
field "Packet Type", 2, fixed: 0b11
field "Payload Type", 6
field "Payload", 8
end
endWhen inspecting the module, we will see the same structure as before:
iex(1)> SaluteSpec.module_info[:exports]
[
...
is_goodbye?: 1,
is_hello?: 1,
is_salute?: 1,
parse_goodbye: 1,
parse_hello: 1,
parse_salute: 1,
...
]Looking at the parse_hello function, we can see that the field we removed
(Source Port), is still there, and in the same order.
Knowing that we can remove redundant fields from "children" specifications, keeping the field and having the order be respected, let's create the finished specification:
defmodule SaluteSpec do
use Etiquette.Spec
packet "My Salute Packet", id: :salute do
field "Source Port", 8
field "Destination Port", 8
field "Packet Type", 2
field "Payload Type", 6
field "Payload", 8
end
packet "Hello Packet", id: :hello, of: :salute do
field "Packet Type", 2, fixed: 0b00
end
packet "Goodbye Packet", id: :goodbye, of: :salute do
field "Packet Type", 2, fixed: 0b11
end
endThis is only one way you can use the :of argument in
packet. Continue reading to learn about how you
can create bespoke documentation for variants of the same field, or even split
the same field of a parent specification into several fields in children
specifications.