barcode-utils/src/main.rs

130 lines
3 KiB
Rust
Raw Normal View History

2025-09-24 18:07:05 +02:00
use reqwest::Client;
use serde::Deserialize;
use serial::prelude::*;
2025-09-25 20:23:22 +02:00
use std::env;
2025-09-23 17:13:23 +02:00
use std::io;
use std::str::from_utf8;
use std::time::Duration;
#[derive(Debug)]
2025-09-24 18:07:05 +02:00
struct BookData {
title: String,
authors: Vec<String>,
thumbnail: String,
description: String,
}
2025-09-24 18:07:05 +02:00
#[allow(unused)]
#[derive(Debug, Deserialize)]
struct BookRoot {
items: Vec<Item>,
}
#[allow(unused, non_snake_case)]
2025-09-24 18:07:05 +02:00
#[derive(Debug, Deserialize)]
struct Item {
volumeInfo: VolumeInfo,
2025-09-24 18:07:05 +02:00
}
2025-09-23 17:13:23 +02:00
#[allow(unused, non_snake_case)]
2025-09-24 18:07:05 +02:00
#[derive(Debug, Deserialize)]
struct VolumeInfo {
title: String,
authors: Option<Vec<String>>,
description: Option<String>,
imageLinks: Option<ImageLinks>,
2025-09-24 18:07:05 +02:00
}
#[allow(unused, non_snake_case)]
2025-09-24 18:07:05 +02:00
#[derive(Debug, Deserialize)]
struct ImageLinks {
thumbnail: Option<String>,
smallThumbnail: Option<String>,
2025-09-24 18:07:05 +02:00
}
#[tokio::main]
async fn main() {
2025-09-25 20:23:22 +02:00
let args: Vec<String> = env::args().collect();
let path_to_scanner = &args[1];
let mut port = serial::open(path_to_scanner).unwrap();
2025-09-24 18:07:05 +02:00
interact(&mut port).await.unwrap();
2025-09-23 17:13:23 +02:00
}
2025-09-24 18:07:05 +02:00
async fn interact<T: SerialPort>(port: &mut T) -> io::Result<()> {
2025-09-23 17:13:23 +02:00
port.reconfigure(&|settings| {
settings.set_baud_rate(serial::Baud9600)?;
settings.set_char_size(serial::Bits8);
settings.set_parity(serial::ParityNone);
settings.set_stop_bits(serial::Stop1);
settings.set_flow_control(serial::FlowNone);
Ok(())
})?;
port.set_timeout(Duration::from_millis(1000000000))?;
loop {
2025-09-24 18:07:05 +02:00
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();
println!("{}", &barcode_id);
if barcode_id.starts_with("978") {
get_book_data(barcode_id.to_string()).await;
}
2025-09-23 17:13:23 +02:00
}
}
2025-09-24 18:07:05 +02:00
async fn get_book_data(isbn: String) {
let lookup_url = format!(
"https://www.googleapis.com/books/v1/volumes?q=isbn:{}",
isbn
);
let cleint = Client::new();
let book_data = cleint
.get(lookup_url)
.send()
.await
.unwrap()
.json::<BookRoot>()
.await
.unwrap();
let book_metadata: Vec<BookData> = book_data.items.into_iter().map(BookData::from).collect();
println!("{}", &book_metadata[0]);
}
impl From<Item> 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 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
)
}
2025-09-24 18:07:05 +02:00
}