doc: Add AI-generated docs
Seems ok, vive les robots!
This commit is contained in:
@@ -1,3 +1,11 @@
|
||||
//! A module to load and iterate over books from a hierarchical directory structure.
|
||||
//!
|
||||
//! The `Loader` struct serves as the entry point for recursively traversing a directory tree,
|
||||
//! parsing each file's contents into `Book` instances. `Loader` implements the `IntoIterator`
|
||||
//! trait to provide a custom iterator (`LoaderIter`) capable of traversing directories and files.
|
||||
//!
|
||||
//! The `LoaderIter` struct manages the iteration process by maintaining a queue of directories
|
||||
//! to traverse and a queue of pending `Book` objects to be returned.
|
||||
use crate::application::parsers;
|
||||
use crate::domain::book::Book;
|
||||
use std::collections::VecDeque;
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
//! The `Loader` struct manages an `inotify` instance that monitors a specified directory
|
||||
//! for file modifications and creations. It acts as a bridge between the filesystem events
|
||||
//! and the higher-level representation of books within the application.
|
||||
use crate::application::parsers;
|
||||
use crate::domain::book::Book;
|
||||
use inotify::{Event, Inotify, WatchMask};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
pub mod services;
|
||||
mod services;
|
||||
mod loaders;
|
||||
mod parsers;
|
||||
mod config;
|
||||
|
||||
@@ -7,6 +7,76 @@ use std::path::Path;
|
||||
use crate::domain::author;
|
||||
use crate::domain::book::Book;
|
||||
|
||||
/// Parses an XML file located at the given path and extracts information about books.
|
||||
///
|
||||
/// This function processes the XML structure using a streaming XML reader to extract details
|
||||
/// about books, including:
|
||||
/// - Title
|
||||
/// - Language
|
||||
/// - Keywords
|
||||
/// - Authors (including optional details like first name, last name, middle name, and nickname)
|
||||
/// - Publication year
|
||||
/// - Publisher
|
||||
/// - Description
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `path` - A reference to the file path (`&Path`) of the XML file to parse.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// Returns a `Result` where:
|
||||
/// - `Ok(Vec<Book>)` contains a vector of `Book` objects constructed from the parsed XML.
|
||||
/// - `Err(String)` contains an error message if the parsing fails at any stage.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns an error in the following scenarios:
|
||||
/// - Unable to open the file specified by `path`.
|
||||
/// - Malformed XML data in the file.
|
||||
/// - Issues during data extraction, such as reading incomplete or invalid values.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```ignore
|
||||
/// use std::path::Path;
|
||||
/// let path = Path::new("books.xml");
|
||||
/// match parse(&path) {
|
||||
/// Ok(books) => {
|
||||
/// for book in books {
|
||||
/// println!("Book Title: {}", book.title);
|
||||
/// }
|
||||
/// },
|
||||
/// Err(err) => eprintln!("Failed to parse XML file: {}", err),
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// # XML Structure
|
||||
///
|
||||
/// The XML should follow a specific schema with the following relevant elements:
|
||||
/// - `<book-title>`: Title of the book.
|
||||
/// - `<lang>`: Language of the book.
|
||||
/// - `<keywords>`: A comma-separated list of keywords/tags.
|
||||
/// - `<author>`: Contains subfields `<first-name>`, `<last-name>`, `<middle-name>`, or `<nickname>`.
|
||||
/// - `<year>`: Year of publication.
|
||||
/// - `<publisher>`: Publisher's name.
|
||||
/// - `<annotation>`: Description or annotation of the book.
|
||||
///
|
||||
/// # Notes
|
||||
///
|
||||
/// - Author data is flexible; if a nickname exists, it will override other name details.
|
||||
/// - The resulting `Vec<Book>` contains just one book object, as indicated in the implementation.
|
||||
///
|
||||
/// # Dependencies
|
||||
///
|
||||
/// This function depends on the following crates:
|
||||
/// - `quick-xml`: For fast XML parsing.
|
||||
/// - `uuid`: To generate a unique identifier for each book.
|
||||
/// - `chrono`: To serialize the current timestamp as an RFC3339 string.
|
||||
///
|
||||
/// # See Also
|
||||
///
|
||||
/// `Book` structure, which represents the parsed data for an individual book.
|
||||
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));
|
||||
|
||||
@@ -6,6 +6,12 @@ mod rs;
|
||||
mod fb2;
|
||||
mod zip;
|
||||
|
||||
|
||||
/// Error enumeration representing possible errors that can occur when parsing files.
|
||||
///
|
||||
/// This enumeration has the following variants:
|
||||
/// - `NotSupported`: Indicates that the file format or extension is not supported.
|
||||
/// - `ParseError`: Contains a `String` representing the error message when a parsing process fails.
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
NotSupported,
|
||||
@@ -21,6 +27,48 @@ impl fmt::Display for Error {
|
||||
}
|
||||
}
|
||||
|
||||
/// Parses a file at the given path and attempts to convert its contents into a vector of `Book` objects.
|
||||
///
|
||||
/// This function determines the file type based on its extension and delegates the parsing duties
|
||||
/// to the appropriate module. Supported file extensions are:
|
||||
/// - `.rs`: Processed by the `rs` module.
|
||||
/// - `.fb2`: Processed by the `fb2` module.
|
||||
/// - `.zip`: Processed by the `zip` module.
|
||||
///
|
||||
/// If the file's extension is unsupported or missing, this function returns a `NotSupported` error.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `path` - A reference to a `PathBuf` that represents the file path to be parsed.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// * `Ok(Vec<Book>)` - A vector of `Book` objects if the file was successfully parsed.
|
||||
/// * `Err(Error)` - An error if the file could not be parsed, the parsing process encountered
|
||||
/// an issue, or the file extension is not supported.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// - `Error::ParseError` - If the file parsing fails.
|
||||
/// - `Error::NotSupported` - If the file's extension is unsupported or missing.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```ignore
|
||||
/// use std::path::PathBuf;
|
||||
///
|
||||
/// let path = PathBuf::from("example.rs");
|
||||
/// let books = parse(&path);
|
||||
/// match books {
|
||||
/// Ok(book_list) => println!("Parsed {} books.", book_list.len()),
|
||||
/// Err(e) => println!("Failed to parse file: {:?}", e),
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// # Notes
|
||||
///
|
||||
/// Ensure that the appropriate parsers (`rs`, `fb2`, `zip`) are properly implemented
|
||||
/// and handle all required logic for their respective file types to avoid unexpected errors.
|
||||
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),
|
||||
|
||||
@@ -2,6 +2,44 @@ use crate::domain::author::Author;
|
||||
use crate::domain::book::Book;
|
||||
use std::path::PathBuf;
|
||||
|
||||
/// Parses a given file path into a vector containing a `Book` object.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `path` - A reference to a `PathBuf` that represents the file path to be parsed.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// * `Result<Vec<Book>, String>` -
|
||||
/// - On success, returns a `Vec<Book>` with a single `Book` object populated based on the input path.
|
||||
/// - On failure, returns an error `String` describing the issue.
|
||||
///
|
||||
/// The function performs the following steps:
|
||||
///
|
||||
/// 1. Creates a new instance of `Book`.
|
||||
/// 2. Sets the `title` of the `Book` to the string representation of the input path.
|
||||
/// 3. Creates a new instance of `Author`.
|
||||
/// 4. Sets the `first_name` of the `Author` to the string representation of the file extension of `path`.
|
||||
/// 5. Pushes the `Author` into the `author` vector of the `Book`.
|
||||
/// 6. Returns a `Vec<Book>` containing the newly created `Book`.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// The function will panic if the input path does not contain a file extension
|
||||
/// (i.e., when `path.extension()` returns `None`).
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```ignore
|
||||
/// use std::path::PathBuf;
|
||||
///
|
||||
/// let path = PathBuf::from("example.txt");
|
||||
/// let books = parse(&path).unwrap();
|
||||
///
|
||||
/// assert_eq!(books.len(), 1);
|
||||
/// assert_eq!(books[0].title, "example.txt");
|
||||
/// assert_eq!(books[0].author[0].first_name, "txt");
|
||||
/// ```
|
||||
pub fn parse(path: &PathBuf) -> Result<Vec<Book>, String> {
|
||||
let mut book = Book::new();
|
||||
|
||||
|
||||
@@ -5,6 +5,54 @@ use std::io::BufReader;
|
||||
use std::path::{Path, PathBuf};
|
||||
use zip::ZipArchive;
|
||||
|
||||
|
||||
/// Parses a ZIP archive to extract a collection of `Book` objects.
|
||||
///
|
||||
/// This function takes a path to a ZIP archive file, reads its contents, and processes
|
||||
/// each file within the archive to extract `Book` objects using a custom parser. If any
|
||||
/// errors occur during file access, archive extraction, or parsing, they are returned as
|
||||
/// a `String`. On success, it returns a vector of `Book` objects contained in the archive.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `path` - A reference to a `Path` representing the file system path to the ZIP archive.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// * `Ok(Vec<Book>)` - A vector containing the `Book` objects successfully parsed
|
||||
/// from the files in the archive.
|
||||
/// * `Err(String)` - An error message if any step in opening the file, reading the archive,
|
||||
/// or parsing the files fails.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function returns an error in the following cases:
|
||||
/// * If the ZIP file cannot be opened.
|
||||
/// * If the ZIP archive cannot be read.
|
||||
/// * If an individual file within the archive cannot be accessed.
|
||||
/// * If the parsing of a file fails.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```ignore
|
||||
/// use std::path::Path;
|
||||
/// use your_crate::parse;
|
||||
///
|
||||
/// let path = Path::new("books_archive.zip");
|
||||
/// match parse(&path) {
|
||||
/// Ok(books) => {
|
||||
/// for book in books {
|
||||
/// println!("Parsed book: {:?}", book);
|
||||
/// }
|
||||
/// }
|
||||
/// Err(e) => eprintln!("Failed to parse books: {}", e),
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// # Dependencies
|
||||
///
|
||||
/// This function relies on the `ZipArchive` for working with ZIP files and a `parsers`
|
||||
/// module for custom file parsing logic.
|
||||
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);
|
||||
|
||||
@@ -1,3 +1,63 @@
|
||||
/*!
|
||||
This module provides an in-memory implementation of a `Repository` for managing books, including their associated authors.
|
||||
It provides mechanisms to add, update, retrieve, and filter books, along with handling relationships to authors.
|
||||
|
||||
# Structs
|
||||
- `Book`: Internal representation of a book for the in-memory repository. It includes fields for metadata such as ID, title, author IDs, language, description, tags, publisher, and timestamps.
|
||||
- `BookRepository`: The main repository struct that provides book and author management. It supports CRUD operations and filtering over books and their metadata.
|
||||
|
||||
# Traits
|
||||
- Implements `Repository<book::Book, BookFilter>` trait to provide repository functionalities for books, such as adding, removing, updating, retrieving, and filtering.
|
||||
|
||||
# Implementation Details
|
||||
|
||||
## `Book` Struct
|
||||
A lightweight representation of a `book::Book` entity used internally in the repository.
|
||||
- It implements conversions for:
|
||||
- **From**: Converts `book::Book` into `Book` by copying metadata and transforming author data.
|
||||
- **Into**: Converts `Book` back into `book::Book`, reconstructing the related author information.
|
||||
|
||||
## `BookRepository` Struct
|
||||
An in-memory implementation of a `Repository` that manages books and their associated authors.
|
||||
- Fields:
|
||||
- `books`: A vector containing all stored books in the repository.
|
||||
- `authors`: A HashMap storing authors by their UUID for quick lookups.
|
||||
- `author_uniques`: A mapping of unique author identifiers to their corresponding UUIDs to ensure uniqueness while managing authors.
|
||||
|
||||
- Methods:
|
||||
- `new() -> Self`: Creates a new, empty `BookRepository`.
|
||||
- `populate_authors(&self, book: &mut book::Book)`: Updates the provided book's author information with detailed metadata (e.g., names) from the repository.
|
||||
- `extract_authors(&mut self, item: &mut book::Book)`: Extracts and stores unique authors from a given book into the repository.
|
||||
|
||||
## Repository Trait Implementation
|
||||
Implements the `Repository<book::Book, BookFilter>` trait for `BookRepository` to provide standard repository features:
|
||||
- `add(&mut self, mut item: book::Book)`: Adds a new book to the repository. Extracts and persists author metadata.
|
||||
- Skips adding a book if a book with the same ID already exists.
|
||||
- `bulk_add(&mut self, items: Vec<book::Book>)`: Adds multiple books to the repository.
|
||||
- Filters out duplicate books before adding.
|
||||
- `remove(&mut self, item: book::Book)`: Removes a book from the repository by its ID.
|
||||
- `get(&self, id: String) -> Option<book::Book>`: Retrieves a book by its ID. Populates the book's author information if it exists.
|
||||
- `update(&mut self, mut item: book::Book)`: Updates an existing book. Extracts author details and persists changes.
|
||||
- `filter(&self, f: BookFilter) -> Box<dyn Iterator<Item = book::Book>>`: Filters books based on the criteria provided in the `BookFilter` struct and returns an iterator over matching books.
|
||||
- Available filters:
|
||||
- Title
|
||||
- Description
|
||||
- Language
|
||||
- Tags
|
||||
- Publisher
|
||||
- Published date
|
||||
- Last updated date
|
||||
- Author (by ID or name)
|
||||
|
||||
# Filtering Logic
|
||||
Filters books based on the provided `BookFilter` parameters:
|
||||
- Matches fields like title, description, language, tags, publisher, and dates.
|
||||
- Supports author-based filtering, allowing searches using either author IDs or names.
|
||||
|
||||
# Notes
|
||||
- Author uniqueness is enforced using a mapping of unique identifiers to `Uuid` (`author_uniques` field) to manage duplicate or overlapping authors efficiently.
|
||||
- Iterators are used for filtered results to allow efficient processing of the filtered data.
|
||||
*/
|
||||
use crate::domain::repository::{BookFilter, Repository};
|
||||
use crate::domain::{author, book};
|
||||
use std::collections::HashMap;
|
||||
|
||||
@@ -2,7 +2,7 @@ use crate::application::application::Application;
|
||||
|
||||
pub mod domain;
|
||||
|
||||
mod application;
|
||||
pub mod application;
|
||||
pub mod infrastructure;
|
||||
|
||||
pub fn demo() -> Application {
|
||||
|
||||
Reference in New Issue
Block a user