Skip to main content

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}