use core::cmp::Ordering;
use std::fmt::Display;
use std::fmt::Formatter;
use crate::prelude::*;
use crate::wad::build::builder::DataBuilder;
use crate::wad::elem::GMElement;
use crate::wad::parse::reader::DataReader;
#[derive(Debug, Clone, Copy, PartialEq, Hash)]
pub enum LtsBranch {
Pre2022,
Lts2022,
PostLts,
}
impl Display for LtsBranch {
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
let string = match self {
Self::Pre2022 => "Pre LTS",
Self::Lts2022 => "2022 LTS",
Self::PostLts => "Post LTS",
};
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::Pre2022);
pub const INF: Self = Self::new(u32::MAX, u32::MAX, u32::MAX, u32::MAX, LtsBranch::PostLts);
pub const ZERO: Self = Self::new(0, 0, 0, 0, LtsBranch::Pre2022);
#[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 != LtsBranch::Pre2022 {
self.branch = new.branch;
}
if *self >= (2023, 1) && self.branch == LtsBranch::Pre2022 {
self.branch = LtsBranch::Lts2022;
}
}
}
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::Pre2022))
}
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}")?;
if release != 0 || build != 0 {
write!(f, ".{release}")?;
if build != 0 {
write!(f, ".{build}")?;
}
}
if self.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> {
compare(self, &other.to_gm_version())
}
}
fn compare(a: &GMVersion, b: &GMVersion) -> Option<Ordering> {
if b.branch == LtsBranch::PostLts && a.branch != LtsBranch::PostLts {
return Some(Ordering::Less);
}
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::Pre2022)
}
}
impl ToGMVersion for (u32, u32, u32) {
fn to_gm_version(&self) -> GMVersion {
GMVersion::new(self.0, self.1, self.2, 0, LtsBranch::Pre2022)
}
}
impl ToGMVersion for (u32, u32) {
fn to_gm_version(&self) -> GMVersion {
GMVersion::new(self.0, self.1, 0, 0, LtsBranch::Pre2022)
}
}
impl ToGMVersion for u32 {
fn to_gm_version(&self) -> GMVersion {
GMVersion::new(*self, 0, 0, 0, LtsBranch::Pre2022)
}
}