faf_replay_parser/version.rs
1//! Trait definitions for parsing different replay versions
2//!
3//! The replay formats for the family of Supreme Commander games are all very similar to each
4//! other. They use the same command framing but not necessarily the same set of valid commands
5//! or same mapping of command ids. The traits in this module are used to abstract over these
6//! differences. Users of this library only need an implementor of the [`Version`] trait. This
7//! trait can be used to instruct the parsing functions which Supreme Commander format to follow.
8
9use crate::reader::ReplayResult;
10use std::convert::TryFrom;
11use std::fmt::{Debug, Display};
12use std::hash::Hash;
13
14use crate::replay::SimData;
15
16/// The top level trait for defining how to parse replays of a certain game version. See the
17/// [Implementors](trait.Version.html#implementors) for a list of implementations that can be used
18/// with the parsing functions.
19///
20/// For implementors it is recommend to implement this trait on an uninstantiable object.
21///
22/// ```rust
23/// #[derive(Debug)]
24/// pub enum CustomVersion {}
25/// ```
26pub trait Version: Debug {
27 /// An enum that commands will be parsed into.
28 type Command: Command;
29 /// A newtype integer for valid command ids for this version. This is used to validate command
30 /// ids before parsing the command data.
31 type CommandId: CommandId;
32}
33
34/// A trait that must be implemented for the enum that command data will be parsed into.
35pub trait Command: Sized + Debug + Display + Clone + PartialEq {
36 /// Attempt to cast as an [`Advance`] command.
37 fn as_advance(&self) -> Option<Advance>;
38
39 /// Parse into a `Command` with given command id and buffer.
40 fn parse_command_data(command_id: u8, reader: &[u8]) -> ReplayResult<Self>;
41
42 /// Update `sim` given the new command.
43 fn process_command(sim: &mut SimData, command: &Self) -> ReplayResult<()>;
44}
45
46/// A trait that must be implemented on a newtype u8, representing command ids for a certain
47/// version of Supreme Commander.
48///
49/// The following example shows the recommended way to implement required trait bounds. Note that
50/// [`TryFrom::try_from`] will be used to perform id validation, so it is important to implement it
51/// and only allow conversion from `u8` if the value maps to a valid command id.
52///
53/// ```rust
54/// use faf_replay_parser::ReplayReadError;
55/// use faf_replay_parser::version::CommandId;
56/// use std::convert::TryFrom;
57///
58/// #[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
59/// pub struct CustomCommandId(u8);
60///
61/// impl TryFrom<u8> for CustomCommandId {
62/// type Error = ReplayReadError;
63///
64/// fn try_from(val: u8) -> Result<Self, Self::Error> {
65/// if val > Self::max() {
66/// return Err(ReplayReadError::Malformed("invalid command"));
67/// }
68/// Ok(Self(val))
69/// }
70/// }
71///
72/// impl CommandId for CustomCommandId {
73/// fn advance() -> Self { Self(0) }
74/// fn as_u8(&self) -> u8 { self.0 }
75/// fn builder_all() -> impl Iterator<Item = Self> {
76/// (0..=Self::max()).map(|c| Self(c))
77/// }
78/// fn builder_defaults() -> impl Iterator<Item = Self> {
79/// [0, 1, 2].iter().map(|c| Self(*c))
80/// }
81/// fn max() -> u8 { 5 }
82/// }
83/// ```
84pub trait CommandId: Sized + Debug + Clone + Copy + Eq + Hash + TryFrom<u8> {
85 /// Returns an instance of `Self` that represents the `Advance` command.
86 ///
87 /// `Advance` is an essential command telling the simulation to advance for a certain number of
88 /// ticks. This is used to determine the in-game time stamps of other commands and so almost
89 /// always needs to be parsed.
90 fn advance() -> Self;
91
92 /// Returns the byte value of this command id.
93 fn as_u8(&self) -> u8;
94
95 /// Returns an iterator that is used to initialize a [`Parser`](struct.Parser.html) when
96 /// [`ParserBuilder::commands_all`](struct.ParserBuilder.html#method.commands_all) is called.
97 fn builder_all() -> impl Iterator<Item = Self>;
98
99 /// Returns an iterator that is used to initialize a [`Parser`](struct.Parser.html) when
100 /// [`ParserBuilder::commands_default`](struct.ParserBuilder.html#method.commands_default) is
101 /// called.
102 fn builder_defaults() -> impl Iterator<Item = Self>;
103
104 /// Returns the maximum valid byte value for a command id.
105 fn max() -> u8;
106}
107
108pub struct Advance {
109 pub ticks: u32,
110}