v1?
This commit is contained in:
138
gamelog/src/action.rs
Normal file
138
gamelog/src/action.rs
Normal file
@@ -0,0 +1,138 @@
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Debug, Deserialize, Clone, Default, PartialEq)]
|
||||
pub enum Action {
|
||||
CrackStudentBodyRightTackle,
|
||||
Curls,
|
||||
FleaFlicker,
|
||||
HalfbackSlam,
|
||||
HalfbackSlipScreen,
|
||||
HalfbackSweep,
|
||||
Mesh,
|
||||
PlayActionBoot,
|
||||
PlayActionComebacks,
|
||||
PlayActionPowerZero,
|
||||
PowerZero,
|
||||
SlantBubble,
|
||||
SlotOut,
|
||||
SpeedOption,
|
||||
StrongFlood,
|
||||
#[default]
|
||||
Unknown,
|
||||
}
|
||||
|
||||
impl Action {
|
||||
// I'd love a better way of doing these
|
||||
// Attributes are probably the way to go,
|
||||
// but I'm not about to write procedural macros for this project.
|
||||
|
||||
/// Returns `true` if `self` is a play action.
|
||||
pub fn is_play_action(&self) -> bool {
|
||||
if let Self::PlayActionBoot | Self::PlayActionComebacks | Self::PlayActionPowerZero = self {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if `self` is a halfback.
|
||||
pub fn is_halfback(&self) -> bool {
|
||||
if let Self::HalfbackSlam | Self::HalfbackSlipScreen | Self::HalfbackSweep = self {
|
||||
true
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if `self` is a running play.
|
||||
pub fn is_run(&self) -> bool {
|
||||
if let Self::HalfbackSlam
|
||||
| Self::SpeedOption
|
||||
| Self::HalfbackSweep
|
||||
| Self::PowerZero
|
||||
| Self::CrackStudentBodyRightTackle = self
|
||||
{
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if `self` is a passing play.
|
||||
pub fn is_pass(&self) -> bool {
|
||||
!self.is_run()
|
||||
}
|
||||
|
||||
/// Returns `true` if `self` is `Event::Unknown`.
|
||||
pub fn is_unknown(&self) -> bool {
|
||||
if let Self::Unknown = self {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the `Playset` that this action belongs to.
|
||||
/// Returns `None` if `Event::Unknown`
|
||||
pub fn playset(&self) -> Option<Playset> {
|
||||
if self.is_unknown() {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(match self {
|
||||
Self::SlantBubble | Self::HalfbackSlam | Self::PlayActionBoot => Playset::PistolSpread,
|
||||
Self::StrongFlood | Self::SpeedOption | Self::HalfbackSlipScreen => {
|
||||
Playset::ShotgunTripleWingsOffset
|
||||
}
|
||||
Self::SlotOut | Self::HalfbackSweep | Self::PlayActionComebacks => {
|
||||
Playset::ShotgunDoubleFlex
|
||||
}
|
||||
Self::Curls | Self::PowerZero | Self::PlayActionPowerZero => Playset::IFormNormal,
|
||||
Self::Mesh | Self::CrackStudentBodyRightTackle | Self::FleaFlicker => {
|
||||
Playset::IFormTight
|
||||
}
|
||||
_ => unreachable!(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns the `Key` that this action belongs to.
|
||||
/// Returns `None` if `Event::Unknown`
|
||||
pub fn key(&self) -> Option<Key> {
|
||||
if self.is_unknown() {
|
||||
return None;
|
||||
}
|
||||
|
||||
// All running plays are on `Key::X`
|
||||
if self.is_run() {
|
||||
return Some(Key::X);
|
||||
}
|
||||
|
||||
Some(match self {
|
||||
Self::SlantBubble | Self::StrongFlood | Self::SlotOut | Self::Curls | Self::Mesh => {
|
||||
Key::Square
|
||||
}
|
||||
Self::PlayActionBoot
|
||||
| Self::HalfbackSlipScreen
|
||||
| Self::PlayActionComebacks
|
||||
| Self::PlayActionPowerZero
|
||||
| Self::FleaFlicker => Key::Triangle,
|
||||
_ => unreachable!(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Playset {
|
||||
PistolSpread,
|
||||
ShotgunTripleWingsOffset,
|
||||
ShotgunDoubleFlex,
|
||||
IFormNormal,
|
||||
IFormTight,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Key {
|
||||
Square,
|
||||
X,
|
||||
Triangle,
|
||||
}
|
||||
@@ -4,32 +4,26 @@ use std::{fmt, io};
|
||||
pub enum LogFileError {
|
||||
FailedToOpen(io::Error),
|
||||
RonSpannedError(ron::error::SpannedError),
|
||||
CompatibilityCheck(semver::Version),
|
||||
}
|
||||
|
||||
impl fmt::Display for LogFileError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::FailedToOpen(err) => write!(f, "{}", err),
|
||||
Self::CompatibilityCheck(ver) => write!(
|
||||
f,
|
||||
"GameLogs cannot be older than {}, but {} was found in logfile.",
|
||||
crate::MIN_VER.to_string(),
|
||||
ver.to_string()
|
||||
),
|
||||
Self::RonSpannedError(err) => write!(f, "{}", err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum DownError {
|
||||
NotKickoff,
|
||||
#[derive(Debug)]
|
||||
pub enum TeamsError {
|
||||
NumberFound(usize),
|
||||
}
|
||||
|
||||
impl fmt::Display for DownError {
|
||||
impl fmt::Display for TeamsError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match *self {
|
||||
Self::NotKickoff => write!(f, "Variant was not Down::Kickoff."),
|
||||
match self {
|
||||
Self::NumberFound(err) => write!(f, "Expected two, found: {:?}", err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
14
gamelog/src/event.rs
Normal file
14
gamelog/src/event.rs
Normal file
@@ -0,0 +1,14 @@
|
||||
use crate::{Play, ScoreChange, Team, TerrainState};
|
||||
|
||||
use serde::Deserialize;
|
||||
|
||||
type Offence = Team;
|
||||
|
||||
#[derive(Debug, Deserialize, Clone)]
|
||||
pub enum Event {
|
||||
Play(Play),
|
||||
Kickoff(Offence),
|
||||
Turnover(Offence),
|
||||
Penalty(TerrainState),
|
||||
ScoreChange(ScoreChange),
|
||||
}
|
||||
@@ -5,6 +5,25 @@ use std::{fs::File, path::PathBuf};
|
||||
#[derive(Debug, Deserialize, Clone)]
|
||||
pub struct LogFile(Vec<super::Game>);
|
||||
|
||||
impl LogFile {
|
||||
pub fn min_ver(&self) -> semver::Version {
|
||||
let mut lowest = semver::Version::new(u64::MAX, u64::MAX, u64::MAX);
|
||||
|
||||
self.0.iter().for_each(|x| {
|
||||
if x.version.cmp_precedence(&lowest).is_lt() {
|
||||
lowest = x.version.clone()
|
||||
}
|
||||
});
|
||||
|
||||
lowest
|
||||
}
|
||||
|
||||
/// Returns if the LogFile min version is compatible.
|
||||
pub fn is_compatible(&self) -> bool {
|
||||
self.min_ver().cmp_precedence(&super::MIN_VER).is_lt()
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<File> for LogFile {
|
||||
type Error = ron::error::SpannedError;
|
||||
|
||||
@@ -31,34 +50,3 @@ impl TryFrom<PathBuf> for LogFile {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl LogFile {
|
||||
pub fn get_min_ver(self) -> semver::Version {
|
||||
let mut lowest = semver::Version::new(u64::MAX, u64::MAX, u64::MAX);
|
||||
|
||||
self.0.iter().for_each(|x| {
|
||||
if x.version.cmp_precedence(&lowest).is_lt() {
|
||||
lowest = x.version.clone()
|
||||
}
|
||||
});
|
||||
|
||||
lowest
|
||||
}
|
||||
|
||||
/// Returns if the LogFile min version is compatible.
|
||||
fn is_compatible(&self) -> bool {
|
||||
self.clone()
|
||||
.get_min_ver()
|
||||
.cmp_precedence(&super::MIN_VER)
|
||||
.is_lt()
|
||||
}
|
||||
|
||||
/// Ensures that the returned gamefile is compatible, else returns Error.
|
||||
pub fn ensure_compatible(self) -> Result<Self, error::LogFileError> {
|
||||
if self.is_compatible() {
|
||||
Ok(self)
|
||||
} else {
|
||||
Err(error::LogFileError::CompatibilityCheck(self.get_min_ver()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
44
gamelog/src/game.rs
Normal file
44
gamelog/src/game.rs
Normal file
@@ -0,0 +1,44 @@
|
||||
use crate::{Event, Period, Play, PlayHandle, Team, error};
|
||||
use serde::Deserialize;
|
||||
|
||||
#[deprecated(since = "0.2.0", note = "Migrated to Game")]
|
||||
pub type GameRecord = Game;
|
||||
|
||||
#[derive(Debug, Deserialize, Clone)]
|
||||
pub struct Game {
|
||||
pub version: semver::Version,
|
||||
pub periods: Vec<Period>,
|
||||
}
|
||||
|
||||
impl Game {
|
||||
/// Returns the teams of this game.
|
||||
pub fn teams(&self) -> Result<Vec<Team>, error::TeamsError> {
|
||||
let mut teams = vec![];
|
||||
|
||||
self.periods.iter().for_each(|period| {
|
||||
period.events.iter().for_each(|event| {
|
||||
if let Event::Kickoff(t) | Event::Turnover(t) = event {
|
||||
if teams.contains(t) {
|
||||
teams.push(t.to_owned())
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
if teams.len() == 2 {
|
||||
Ok(teams)
|
||||
} else {
|
||||
Err(error::TeamsError::NumberFound(teams.len()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PlayHandle for Game {
|
||||
fn plays(&self) -> Vec<Play> {
|
||||
self.periods
|
||||
.iter()
|
||||
.map(|period| period.plays())
|
||||
.collect::<Vec<Vec<Play>>>() // Make compiler happy with turbofish.
|
||||
.concat()
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,23 @@
|
||||
#![allow(deprecated)]
|
||||
|
||||
mod action;
|
||||
mod error;
|
||||
mod event;
|
||||
mod file;
|
||||
mod game;
|
||||
mod period;
|
||||
mod play;
|
||||
mod score;
|
||||
mod terrain;
|
||||
|
||||
#[allow(unused)]
|
||||
pub const MIN_VER: semver::Version = semver::Version::new(0, 3, 0);
|
||||
|
||||
pub use action::*;
|
||||
pub use event::Event;
|
||||
pub use file::LogFile;
|
||||
pub use game::{Game, GameRecord};
|
||||
pub use period::*;
|
||||
pub use play::*;
|
||||
pub use score::ScoreChange;
|
||||
pub use terrain::TerrainState;
|
||||
|
||||
@@ -1,22 +1,29 @@
|
||||
use crate::{Event, PlayHandle};
|
||||
use serde::Deserialize;
|
||||
|
||||
#[deprecated(since = "0.2.0", note = "Migrated to Game")]
|
||||
type GameRecord = Game;
|
||||
|
||||
#[derive(Debug, Deserialize, Clone)]
|
||||
pub struct Game {
|
||||
pub version: semver::Version,
|
||||
periods: Vec<Option<Period>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Clone)]
|
||||
pub struct Period {
|
||||
start: Quarter,
|
||||
end: Option<Quarter>,
|
||||
plays: Vec<super::Play>,
|
||||
pub start: Quarter,
|
||||
pub end: Option<Quarter>,
|
||||
pub events: Vec<Event>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Clone)]
|
||||
impl PlayHandle for Period {
|
||||
fn plays(&self) -> Vec<crate::Play> {
|
||||
self.events
|
||||
.iter()
|
||||
.filter_map(|event| {
|
||||
if let Event::Play(play) = event {
|
||||
Some(play.to_owned())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Clone, PartialEq)]
|
||||
pub enum Quarter {
|
||||
First,
|
||||
Second,
|
||||
|
||||
@@ -1,57 +1,35 @@
|
||||
use crate::{TerrainState, error};
|
||||
use crate::{Action, TerrainState};
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Debug, Deserialize, Clone)]
|
||||
pub trait PlayHandle {
|
||||
/// Returns all plays within object's scope.
|
||||
fn plays(&self) -> Vec<Play>;
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Clone, Default, PartialEq)]
|
||||
pub struct Play {
|
||||
down: Down,
|
||||
terrain: TerrainState,
|
||||
pub action: Action,
|
||||
pub down: Down,
|
||||
pub terrain: TerrainState,
|
||||
}
|
||||
|
||||
type Offence = Team;
|
||||
impl Offence {}
|
||||
|
||||
#[derive(Debug, Deserialize, Clone)]
|
||||
pub enum Event {
|
||||
CrackStudentBodyRightTackle(Play),
|
||||
Curls(Play),
|
||||
FleaFlicker(Play),
|
||||
HalfbackSlam(Play),
|
||||
HalfbackSlipScreen(Play),
|
||||
HalfbackSweep(Play),
|
||||
Mesh(Play),
|
||||
PlayActionBoot(Play),
|
||||
PlayActionComebacks(Play),
|
||||
PlayActionPowerZero(Play),
|
||||
PowerZero(Play),
|
||||
SlantBubble(Play),
|
||||
SlotOut(Play),
|
||||
SpeedOption(Play),
|
||||
StrongFlood(Play),
|
||||
Unknown(Play),
|
||||
Kickoff { offence: Team },
|
||||
Turnover { offence: Team },
|
||||
Penalty { terrain: TerrainState },
|
||||
impl PlayHandle for Play {
|
||||
fn plays(&self) -> Vec<Play> {
|
||||
vec![self.to_owned()]
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Clone)]
|
||||
#[derive(Debug, Deserialize, Clone, Default, PartialEq)]
|
||||
pub enum Down {
|
||||
#[default]
|
||||
First,
|
||||
Second,
|
||||
Third,
|
||||
Fourth,
|
||||
PointAfterTouchdown,
|
||||
PointAfterTouchdown(Option<u8>),
|
||||
}
|
||||
|
||||
impl Down {
|
||||
fn get_offence(&self) -> Result<&Team, error::DownError> {
|
||||
match self {
|
||||
Self::Kickoff { offence } => Ok(offence),
|
||||
_ => Err(error::DownError::NotKickoff),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Clone)]
|
||||
#[derive(Debug, Deserialize, Clone, PartialEq)]
|
||||
pub enum Team {
|
||||
ArizonaState,
|
||||
#[deprecated(since = "0.2.0", note = "Team left the project.")]
|
||||
|
||||
8
gamelog/src/score.rs
Normal file
8
gamelog/src/score.rs
Normal file
@@ -0,0 +1,8 @@
|
||||
use crate::Team;
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Debug, Deserialize, Clone, PartialEq)]
|
||||
pub struct ScoreChange {
|
||||
pub team: Team,
|
||||
pub score: u8,
|
||||
}
|
||||
@@ -1,11 +1,12 @@
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Debug, Deserialize, Clone)]
|
||||
#[derive(Debug, Deserialize, Clone, Default, PartialEq)]
|
||||
pub enum TerrainState {
|
||||
#[deprecated(since = "0.2.0", note = "Replaced in favour of TerrainState::Yards")]
|
||||
Distance(u8),
|
||||
Yards(u8),
|
||||
GoalLine,
|
||||
Inches,
|
||||
#[default]
|
||||
Unknown,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user