feat: Add zip parser

ref: Parsers now returns vector, not a single book
This commit is contained in:
2025-09-09 11:00:27 +03:00
parent c86be31d54
commit a71ce6d26f
8 changed files with 497 additions and 18 deletions

View File

@@ -10,6 +10,7 @@ pub struct Loader {
pub struct LoaderIter {
queue: VecDeque<PathBuf>,
pending_books: VecDeque<Book>,
}
impl Loader {
@@ -25,13 +26,20 @@ impl IntoIterator for Loader {
fn into_iter(self) -> Self::IntoIter {
let mut queue = VecDeque::new();
queue.push_back(self.root);
LoaderIter { queue }
LoaderIter {
queue,
pending_books: VecDeque::new(),
}
}
}
impl Iterator for LoaderIter {
type Item = Book;
fn next(&mut self) -> Option<Self::Item> {
if let Some(book) = self.pending_books.pop_front() {
return Some(book);
}
while let Some(path) = self.queue.pop_front() {
match path.is_dir() {
true => {
@@ -42,9 +50,11 @@ impl Iterator for LoaderIter {
}
}
false => {
let book = Self::parse_path(&path);
if book.is_some() {
return book;
if let Some(books) = Self::parse_path(&path) {
self.pending_books.extend(books);
if let Some(book) = self.pending_books.pop_front() {
return Some(book);
}
}
continue;
@@ -57,9 +67,9 @@ impl Iterator for LoaderIter {
}
impl LoaderIter {
fn parse_path(path: &PathBuf) -> Option<Book> {
fn parse_path(path: &PathBuf) -> Option<Vec<Book>> {
match parsers::parse(&path) {
Ok(book) => return Some(book),
Ok(books) => return Some(books),
Err(err) => {
match err {
parsers::Error::ParseError(err) => {

View File

@@ -48,9 +48,8 @@ impl<'a> Iterator for LoaderIter<'a> {
Ok(events) => {
for ev in events {
println!("{:?}", ev);
if let Some(book) = Self::process_event(ev) {
println!("{}", book);
self.queue.push_back(book);
if let Some(books) = Self::process_event(ev) {
self.queue.extend(books);
}
}
self.queue.pop_front()
@@ -64,16 +63,21 @@ impl<'a> Iterator for LoaderIter<'a> {
}
impl<'a> LoaderIter<'a> {
fn process_event(event: Event<&OsStr>) -> Option<Book> {
fn process_event(event: Event<&OsStr>) -> Option<Vec<Book>> {
let path = PathBuf::from(event.name?);
if !path.exists() {
return None;
}
match parsers::parse(&path) {
Ok(book) => Some(book),
Ok(books) => {
for book in &books {
println!("{}", book);
}
Some(books)
},
Err(err) => {
eprintln!("Failed to parse book from {:?}: {:?}", path, err);
eprintln!("Failed to parse book from {:?}: {}", path, err);
None
}
}

View File

@@ -7,7 +7,7 @@ use std::path::Path;
use crate::domain::author;
use crate::domain::book::Book;
pub fn parse(path: &Path) -> Result<Book, String> {
pub fn parse(path: &Path) -> Result<Vec<Book>, String> {
let file = File::open(path).map_err(|e| e.to_string())?;
let mut reader = Reader::from_reader(BufReader::new(file));
let mut buf = Vec::new();
@@ -131,7 +131,7 @@ pub fn parse(path: &Path) -> Result<Book, String> {
buf.clear();
}
Ok(Book{
Ok(vec![Book{
id: Uuid::new_v4(),
title,
author: authors,
@@ -141,5 +141,5 @@ pub fn parse(path: &Path) -> Result<Book, String> {
published_at,
publisher,
updated: chrono::Utc::now().to_rfc3339(),
})
}])
}

View File

@@ -1,18 +1,31 @@
use std::fmt;
use crate::domain::book::Book;
use std::path::PathBuf;
mod rs;
mod fb2;
mod zip;
#[derive(Debug)]
pub enum Error {
NotSupported,
ParseError(String),
}
pub fn parse(path: &PathBuf) -> Result<Book, Error> {
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Error::NotSupported => write!(f, "File format not supported"),
Error::ParseError(msg) => write!(f, "Parse error: {}", msg),
}
}
}
pub fn parse(path: &PathBuf) -> Result<Vec<Book>, Error> {
match path.extension().and_then(|s| s.to_str()) {
Some("rs") => rs::parse(path).map_err(Error::ParseError),
Some("fb2") => fb2::parse(path).map_err(Error::ParseError),
Some("zip") => zip::parse(path).map_err(Error::ParseError),
Some(_) | None => Err(Error::NotSupported),
}
}

View File

@@ -2,7 +2,7 @@ use crate::domain::author::Author;
use crate::domain::book::Book;
use std::path::PathBuf;
pub fn parse(path: &PathBuf) -> Result<Book, String> {
pub fn parse(path: &PathBuf) -> Result<Vec<Book>, String> {
let mut book = Book::new();
book.title = path.to_string_lossy().to_string();
@@ -11,5 +11,5 @@ pub fn parse(path: &PathBuf) -> Result<Book, String> {
author.first_name = path.extension().unwrap().to_string_lossy().to_string();
book.author.push(author);
return Ok(book);
return Ok(vec![ book]);
}

View File

@@ -0,0 +1,26 @@
use crate::application::parsers;
use crate::domain::book::Book;
use std::fs::File;
use std::io::BufReader;
use std::path::{Path, PathBuf};
use zip::ZipArchive;
pub fn parse(path: &Path) -> Result<Vec<Book>, String> {
let file = File::open(path).map_err(|e| e.to_string())?;
let reader = BufReader::new(file);
let mut archive = ZipArchive::new(reader).map_err(|e| e.to_string())?;
let mut books: Vec<Book> = Vec::new();
for i in 0..archive.len() {
let file = archive.by_index(i).map_err(|e| e.to_string())?;
let name = file.name().to_string();
match parsers::parse(&PathBuf::from(name.to_lowercase())) {
Ok(new_books) => books.extend(new_books),
Err(e) => return Err(e.to_string()),
}
}
Ok(books)
}