fbsim_core/team/offense.rs
1#![doc = include_str!("../../docs/team/offense.md")]
2#[cfg(feature = "rocket_okapi")]
3use rocket_okapi::okapi::schemars;
4#[cfg(feature = "rocket_okapi")]
5use rocket_okapi::okapi::schemars::JsonSchema;
6use serde::{Serialize, Deserialize, Deserializer};
7
8/// # `FootballTeamOffenseRaw` struct
9///
10/// A `FootballTeamOffenseRaw` is a `FootballTeamOffense` before its properties
11/// have been validated
12#[cfg_attr(feature = "rocket_okapi", derive(JsonSchema))]
13#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Default, Serialize, Deserialize)]
14pub struct FootballTeamOffenseRaw {
15 passing: u32,
16 blocking: u32,
17 rushing: u32,
18 receiving: u32,
19 scrambling: u32,
20 turnovers: u32,
21 field_goals: u32,
22 punting: u32,
23 kickoffs: u32,
24 kick_return_defense: u32
25}
26
27impl FootballTeamOffenseRaw {
28 pub fn validate(&self) -> Result<(), String> {
29 // Ensure each property is no greater than 100
30 if self.passing > 100 {
31 return Err(
32 format!(
33 "Passing attribute is out of range [0, 100]: {}",
34 self.passing
35 )
36 )
37 }
38 if self.blocking > 100 {
39 return Err(
40 format!(
41 "Blocking attribute is out of range [0, 100]: {}",
42 self.blocking
43 )
44 )
45 }
46 if self.rushing > 100 {
47 return Err(
48 format!(
49 "Rushing attribute is out of range [0, 100]: {}",
50 self.rushing
51 )
52 )
53 }
54 if self.receiving > 100 {
55 return Err(
56 format!(
57 "Receiving attribute is out of range [0, 100]: {}",
58 self.receiving
59 )
60 )
61 }
62 if self.scrambling > 100 {
63 return Err(
64 format!(
65 "Scrambling attribute is out of range [0, 100]: {}",
66 self.scrambling
67 )
68 )
69 }
70 if self.turnovers > 100 {
71 return Err(
72 format!(
73 "Turnovers attribute is out of range [0, 100]: {}",
74 self.turnovers
75 )
76 )
77 }
78 if self.field_goals > 100 {
79 return Err(
80 format!(
81 "Field goals attribute is out of range [0, 100]: {}",
82 self.field_goals
83 )
84 )
85 }
86 if self.punting > 100 {
87 return Err(
88 format!(
89 "Punting attribute is out of range [0, 100]: {}",
90 self.punting
91 )
92 )
93 }
94 if self.kickoffs > 100 {
95 return Err(
96 format!(
97 "Kickoffs attribute is out of range [0, 100]: {}",
98 self.kickoffs
99 )
100 )
101 }
102 if self.kick_return_defense > 100 {
103 return Err(
104 format!(
105 "Kick return defense attribute is out of range [0, 100]: {}",
106 self.kick_return_defense
107 )
108 )
109 }
110 Ok(())
111 }
112}
113
114/// # `FootballTeamOffense` struct
115///
116/// A `FootballTeamOffense` represents a football team offense
117#[cfg_attr(feature = "rocket_okapi", derive(JsonSchema))]
118#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Serialize)]
119pub struct FootballTeamOffense {
120 passing: u32,
121 blocking: u32,
122 rushing: u32,
123 receiving: u32,
124 scrambling: u32,
125 turnovers: u32,
126 field_goals: u32,
127 punting: u32,
128 kickoffs: u32,
129 kick_return_defense: u32
130}
131
132impl TryFrom<FootballTeamOffenseRaw> for FootballTeamOffense {
133 type Error = String;
134
135 fn try_from(item: FootballTeamOffenseRaw) -> Result<Self, Self::Error> {
136 // Validate the raw coach
137 match item.validate() {
138 Ok(()) => (),
139 Err(error) => return Err(error),
140 };
141
142 // If valid, then convert
143 Ok(
144 FootballTeamOffense{
145 passing: item.passing,
146 blocking: item.blocking,
147 rushing: item.rushing,
148 receiving: item.receiving,
149 scrambling: item.scrambling,
150 turnovers: item.turnovers,
151 field_goals: item.field_goals,
152 punting: item.punting,
153 kickoffs: item.kickoffs,
154 kick_return_defense: item.kick_return_defense
155 }
156 )
157 }
158}
159
160impl<'de> Deserialize<'de> for FootballTeamOffense {
161 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
162 where
163 D: Deserializer<'de>,
164 {
165 // Only deserialize if the conversion from raw succeeds
166 let raw = FootballTeamOffenseRaw::deserialize(deserializer)?;
167 FootballTeamOffense::try_from(raw).map_err(serde::de::Error::custom)
168 }
169}
170
171impl Default for FootballTeamOffense {
172 /// Default constructor for the FootballTeamOffense class
173 ///
174 /// ### Example
175 /// ```
176 /// use fbsim_core::team::offense::FootballTeamOffense;
177 ///
178 /// let my_offense = FootballTeamOffense::default();
179 /// ```
180 fn default() -> Self {
181 FootballTeamOffense{
182 passing: 50_u32,
183 blocking: 50_u32,
184 rushing: 50_u32,
185 receiving: 50_u32,
186 scrambling: 50_u32,
187 turnovers: 50_u32,
188 field_goals: 50_u32,
189 punting: 50_u32,
190 kickoffs: 50_u32,
191 kick_return_defense: 50_u32
192 }
193 }
194}
195
196impl FootballTeamOffense {
197 /// Constructor for the `FootballTeamOffense` struct in which each
198 /// skill level is defaulted to 50
199 ///
200 /// ### Example
201 /// ```
202 /// use fbsim_core::team::offense::FootballTeamOffense;
203 ///
204 /// let my_offense = FootballTeamOffense::new();
205 /// ```
206 pub fn new() -> FootballTeamOffense {
207 FootballTeamOffense::default()
208 }
209
210 /// Constructor for the `FootballTeamOffense` struct in which each
211 /// skill level is set to the provided overall
212 ///
213 /// ### Example
214 /// ```
215 /// use fbsim_core::team::offense::FootballTeamOffense;
216 ///
217 /// let my_offense = FootballTeamOffense::from_overall(20).unwrap();
218 /// assert!(my_offense.overall() == 20_u32);
219 /// ```
220 pub fn from_overall(overall: u32) -> Result<FootballTeamOffense, String> {
221 let raw = FootballTeamOffenseRaw{
222 passing: overall,
223 blocking: overall,
224 rushing: overall,
225 receiving: overall,
226 scrambling: overall,
227 turnovers: overall,
228 field_goals: overall,
229 punting: overall,
230 kickoffs: overall,
231 kick_return_defense: overall
232 };
233 FootballTeamOffense::try_from(raw)
234 }
235
236 /// Calculate the offense's overall rating
237 ///
238 /// ### Example
239 /// ```
240 /// use fbsim_core::team::offense::FootballTeamOffense;
241 ///
242 /// let my_offense = FootballTeamOffense::new();
243 /// let overall = my_offense.overall();
244 /// assert!(overall == 50_u32);
245 /// ```
246 pub fn overall(&self) -> u32 {
247 (
248 (
249 self.passing + self.blocking + self.rushing +
250 self.receiving + self.scrambling + self.turnovers +
251 self.field_goals + self.punting + self.kickoffs +
252 self.kick_return_defense
253 ) as f32 / 10_f32
254 ) as u32
255 }
256
257 /// Get the offense's rushing skill level
258 ///
259 /// ### Example
260 /// ```
261 /// use fbsim_core::team::offense::FootballTeamOffense;
262 ///
263 /// let my_offense = FootballTeamOffense::new();
264 /// let rushing = my_offense.rushing();
265 /// assert!(rushing == 50_u32);
266 /// ```
267 pub fn rushing(&self) -> u32 {
268 self.rushing
269 }
270
271 /// Get the offense's passing skill level
272 ///
273 /// ### Example
274 /// ```
275 /// use fbsim_core::team::offense::FootballTeamOffense;
276 ///
277 /// let my_offense = FootballTeamOffense::new();
278 /// let passing = my_offense.passing();
279 /// assert!(passing == 50_u32);
280 /// ```
281 pub fn passing(&self) -> u32 {
282 self.passing
283 }
284
285 /// Get the offense's receiving skill level
286 ///
287 /// ### Example
288 /// ```
289 /// use fbsim_core::team::offense::FootballTeamOffense;
290 ///
291 /// let my_offense = FootballTeamOffense::new();
292 /// let receiving = my_offense.receiving();
293 /// assert!(receiving == 50_u32);
294 /// ```
295 pub fn receiving(&self) -> u32 {
296 self.receiving
297 }
298
299 /// Get the offense's scrambling skill level
300 ///
301 /// ### Example
302 /// ```
303 /// use fbsim_core::team::offense::FootballTeamOffense;
304 ///
305 /// let my_offense = FootballTeamOffense::new();
306 /// let scrambling = my_offense.scrambling();
307 /// assert!(scrambling == 50_u32);
308 /// ```
309 pub fn scrambling(&self) -> u32 {
310 self.scrambling
311 }
312
313 /// Get the offense's blocking skill level
314 ///
315 /// ### Example
316 /// ```
317 /// use fbsim_core::team::offense::FootballTeamOffense;
318 ///
319 /// let my_offense = FootballTeamOffense::new();
320 /// let blocking = my_offense.blocking();
321 /// assert!(blocking == 50_u32);
322 /// ```
323 pub fn blocking(&self) -> u32 {
324 self.blocking
325 }
326
327 /// Get the offense's turnovers skill level
328 ///
329 /// ### Example
330 /// ```
331 /// use fbsim_core::team::offense::FootballTeamOffense;
332 ///
333 /// let my_offense = FootballTeamOffense::new();
334 /// let turnovers = my_offense.turnovers();
335 /// assert!(turnovers == 50_u32);
336 /// ```
337 pub fn turnovers(&self) -> u32 {
338 self.turnovers
339 }
340
341 /// Get the offense's field goal kicking skill level
342 ///
343 /// ### Example
344 /// ```
345 /// use fbsim_core::team::offense::FootballTeamOffense;
346 ///
347 /// let my_offense = FootballTeamOffense::new();
348 /// let field_goals = my_offense.field_goals();
349 /// assert!(field_goals == 50_u32);
350 /// ```
351 pub fn field_goals(&self) -> u32 {
352 self.field_goals
353 }
354
355 /// Get the offense's punting skill level
356 ///
357 /// ### Example
358 /// ```
359 /// use fbsim_core::team::offense::FootballTeamOffense;
360 ///
361 /// let my_offense = FootballTeamOffense::new();
362 /// let punting = my_offense.punting();
363 /// assert!(punting == 50_u32);
364 /// ```
365 pub fn punting(&self) -> u32 {
366 self.punting
367 }
368
369 /// Get the offense's kickoffs skill level
370 ///
371 /// ### Example
372 /// ```
373 /// use fbsim_core::team::offense::FootballTeamOffense;
374 ///
375 /// let my_offense = FootballTeamOffense::new();
376 /// let kickoffs = my_offense.kickoffs();
377 /// assert!(kickoffs == 50_u32);
378 /// ```
379 pub fn kickoffs(&self) -> u32 {
380 self.kickoffs
381 }
382
383 /// Get the offense's kick return defense skill level
384 ///
385 /// ### Example
386 /// ```
387 /// use fbsim_core::team::offense::FootballTeamOffense;
388 ///
389 /// let my_offense = FootballTeamOffense::new();
390 /// let kick_return_defense = my_offense.kick_return_defense();
391 /// assert!(kick_return_defense == 50_u32);
392 /// ```
393 pub fn kick_return_defense(&self) -> u32 {
394 self.kick_return_defense
395 }
396}
397
398/// # `FootballTeamOffenseBuilder` struct
399///
400/// A `FootballTeamOffenseBuilder` implements the builder pattern for the
401/// `FootballTeamOffense` struct
402#[cfg_attr(feature = "rocket_okapi", derive(JsonSchema))]
403#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Serialize)]
404pub struct FootballTeamOffenseBuilder {
405 passing: u32,
406 blocking: u32,
407 rushing: u32,
408 receiving: u32,
409 scrambling: u32,
410 turnovers: u32,
411 field_goals: u32,
412 punting: u32,
413 kickoffs: u32,
414 kick_return_defense: u32
415}
416
417impl Default for FootballTeamOffenseBuilder {
418 /// Default constructor for the FootballTeamOffenseBuilder class
419 ///
420 /// ### Example
421 /// ```
422 /// use fbsim_core::team::offense::FootballTeamOffenseBuilder;
423 ///
424 /// let my_offense = FootballTeamOffenseBuilder::default();
425 /// ```
426 fn default() -> Self {
427 FootballTeamOffenseBuilder{
428 passing: 50_u32,
429 blocking: 50_u32,
430 rushing: 50_u32,
431 receiving: 50_u32,
432 scrambling: 50_u32,
433 turnovers: 50_u32,
434 field_goals: 50_u32,
435 punting: 50_u32,
436 kickoffs: 50_u32,
437 kick_return_defense: 50_u32
438 }
439 }
440}
441
442impl FootballTeamOffenseBuilder {
443 /// Initialize a new offense builder
444 ///
445 /// ### Example
446 /// ```
447 /// use fbsim_core::team::offense::FootballTeamOffenseBuilder;
448 ///
449 /// let mut my_offense_builder = FootballTeamOffenseBuilder::new();
450 /// ```
451 pub fn new() -> FootballTeamOffenseBuilder {
452 FootballTeamOffenseBuilder::default()
453 }
454
455 /// Set the passing property
456 ///
457 /// ### Example
458 /// ```
459 /// use fbsim_core::team::offense::{FootballTeamOffense, FootballTeamOffenseBuilder};
460 ///
461 /// let my_offense = FootballTeamOffenseBuilder::new()
462 /// .passing(60)
463 /// .build()
464 /// .unwrap();
465 /// assert!(my_offense.passing() == 60);
466 /// ```
467 pub fn passing(mut self, passing: u32) -> Self {
468 self.passing = passing;
469 self
470 }
471
472 /// Set the blocking property
473 ///
474 /// ### Example
475 /// ```
476 /// use fbsim_core::team::offense::{FootballTeamOffense, FootballTeamOffenseBuilder};
477 ///
478 /// let my_offense = FootballTeamOffenseBuilder::new()
479 /// .blocking(60)
480 /// .build()
481 /// .unwrap();
482 /// assert!(my_offense.blocking() == 60);
483 /// ```
484 pub fn blocking(mut self, blocking: u32) -> Self {
485 self.blocking = blocking;
486 self
487 }
488
489 /// Set the rushing property
490 ///
491 /// ### Example
492 /// ```
493 /// use fbsim_core::team::offense::{FootballTeamOffense, FootballTeamOffenseBuilder};
494 ///
495 /// let my_offense = FootballTeamOffenseBuilder::new()
496 /// .rushing(60)
497 /// .build()
498 /// .unwrap();
499 /// assert!(my_offense.rushing() == 60);
500 /// ```
501 pub fn rushing(mut self, rushing: u32) -> Self {
502 self.rushing = rushing;
503 self
504 }
505
506 /// Set the receiving property
507 ///
508 /// ### Example
509 /// ```
510 /// use fbsim_core::team::offense::{FootballTeamOffense, FootballTeamOffenseBuilder};
511 ///
512 /// let my_offense = FootballTeamOffenseBuilder::new()
513 /// .receiving(60)
514 /// .build()
515 /// .unwrap();
516 /// assert!(my_offense.receiving() == 60);
517 /// ```
518 pub fn receiving(mut self, receiving: u32) -> Self {
519 self.receiving = receiving;
520 self
521 }
522
523 /// Set the scrambling property
524 ///
525 /// ### Example
526 /// ```
527 /// use fbsim_core::team::offense::{FootballTeamOffense, FootballTeamOffenseBuilder};
528 ///
529 /// let my_offense = FootballTeamOffenseBuilder::new()
530 /// .scrambling(60)
531 /// .build()
532 /// .unwrap();
533 /// assert!(my_offense.scrambling() == 60);
534 /// ```
535 pub fn scrambling(mut self, scrambling: u32) -> Self {
536 self.scrambling = scrambling;
537 self
538 }
539
540 /// Set the turnovers property
541 ///
542 /// ### Example
543 /// ```
544 /// use fbsim_core::team::offense::{FootballTeamOffense, FootballTeamOffenseBuilder};
545 ///
546 /// let my_offense = FootballTeamOffenseBuilder::new()
547 /// .turnovers(60)
548 /// .build()
549 /// .unwrap();
550 /// assert!(my_offense.turnovers() == 60);
551 /// ```
552 pub fn turnovers(mut self, turnovers: u32) -> Self {
553 self.turnovers = turnovers;
554 self
555 }
556
557 /// Set the field_goals property
558 ///
559 /// ### Example
560 /// ```
561 /// use fbsim_core::team::offense::{FootballTeamOffense, FootballTeamOffenseBuilder};
562 ///
563 /// let my_offense = FootballTeamOffenseBuilder::new()
564 /// .field_goals(60)
565 /// .build()
566 /// .unwrap();
567 /// assert!(my_offense.field_goals() == 60);
568 /// ```
569 pub fn field_goals(mut self, field_goals: u32) -> Self {
570 self.field_goals = field_goals;
571 self
572 }
573
574 /// Set the punting property
575 ///
576 /// ### Example
577 /// ```
578 /// use fbsim_core::team::offense::{FootballTeamOffense, FootballTeamOffenseBuilder};
579 ///
580 /// let my_offense = FootballTeamOffenseBuilder::new()
581 /// .punting(60)
582 /// .build()
583 /// .unwrap();
584 /// assert!(my_offense.punting() == 60);
585 /// ```
586 pub fn punting(mut self, punting: u32) -> Self {
587 self.punting = punting;
588 self
589 }
590
591 /// Set the kickoffs property
592 ///
593 /// ### Example
594 /// ```
595 /// use fbsim_core::team::offense::{FootballTeamOffense, FootballTeamOffenseBuilder};
596 ///
597 /// let my_offense = FootballTeamOffenseBuilder::new()
598 /// .kickoffs(60)
599 /// .build()
600 /// .unwrap();
601 /// assert!(my_offense.kickoffs() == 60);
602 /// ```
603 pub fn kickoffs(mut self, kickoffs: u32) -> Self {
604 self.kickoffs = kickoffs;
605 self
606 }
607
608 /// Set the kick_return_defense property
609 ///
610 /// ### Example
611 /// ```
612 /// use fbsim_core::team::offense::{FootballTeamOffense, FootballTeamOffenseBuilder};
613 ///
614 /// let my_offense = FootballTeamOffenseBuilder::new()
615 /// .kick_return_defense(60)
616 /// .build()
617 /// .unwrap();
618 /// assert!(my_offense.kick_return_defense() == 60);
619 /// ```
620 pub fn kick_return_defense(mut self, kick_return_defense: u32) -> Self {
621 self.kick_return_defense = kick_return_defense;
622 self
623 }
624
625 /// Build the offense
626 ///
627 /// ### Example
628 /// ```
629 /// use fbsim_core::team::offense::{FootballTeamOffense, FootballTeamOffenseBuilder};
630 ///
631 /// let my_offense = FootballTeamOffenseBuilder::new()
632 /// .passing(25)
633 /// .blocking(30)
634 /// .rushing(35)
635 /// .receiving(40)
636 /// .scrambling(45)
637 /// .turnovers(50)
638 /// .field_goals(55)
639 /// .punting(60)
640 /// .kickoffs(65)
641 /// .kick_return_defense(70)
642 /// .build()
643 /// .unwrap();
644 /// ```
645 pub fn build(self) -> Result<FootballTeamOffense, String> {
646 let raw = FootballTeamOffenseRaw{
647 passing: self.passing,
648 blocking: self.blocking,
649 rushing: self.rushing,
650 receiving: self.receiving,
651 scrambling: self.scrambling,
652 turnovers: self.turnovers,
653 field_goals: self.field_goals,
654 punting: self.punting,
655 kickoffs: self.kickoffs,
656 kick_return_defense: self.kick_return_defense
657 };
658 FootballTeamOffense::try_from(raw)
659 }
660}