use std::default::Default;
use std::cmp::{PartialOrd, Ordering};
use std::fmt::{Display, Formatter, Error};
#[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)
}
}
pub type QrResult<T> = Result<T, QrError>;
#[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) => Ok(table[v as usize - 1][ec_level as usize]),
Version::Micro(v @ 1...4) => {
let obj = table[v as usize + 39][ec_level as usize];
if obj != Default::default() {
Ok(obj)
} else {
Err(QrError::InvalidVersion)
}
}
_ => Err(QrError::InvalidVersion)
}
}
pub fn mode_bits_count(&self) -> usize {
match *self {
Version::Micro(a) => (a - 1) as usize,
_ => 4,
}
}
pub fn is_micro(&self) -> bool {
match *self {
Version::Normal(_) => false,
Version::Micro(_) => true,
}
}
}
#[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 => 8,
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,
}
}
pub fn max(&self, other: Mode) -> Mode {
match self.partial_cmp(&other) {
Some(Ordering::Less) | Some(Ordering::Equal) => other,
Some(Ordering::Greater) => *self,
None => Mode::Byte,
}
}
}
impl PartialOrd for Mode {
fn partial_cmp(&self, other: &Mode) -> Option<Ordering> {
match (*self, *other) {
(Mode::Numeric, Mode::Alphanumeric) => Some(Ordering::Less),
(Mode::Alphanumeric, Mode::Numeric) => Some(Ordering::Greater),
(Mode::Numeric, Mode::Byte) => Some(Ordering::Less),
(Mode::Byte, Mode::Numeric) => Some(Ordering::Greater),
(Mode::Alphanumeric, Mode::Byte) => Some(Ordering::Less),
(Mode::Byte, Mode::Alphanumeric) => Some(Ordering::Greater),
(Mode::Kanji, Mode::Byte) => Some(Ordering::Less),
(Mode::Byte, Mode::Kanji) => Some(Ordering::Greater),
(a, b) if a == b => Some(Ordering::Equal),
_ => None,
}
}
}
#[cfg(test)]
mod mode_tests {
use types::Mode::{Numeric, Alphanumeric, Byte, Kanji};
#[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);
}
}