faf_replay_parser/parser_builder.rs
1use crate::parser::{Parser, ParserOptions, StreamParser};
2use crate::version::{CommandId, Version};
3use std::collections::HashSet;
4
5/// A builder for configuring replay [`Parser`]s. See [`build`] for an example.
6///
7/// [`build`]: struct.ParserBuilder.html#method.build
8#[derive(Debug)]
9pub struct ParserBuilder<V: Version> {
10 options: ParserOptions<V>,
11}
12
13impl<V: Version> ParserBuilder<V> {
14 /// Initialize with default options
15 pub fn new() -> ParserBuilder<V> {
16 ParserBuilder {
17 options: ParserOptions {
18 commands: HashSet::new(),
19 limit: None,
20 save_commands: true,
21 stop_on_desync: true,
22 },
23 }
24 }
25
26 /// Creates a new [`Parser`] by moving the internal [`ParserOptions`] and consuming self.
27 ///
28 /// # Example
29 ///
30 /// ```rust
31 /// use faf_replay_parser::scfa::{replay_command, ReplayCommandId};
32 /// use faf_replay_parser::{ParserBuilder, SCFA};
33 /// use std::convert::TryFrom;
34 ///
35 /// let parser = ParserBuilder::<SCFA>::new()
36 /// .commands(&[
37 /// ReplayCommandId::try_from(replay_command::ADVANCE).unwrap(),
38 /// ReplayCommandId::try_from(replay_command::VERIFY_CHECKSUM).unwrap(),
39 /// ReplayCommandId::try_from(replay_command::END_GAME).unwrap(),
40 /// ])
41 /// .build();
42 /// ```
43 pub fn build(self) -> Parser<V> {
44 Parser::with_options(self.options)
45 }
46
47 /// Like [`build`] but creates a new [`StreamParser`].
48 ///
49 /// [`build`]: struct.ParserBuilder.html#method.build
50 pub fn build_stream(self) -> StreamParser<V> {
51 StreamParser::with_options(self.options)
52 }
53
54 /// Creates a new [`Parser`] by cloning the internal [`ParserOptions`]. Useful when constructing
55 /// many parsers with similar options.
56 ///
57 /// Example
58 ///
59 /// ```rust
60 /// use faf_replay_parser::scfa::{replay_command, ReplayCommandId};
61 /// use faf_replay_parser::{ParserBuilder, SCFA};
62 /// use std::convert::TryFrom;
63 ///
64 /// let mut builder = ParserBuilder::<SCFA>::new().commands(&[
65 /// ReplayCommandId::try_from(replay_command::ADVANCE).unwrap(),
66 /// ReplayCommandId::try_from(replay_command::END_GAME).unwrap(),
67 /// ]);
68 ///
69 /// // Can only count game ticks
70 /// let tick_parser = builder.build_clone();
71 ///
72 /// let builder =
73 /// builder.command(ReplayCommandId::try_from(replay_command::VERIFY_CHECKSUM).unwrap());
74 /// // Can count game ticks and detect desyncs
75 /// let desync_parser = builder.build_clone();
76 ///
77 /// let builder =
78 /// builder.command(ReplayCommandId::try_from(replay_command::LUA_SIM_CALLBACK).unwrap());
79 /// // Can count game ticks, detect desyncs, and extract chat messages
80 /// let message_parser = builder.build();
81 /// ```
82 pub fn build_clone(&self) -> Parser<V> {
83 Parser::with_options(self.options.clone())
84 }
85
86 /// Like [`build_clone`] but creates a new [`StreamParser`].
87 ///
88 /// [`build_clone`]: struct.ParserBuilder.html#method.build_clone
89 pub fn build_stream_clone(&self) -> StreamParser<V> {
90 StreamParser::with_options(self.options.clone())
91 }
92
93 /// Add a command to the [`ParserOptions`].
94 pub fn command(mut self, command: V::CommandId) -> ParserBuilder<V> {
95 self.options.commands.insert(command);
96 self
97 }
98
99 /// Add multiple commands to the [`ParserOptions`].
100 pub fn commands(mut self, commands: &[V::CommandId]) -> ParserBuilder<V> {
101 commands.iter().for_each(|cmd| {
102 self.options.commands.insert(*cmd);
103 });
104 self
105 }
106
107 /// Add all supported commands to the [`ParserOptions`].
108 pub fn commands_all(self) -> ParserBuilder<V> {
109 self.commands(
110 V::CommandId::builder_all()
111 .collect::<Vec<V::CommandId>>()
112 .as_slice(),
113 )
114 }
115
116 /// Add the default set of commands to the [`ParserOptions`].
117 ///
118 /// Default commands are `ADVANCE`, `SET_COMMAND_SOURCE`, `COMMAND_SOURCE_TERMINATED`,
119 /// `VERIFY_CHECKSUM`, and `END_GAME`
120 pub fn commands_default(self) -> ParserBuilder<V> {
121 self.commands(
122 V::CommandId::builder_defaults()
123 .collect::<Vec<V::CommandId>>()
124 .as_slice(),
125 )
126 }
127
128 /// Set a limit on the number of commands to parse. This only applies to commands in the
129 /// `ParserOptions.commands` field.
130 pub fn limit(mut self, limit: Option<usize>) -> ParserBuilder<V> {
131 self.options.limit = limit;
132 self
133 }
134
135 /// Whether or not to store the parsed commands in the parsed `ReplayBody`. When set to false,
136 /// `Replay.body.commands.len()` will always be `0`.
137 pub fn save_commands(mut self, save_commands: bool) -> ParserBuilder<V> {
138 self.options.save_commands = save_commands;
139 self
140 }
141
142 /// Whether or not to return an error if a desync is detected. When set to false,
143 /// `Replay.sim.desync_ticks` will be a list of all desynced ticks.
144 pub fn stop_on_desync(mut self, stop_on_desync: bool) -> ParserBuilder<V> {
145 self.options.stop_on_desync = stop_on_desync;
146 self
147 }
148}