I’m deserializing a byte stream that could contain a fixed set of different message types.
I’m using a union for this. When deserializing, I first identify the type of message in the buffer, then I call the specific deserialization function for the message type I identify.
- How can I write this without having to explicitly create the union tag type? Specifically, can I write the following code without defining
InContentTag
? - Do you think attempting to remove what feels like redundant code will be too detrimental to readability?
pub const InContentTag = enum {
expedited,
normal,
segment,
abort,
emergency,
};
/// MailboxIn Content for CoE.
pub const InContent = union(InContentTag) {
expedited: server.Expedited,
normal: server.Normal,
segment: server.Segment,
abort: server.Abort,
emergency: server.Emergency,
// TODO: implement remaining CoE content types
pub fn deserialize(buf: []const u8) !InContent {
switch (try identify(buf)) {
.expedited => return InContent{ .expedited = try server.Expedited.deserialize(buf) },
.normal => return InContent{ .normal = try server.Normal.deserialize(buf) },
.segment => return InContent{ .segment = try server.Segment.deserialize(buf) },
.abort => return InContent{ .abort = try server.Abort.deserialize(buf) },
.emergency => return InContent{ .emergency = try server.Emergency.deserialize(buf) },
}
}
/// Identify what kind of CoE content is in MailboxIn
pub fn identify(buf: []const u8) !InContentTag {
var fbs = std.io.fixedBufferStream(buf);
const reader = fbs.reader();
const mbx_header = try wire.packFromECatReader(mailbox.Header, reader);
switch (mbx_header.type) {
.CoE => {},
else => return error.WrongMbxProtocol,
}
const header = try wire.packFromECatReader(Header, reader);
switch (header.service) {
.tx_pdo => return error.NotImplemented,
.rx_pdo => return error.NotImplemented,
.tx_pdo_remote_request => return error.NotImplemented,
.rx_pdo_remote_request => return error.NotImplemented,
.sdo_info => return error.NotImplemented,
.sdo_request => {
const sdo_header = try wire.packFromECatReader(server.SDOHeader, reader);
return switch (sdo_header.command) {
.abort_transfer_request => .abort,
else => error.InvalidSDORequest,
};
},
.sdo_response => {
const sdo_header = try wire.packFromECatReader(server.SDOHeader, reader);
switch (sdo_header.command) {
.upload_segment_response => return .segment,
.download_segment_response => return .segment,
.initiate_upload_response => switch (sdo_header.transfer_type) {
.normal => return .normal,
.expedited => return .expedited,
},
.initiate_download_response => return .expedited,
.abort_transfer_request => return .abort,
_ => return error.InvalidSDOResponseSDOHeader,
}
},
.emergency => return .emergency,
_ => return error.InvalidCoEService,
}
}
};
I know that unions can infer their enum tag type with
pub const InContent = union (enum) { ...}