Skip to content

Instantly share code, notes, and snippets.

@karronoli
Last active January 4, 2021 04:00
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save karronoli/8046f7b6cdc6e357c394086d777db66f to your computer and use it in GitHub Desktop.
Save karronoli/8046f7b6cdc6e357c394086d777db66f to your computer and use it in GitHub Desktop.
Read, Eval, Print Barcode
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
name = "aho-corasick"
version = "0.7.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5"
dependencies = [
"memchr",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "memchr"
version = "2.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525"
[[package]]
name = "read-eval-print-barcode"
version = "0.1.0"
dependencies = [
"lazy_static",
"regex",
]
[[package]]
name = "regex"
version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38cf2c13ed4745de91a5eb834e11c00bcc3709e773173b2ce4c56c9fbde04b9c"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
"thread_local",
]
[[package]]
name = "regex-syntax"
version = "0.6.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b181ba2dcf07aaccad5448e8ead58db5b742cf85dfe035e2227f137a539a189"
[[package]]
name = "thread_local"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14"
dependencies = [
"lazy_static",
]
[package]
name = "read-eval-print-barcode"
version = "0.1.0"
authors = ["karronoli <karo+git@karonori.com>"]
edition = "2018"
[dependencies]
regex = "1"
lazy_static = "1.4.0"
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "(lldb) Launch",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/target/debug/read-eval-print-barcode",
"args": [],
"stopAtEntry": false,
"cwd": "${workspaceFolder}",
"environment": [],
"externalConsole": false,
"MIMode": "lldb"
}
]
}
#[macro_use]
extern crate lazy_static;
extern crate regex;
use regex::Regex;
use std::collections::HashMap;
use std::fmt;
use std::io::{self, Read};
#[derive(PartialEq, Debug)]
enum BarcodeType {
NW7,
CODE39,
}
impl fmt::Display for BarcodeType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
BarcodeType::NW7 => write!(f, "NW7"),
BarcodeType::CODE39 => write!(f, "CODE39"),
}
}
}
const ESC: char = '\x1d';
const LF: char = '\x0a';
const NULL: char = '\x00';
const NW7_DIGIT: usize = 9;
const NW7_START_A_WITH_QUIET: &str = "01011001001";
const NW7_END_A_WITH_GAP_AND_QUIET: &str = "010110010010";
const CODE39_DIGIT: usize = 15;
const CODE39_START_ASTERISK_WITH_QUIET: &str = "0100010111011101";
const CODE39_END_ASTERISK_WITH_GAP_AND_QUIET: &str = "01000101110111010";
lazy_static! {
static ref NW7_SYMBOL_TABLE: HashMap<&'static str, char> = {
[("101011001", '1'), ("101001011", '2')]
.iter()
.cloned()
.collect()
};
static ref CODE39_SYMBOL_TABLE: HashMap<&'static str, char> = {
[("111010001010111", '1'), ("101110001010111", '2')]
.iter()
.cloned()
.collect()
};
}
fn extract(input: &str) -> (BarcodeType, String) {
lazy_static! {
static ref UNDERSCORE: Regex = Regex::new(r"_").unwrap();
static ref NW7_PATTERN: String = format!(
r"(?x)
{}
(.+) # body
{}",
NW7_START_A_WITH_QUIET, NW7_END_A_WITH_GAP_AND_QUIET
);
static ref CODE39_PATTERN: String = format!(
r"(?x)
{}
(.+) # body
{}",
CODE39_START_ASTERISK_WITH_QUIET, CODE39_END_ASTERISK_WITH_GAP_AND_QUIET
);
static ref NW7: Regex = Regex::new(&NW7_PATTERN).unwrap();
static ref CODE39: Regex = Regex::new(&CODE39_PATTERN).unwrap();
}
let digit = UNDERSCORE.replace_all(input, "");
if NW7.is_match(&digit) {
let caps = NW7.captures(&digit).unwrap();
return (BarcodeType::NW7, (&caps[1]).to_string());
} else if CODE39.is_match(&digit) {
let caps = CODE39.captures(&digit).unwrap();
return (BarcodeType::CODE39, (&caps[1]).to_string());
} else {
panic!("unknown input.");
}
}
fn parse(barcode_type: &BarcodeType, body: &str) -> Vec<char> {
let mut result: Vec<char> = vec![];
let mut index = 0;
match barcode_type {
BarcodeType::NW7 => loop {
if index >= body.len() {
break;
}
let gap = &body[index..index + 1];
if gap == "1" {
panic!("no gap. i:{}", index);
}
let character = &body[index + 1..index + 1 + NW7_DIGIT];
if !NW7_SYMBOL_TABLE.contains_key(character) {
panic!("no symbol. character:{}", character);
}
let value = NW7_SYMBOL_TABLE[character];
result.push(value);
index += NW7_DIGIT + 1;
},
BarcodeType::CODE39 => loop {
if index >= body.len() {
break;
}
let gap = &body[index..index + 1];
if gap == "1" {
panic!("no gap. i:{}", index);
}
let character = &body[index + 1..index + 1 + CODE39_DIGIT];
if !CODE39_SYMBOL_TABLE.contains_key(character) {
panic!("no symbol. character:{}", character);
}
let value = CODE39_SYMBOL_TABLE[character];
result.push(value);
index += CODE39_DIGIT + 1;
},
};
return result;
}
fn main() -> io::Result<()> {
let mut buffer = String::new();
io::stdin().read_to_string(&mut buffer)?;
let (barcode_type, body) = extract(&buffer);
let result = parse(&barcode_type, &body);
print!("type: {}{LF}", barcode_type, LF = LF);
let value: String = result.into_iter().collect();
print!("value: {}{LF}", value, LF = LF);
print!(
"{ESC}w\x03{ESC}h\x32{ESC}f\x00{ESC}H\x00{ESC}{}{NULL}{LF}",
match barcode_type {
BarcodeType::NW7 => format!("k\x06A{}A", value),
BarcodeType::CODE39 => format!("k\x04{}", value),
},
ESC = ESC,
NULL = NULL,
LF = LF
);
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn nw7_valid() {
let input = "0_1011001001_0_101011001_0_101001011_0_10110010010";
let (barcode_type, body) = extract(input);
assert_eq!(&barcode_type, &BarcodeType::NW7);
let result = parse(&barcode_type, &body);
assert_eq!(&result, &['1', '2']);
}
#[test]
fn code39_valid() {
let input = "0_100010111011101_0_111010001010111_0_101110001010111_0_100010111011101_0";
let (barcode_type, body) = extract(input);
assert_eq!(&barcode_type, &BarcodeType::CODE39);
let result = parse(&barcode_type, &body);
assert_eq!(&result, &['1', '2']);
}
}
use strict;
use warnings;
use utf8;
use Test::More;
# 0 を 白線,1 を黒線とする
use constant {
ESC => "\x{1d}",
TYPE_NW7 => 'NW7',
TYPE_CODE39 => 'CODE39',
NW7_DIGIT => 9,
NW7_START_A_WITH_QUIET => '01011001001', # start A
NW7_END_A_WITH_GAP_AND_QUIET => '010110010010', # end A
CODE39_DIGIT => 15,
CODE39_START_ASTERISK_WITH_QUIET => '0100010111011101', # start *
CODE39_END_ASTERISK_WITH_GAP_AND_QUIET => '01000101110111010', # end *
};
use constant {
# NW7 のスタート・ストップキャラクタは A のみ対応
NW7_PATTERN => qr/^
@{[NW7_START_A_WITH_QUIET]}
(.+) # body
@{[NW7_END_A_WITH_GAP_AND_QUIET]}
$/x,
# CODE39 のスタート・ストップキャラクタは * のみ対応
CODE39_PATTERN => qr/^
@{[CODE39_START_ASTERISK_WITH_QUIET]}
(.+) # body
@{[CODE39_END_ASTERISK_WITH_GAP_AND_QUIET]}
$/x
};
my %NW7_SYMBOL_TABLE = (
'101011001' => '1',
'101001011' => '2',
);
my %CODE39_SYMBOL_TABLE = (
'111010001010111' => '1',
'101110001010111' => '2',
);
sub extract {
my ($digit) = @_;
# _ は桁の区切りを見易くするためだけに許容する
die q(must contain '0' or '1' or '_' only) if $digit =~ /[^01_]/;
$digit =~ s/_//g;
my $body;
if (($body) = $digit =~ NW7_PATTERN) {
return (TYPE_NW7, $body);
} elsif (($body) = $digit =~ CODE39_PATTERN) {
return (TYPE_CODE39, $body);
} else {
die 'unknown input.';
}
}
sub parse {
my ($type, $body) = @_;
my @result;
if ($type eq TYPE_NW7) {
# キャラクタ間ギャップは細エレメントと同一幅のみ受け付ける
for (my $i = 0; $i < length($body); $i += NW7_DIGIT + 1) {
my $gap = substr($body, $i, 1);
my $character = substr($body, $i + 1, NW7_DIGIT);
die 'no gap. i:' . $i if $gap eq '1';
die 'no symbol. character:' . $character unless $NW7_SYMBOL_TABLE{$character};
push @result, $NW7_SYMBOL_TABLE{$character};
}
} elsif ($type eq TYPE_CODE39) {
# キャラクタ間ギャップは細エレメントと同一幅のみ受け付ける
for (my $i = 0; $i < length($body); $i += CODE39_DIGIT + 1) {
my $gap = substr($body, $i, 1);
my $character = substr($body, $i + 1, CODE39_DIGIT);
die 'no gap. i:' . $i if $gap eq '1';
die 'no symbol. character:' . $character unless $CODE39_SYMBOL_TABLE{$character};
push @result, $CODE39_SYMBOL_TABLE{$character};
}
} else {
die "unknown type. type:$type";
}
return @result;
}
if (-t STDIN) {
subtest 'NW7 valid' => sub {
plan tests => 2;
my $input = '0_1011001001_0_101011001_0_101001011_0_10110010010';
my ($type, $body) = extract($input);
is(TYPE_NW7, $type);
my @result = parse($type, $body);
is_deeply([1, 2], \@result);
};
subtest 'CODE39 valid' => sub {
plan tests => 2;
my $input = '0_100010111011101_0_111010001010111_0_101110001010111_0_100010111011101_0';
my ($type, $body) = extract($input);
is(TYPE_CODE39, $type);
my @result = parse($type, $body);
is_deeply([1, 2], \@result);
};
done_testing;
} else {
chomp(my $line = readline(STDIN));
my ($type, $body) = extract($line);
my @result = parse($type, $body);
print "type: $type\n";
my $value = join '', @result;
print "value: $value\n";
# ESC/POS で印刷する
# https://reference.epson-biz.com/modules/ref_escpos_ja/index.php?content_id=128
if ($type eq TYPE_NW7) {
print ESC . "w\x{03}"
. ESC . "h\x{32}"
. ESC . "f\x{00}"
. ESC . "H\x{00}"
. ESC . "k\x{06}" . "A${value}A\x{00}"
. "\x{0a}";
} elsif ($type eq TYPE_CODE39) {
print ESC . "w\x{03}"
. ESC . "h\x{32}"
. ESC . "f\x{00}"
. ESC . "H\x{00}"
. ESC . "k\x{04}" . "${value}\x{00}"
. "\x{0a}";
} else {
die 'unknown barcode type.'
}
}
@karronoli
Copy link
Author

Rust

$ cargo test
    Finished test [unoptimized + debuginfo] target(s) in 0.01s
     Running target/debug/deps/read_eval_print_barcode-0652ec25b4f3ec4f

running 2 tests
test tests::nw7_valid ... ok
test tests::code39_valid ... ok

test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

$

Perl

$ prove read-eval-print-barcode.pl
read-eval-print-barcode.pl .. ok
All tests successful.
Files=1, Tests=2,  0 wallclock secs ( 0.03 usr  0.01 sys +  0.06 cusr  0.01 csys =  0.11 CPU)
Result: PASS
$

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment