use core::cmp::Ordering;
use std::fmt::Display;
use std::fmt::Formatter;
use crate::prelude::*;
use crate::wad::deserialize::reader::DataReader;
use crate::wad::elements::GMElement;
use crate::wad::serialize::builder::DataBuilder;
#[derive(Debug, Clone, Copy, PartialEq, Hash)]
pub enum LTSBranch {
PreLTS,
LTS,
PostLTS,
}
impl PartialOrd for LTSBranch {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
use LTSBranch::*;
use Ordering::*;
match (self, other) {
(PreLTS, PreLTS) => Some(Equal),
(PreLTS, LTS) => None,
(PreLTS, PostLTS) => Some(Less),
(LTS, PreLTS) => None,
(LTS, LTS) => Some(Equal),
(LTS, PostLTS) => Some(Less),
(PostLTS, PreLTS) => Some(Greater),
(PostLTS, LTS) => Some(Less),
(PostLTS, PostLTS) => Some(Equal),
}
}
}
impl Display for LTSBranch {
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
let string = match self {
Self::PreLTS => "PreLTS",
Self::LTS => "LTS",
Self::PostLTS => "PostLTS",
};
f.write_str(string)
}
}
#[derive(Debug, Clone, Copy)]
pub struct GMVersion {
pub major: u32,
pub minor: u32,
pub release: u32,
pub build: u32,
pub branch: LTSBranch,
}
impl GMVersion {
pub const GMS2: Self = Self::new(2, 0, 0, 0, LTSBranch::PreLTS);
pub const INF: Self = Self::new(u32::MAX, u32::MAX, u32::MAX, u32::MAX, LTSBranch::PostLTS);
pub const NULL: Self = Self::new(0, 0, 0, 0, LTSBranch::PreLTS);
#[must_use]
pub const fn new(major: u32, minor: u32, release: u32, build: u32, branch: LTSBranch) -> Self {
Self { major, minor, release, build, branch }
}
pub fn set_version(&mut self, new_version: impl ToGMVersion) {
let new: Self = new_version.into_gm_version();
self.major = new.major;
self.minor = new.minor;
self.release = new.release;
self.build = new.build;
if new.branch > self.branch {
self.branch = new.branch;
}
}
}
impl GMElement for GMVersion {
fn deserialize(reader: &mut DataReader) -> Result<Self> {
let major = reader.read_u32()?;
let minor = reader.read_u32()?;
let release = reader.read_u32()?;
let build = reader.read_u32()?;
Ok(Self::new(major, minor, release, build, LTSBranch::PreLTS))
}
fn serialize(&self, builder: &mut DataBuilder) -> Result<()> {
builder.write_u32(self.major);
builder.write_u32(self.minor);
builder.write_u32(self.release);
builder.write_u32(self.build);
Ok(())
}
}
impl Default for GMVersion {
fn default() -> Self {
Self::GMS2
}
}
impl Display for GMVersion {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let major = self.major;
let minor = self.minor;
let release = self.release;
let build = self.build;
write!(f, "{major}.{minor}")?;
match (release, build) {
(0, 0) => {}
(_, 0) => write!(f, ".{release}")?,
(_, _) => write!(f, ".{build}")?,
}
if major >= 2022 {
write!(f, " ({})", self.branch)?;
}
Ok(())
}
}
impl<T: ToGMVersion> PartialEq<T> for GMVersion {
fn eq(&self, other: &T) -> bool {
let a = self;
let b = other.to_gm_version();
a.major == b.major
&& a.minor == b.minor
&& a.release == b.release
&& a.build == b.build
&& a.branch == b.branch
}
}
impl<T: ToGMVersion> PartialOrd<T> for GMVersion {
fn partial_cmp(&self, other: &T) -> Option<Ordering> {
let other = other.to_gm_version();
compare(self, &other)
}
}
fn compare(a: &GMVersion, b: &GMVersion) -> Option<Ordering> {
match a.branch.partial_cmp(&b.branch) {
Some(Ordering::Equal) | None => {}
ord => return ord,
}
match a.major.partial_cmp(&b.major) {
Some(Ordering::Equal) => {}
ord => return ord,
}
match a.minor.partial_cmp(&b.minor) {
Some(Ordering::Equal) => {}
ord => return ord,
}
match a.release.partial_cmp(&b.release) {
Some(Ordering::Equal) => {}
ord => return ord,
}
a.build.partial_cmp(&b.build)
}
pub trait ToGMVersion: Copy {
#[must_use]
fn to_gm_version(&self) -> GMVersion;
#[must_use]
fn into_gm_version(self) -> GMVersion {
self.to_gm_version()
}
}
impl ToGMVersion for GMVersion {
fn to_gm_version(&self) -> GMVersion {
*self
}
fn into_gm_version(self) -> GMVersion {
self
}
}
impl ToGMVersion for &GMVersion {
fn to_gm_version(&self) -> GMVersion {
**self
}
}
impl ToGMVersion for (u32, u32, u32, u32, LTSBranch) {
fn to_gm_version(&self) -> GMVersion {
GMVersion::new(self.0, self.1, self.2, self.3, self.4)
}
}
impl ToGMVersion for (u32, u32, u32, LTSBranch) {
fn to_gm_version(&self) -> GMVersion {
GMVersion::new(self.0, self.1, self.2, 0, self.3)
}
}
impl ToGMVersion for (u32, u32, LTSBranch) {
fn to_gm_version(&self) -> GMVersion {
GMVersion::new(self.0, self.1, 0, 0, self.2)
}
}
impl ToGMVersion for (u32, u32, u32, u32) {
fn to_gm_version(&self) -> GMVersion {
GMVersion::new(self.0, self.1, self.2, self.3, LTSBranch::PreLTS)
}
}
impl ToGMVersion for (u32, u32, u32) {
fn to_gm_version(&self) -> GMVersion {
GMVersion::new(self.0, self.1, self.2, 0, LTSBranch::PreLTS)
}
}
impl ToGMVersion for (u32, u32) {
fn to_gm_version(&self) -> GMVersion {
GMVersion::new(self.0, self.1, 0, 0, LTSBranch::PreLTS)
}
}
impl ToGMVersion for u32 {
fn to_gm_version(&self) -> GMVersion {
GMVersion::new(*self, 0, 0, 0, LTSBranch::PreLTS)
}
}