use crate::cast::As;
use std::cmp::{Ordering, PartialOrd};
use std::default::Default;
use std::fmt::{Display, Error, Formatter};
use std::ops::Not;
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub enum QrError {
DataTooLong,
InvalidVersion,
UnsupportedCharacterSet,
InvalidEciDesignator,
InvalidCharacter,
}
impl Display for QrError {
fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
let msg = match *self {
QrError::DataTooLong => "data too long",
QrError::InvalidVersion => "invalid version",
QrError::UnsupportedCharacterSet => "unsupported character set",
QrError::InvalidEciDesignator => "invalid ECI designator",
QrError::InvalidCharacter => "invalid character",
};
fmt.write_str(msg)
}
}
impl ::std::error::Error for QrError {}
pub type QrResult<T> = Result<T, QrError>;
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub enum Color {
Light,
Dark,
}
impl Color {
pub fn select<T>(self, dark: T, light: T) -> T {
match self {
Color::Light => light,
Color::Dark => dark,
}
}
}
impl Not for Color {
type Output = Self;
fn not(self) -> Self {
match self {
Color::Light => Color::Dark,
Color::Dark => Color::Light,
}
}
}
#[derive(Debug, PartialEq, Eq, Copy, Clone, PartialOrd, Ord)]
pub enum EcLevel {
L = 0,
M = 1,
Q = 2,
H = 3,
}
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub enum Version {
Normal(i16),
Micro(i16),
}
impl Version {
pub fn width(self) -> i16 {
match self {
Version::Normal(v) => v * 4 + 17,
Version::Micro(v) => v * 2 + 9,
}
}
pub fn fetch<T>(self, ec_level: EcLevel, table: &[[T; 4]]) -> QrResult<T>
where
T: PartialEq + Default + Copy,
{
match self {
Version::Normal(v @ 1..=40) => {
return Ok(table[(v - 1).as_usize()][ec_level as usize]);
}
Version::Micro(v @ 1..=4) => {
let obj = table[(v + 39).as_usize()][ec_level as usize];
if obj != T::default() {
return Ok(obj);
}
}
_ => {}
}
Err(QrError::InvalidVersion)
}
pub fn mode_bits_count(self) -> usize {
if let Version::Micro(a) = self {
(a - 1).as_usize()
} else {
4
}
}
pub fn is_micro(self) -> bool {
matches!(self, Version::Micro(_))
}
}
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub enum Mode {
Numeric,
Alphanumeric,
Byte,
Kanji,
}
impl Mode {
pub fn length_bits_count(self, version: Version) -> usize {
match version {
Version::Micro(a) => {
let a = a.as_usize();
match self {
Mode::Numeric => 2 + a,
Mode::Alphanumeric | Mode::Byte => 1 + a,
Mode::Kanji => a,
}
}
Version::Normal(1..=9) => match self {
Mode::Numeric => 10,
Mode::Alphanumeric => 9,
Mode::Byte | Mode::Kanji => 8,
},
Version::Normal(10..=26) => match self {
Mode::Numeric => 12,
Mode::Alphanumeric => 11,
Mode::Byte => 16,
Mode::Kanji => 10,
},
Version::Normal(_) => match self {
Mode::Numeric => 14,
Mode::Alphanumeric => 13,
Mode::Byte => 16,
Mode::Kanji => 12,
},
}
}
pub fn data_bits_count(self, raw_data_len: usize) -> usize {
match self {
Mode::Numeric => (raw_data_len * 10 + 2) / 3,
Mode::Alphanumeric => (raw_data_len * 11 + 1) / 2,
Mode::Byte => raw_data_len * 8,
Mode::Kanji => raw_data_len * 13,
}
}
#[must_use]
pub fn max(self, other: Self) -> Self {
match self.partial_cmp(&other) {
Some(Ordering::Greater) => self,
Some(_) => other,
None => Mode::Byte,
}
}
}
impl PartialOrd for Mode {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
match (*self, *other) {
(a, b) if a == b => Some(Ordering::Equal),
(Mode::Numeric, Mode::Alphanumeric) | (_, Mode::Byte) => Some(Ordering::Less),
(Mode::Alphanumeric, Mode::Numeric) | (Mode::Byte, _) => Some(Ordering::Greater),
_ => None,
}
}
}
#[cfg(test)]
mod mode_tests {
use crate::types::Mode::{Alphanumeric, Byte, Kanji, Numeric};
#[test]
fn test_mode_order() {
assert!(Numeric < Alphanumeric);
assert!(Byte > Kanji);
assert!(!(Numeric < Kanji));
assert!(!(Numeric >= Kanji));
}
#[test]
fn test_max() {
assert_eq!(Byte.max(Kanji), Byte);
assert_eq!(Numeric.max(Alphanumeric), Alphanumeric);
assert_eq!(Alphanumeric.max(Alphanumeric), Alphanumeric);
assert_eq!(Numeric.max(Kanji), Byte);
assert_eq!(Kanji.max(Numeric), Byte);
assert_eq!(Alphanumeric.max(Numeric), Alphanumeric);
assert_eq!(Kanji.max(Kanji), Kanji);
}
}