use std::cmp::Ordering;
use crate::consts::QR_DATA_CAPACITY;
#[derive(Debug, thiserror::Error, PartialEq, Eq)]
pub enum VersionError {
#[error("Invalid version number, must be between 1 and 40, got {0}")]
InvalidVersionNumber(usize),
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum Version {
V01 = 0,
V02 = 1,
V03 = 2,
V04 = 3,
V05 = 4,
V06 = 5,
V07 = 6,
V08 = 7,
V09 = 8,
V10 = 9,
V11 = 10,
V12 = 11,
V13 = 12,
V14 = 13,
V15 = 14,
V16 = 15,
V17 = 16,
V18 = 17,
V19 = 18,
V20 = 19,
V21 = 20,
V22 = 21,
V23 = 22,
V24 = 23,
V25 = 24,
V26 = 25,
V27 = 26,
V28 = 27,
V29 = 28,
V30 = 29,
V31 = 30,
V32 = 31,
V33 = 32,
V34 = 33,
V35 = 34,
V36 = 35,
V37 = 36,
V38 = 37,
V39 = 38,
V40 = 39,
}
#[derive(Copy, Clone, Debug)]
#[allow(dead_code)]
pub enum ErrorCorrectionLevel {
Low,
Medium,
Quartile,
High,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[allow(dead_code)]
enum CapacityTableEncoding {
TotalBits = 0,
Numeric = 1,
Alphanumeric = 2,
Byte = 3,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct QrsNeeded {
pub version: Version,
pub count: usize,
pub data_per_qr: usize,
}
impl Ord for QrsNeeded {
fn cmp(&self, other: &Self) -> Ordering {
match self.count.cmp(&other.count) {
Ordering::Equal => {
let v1 = self.version as usize;
let v2 = other.version as usize;
v1.cmp(&v2)
}
other => other,
}
}
}
impl PartialOrd for QrsNeeded {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Version {
pub fn data_capacity(&self) -> usize {
let error_correction_level = ErrorCorrectionLevel::Low;
let encoding = CapacityTableEncoding::Alphanumeric;
QR_DATA_CAPACITY[*self as usize][error_correction_level as usize][encoding as usize]
as usize
}
pub(crate) fn from_index(version_index: usize) -> Self {
Self::from_number(version_index + 1)
}
pub(crate) fn from_number(version_number: usize) -> Self {
match version_number {
1 => Version::V01,
2 => Version::V02,
3 => Version::V03,
4 => Version::V04,
5 => Version::V05,
6 => Version::V06,
7 => Version::V07,
8 => Version::V08,
9 => Version::V09,
10 => Version::V10,
11 => Version::V11,
12 => Version::V12,
13 => Version::V13,
14 => Version::V14,
15 => Version::V15,
16 => Version::V16,
17 => Version::V17,
18 => Version::V18,
19 => Version::V19,
20 => Version::V20,
21 => Version::V21,
22 => Version::V22,
23 => Version::V23,
24 => Version::V24,
25 => Version::V25,
26 => Version::V26,
27 => Version::V27,
28 => Version::V28,
29 => Version::V29,
30 => Version::V30,
31 => Version::V31,
32 => Version::V32,
33 => Version::V33,
34 => Version::V34,
35 => Version::V35,
36 => Version::V36,
37 => Version::V37,
38 => Version::V38,
39 => Version::V39,
40 => Version::V40,
_ => panic!("Invalid version number"),
}
}
}
impl TryFrom<usize> for Version {
type Error = VersionError;
fn try_from(version_number: usize) -> Result<Self, Self::Error> {
try_from_number(version_number)
}
}
impl TryFrom<u8> for Version {
type Error = VersionError;
fn try_from(version_number: u8) -> Result<Self, Self::Error> {
try_from_number(version_number as usize)
}
}
impl TryFrom<u16> for Version {
type Error = VersionError;
fn try_from(version_number: u16) -> Result<Self, Self::Error> {
try_from_number(version_number as usize)
}
}
impl TryFrom<u32> for Version {
type Error = VersionError;
fn try_from(version_number: u32) -> Result<Self, Self::Error> {
try_from_number(version_number as usize)
}
}
impl TryFrom<u64> for Version {
type Error = VersionError;
fn try_from(version_number: u64) -> Result<Self, Self::Error> {
try_from_number(version_number as usize)
}
}
impl TryFrom<i8> for Version {
type Error = VersionError;
fn try_from(version_number: i8) -> Result<Self, Self::Error> {
try_from_number(version_number as usize)
}
}
impl TryFrom<i16> for Version {
type Error = VersionError;
fn try_from(version_number: i16) -> Result<Self, Self::Error> {
try_from_number(version_number as usize)
}
}
impl TryFrom<i32> for Version {
type Error = VersionError;
fn try_from(version_number: i32) -> Result<Self, Self::Error> {
try_from_number(version_number as usize)
}
}
impl TryFrom<i64> for Version {
type Error = VersionError;
fn try_from(version_number: i64) -> Result<Self, Self::Error> {
try_from_number(version_number as usize)
}
}
impl TryFrom<isize> for Version {
type Error = VersionError;
fn try_from(version_number: isize) -> Result<Self, Self::Error> {
try_from_number(version_number as usize)
}
}
fn try_from_number(version_number: usize) -> Result<Version, VersionError> {
if (1..=40).contains(&version_number) {
return Ok(Version::from_number(version_number));
}
Err(VersionError::InvalidVersionNumber(version_number))
}
#[cfg(feature = "qr-codes")]
impl From<Version> for fast_qr::Version {
fn from(version: Version) -> Self {
use fast_qr::Version as FastQrVersion;
match version {
Version::V01 => FastQrVersion::V01,
Version::V02 => FastQrVersion::V02,
Version::V03 => FastQrVersion::V03,
Version::V04 => FastQrVersion::V04,
Version::V05 => FastQrVersion::V05,
Version::V06 => FastQrVersion::V06,
Version::V07 => FastQrVersion::V07,
Version::V08 => FastQrVersion::V08,
Version::V09 => FastQrVersion::V09,
Version::V10 => FastQrVersion::V10,
Version::V11 => FastQrVersion::V11,
Version::V12 => FastQrVersion::V12,
Version::V13 => FastQrVersion::V13,
Version::V14 => FastQrVersion::V14,
Version::V15 => FastQrVersion::V15,
Version::V16 => FastQrVersion::V16,
Version::V17 => FastQrVersion::V17,
Version::V18 => FastQrVersion::V18,
Version::V19 => FastQrVersion::V19,
Version::V20 => FastQrVersion::V20,
Version::V21 => FastQrVersion::V21,
Version::V22 => FastQrVersion::V22,
Version::V23 => FastQrVersion::V23,
Version::V24 => FastQrVersion::V24,
Version::V25 => FastQrVersion::V25,
Version::V26 => FastQrVersion::V26,
Version::V27 => FastQrVersion::V27,
Version::V28 => FastQrVersion::V28,
Version::V29 => FastQrVersion::V29,
Version::V30 => FastQrVersion::V30,
Version::V31 => FastQrVersion::V31,
Version::V32 => FastQrVersion::V32,
Version::V33 => FastQrVersion::V33,
Version::V34 => FastQrVersion::V34,
Version::V35 => FastQrVersion::V35,
Version::V36 => FastQrVersion::V36,
Version::V37 => FastQrVersion::V37,
Version::V38 => FastQrVersion::V38,
Version::V39 => FastQrVersion::V39,
Version::V40 => FastQrVersion::V40,
}
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_correct_capacity() {
let expected = 4296;
let actual = Version::V40.data_capacity();
assert_eq!(expected, actual);
let expected = 25;
let actual = Version::V01.data_capacity();
assert_eq!(expected, actual);
let expected = 1990;
let actual = Version::V26.data_capacity();
assert_eq!(expected, actual);
}
#[test]
fn version_test() {
let actual = Version::from_number(1);
matches!(actual, Version::V01);
let actual = Version::from_number(40);
matches!(actual, Version::V40);
assert_eq!(Version::V01 as usize, 0);
}
}