Refactor gamelog module.
This commit is contained in:
35
src/error.rs
Normal file
35
src/error.rs
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
use std::{fmt, io};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
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::gamelog::GAMELOG_MIN_VER.to_string(),
|
||||||
|
ver.to_string()
|
||||||
|
),
|
||||||
|
Self::RonSpannedError(err) => write!(f, "{}", err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum DownError {
|
||||||
|
NotKickoff,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for DownError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match *self {
|
||||||
|
Self::NotKickoff => write!(f, "Variant was not Down::Kickoff."),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
180
src/gamelog.rs
180
src/gamelog.rs
@@ -1,180 +0,0 @@
|
|||||||
use serde::Deserialize;
|
|
||||||
use std::{fmt, fs::File, io, path::PathBuf, u64};
|
|
||||||
|
|
||||||
pub const GAMELOG_MIN_VER: semver::Version = semver::Version::new(0, 2, 0);
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
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.",
|
|
||||||
GAMELOG_MIN_VER.to_string(),
|
|
||||||
ver.to_string()
|
|
||||||
),
|
|
||||||
Self::RonSpannedError(err) => write!(f, "{}", err),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
pub struct LogFile(Vec<GameRecord>);
|
|
||||||
|
|
||||||
impl TryFrom<File> for LogFile {
|
|
||||||
type Error = ron::error::SpannedError;
|
|
||||||
|
|
||||||
fn try_from(file: File) -> Result<Self, Self::Error> {
|
|
||||||
ron::de::from_reader(file)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TryFrom<PathBuf> for LogFile {
|
|
||||||
type Error = LogFileError;
|
|
||||||
|
|
||||||
fn try_from(path: PathBuf) -> Result<Self, Self::Error> {
|
|
||||||
match Self::try_from(
|
|
||||||
match std::fs::OpenOptions::new() // Defaults to setting all options false.
|
|
||||||
.read(true) // Only need ensure that reading is possible.
|
|
||||||
.open(path.as_path())
|
|
||||||
{
|
|
||||||
Ok(f) => f,
|
|
||||||
Err(err) => return Err(LogFileError::FailedToOpen(err)),
|
|
||||||
},
|
|
||||||
) {
|
|
||||||
Ok(f) => Ok(f),
|
|
||||||
Err(err) => Err(LogFileError::RonSpannedError(err)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LogFile {
|
|
||||||
pub fn get_min_ver(&mut 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(&mut self) -> bool {
|
|
||||||
self.get_min_ver().cmp_precedence(&GAMELOG_MIN_VER).is_lt()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Ensures that the returned gamefile is compatible, else returns Error.
|
|
||||||
pub fn ensure_compatible(&mut self) -> Result<&mut Self, LogFileError> {
|
|
||||||
if self.is_compatible() {
|
|
||||||
Ok(self)
|
|
||||||
} else {
|
|
||||||
Err(LogFileError::CompatibilityCheck(self.get_min_ver()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
struct GameRecord {
|
|
||||||
version: semver::Version,
|
|
||||||
periods: Vec<Option<Period>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
struct Period {
|
|
||||||
start: Quarter,
|
|
||||||
end: Option<Quarter>,
|
|
||||||
plays: Vec<Play>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
enum Quarter {
|
|
||||||
First,
|
|
||||||
Second,
|
|
||||||
Third,
|
|
||||||
Fourth,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
enum TerrainState {
|
|
||||||
Yards(u8),
|
|
||||||
GoalLine,
|
|
||||||
Inches,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
struct Play {
|
|
||||||
action: Option<Action>,
|
|
||||||
down: Down,
|
|
||||||
terrain: TerrainState,
|
|
||||||
}
|
|
||||||
|
|
||||||
enum DownError {
|
|
||||||
NotKickoff,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for DownError {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
match *self {
|
|
||||||
Self::NotKickoff => write!(f, "Variant was not Down::Kickoff."),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
enum Down {
|
|
||||||
Kickoff { offence: Team },
|
|
||||||
First,
|
|
||||||
Second,
|
|
||||||
Third,
|
|
||||||
Fourth,
|
|
||||||
PointAfterTouchdown,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Down {
|
|
||||||
fn get_offence(&self) -> Result<&Team, DownError> {
|
|
||||||
match self {
|
|
||||||
Self::Kickoff { offence } => Ok(offence),
|
|
||||||
_ => Err(DownError::NotKickoff),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
enum Action {
|
|
||||||
CrackStudentBodyRightTackle,
|
|
||||||
Curls,
|
|
||||||
FleaFlicker,
|
|
||||||
HalfbackSlam,
|
|
||||||
HalfbackSlipScreen,
|
|
||||||
HalfbackSweep,
|
|
||||||
Mesh,
|
|
||||||
PlayActionBoot,
|
|
||||||
PlayActionComebacks,
|
|
||||||
PlayActionPowerZero,
|
|
||||||
PowerZero,
|
|
||||||
SlantBubble,
|
|
||||||
SlotOut,
|
|
||||||
SpeedOption,
|
|
||||||
StrongFlood,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Clone)]
|
|
||||||
enum Team {
|
|
||||||
ArizonaState,
|
|
||||||
Colorado,
|
|
||||||
Iowa,
|
|
||||||
Nebraska,
|
|
||||||
SouthCarolina,
|
|
||||||
Syracuse,
|
|
||||||
TexasAnM,
|
|
||||||
}
|
|
||||||
64
src/gamelog/file.rs
Normal file
64
src/gamelog/file.rs
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
use crate::error;
|
||||||
|
use serde::Deserialize;
|
||||||
|
use std::{fs::File, path::PathBuf};
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
|
pub struct LogFile(Vec<super::Game>);
|
||||||
|
|
||||||
|
impl TryFrom<File> for LogFile {
|
||||||
|
type Error = ron::error::SpannedError;
|
||||||
|
|
||||||
|
fn try_from(file: File) -> Result<Self, Self::Error> {
|
||||||
|
ron::de::from_reader(file)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<PathBuf> for LogFile {
|
||||||
|
type Error = error::LogFileError;
|
||||||
|
|
||||||
|
fn try_from(path: PathBuf) -> Result<Self, Self::Error> {
|
||||||
|
match Self::try_from(
|
||||||
|
match std::fs::OpenOptions::new() // Defaults to setting all options false.
|
||||||
|
.read(true) // Only need ensure that reading is possible.
|
||||||
|
.open(path.as_path())
|
||||||
|
{
|
||||||
|
Ok(f) => f,
|
||||||
|
Err(err) => return Err(error::LogFileError::FailedToOpen(err)),
|
||||||
|
},
|
||||||
|
) {
|
||||||
|
Ok(f) => Ok(f),
|
||||||
|
Err(err) => Err(error::LogFileError::RonSpannedError(err)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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::GAMELOG_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()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
11
src/gamelog/mod.rs
Normal file
11
src/gamelog/mod.rs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
mod file;
|
||||||
|
mod period;
|
||||||
|
mod play;
|
||||||
|
mod terrain;
|
||||||
|
|
||||||
|
pub const GAMELOG_MIN_VER: semver::Version = semver::Version::new(0, 2, 0);
|
||||||
|
|
||||||
|
pub use file::LogFile;
|
||||||
|
pub use period::*;
|
||||||
|
pub use play::*;
|
||||||
|
pub use terrain::TerrainState;
|
||||||
22
src/gamelog/period.rs
Normal file
22
src/gamelog/period.rs
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
#[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>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
|
pub enum Quarter {
|
||||||
|
First,
|
||||||
|
Second,
|
||||||
|
Third,
|
||||||
|
Fourth,
|
||||||
|
}
|
||||||
58
src/gamelog/play.rs
Normal file
58
src/gamelog/play.rs
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
use crate::error;
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
|
pub struct Play {
|
||||||
|
action: Option<Action>,
|
||||||
|
down: Down,
|
||||||
|
terrain: super::TerrainState,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
|
pub enum Action {
|
||||||
|
CrackStudentBodyRightTackle,
|
||||||
|
Curls,
|
||||||
|
FleaFlicker,
|
||||||
|
HalfbackSlam,
|
||||||
|
HalfbackSlipScreen,
|
||||||
|
HalfbackSweep,
|
||||||
|
Mesh,
|
||||||
|
PlayActionBoot,
|
||||||
|
PlayActionComebacks,
|
||||||
|
PlayActionPowerZero,
|
||||||
|
PowerZero,
|
||||||
|
SlantBubble,
|
||||||
|
SlotOut,
|
||||||
|
SpeedOption,
|
||||||
|
StrongFlood,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
|
pub enum Down {
|
||||||
|
Kickoff { offence: Team },
|
||||||
|
First,
|
||||||
|
Second,
|
||||||
|
Third,
|
||||||
|
Fourth,
|
||||||
|
PointAfterTouchdown,
|
||||||
|
}
|
||||||
|
|
||||||
|
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)]
|
||||||
|
pub enum Team {
|
||||||
|
ArizonaState,
|
||||||
|
Colorado,
|
||||||
|
Iowa,
|
||||||
|
Nebraska,
|
||||||
|
SouthCarolina,
|
||||||
|
Syracuse,
|
||||||
|
TexasAnM,
|
||||||
|
}
|
||||||
8
src/gamelog/terrain.rs
Normal file
8
src/gamelog/terrain.rs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
|
pub enum TerrainState {
|
||||||
|
Yards(u8),
|
||||||
|
GoalLine,
|
||||||
|
Inches,
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user