Refactor gamelog module.

This commit is contained in:
Cutieguwu
2025-03-26 20:54:11 -04:00
parent 94704a5558
commit 6a75bdbf9f
7 changed files with 198 additions and 180 deletions

35
src/error.rs Normal file
View 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."),
}
}
}

View File

@@ -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
View 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
View 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
View 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
View 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
View File

@@ -0,0 +1,8 @@
use serde::Deserialize;
#[derive(Debug, Deserialize, Clone)]
pub enum TerrainState {
Yards(u8),
GoalLine,
Inches,
}