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}