use crate::byte_reader::ByteReader;
use crate::error::Error::InvalidVersion;
use crate::error::Result;
use byteorder::{BigEndian, WriteBytesExt};
use std::fmt;
pub const JAVA_1_0_2: Version = Version::Java1_0_2 { minor: 0 };
pub const JAVA_1_1: Version = Version::Java1_1 { minor: 0 };
pub const JAVA_1_2: Version = Version::Java1_2 { minor: 0 };
pub const JAVA_1_3: Version = Version::Java1_3 { minor: 0 };
pub const JAVA_1_4: Version = Version::Java1_4 { minor: 0 };
pub const JAVA_5: Version = Version::Java5 { minor: 0 };
pub const JAVA_6: Version = Version::Java6 { minor: 0 };
pub const JAVA_7: Version = Version::Java7 { minor: 0 };
pub const JAVA_8: Version = Version::Java8 { minor: 0 };
pub const JAVA_9: Version = Version::Java9 { minor: 0 };
pub const JAVA_10: Version = Version::Java10 { minor: 0 };
pub const JAVA_11: Version = Version::Java11 { minor: 0 };
pub const JAVA_12: Version = Version::Java12 { minor: 0 };
pub const JAVA_13: Version = Version::Java13 { minor: 0 };
pub const JAVA_14: Version = Version::Java14 { minor: 0 };
pub const JAVA_15: Version = Version::Java15 { minor: 0 };
pub const JAVA_16: Version = Version::Java16 { minor: 0 };
pub const JAVA_17: Version = Version::Java17 { minor: 0 };
pub const JAVA_18: Version = Version::Java18 { minor: 0 };
pub const JAVA_19: Version = Version::Java19 { minor: 0 };
pub const JAVA_20: Version = Version::Java20 { minor: 0 };
pub const JAVA_21: Version = Version::Java21 { minor: 0 };
pub const JAVA_22: Version = Version::Java22 { minor: 0 };
pub const JAVA_23: Version = Version::Java23 { minor: 0 };
pub const JAVA_24: Version = Version::Java24 { minor: 0 };
pub const JAVA_25: Version = Version::Java25 { minor: 0 };
pub const JAVA_PREVIEW_MINOR_VERSION: u16 = 65535;
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd)]
pub enum Version {
Java1_0_2 { minor: u16 },
Java1_1 { minor: u16 },
Java1_2 { minor: u16 },
Java1_3 { minor: u16 },
Java1_4 { minor: u16 },
Java5 { minor: u16 },
Java6 { minor: u16 },
Java7 { minor: u16 },
Java8 { minor: u16 },
Java9 { minor: u16 },
Java10 { minor: u16 },
Java11 { minor: u16 },
Java12 { minor: u16 },
Java13 { minor: u16 },
Java14 { minor: u16 },
Java15 { minor: u16 },
Java16 { minor: u16 },
Java17 { minor: u16 },
Java18 { minor: u16 },
Java19 { minor: u16 },
Java20 { minor: u16 },
Java21 { minor: u16 },
Java22 { minor: u16 },
Java23 { minor: u16 },
Java24 { minor: u16 },
Java25 { minor: u16 },
}
impl Version {
pub fn from(major: u16, minor: u16) -> Result<Self> {
if major >= 56 && minor != 0 && minor != JAVA_PREVIEW_MINOR_VERSION {
return Err(InvalidVersion { major, minor });
}
let version = match major {
45 => Version::Java1_0_2 { minor },
46 => Version::Java1_2 { minor },
47 => Version::Java1_3 { minor },
48 => Version::Java1_4 { minor },
49 => Version::Java5 { minor },
50 => Version::Java6 { minor },
51 => Version::Java7 { minor },
52 => Version::Java8 { minor },
53 => Version::Java9 { minor },
54 => Version::Java10 { minor },
55 => Version::Java11 { minor },
56 => Version::Java12 { minor },
57 => Version::Java13 { minor },
58 => Version::Java14 { minor },
59 => Version::Java15 { minor },
60 => Version::Java16 { minor },
61 => Version::Java17 { minor },
62 => Version::Java18 { minor },
63 => Version::Java19 { minor },
64 => Version::Java20 { minor },
65 => Version::Java21 { minor },
66 => Version::Java22 { minor },
67 => Version::Java23 { minor },
68 => Version::Java24 { minor },
69 => Version::Java25 { minor },
_ => return Err(InvalidVersion { major, minor }),
};
Ok(version)
}
#[must_use]
pub fn major(&self) -> u16 {
match self {
Version::Java1_0_2 { .. } | Version::Java1_1 { .. } => 45,
Version::Java1_2 { .. } => 46,
Version::Java1_3 { .. } => 47,
Version::Java1_4 { .. } => 48,
Version::Java5 { .. } => 49,
Version::Java6 { .. } => 50,
Version::Java7 { .. } => 51,
Version::Java8 { .. } => 52,
Version::Java9 { .. } => 53,
Version::Java10 { .. } => 54,
Version::Java11 { .. } => 55,
Version::Java12 { .. } => 56,
Version::Java13 { .. } => 57,
Version::Java14 { .. } => 58,
Version::Java15 { .. } => 59,
Version::Java16 { .. } => 60,
Version::Java17 { .. } => 61,
Version::Java18 { .. } => 62,
Version::Java19 { .. } => 63,
Version::Java20 { .. } => 64,
Version::Java21 { .. } => 65,
Version::Java22 { .. } => 66,
Version::Java23 { .. } => 67,
Version::Java24 { .. } => 68,
Version::Java25 { .. } => 69,
}
}
#[must_use]
pub fn minor(&self) -> u16 {
match self {
Version::Java1_0_2 { minor, .. }
| Version::Java1_1 { minor, .. }
| Version::Java1_2 { minor, .. }
| Version::Java1_3 { minor, .. }
| Version::Java1_4 { minor, .. }
| Version::Java5 { minor, .. }
| Version::Java6 { minor, .. }
| Version::Java7 { minor, .. }
| Version::Java8 { minor, .. }
| Version::Java9 { minor, .. }
| Version::Java10 { minor, .. }
| Version::Java11 { minor, .. }
| Version::Java12 { minor, .. }
| Version::Java13 { minor, .. }
| Version::Java14 { minor, .. }
| Version::Java15 { minor, .. }
| Version::Java16 { minor, .. }
| Version::Java17 { minor, .. }
| Version::Java18 { minor, .. }
| Version::Java19 { minor, .. }
| Version::Java20 { minor, .. }
| Version::Java21 { minor, .. }
| Version::Java22 { minor, .. }
| Version::Java23 { minor, .. }
| Version::Java24 { minor, .. }
| Version::Java25 { minor, .. } => *minor,
}
}
#[must_use]
pub fn java(&self) -> u16 {
self.major() - 44
}
#[must_use]
pub fn supports(&self, version: &Version) -> bool {
self.major() >= version.major()
}
#[must_use]
pub fn is_preview(&self) -> bool {
self.major() >= 56 && self.minor() == JAVA_PREVIEW_MINOR_VERSION
}
#[inline]
pub fn from_bytes(bytes: &mut ByteReader<'_>) -> Result<Version> {
let minor = bytes.read_u16()?;
let major = bytes.read_u16()?;
Version::from(major, minor)
}
pub fn to_bytes(&self, bytes: &mut Vec<u8>) -> Result<()> {
bytes.write_u16::<BigEndian>(self.minor())?;
bytes.write_u16::<BigEndian>(self.major())?;
Ok(())
}
}
impl Default for Version {
fn default() -> Self {
Version::Java1_0_2 { minor: 0 }
}
}
impl fmt::Display for Version {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Version::Java1_0_2 { .. } => write!(f, "Java 1.0.2"),
Version::Java1_1 { .. } => write!(f, "Java 1.1"),
Version::Java1_2 { .. } => write!(f, "Java 1.2"),
Version::Java1_3 { .. } => write!(f, "Java 1.3"),
Version::Java1_4 { .. } => write!(f, "Java 1.4"),
Version::Java5 { .. } => write!(f, "Java 5"),
Version::Java6 { .. } => write!(f, "Java 6"),
Version::Java7 { .. } => write!(f, "Java 7"),
Version::Java8 { .. } => write!(f, "Java 8"),
Version::Java9 { .. } => write!(f, "Java 9"),
Version::Java10 { .. } => write!(f, "Java 10"),
Version::Java11 { .. } => write!(f, "Java 11"),
Version::Java12 { .. } => write!(f, "Java 12"),
Version::Java13 { .. } => write!(f, "Java 13"),
Version::Java14 { .. } => write!(f, "Java 14"),
Version::Java15 { .. } => write!(f, "Java 15"),
Version::Java16 { .. } => write!(f, "Java 16"),
Version::Java17 { .. } => write!(f, "Java 17"),
Version::Java18 { .. } => write!(f, "Java 18"),
Version::Java19 { .. } => write!(f, "Java 19"),
Version::Java20 { .. } => write!(f, "Java 20"),
Version::Java21 { .. } => write!(f, "Java 21"),
Version::Java22 { .. } => write!(f, "Java 22"),
Version::Java23 { .. } => write!(f, "Java 23"),
Version::Java24 { .. } => write!(f, "Java 24"),
Version::Java25 { .. } => write!(f, "Java 25"),
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum VersionSpecification {
Any,
Equal(Version),
NotEqual(Version),
LessThan(Version),
LessThanOrEqual(Version),
GreaterThan(Version),
GreaterThanOrEqual(Version),
Between(Version, Version),
In(&'static [Version]),
}
impl VersionSpecification {
#[must_use]
pub fn matches(&self, version: &Version) -> bool {
let major = version.major();
match self {
VersionSpecification::Any => true,
VersionSpecification::Equal(specification) => major == specification.major(),
VersionSpecification::NotEqual(specification) => major != specification.major(),
VersionSpecification::LessThan(specification) => major < specification.major(),
VersionSpecification::LessThanOrEqual(specification) => major <= specification.major(),
VersionSpecification::GreaterThan(specification) => major > specification.major(),
VersionSpecification::GreaterThanOrEqual(specification) => {
major >= specification.major()
}
VersionSpecification::Between(start_version, end_version) => {
major >= start_version.major() && major <= end_version.major()
}
VersionSpecification::In(versions) => versions.iter().any(|v| v.major() == major),
}
}
}
#[cfg(test)]
mod test {
use super::*;
const MIN_MAJOR: u16 = 45;
const MAX_MAJOR: u16 = 69;
#[test]
fn all_known_versions() -> Result<()> {
let versions = [
JAVA_1_0_2, JAVA_1_1, JAVA_1_2, JAVA_1_3, JAVA_1_4, JAVA_5, JAVA_6, JAVA_7, JAVA_8,
JAVA_9, JAVA_10, JAVA_11, JAVA_12, JAVA_13, JAVA_14, JAVA_15, JAVA_16, JAVA_17,
JAVA_18, JAVA_19, JAVA_20, JAVA_21, JAVA_22, JAVA_23, JAVA_24, JAVA_25,
];
for (index, version) in versions.iter().enumerate() {
let mut index = u16::try_from(index)?;
let major = version.major();
if major == MIN_MAJOR {
index = 0;
} else {
index -= 1;
}
assert!(version.to_string().starts_with("Java "));
assert_eq!(major, MIN_MAJOR + index);
assert_eq!(version.minor(), 0);
assert_eq!(version.java(), version.major() - 44);
}
Ok(())
}
#[test]
fn test_from() -> Result<()> {
for major in MIN_MAJOR..=MAX_MAJOR {
let version = Version::from(major, 0)?;
assert_eq!(version.major(), major);
let version = Version::from(major, JAVA_PREVIEW_MINOR_VERSION)?;
assert_eq!(version.major(), major);
}
Ok(())
}
#[test]
fn test_from_invalid_version() {
assert_eq!(
Err(InvalidVersion {
major: MIN_MAJOR - 1,
minor: 0
}),
Version::from(MIN_MAJOR - 1, 0)
);
assert_eq!(
Err(InvalidVersion {
major: 56,
minor: 42
}),
Version::from(56, 42)
);
}
#[test]
fn test_major() {
assert_eq!(JAVA_21.major(), 65);
}
#[test]
fn test_minor() {
let minor = 3;
let version = Version::Java11 { minor };
assert_eq!(version.minor(), minor);
}
#[test]
fn test_supports() {
assert!(JAVA_11.supports(&JAVA_5));
assert!(!JAVA_5.supports(&JAVA_11));
}
#[test]
fn test_is_preview() {
assert!(!JAVA_11.is_preview());
assert!(
Version::Java21 {
minor: JAVA_PREVIEW_MINOR_VERSION
}
.is_preview()
);
}
#[test]
fn test_default() {
let version = Version::default();
assert_eq!(version, JAVA_1_0_2);
}
#[test]
fn test_serialization() -> Result<()> {
let version = Version::Java21 {
minor: JAVA_PREVIEW_MINOR_VERSION,
};
let expected_value: u32 = 4_294_901_825;
let mut bytes = Vec::new();
version.clone().to_bytes(&mut bytes)?;
let mut reader = ByteReader::new(&bytes);
assert_eq!(expected_value, reader.read_u32()?);
let data = expected_value.to_be_bytes().to_vec();
let mut bytes = ByteReader::new(&data);
assert_eq!(Ok(version), Version::from_bytes(&mut bytes));
Ok(())
}
#[test]
fn test_version_specification() {
assert!(VersionSpecification::Any.matches(&JAVA_11));
assert!(VersionSpecification::Equal(JAVA_11.clone()).matches(&JAVA_11));
assert!(!VersionSpecification::Equal(JAVA_17.clone()).matches(&JAVA_11));
assert!(!VersionSpecification::NotEqual(JAVA_11.clone()).matches(&JAVA_11));
assert!(VersionSpecification::NotEqual(JAVA_17.clone()).matches(&JAVA_11));
assert!(VersionSpecification::LessThan(JAVA_17.clone()).matches(&JAVA_11));
assert!(!VersionSpecification::LessThan(JAVA_11.clone()).matches(&JAVA_11));
assert!(VersionSpecification::LessThanOrEqual(JAVA_11.clone()).matches(&JAVA_11));
assert!(VersionSpecification::LessThanOrEqual(JAVA_17.clone()).matches(&JAVA_11));
assert!(!VersionSpecification::LessThanOrEqual(JAVA_17.clone()).matches(&JAVA_21));
assert!(VersionSpecification::GreaterThan(JAVA_11.clone()).matches(&JAVA_17));
assert!(!VersionSpecification::GreaterThan(JAVA_17.clone()).matches(&JAVA_17));
assert!(VersionSpecification::GreaterThanOrEqual(JAVA_11.clone()).matches(&JAVA_11));
assert!(VersionSpecification::GreaterThanOrEqual(JAVA_17.clone()).matches(&JAVA_21));
assert!(!VersionSpecification::GreaterThanOrEqual(JAVA_21.clone()).matches(&JAVA_17));
let between = VersionSpecification::Between(JAVA_11, JAVA_17);
assert!(between.matches(&JAVA_11));
assert!(between.matches(&JAVA_17));
assert!(!between.matches(&JAVA_21));
let versions_in = VersionSpecification::In(&[JAVA_11, JAVA_21]);
assert!(versions_in.matches(&JAVA_11));
assert!(versions_in.matches(&JAVA_21));
assert!(!versions_in.matches(&JAVA_17));
}
}