diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 05e6633301..bbf24d0215 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -186,10 +186,12 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: [ubuntu-latest, macos-latest, windows-latest] + os: [ubuntu-latest, macos-13, windows-latest] steps: - uses: actions/checkout@v4 + with: + submodules: true - name: Lint working-directory: wrappers/rust diff --git a/core/src/WriteBarcode.cpp b/core/src/WriteBarcode.cpp index f2cde18732..fbd2540dd1 100644 --- a/core/src/WriteBarcode.cpp +++ b/core/src/WriteBarcode.cpp @@ -35,7 +35,7 @@ struct CreatorOptions::Data mutable std::unique_ptr zint; -#if __cplusplus <= 201703L +#if __cplusplus <= 201703L || defined(__APPLE__) Data(BarcodeFormat f) : format(f) {} #endif }; @@ -283,7 +283,7 @@ struct SetCommonWriterOptions zint->output_options |= opts.withQuietZones() ? BARCODE_QUIET_ZONES : BARCODE_NO_QUIET_ZONES; if (opts.scale()) - zint->scale = opts.scale(); + zint->scale = opts.scale() / 2.f; else if (opts.sizeHint()) { int size = std::max(zint->width, zint->rows); zint->scale = std::max(1, int(float(opts.sizeHint()) / size)) / 2.f; diff --git a/wrappers/rust/Cargo.toml b/wrappers/rust/Cargo.toml index 6e5052c59e..fa473bb8b1 100644 --- a/wrappers/rust/Cargo.toml +++ b/wrappers/rust/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "zxing-cpp" -version = "0.3.0" +version = "0.4.0" edition = "2021" # authors = ["Axel Waggershauser "] license = "Apache-2.0" @@ -10,9 +10,8 @@ readme = "README.md" keywords = ["zxing", "barcode", "barcode_reader", "ffi"] categories = ["api-bindings", "computer-vision"] exclude = [ - "core/**/*Write*", + "core/**/*Writer*", "core/**/*Encode*", - "core/src/libzint/**", ] [lib] diff --git a/wrappers/rust/README.md b/wrappers/rust/README.md index f16960f05d..6cf963d165 100644 --- a/wrappers/rust/README.md +++ b/wrappers/rust/README.md @@ -15,34 +15,42 @@ In your Cargo.toml: # `bundled` causes cargo to compile and statically link an up to # date version of the c++ core library. This is the most convenient # and safe way to build the library. -zxing-cpp = { version = "0.3.0", features = ["bundled", "image"] } +zxing-cpp = { version = "0.4.0", features = ["bundled", "image"] } ``` -Simple example usage: +Simple example reading some barcodes from a jpg file: ```rust -use zxingcpp::{BarcodeFormat, BarcodeReader, ImageView}; +use zxingcpp::BarcodeFormat; fn main() -> anyhow::Result<()> { let image = image::open("some-image-file.jpg")?; - let reader = BarcodeReader::new() + + let read_barcodes = zxingcpp::read() .formats(BarcodeFormat::QRCode | BarcodeFormat::LinearCodes) .try_invert(false); - let barcodes = reader.read(&image)?; + let barcodes = read_barcodes.from(&image)?; - if barcodes.is_empty() { - println!("No barcode found."); - } else { - for barcode in barcodes { - println!("{}: {}", barcode.format(), barcode.text()); - } + for barcode in barcodes { + println!("{}: {}", barcode.format(), barcode.text()); } Ok(()) } ``` +Simple example creating a barcode and writing it to a svg file: +```rust +fn main() -> anyhow::Result<()> { + let svg = zxingcpp::create(zxingcpp::BarcodeFormat::QRCode) + .from_str("https://github.com/zxing-cpp/zxing-cpp")? + .to_svg_with(&zxingcpp::write().scale(5))?; + std::fs::write("zxingcpp.svg", svg)?; + Ok(()) +} +``` + Note: This should currently be considered a pre-release. The API may change slightly to be even more "rusty" depending on community feedback. diff --git a/wrappers/rust/build.rs b/wrappers/rust/build.rs index 8f43f3a1be..88b4f0774f 100644 --- a/wrappers/rust/build.rs +++ b/wrappers/rust/build.rs @@ -6,8 +6,10 @@ fn main() -> miette::Result<()> { let mut dst = cmake::Config::new("core") .define("BUILD_SHARED_LIBS", "OFF") .define("BUILD_READERS", "ON") - .define("BUILD_WRITERS", "OFF") + .define("BUILD_WRITERS", "NEW") + .define("BUILD_EXPERIMENTAL_API", "ON") .define("BUILD_C_API", "ON") + .define("ZXING_USE_BUNDLED_ZINT", "ON") .define("CMAKE_CXX_STANDARD", "20") .build(); dst.push("lib"); diff --git a/wrappers/rust/examples/demo.rs b/wrappers/rust/examples/demo_reader.rs similarity index 91% rename from wrappers/rust/examples/demo.rs rename to wrappers/rust/examples/demo_reader.rs index 5a89f451d1..e901353f45 100644 --- a/wrappers/rust/examples/demo.rs +++ b/wrappers/rust/examples/demo_reader.rs @@ -18,7 +18,7 @@ fn main() -> anyhow::Result<()> { let iv = ImageView::from_slice(&lum_img, lum_img.width(), lum_img.height(), ImageFormat::Lum)?; let formats = BarcodeFormats::from_str(formats.unwrap_or_default())?; - let reader = BarcodeReader::new() + let read_barcodes = BarcodeReader::new() .formats(formats) .try_harder(!fast) .try_invert(!fast) @@ -27,9 +27,9 @@ fn main() -> anyhow::Result<()> { .return_errors(true); #[cfg(feature = "image")] - let barcodes = reader.read(&image)?; + let barcodes = read_barcodes.from(&image)?; #[cfg(not(feature = "image"))] - let barcodes = reader.read(iv)?; + let barcodes = read_barcodes.from(iv)?; if barcodes.is_empty() { println!("No barcode found."); diff --git a/wrappers/rust/examples/demo_writer.rs b/wrappers/rust/examples/demo_writer.rs new file mode 100644 index 0000000000..e714a537af --- /dev/null +++ b/wrappers/rust/examples/demo_writer.rs @@ -0,0 +1,25 @@ +/* +* Copyright 2024 Axel Waggershauser +*/ +// SPDX-License-Identifier: Apache-2.0 + +use image; +use std::fs; +use zxingcpp::*; + +fn main() -> anyhow::Result<()> { + let text = std::env::args().nth(1).expect("no input text provided"); + let format = std::env::args().nth(2).expect("no format provided"); + let filename = std::env::args().nth(3).expect("no output file name provided"); + + let create_barcode = create(BarcodeFormat::from_str(format)?); + let bc = create_barcode.from_str(text)?; + + if filename.ends_with(".svg") { + fs::write(filename, bc.to_svg_with(&write().with_hrt(true))?).expect("Unable to write file"); + } else { + image::GrayImage::from(&bc.to_image_with(&write().scale(4))?).save(filename)?; + } + + Ok(()) +} diff --git a/wrappers/rust/src/bindings.rs b/wrappers/rust/src/bindings.rs index e1f8902d91..6b8662fea1 100644 --- a/wrappers/rust/src/bindings.rs +++ b/wrappers/rust/src/bindings.rs @@ -25,6 +25,16 @@ pub struct ZXing_Image { pub struct ZXing_ReaderOptions { _unused: [u8; 0], } +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct ZXing_CreatorOptions { + _unused: [u8; 0], +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct ZXing_WriterOptions { + _unused: [u8; 0], +} pub const ZXing_ImageFormat_None: ZXing_ImageFormat = 0; pub const ZXing_ImageFormat_Lum: ZXing_ImageFormat = 16777216; pub const ZXing_ImageFormat_LumA: ZXing_ImageFormat = 33554432; @@ -188,6 +198,40 @@ extern "C" { pub fn ZXing_ReaderOptions_getMaxNumberOfSymbols(opts: *const ZXing_ReaderOptions) -> ::core::ffi::c_int; pub fn ZXing_ReadBarcode(iv: *const ZXing_ImageView, opts: *const ZXing_ReaderOptions) -> *mut ZXing_Barcode; pub fn ZXing_ReadBarcodes(iv: *const ZXing_ImageView, opts: *const ZXing_ReaderOptions) -> *mut ZXing_Barcodes; + pub fn ZXing_CreatorOptions_new(format: ZXing_BarcodeFormat) -> *mut ZXing_CreatorOptions; + pub fn ZXing_CreatorOptions_delete(opts: *mut ZXing_CreatorOptions); + pub fn ZXing_CreatorOptions_setFormat(opts: *mut ZXing_CreatorOptions, format: ZXing_BarcodeFormat); + pub fn ZXing_CreatorOptions_getFormat(opts: *const ZXing_CreatorOptions) -> ZXing_BarcodeFormat; + pub fn ZXing_CreatorOptions_setReaderInit(opts: *mut ZXing_CreatorOptions, readerInit: bool); + pub fn ZXing_CreatorOptions_getReaderInit(opts: *const ZXing_CreatorOptions) -> bool; + pub fn ZXing_CreatorOptions_setForceSquareDataMatrix(opts: *mut ZXing_CreatorOptions, forceSquareDataMatrix: bool); + pub fn ZXing_CreatorOptions_getForceSquareDataMatrix(opts: *const ZXing_CreatorOptions) -> bool; + pub fn ZXing_CreatorOptions_setEcLevel(opts: *mut ZXing_CreatorOptions, ecLevel: *const ::core::ffi::c_char); + pub fn ZXing_CreatorOptions_getEcLevel(opts: *const ZXing_CreatorOptions) -> *mut ::core::ffi::c_char; + pub fn ZXing_WriterOptions_new() -> *mut ZXing_WriterOptions; + pub fn ZXing_WriterOptions_delete(opts: *mut ZXing_WriterOptions); + pub fn ZXing_WriterOptions_setScale(opts: *mut ZXing_WriterOptions, scale: ::core::ffi::c_int); + pub fn ZXing_WriterOptions_getScale(opts: *const ZXing_WriterOptions) -> ::core::ffi::c_int; + pub fn ZXing_WriterOptions_setSizeHint(opts: *mut ZXing_WriterOptions, sizeHint: ::core::ffi::c_int); + pub fn ZXing_WriterOptions_getSizeHint(opts: *const ZXing_WriterOptions) -> ::core::ffi::c_int; + pub fn ZXing_WriterOptions_setRotate(opts: *mut ZXing_WriterOptions, rotate: ::core::ffi::c_int); + pub fn ZXing_WriterOptions_getRotate(opts: *const ZXing_WriterOptions) -> ::core::ffi::c_int; + pub fn ZXing_WriterOptions_setWithHRT(opts: *mut ZXing_WriterOptions, withHRT: bool); + pub fn ZXing_WriterOptions_getWithHRT(opts: *const ZXing_WriterOptions) -> bool; + pub fn ZXing_WriterOptions_setWithQuietZones(opts: *mut ZXing_WriterOptions, withQuietZones: bool); + pub fn ZXing_WriterOptions_getWithQuietZones(opts: *const ZXing_WriterOptions) -> bool; + pub fn ZXing_CreateBarcodeFromText( + data: *const ::core::ffi::c_char, + size: ::core::ffi::c_int, + opts: *const ZXing_CreatorOptions, + ) -> *mut ZXing_Barcode; + pub fn ZXing_CreateBarcodeFromBytes( + data: *const ::core::ffi::c_void, + size: ::core::ffi::c_int, + opts: *const ZXing_CreatorOptions, + ) -> *mut ZXing_Barcode; + pub fn ZXing_WriteBarcodeToSVG(barcode: *const ZXing_Barcode, opts: *const ZXing_WriterOptions) -> *mut ::core::ffi::c_char; + pub fn ZXing_WriteBarcodeToImage(barcode: *const ZXing_Barcode, opts: *const ZXing_WriterOptions) -> *mut ZXing_Image; pub fn ZXing_LastErrorMsg() -> *mut ::core::ffi::c_char; pub fn ZXing_free(ptr: *mut ::core::ffi::c_void); } diff --git a/wrappers/rust/src/lib.rs b/wrappers/rust/src/lib.rs index ca7d47ad1b..2f0a3ce493 100644 --- a/wrappers/rust/src/lib.rs +++ b/wrappers/rust/src/lib.rs @@ -90,6 +90,103 @@ macro_rules! last_error_or { }; } +macro_rules! last_error_if_null_or { + ($ptr:ident, $expr:expr) => { + match $ptr.is_null() { + true => Err(last_error()), + false => Ok($expr), + } + }; +} + +macro_rules! make_zxing_class { + ($r_class:ident, $c_class:ident) => { + paste! { + pub struct $r_class(*mut $c_class); + + impl Drop for $r_class { + fn drop(&mut self) { + unsafe { [<$c_class _delete>](self.0) } + } + } + } + }; +} + +macro_rules! make_zxing_class_with_default { + ($r_class:ident, $c_class:ident) => { + make_zxing_class!($r_class, $c_class); + paste! { + impl $r_class { + pub fn new() -> Self { + unsafe { $r_class([<$c_class _new>]()) } + } + } + + impl Default for $r_class { + fn default() -> Self { + Self::new() + } + } + } + }; +} + +macro_rules! getter { + ($class:ident, $c_name:ident, $r_name:ident, $conv:expr, $type:ty) => { + pub fn $r_name(&self) -> $type { + paste! { unsafe { $conv([](self.0)) } } + } + }; + ($class:ident, $c_name:ident, $conv:expr, $type:ty) => { + paste! { getter! { $class, $c_name, [<$c_name:snake>], $conv, $type } } + }; +} + +macro_rules! property { + ($class:ident, $c_name:ident, $r_name:ident, String) => { + pub fn $r_name(self, v: impl AsRef) -> Self { + let cstr = CString::new(v.as_ref()).unwrap(); + paste! { unsafe { [](self.0, cstr.as_ptr()) } }; + self + } + + paste! { + pub fn [](&mut self, v : impl AsRef) -> &mut Self { + let cstr = CString::new(v.as_ref()).unwrap(); + unsafe { [](self.0, cstr.as_ptr()) }; + self + } + + pub fn [](&self) -> String { + unsafe { c2r_str([](self.0)) } + } + } + }; + + ($class:ident, $c_name:ident, $r_name:ident, $type:ty) => { + pub fn $r_name(self, v: impl Into<$type>) -> Self { + paste! { unsafe { [](self.0, transmute(v.into())) } }; + self + } + + paste! { + pub fn [](&mut self, v : impl Into<$type>) -> &mut Self { + unsafe { [](self.0, transmute(v.into())) }; + self + } + + pub fn [](&self) -> $type { + unsafe { transmute([](self.0)) } + } + } + }; + + ($class:ident, $c_name:ident, $type:ty) => { + paste! { property! { $class, $c_name, [<$c_name:snake>], $type } } + }; +} + macro_rules! make_zxing_enum { ($name:ident { $($field:ident),* }) => { #[repr(u32)] @@ -145,6 +242,17 @@ pub trait FromStr: Sized { fn from_str(str: impl AsRef) -> Result; } +impl FromStr for BarcodeFormat { + fn from_str(str: impl AsRef) -> Result { + let cstr = CString::new(str.as_ref())?; + let res = unsafe { BarcodeFormats::new_unchecked(ZXing_BarcodeFormatFromString(cstr.as_ptr())) }; + match res.bits() { + u32::MAX => last_error_or!(BarcodeFormat::None), + _ => Ok(res.into_iter().last().unwrap()), + } + } +} + impl FromStr for BarcodeFormats { fn from_str(str: impl AsRef) -> Result { let cstr = CString::new(str.as_ref())?; @@ -204,11 +312,7 @@ impl<'a> ImageView<'a> { Self::try_into_int(row_stride)?, Self::try_into_int(pix_stride)?, ); - if iv.is_null() { - Err(last_error()) - } else { - Ok(ImageView(Rc::new(ImageViewOwner(iv, PhantomData)))) - } + last_error_if_null_or!(iv, ImageView(Rc::new(ImageViewOwner(iv, PhantomData)))) } pub fn from_slice>(data: &'a [u8], width: T, height: T, format: ImageFormat) -> Result { @@ -222,11 +326,7 @@ impl<'a> ImageView<'a> { 0, 0, ); - if iv.is_null() { - Err(last_error()) - } else { - Ok(ImageView(Rc::new(ImageViewOwner(iv, PhantomData)))) - } + last_error_if_null_or!(iv, ImageView(Rc::new(ImageViewOwner(iv, PhantomData)))) } } @@ -270,11 +370,27 @@ impl<'a> TryFrom<&'a image::DynamicImage> for ImageView<'a> { } } -pub struct Barcode(*mut ZXing_Barcode); +make_zxing_class!(Image, ZXing_Image); -impl Drop for Barcode { - fn drop(&mut self) { - unsafe { ZXing_Barcode_delete(self.0) } +impl Image { + getter!(Image, width, transmute, i32); + getter!(Image, height, transmute, i32); + getter!(Image, format, transmute, ImageFormat); + + pub fn data(&self) -> Vec { + let ptr = unsafe { ZXing_Image_data(self.0) }; + if ptr.is_null() { + Vec::::new() + } else { + unsafe { std::slice::from_raw_parts(ptr, (self.width() * self.height()) as usize).to_vec() } + } + } +} + +#[cfg(feature = "image")] +impl From<&Image> for image::GrayImage { + fn from(img: &Image) -> image::GrayImage { + image::GrayImage::from_vec(img.width() as u32, img.height() as u32, img.data()).unwrap() } } @@ -317,27 +433,21 @@ impl Display for Position { } } -macro_rules! getter { - ($r_name:ident, $c_name:ident, $conv:expr, $type:ty) => { - pub fn $r_name(&self) -> $type { - paste! { unsafe { $conv([](self.0)) } } - } - }; -} +make_zxing_class!(Barcode, ZXing_Barcode); impl Barcode { - getter!(is_valid, isValid, transmute, bool); - getter!(format, format, (|f| BarcodeFormats::new(f).unwrap().into_iter().last().unwrap()), BarcodeFormat); - getter!(content_type, contentType, transmute, ContentType); - getter!(text, text, c2r_str, String); - getter!(ec_level, ecLevel, c2r_str, String); - getter!(symbology_identifier, symbologyIdentifier, c2r_str, String); - getter!(position, position, transmute, Position); - getter!(orientation, orientation, transmute, i32); - getter!(has_eci, hasECI, transmute, bool); - getter!(is_inverted, isInverted, transmute, bool); - getter!(is_mirrored, isMirrored, transmute, bool); - getter!(line_count, lineCount, transmute, i32); + getter!(Barcode, isValid, transmute, bool); + getter!(Barcode, format, (|f| BarcodeFormats::new(f).unwrap().into_iter().last().unwrap()), BarcodeFormat); + getter!(Barcode, contentType, transmute, ContentType); + getter!(Barcode, text, c2r_str, String); + getter!(Barcode, ecLevel, c2r_str, String); + getter!(Barcode, symbologyIdentifier, c2r_str, String); + getter!(Barcode, position, transmute, Position); + getter!(Barcode, orientation, transmute, i32); + getter!(Barcode, hasECI, has_eci, transmute, bool); + getter!(Barcode, isInverted, transmute, bool); + getter!(Barcode, isMirrored, transmute, bool); + getter!(Barcode, lineCount, transmute, i32); pub fn bytes(&self) -> Vec { let mut len: c_int = 0; @@ -360,61 +470,43 @@ impl Barcode { _ => panic!("Internal error: invalid ZXing_ErrorType"), } } -} -pub struct BarcodeReader(*mut ZXing_ReaderOptions); - -impl Drop for BarcodeReader { - fn drop(&mut self) { - unsafe { ZXing_ReaderOptions_delete(self.0) } + pub fn to_svg_with(&self, opts: &BarcodeWriter) -> Result { + let str = unsafe { ZXing_WriteBarcodeToSVG(self.0, opts.0) }; + last_error_if_null_or!(str, c2r_str(str)) } -} -impl Default for BarcodeReader { - fn default() -> Self { - Self::new() + pub fn to_svg(&self) -> Result { + self.to_svg_with(&BarcodeWriter::default()) } -} - -macro_rules! property { - ($name:ident, $type:ty) => { - pub fn $name(self, v: impl Into<$type>) -> Self { - paste! { unsafe { [](self.0, transmute(v.into())) } }; - self - } - paste! { - pub fn [](&mut self, v : impl Into<$type>) -> &mut Self { - unsafe { [](self.0, transmute(v.into())) }; - self - } + pub fn to_image_with(&self, opts: &BarcodeWriter) -> Result { + let img = unsafe { ZXing_WriteBarcodeToImage(self.0, opts.0) }; + last_error_if_null_or!(img, Image(img)) + } - pub fn [](&self) -> $type { - unsafe { transmute([](self.0)) } - } - } - }; + pub fn to_image(&self) -> Result { + self.to_image_with(&BarcodeWriter::default()) + } } +make_zxing_class_with_default!(BarcodeReader, ZXing_ReaderOptions); + impl BarcodeReader { - pub fn new() -> Self { - unsafe { BarcodeReader(ZXing_ReaderOptions_new()) } - } - - property!(try_harder, bool); - property!(try_rotate, bool); - property!(try_invert, bool); - property!(try_downscale, bool); - property!(is_pure, bool); - property!(return_errors, bool); - property!(formats, BarcodeFormats); - property!(text_mode, TextMode); - property!(binarizer, Binarizer); - property!(ean_add_on_symbol, EanAddOnSymbol); - property!(max_number_of_symbols, i32); - property!(min_line_count, i32); - - pub fn read<'a, IV>(&self, image: IV) -> Result, Error> + property!(ReaderOptions, TryHarder, bool); + property!(ReaderOptions, TryRotate, bool); + property!(ReaderOptions, TryInvert, bool); + property!(ReaderOptions, TryDownscale, bool); + property!(ReaderOptions, IsPure, bool); + property!(ReaderOptions, ReturnErrors, bool); + property!(ReaderOptions, Formats, BarcodeFormats); + property!(ReaderOptions, TextMode, TextMode); + property!(ReaderOptions, Binarizer, Binarizer); + property!(ReaderOptions, EanAddOnSymbol, EanAddOnSymbol); + property!(ReaderOptions, MaxNumberOfSymbols, i32); + property!(ReaderOptions, MinLineCount, i32); + + pub fn from<'a, IV>(&self, image: IV) -> Result, Error> where IV: TryInto>, IV::Error: Into, @@ -436,3 +528,49 @@ impl BarcodeReader { } } } + +make_zxing_class!(BarcodeCreator, ZXing_CreatorOptions); + +impl BarcodeCreator { + pub fn new(format: BarcodeFormat) -> Self { + unsafe { BarcodeCreator(ZXing_CreatorOptions_new(BarcodeFormats::from(format).bits())) } + } + + property!(CreatorOptions, ReaderInit, bool); + property!(CreatorOptions, ForceSquareDataMatrix, bool); + property!(CreatorOptions, EcLevel, String); + + pub fn from_str(&self, str: impl AsRef) -> Result { + let cstr = CString::new(str.as_ref())?; + let bc = unsafe { ZXing_CreateBarcodeFromText(cstr.as_ptr(), 0, self.0) }; + last_error_if_null_or!(bc, Barcode(bc)) + } + + pub fn from_slice(&self, data: impl AsRef<[u8]>) -> Result { + let data = data.as_ref(); + let bc = unsafe { ZXing_CreateBarcodeFromBytes(data.as_ptr() as *const c_void, data.len() as i32, self.0) }; + last_error_if_null_or!(bc, Barcode(bc)) + } +} + +make_zxing_class_with_default!(BarcodeWriter, ZXing_WriterOptions); + +impl BarcodeWriter { + property!(WriterOptions, Scale, i32); + property!(WriterOptions, SizeHint, i32); + property!(WriterOptions, Rotate, i32); + property!(WriterOptions, WithHRT, with_hrt, bool); + property!(WriterOptions, WithQuietZones, bool); +} + +pub fn read() -> BarcodeReader { + BarcodeReader::default() +} + +pub fn create(format: BarcodeFormat) -> BarcodeCreator { + BarcodeCreator::new(format) +} + +pub fn write() -> BarcodeWriter { + BarcodeWriter::default() +} diff --git a/wrappers/rust/src/tests.rs b/wrappers/rust/src/tests.rs index 45254ccba7..ec7c695f9a 100644 --- a/wrappers/rust/src/tests.rs +++ b/wrappers/rust/src/tests.rs @@ -30,12 +30,50 @@ mod tests { assert_eq!(o1.get_text_mode(), TextMode::Hex); } + #[test] + fn barcode_creator_new() { + let mut o1 = BarcodeCreator::new(BarcodeFormat::QRCode); + assert_eq!(o1.get_reader_init(), false); + o1.set_reader_init(true); + assert_eq!(o1.get_reader_init(), true); + } + #[test] #[should_panic] fn barcode_formats_from_str_invalid() { let _ = BarcodeFormats::from_str("qrcoder").unwrap(); } + #[test] + fn create_from_str() { + let str = "123456"; + let res = create(BarcodeFormat::QRCode).ec_level("Q").from_str(str).unwrap(); + + assert_eq!(res.is_valid(), true); + assert_eq!(res.format(), BarcodeFormat::QRCode); + assert_eq!(res.text(), str); + assert_eq!(res.bytes(), str.as_bytes()); + assert_eq!(res.has_eci(), false); + assert_eq!(res.content_type(), ContentType::Text); + assert!(matches!(res.error(), BarcodeError::None())); + assert_eq!(res.error().to_string(), ""); + } + + #[test] + fn create_from_slice() { + let data = [1, 2, 3, 4, 5]; + let res = create(BarcodeFormat::QRCode).reader_init(true).from_slice(&data).unwrap(); + + assert_eq!(res.is_valid(), true); + assert_eq!(res.format(), BarcodeFormat::QRCode); + assert_eq!(res.bytes(), data); + assert_eq!(res.has_eci(), true); + // assert_eq!(res.reader_init(), true); // TODO + assert_eq!(res.content_type(), ContentType::Binary); + assert!(matches!(res.error(), BarcodeError::None())); + assert_eq!(res.error().to_string(), ""); + } + #[test] fn read_pure() { let mut data = Vec::::new(); @@ -43,7 +81,7 @@ mod tests { data.push(if v == '0' { 255 } else { 0 }); } let iv = ImageView::from_slice(&data, data.len(), 1, ImageFormat::Lum).unwrap(); - let res = BarcodeReader::new().binarizer(Binarizer::BoolCast).read(&iv).unwrap(); + let res = read().binarizer(Binarizer::BoolCast).from(&iv).unwrap(); let expected = "96385074";