use eframe::egui; use eframe::egui::ImageSource; use log::*; use reqwest::Client; use serde::Deserialize; use serial::prelude::*; use std::fs::File; use std::io::{Cursor, copy}; use std::str::from_utf8; use std::time::Duration; use std::{env, fs}; use tokio; use tokio::runtime::Runtime; #[derive(Debug, Clone)] struct BookData { title: String, authors: Vec, thumbnail: String, description: String, } #[allow(unused)] #[derive(Debug, Deserialize)] struct BookRoot { items: Vec, } #[allow(unused, non_snake_case)] #[derive(Debug, Deserialize)] struct Item { volumeInfo: VolumeInfo, } #[allow(unused, non_snake_case)] #[derive(Debug, Deserialize)] struct VolumeInfo { title: String, authors: Option>, description: Option, imageLinks: Option, } #[allow(unused, non_snake_case)] #[derive(Debug, Deserialize)] struct ImageLinks { thumbnail: Option, smallThumbnail: Option, } fn main() -> eframe::Result { env_logger::init(); let options = eframe::NativeOptions { viewport: egui::ViewportBuilder::default().with_inner_size([320.0, 880.0]), ..Default::default() }; eframe::run_native( "Barcode Scanner", options, Box::new(|cc| { egui_extras::install_image_loaders(&cc.egui_ctx); Ok(Box::::default()) }), ) } fn get_ean13_data() -> String { let args: Vec = env::args().collect(); let path_to_scanner = &args[1]; let mut port = serial::open(path_to_scanner).unwrap(); interact(&mut port).to_string() } fn interact(port: &mut T) -> String { port .reconfigure(&|settings| { settings.set_baud_rate(serial::Baud9600).unwrap(); settings.set_char_size(serial::Bits8); settings.set_parity(serial::ParityNone); settings.set_stop_bits(serial::Stop1); settings.set_flow_control(serial::FlowNone); Ok(()) }) .unwrap(); port.set_timeout(Duration::from_millis(1000000000)).unwrap(); let buf: &mut [u8; 14] = &mut [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; port.read_exact(buf).unwrap(); let barcode_id = from_utf8(buf).unwrap(); debug!("{}", &barcode_id); barcode_id.to_string() } fn get_book_data(isbn: String) -> BookData { let runtime = tokio::runtime::Runtime::new().unwrap(); let lookup_url = format!( "https://www.googleapis.com/books/v1/volumes?q=isbn:{}", isbn ); let cleint = Client::new(); let book_data = runtime .block_on(cleint.get(lookup_url).send()) .unwrap() .json::(); let book_data = runtime.block_on(book_data).unwrap(); let book_metadata: Vec = book_data.items.into_iter().map(BookData::from).collect(); println!("{}", &book_metadata[0]); return book_metadata[0].clone(); } impl From for BookData { fn from(item: Item) -> Self { let info = item.volumeInfo; BookData { title: info.title, authors: info.authors.unwrap_or_default(), description: info.description.as_deref().unwrap_or("").to_string(), thumbnail: info .imageLinks .as_ref() .and_then(|links| links.thumbnail.as_deref()) .unwrap_or("") .to_string(), } } } impl BookData { fn new() -> BookData { BookData { title: "Unknown".to_string(), authors: vec!["Unknown".to_string()], thumbnail: "https://www.rust-lang.org/logos/rust-logo-512x512.png".to_string(), description: "No description available".to_string(), } } } impl std::fmt::Display for BookData { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( f, "Title: {}\nAuthors: {}\nDescription: {}\nThumbnail: {}", self.title, if self.authors.is_empty() { "Unknown".to_string() } else { self.authors.join(", ") }, if self.description.is_empty() { "No description available".to_string() } else { self.description.clone() }, self.thumbnail ) } } fn get_thumbnail(url: String) { let runtime = Runtime::new().unwrap(); let url = if url.is_empty() { "https://www.rust-lang.org/logos/rust-logo-512x512.png".to_string() } else { url }; fs::create_dir_all("cache").unwrap(); let mut dest = File::create("cache/image.jpeg").unwrap(); let image = runtime.block_on(reqwest::get(url)).unwrap(); let bytes = runtime.block_on(image.bytes()).unwrap(); let mut output = Cursor::new(bytes); copy(&mut output, &mut dest).unwrap(); } #[derive(Default)] struct BarcodeScanner {} impl eframe::App for BarcodeScanner { fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) -> () { egui_extras::install_image_loaders(ctx); egui::CentralPanel::default().show(ctx, |ui| { ui.heading("Barcode Scanner"); ui.group(|ui| { //loop { let barcode = get_ean13_data(); ui.label(format!("EAN13: {}", barcode)); let book_metadata = if barcode.starts_with("978") { get_book_data(barcode) } else { BookData::new() }; get_thumbnail(book_metadata.thumbnail.clone()); ui.label(format!("Title: {}", book_metadata.title)); ui.label(format!("Description: {}", book_metadata.description)); ui.label(format!("Authors: {:?}", book_metadata.authors)); if !book_metadata.thumbnail.is_empty() { ui.image(egui::ImageSource::Uri(book_metadata.thumbnail.into())) } else { ui.image(egui::ImageSource::Uri(std::borrow::Cow::Borrowed( "https://www.rust-lang.org/logos/rust-logo-512x512.png", ))) } // } }) }); } }