diff --git a/CHANGELOG.md b/CHANGELOG.md index ebf569c..e5ad7ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,28 @@ All notable changes to this project will be documented in this file. +## [0.4.5] - 2025-11-11 + +### Added + +- **πŸ” New `search` command** + - Allows searching books by title, author, editor, genre, or language. + - Usage: + ```bash + librius search "dune" + librius search "frank herbert" --short + ``` + - Supports both full (`BookFull`) and compact (`BookShort`) table views. + - Integrated with i18n for localized output messages and help text. + +### Changed + +- Unified search output with `print_info`, `print_ok`, and `print_warn` for consistent message style. +- Renamed `commands/search.rs` β†’ `commands/search_book.rs` to avoid ambiguous glob re-exports. +- Refactored `search_books()` in `db/search.rs` to remove redundant closure for Clippy compliance. + +--- + ## [0.4.1] - 2025-10-22 ### Added diff --git a/Cargo.lock b/Cargo.lock index ccb410e..f8c1fd2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -269,9 +269,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.48" +version = "4.5.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2134bb3ea021b78629caa971416385309e0131b351b25e01dc16fb54e1b5fae" +checksum = "4c26d721170e0295f191a69bd9a1f93efcdb0aff38684b61ab5750468972e5f5" dependencies = [ "clap_builder", "clap_derive", @@ -279,9 +279,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.48" +version = "4.5.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2ba64afa3c0a6df7fa517765e31314e983f51dda798ffba27b988194fb65dc9" +checksum = "75835f0c7bf681bfd05abe44e965760fea999a5286c6eb2d59883634fd02011a" dependencies = [ "anstream", "anstyle", @@ -291,9 +291,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.47" +version = "4.5.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbfd7eae0b0f1a6e63d4b13c9c478de77c2eb546fba158ad50b4203dc24b9f9c" +checksum = "2a0b5487afeab2deb2ff4e03a807ad1a03ac532ff5a2cee5d86884440c7f7671" dependencies = [ "heck", "proc-macro2", @@ -404,14 +404,14 @@ dependencies = [ [[package]] name = "csv" -version = "1.3.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acdc4883a9c96732e4733212c01447ebd805833b7275a73ca3ee080fd77afdaf" +checksum = "52cd9d68cf7efc6ddfaaee42e7288d3a99d613d4b50f76ce9827ae0c6e14f938" dependencies = [ "csv-core", "itoa", "ryu", - "serde", + "serde_core", ] [[package]] @@ -572,9 +572,9 @@ checksum = "52051878f80a721bb68ebfbc930e07b65ba72f2da88968ea5c06fd6ca3d3a127" [[package]] name = "flate2" -version = "1.1.4" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc5a4e564e38c699f2880d3fda590bedc2e69f3f84cd48b457bd892ce61d0aa9" +checksum = "bfe33edd8e85a12a67454e37f8c75e730830d83e313556ab9ebf9ee7fbeb3bfb" dependencies = [ "crc32fast", "libz-rs-sys", @@ -1156,7 +1156,7 @@ dependencies = [ [[package]] name = "librius" -version = "0.4.1" +version = "0.4.5" dependencies = [ "chrono", "clap", @@ -1649,6 +1649,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "165ca6e57b20e1351573e3729b958bc62f0e48025386970b6e4d29e7a7e71f3f" dependencies = [ "bitflags", + "chrono", "fallible-iterator", "fallible-streaming-iterator", "hashlink", @@ -1791,11 +1792,11 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.9" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" +checksum = "e24345aa0fe688594e73770a5f6d1b216508b4f93484c0026d521acd30134392" dependencies = [ - "serde", + "serde_core", ] [[package]] @@ -2136,44 +2137,42 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.23" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" +checksum = "f0dc8b1fb61449e27716ec0e1bdf0f6b8f3e8f6b05391e8497b8b6d7804ea6d8" dependencies = [ - "serde", + "indexmap 2.11.4", + "serde_core", "serde_spanned", "toml_datetime", - "toml_edit", + "toml_parser", + "toml_writer", + "winnow", ] [[package]] name = "toml_datetime" -version = "0.6.11" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" +checksum = "f2cdb639ebbc97961c51720f858597f7f24c4fc295327923af55b74c3c724533" dependencies = [ - "serde", + "serde_core", ] [[package]] -name = "toml_edit" -version = "0.22.27" +name = "toml_parser" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" +checksum = "c0cbe268d35bdb4bb5a56a2de88d0ad0eb70af5384a99d648cd4b3d04039800e" dependencies = [ - "indexmap 2.11.4", - "serde", - "serde_spanned", - "toml_datetime", - "toml_write", "winnow", ] [[package]] -name = "toml_write" -version = "0.1.2" +name = "toml_writer" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" +checksum = "df8b2b54733674ad286d16267dcfc7a71ed5c776e4ac7aa3c3e2561f7c637bf2" [[package]] name = "tower" @@ -2726,15 +2725,12 @@ name = "winnow" version = "0.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" -dependencies = [ - "memchr", -] [[package]] name = "winresource" -version = "0.1.23" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edcacf11b6f48dd21b9ba002f991bdd5de29b2da8cc2800412f4b80f677e4957" +checksum = "f1ef04dd590e94ff7431a8eda99d5ca659e688d60e930bd0a330062acea4608f" dependencies = [ "toml", "version_check", diff --git a/Cargo.toml b/Cargo.toml index 2aef2a1..cd21897 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "librius" -version = "0.4.1" +version = "0.4.5" edition = "2024" authors = ["Alessandro Maestri "] description = "A personal library manager CLI written in Rust." @@ -10,15 +10,6 @@ homepage = "https://github.com/umpire274/librius" repository = "https://github.com/umpire274/librius" keywords = ["books", "library", "cli", "sqlite", "manager"] categories = ["command-line-utilities", "database", "data-structures", "rust-patterns"] -exclude = [ - "target/*", - ".idea/*", - ".github/*", - ".gitignore", - "config/*", - "*.sh", - "*.bat", -] build = "build.rs" [package.metadata.bundle] @@ -27,20 +18,20 @@ identifier = "eu.umpire274.librius" icon = ["res/librius.png"] [dependencies] -clap = { version = "4.5.48", features = ["derive"] } +clap = { version = "4.5.51", features = ["derive"] } serde = { version = "1.0.228", features = ["derive"] } serde_json = "1.0.145" once_cell = "1.21.3" chrono = { version = "0.4.42", features = ["serde"] } serde_yaml = "0.9.33" -rusqlite = { version = "0.37.0", features = ["bundled"] } +rusqlite = { version = "0.37.0", features = ["bundled", "chrono"] } colored = "3.0.0" zip = { version = "6.0.0", optional = true } -flate2 = { version = "1.1.4", optional = true } +flate2 = { version = "1.1.5", optional = true } tar = { version = "0.4.44", optional = true } dirs = "6.0.0" umya-spreadsheet = "2.3.3" -csv = "1.3.1" +csv = "1.4.0" tabled = "0.20.0" reqwest = { version = "0.12.24", features = ["blocking", "json"] } isbn2 = "0.4.0" @@ -53,7 +44,7 @@ flate2 = "1.1.4" tar = "0.4.44" [build-dependencies] -winresource = "0.1.23" +winresource = "0.1.27" [profile.release] opt-level = 3 diff --git a/README.md b/README.md index 8394b5e..a541ea0 100644 --- a/README.md +++ b/README.md @@ -23,25 +23,23 @@ and import/export support. --- -## ✨ New in v0.4.1 - -**πŸ—‘οΈ Book deletion command** - -- Introduced the new `del ` command for removing books from your library. -- Supports both numeric IDs and ISBN codes with automatic detection. -- Added **interactive confirmation** to prevent accidental deletions. -- Added `--force` flag for non-interactive or scripted deletions. -- All deletions are now recorded via `write_log()` for full audit traceability. - -**🌍 Localization & Help** - -- Localized all new messages, confirmations, and help strings in **English** and **Italian**. -- Updated CLI help sections to include the new command and `--force` option. - -**🧰 Developer improvements** - -- Added developer scripts in `/tools` for build and submodule verification. -- Updated private submodule `tools_private` to the latest revision. +### ✨ New in v0.4.5 + +**πŸ” Full-text book search** + +- Introduced the new `search` command: + ```bash + librius search [--short] + ``` + #### Example usage: + ```bash + $ librius search "dune" + $ librius search "frank herbert" --short + ``` +- Performs full-text lookup across **title, author, editor, genre, and language** fields. +- Supports both **compact** (`--short`) and **full** table views. +- Uses the same localized message system (`tr()`) as the rest of the CLI. +- Unified output style with `print_info`, `print_ok`, and `print_warn` for consistent visual feedback. --- @@ -78,74 +76,165 @@ cargo install rtimelogger ## βš™οΈ Features -| Feature | Command | Description | -|:-------------------------|:---------------------------------|:--------------------------------------------------------------------------------------------------------------| -| **List** | `librius list` | Display all books stored in the local database, in full or compact view | -| **Config management** | `librius config` | Manage YAML configuration via `--print`, `--init`, `--edit`, `--editor` | -| **Backup** | `librius backup` | Create plain or compressed database backups (`.sqlite`, `.zip`, `.tar.gz`) | -| **Export** | `librius export` | Export data in CSV, JSON, or XLSX format | -| **Import** | `librius import` | Import data from CSV or JSON files (duplicate-safe via ISBN) | -| **Database migrations** | *(automatic)* | Automatic schema upgrades and integrity checks at startup | -| **Logging system** | *(internal)* | Records all operations and migrations in an internal log table | -| **Multilanguage (i18n)** | `librius --lang ` | Fully localized CLI (commands, help, messages); `--lang` flag and config key | -| **Add book** | `librius add book --isbn ` | Add new books using ISBN lookup via Google Books API | -| **Edit book** | `librius edit book ` | Edit existing records by ID or ISBN; dynamic field generation, language conversion, and plural-aware messages | -| **Delete book** | `del ` | Delete books by ID or ISBN, with interactive confirmation, `--force` flag, and logged deletions | -| **Dynamic help system** | `librius help ` | Ordered and grouped help output using `display_order()` and `next_help_heading()` | +| Feature | Command | Description | +|:-------------------------|:---------------------------------|:---------------------------------------------------------------------------------------------------------------| +| **List** | `librius list` | Display all books stored in the local database, in full or compact view | +| **Search** | `librius search ` | Full-text search across title, author, editor, genre, and language fields; supports `--short` for compact view | +| **Config management** | `librius config` | Manage YAML configuration via `--print`, `--init`, `--edit`, `--editor` | +| **Backup** | `librius backup` | Create plain or compressed database backups (`.sqlite`, `.zip`, `.tar.gz`) | +| **Export** | `librius export` | Export data in CSV, JSON, or XLSX format | +| **Import** | `librius import` | Import data from CSV or JSON files (duplicate-safe via ISBN) | +| **Database migrations** | *(automatic)* | Automatic schema upgrades and integrity checks at startup | +| **Logging system** | *(internal)* | Records all operations and migrations in an internal log table | +| **Multilanguage (i18n)** | `librius --lang ` | Fully localized CLI (commands, help, messages); `--lang` flag and config key | +| **Add book** | `librius add book --isbn ` | Add new books using ISBN lookup via Google Books API | +| **Edit book** | `librius edit book ` | Edit existing records by ID or ISBN; dynamic field generation, language conversion, and plural-aware messages | +| **Delete book** | `del ` | Delete books by ID or ISBN, with interactive confirmation, `--force` flag, and logged deletions | +| **Dynamic help system** | `librius help ` | Ordered and grouped help output using `display_order()` and `next_help_heading()` | --- -## πŸ’» Usage +## πŸ“– Commands Overview + +### πŸ“˜ list + +List all books or a specific book by ID. ```bash -librius list # Full detailed list -librius list --short # Compact list (ID, Title, Author, Editor, Year) -``` +$ librius list [--short] [--id ] [--details] +``` ---- +**Options**: -## 🧱 Example output +- `--short` Compact view +- `--id` Show book by ID +- `--details` Show extended metadata + +### πŸ” search + +Search for books by title, author, editor, genre, or language. ```bash -$ librius list --short +$ librius search [--short] +``` -πŸ“š Personal Library -════════════════════════════════════════════════════════════════════════════════ +**Options**: -β”Œβ”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β” -β”‚ ID β”‚ Title β”‚ Author β”‚ Editor β”‚ Year β”‚ -β”œβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€ -β”‚ 1 β”‚ The Rust Programming Languageβ”‚ Steve Klabnik β”‚ No Starch β”‚ 2018 β”‚ -β”‚ 2 β”‚ Clean Code β”‚ Robert C. Martin β”‚ Pearson β”‚ 2008 β”‚ -β””β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”˜ +- `--short` Show compact view (ID, Title, Author, Editor, Year, ISBN) +- `` Search term +- `--help` Show command help +### βž• add book -# βž• Add a book automatically by ISBN -$ librius add book --isbn 9788820382698 -πŸ“˜ Book β€œLa lingua dell'antico Egitto” successfully added. +Add a new book using its ISBN. -# ✏️ Edit book details -$ librius edit book 9788820382698 --year 2020 -βœ… Field β€œyear” updated successfully (2018 β†’ 2020). -βœ… Book 9788820382698 successfully updated (1 field modified). +```bash +$ librius add book --isbn +``` + +**Options**: + +- `--isbn ` ISBN of the book to add +- `--help` Show command help -# 🌍 Update language (auto conversion from ISO) -$ librius edit book 9788820382698 --lang_book en -βœ… Field β€œlanguage” updated successfully (Italian β†’ English). +### ✏️ edit book + +Edit an existing book by ID or ISBN. + +```bash +$ librius edit book [--title ] [--author <AUTHOR>] [--editor <EDITOR>] [--year <YEAR>] [--genre <GENRE>] [--language <LANGUAGE>] [--isbn <ISBN>] +``` -# πŸ“š List your library (compact view) -$ librius list --short +**Options**: -$ librius del 128 -Sei sicuro di voler eliminare il libro 128? [y/N]: y -βœ… Libro 128 eliminato correttamente. +- `<ID/ISBN>` ID or ISBN of the book to edit +- `--title <TITLE>` New title +- `--author <AUTHOR>` New author +- `--editor <EDITOR>` New editor +- `--year <YEAR>` New publication year +- `--genre <GENRE>` New genre +- `--language <LANGUAGE>` New language +- `--isbn <ISBN>` New ISBN +- `--help` Show command help -$ librius del 9788820382698 --force -βœ… Book 9788820382698 deleted successfully. +### ❌ delete book +Delete a book by ID or ISBN. +```bash +$ librius del <ID/ISBN> [--force] ``` +**Options**: + +- `<ID/ISBN>` ID or ISBN of the book to delete +- `--force` Skip confirmation prompt +- `--help` Show command help + +### βš™οΈ config + +Manage application configuration. + +```bash +$ librius config [--print] [--init] [--edit] [--editor <EDITOR>] +``` + +**Options**: + +- `--print` Print current configuration +- `--init` Create default config file +- `--edit` Open config file in editor +- `--editor <EDITOR>` Specify editor (default: `$EDITOR` or `nano` + +### πŸ’Ύ backup + +Create a backup of the database. + +```bash +$ librius backup [--compress] +``` + +**Options**: + +- `--compress` Create a compressed backup (`.zip` or `.tar.gz`) +- `--help` Show command help + +### πŸ“€ export + +Export library data to CSV, JSON, or XLSX. + +```bash +$ librius export [--csv | --json | --xlsx] [-o|--output <FILE>] +``` + +**Options**: + +- `--csv` Export as CSV (default) +- `--json` Export as JSON +- `--xlsx` Export as XLSX +- `-o, --output <FILE>` Specify output file path +- `--help` Show command help + +### πŸ“₯ import + +Import library data from CSV or JSON. + +```bash +$ librius import --file <FILE> [--json] [--csv] [-d|--delimiter <CHAR>] +``` + +**Options**: + +- `--file <FILE>` Path to input file +- `--json` Specify if the input file is JSON (default is CSV) +- `--csv` Specify if the input file is CSV +- `-d, --delimiter <CHAR>` Specify CSV delimiter (default: `,`) +- `--help` Show command help + +### 🧠 Note + +- Every command is fully **localized** in english (default) and italian. + --- ## 🌍 Multilanguage support (i18n) @@ -211,7 +300,7 @@ Each `.json` file contains key–value pairs like: Variables can be inserted at runtime: ```rust -tr_with("db.path.open_existing", & [("path", & db_path)]); +tr_with!("db.path.open_existing", & [("path", & db_path)]); ``` --- @@ -370,7 +459,7 @@ Librius now follows a standard Rust modular structure: ### Example import ```rust -use librius::{build_cli, handle_list; tr}; +use librius::{build_cli, handle_list, tr}; ``` --- @@ -411,7 +500,7 @@ Librius automatically verifies and upgrades the SQLite database schema at startu The latest migration adds a unique index on `isbn` to guarantee that duplicate imports are ignored safely. -```sql +```sqlite CREATE UNIQUE INDEX IF NOT EXISTS idx_books_isbn ON books(isbn); ``` diff --git a/src/cli.rs b/src/cli.rs index 5cac9b9..3aa6e34 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,8 +1,7 @@ -use crate::commands::{handle_config, handle_edit_book, handle_list}; use crate::fields::EDITABLE_FIELDS; use crate::i18n::{tr, tr_s}; -use crate::tr_with; use crate::utils::print_err; +use crate::{handle_config, handle_edit_book, handle_list, handle_search, tr_with}; use clap::{Arg, ArgAction, Command, Subcommand}; use rusqlite::Connection; @@ -75,6 +74,29 @@ pub fn build_cli() -> Command { .display_order(13), ), ) + // πŸ” search command + .subcommand( + Command::new("search") + .about(tr_s("search_about")) + .display_order(15) + .arg( + Arg::new("query") + .help(tr_s("search_query_help")) + .required(true) + .value_name("QUERY") + .num_args(1) + .help_heading("Search-specific options") + .display_order(16), + ) + .arg( + Arg::new("short") + .long("short") + .help(tr_s("search_short_help")) + .action(ArgAction::SetTrue) + .help_heading("Search-specific options") + .display_order(17), + ), + ) // βš™οΈ config command .subcommand( Command::new("config") @@ -324,6 +346,14 @@ pub fn run_cli( let details = matches.get_flag("details"); handle_list(conn, short, id, details)?; Ok(()) + } else if let Some(("search", sub_m)) = matches.subcommand() { + if let Some(query) = sub_m.get_one::<String>("query") { + let short = sub_m.get_flag("short"); + handle_search(conn, query, short)?; + } else { + print_err(&tr("search_query_help")); + } + Ok(()) } else if let Some(("config", sub_m)) = matches.subcommand() { let init = sub_m.get_flag("init"); let print = sub_m.get_flag("print"); diff --git a/src/commands/mod.rs b/src/commands/mod.rs index 77684e0..48400dd 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -13,6 +13,7 @@ pub mod edit_book; pub mod export; pub mod import; pub mod list; +pub mod search_book; pub use add::handle_add; pub use add_book::handle_add_book; @@ -26,3 +27,4 @@ pub use export::handle_export_xlsx; pub use import::handle_import_csv; pub use import::handle_import_json; pub use list::handle_list; +pub use search_book::handle_search; diff --git a/src/commands/search_book.rs b/src/commands/search_book.rs new file mode 100644 index 0000000..88c5e1b --- /dev/null +++ b/src/commands/search_book.rs @@ -0,0 +1,26 @@ +use crate::db::search_books; +use crate::i18n::tr; +use crate::models::{Book, BookFull, BookShort}; +use crate::print_warn; +use crate::utils::table::build_table; +use rusqlite::Connection; +use std::error::Error; + +pub fn handle_search(conn: &Connection, query: &str, short: bool) -> Result<(), Box<dyn Error>> { + let results: Vec<Book> = search_books(conn, query)?; + + if results.is_empty() { + print_warn(&tr("search.no_results")); + return Ok(()); + } + + if short { + let wrapped: Vec<BookShort> = results.iter().map(BookShort).collect(); + println!("{}", build_table(&wrapped)); + } else { + let wrapped: Vec<BookFull> = results.iter().map(BookFull).collect(); + println!("{}", build_table(&wrapped)); + } + + Ok(()) +} diff --git a/src/db/mod.rs b/src/db/mod.rs index 98af129..a63d314 100644 --- a/src/db/mod.rs +++ b/src/db/mod.rs @@ -11,7 +11,9 @@ pub mod books; pub mod load_db; pub mod migrate_db; +pub mod search; pub use books::{get_book_fields, update_book_by_id, update_book_by_isbn}; pub use load_db::{ensure_schema, init_db, start_db}; pub use migrate_db::{MigrationResult, run_migrations}; +pub use search::search_books; diff --git a/src/db/search.rs b/src/db/search.rs new file mode 100644 index 0000000..f6d790b --- /dev/null +++ b/src/db/search.rs @@ -0,0 +1,28 @@ +use crate::models::Book; +use rusqlite::{Connection, Result}; + +pub fn search_books(conn: &Connection, query: &str) -> Result<Vec<Book>> { + let like = format!("%{}%", query); + + let mut stmt = conn.prepare( + r#" + SELECT id, title, author, editor, year, isbn, language, pages, + genre, summary, room, shelf, row, position, added_at + FROM books + WHERE title LIKE ?1 + OR author LIKE ?1 + OR editor LIKE ?1 + OR genre LIKE ?1 + OR language LIKE ?1 + ORDER BY title COLLATE NOCASE ASC; + "#, + )?; + + let rows = stmt.query_map([like], Book::from_row)?; + + let mut out = Vec::new(); + for r in rows { + out.push(r?); + } + Ok(out) +} diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json index f25ff33..342226d 100644 --- a/src/i18n/locales/en.json +++ b/src/i18n/locales/en.json @@ -169,4 +169,8 @@ "del.book.cancelled": "Deletion of book {key} cancelled.", "help.del.force": "Delete without asking for confirmation.", "log.record.unable_to_write": "Unable to record log entry: {log_error}", + "search_about": "Search for books by title, author, editor, genre or language.", + "search_query_help": "The keyword or phrase to search for.", + "search_short_help": "Show compact view (ID, Title, Author, Editor, Year, ISBN).", + "search.no_results": "No results found." } diff --git a/src/i18n/locales/it.json b/src/i18n/locales/it.json index 083b462..31edac6 100644 --- a/src/i18n/locales/it.json +++ b/src/i18n/locales/it.json @@ -168,5 +168,9 @@ "del.book.confirm": "Sei sicuro di voler eliminare il libro {key}? [y/N]:", "del.book.cancelled": "Eliminazione del libro {key} annullata.", "help.del.force": "Elimina senza chiedere conferma.", - "log.record.unable_to_write": "Impossibile scrivere il log: {log_error}" + "log.record.unable_to_write": "Impossibile scrivere il log: {log_error}", + "search_about": "Cerca libri per titolo, autore, editore, genere o lingua.", + "search_query_help": "La parola o frase da cercare.", + "search_short_help": "Mostra vista compatta (ID, Titolo, Autore, Editore, Anno, ISBN).", + "search.no_results": "Nessun risultato trovato." } diff --git a/src/models/book.rs b/src/models/book.rs index cccb0db..dec2725 100644 --- a/src/models/book.rs +++ b/src/models/book.rs @@ -93,3 +93,27 @@ impl<'a> Tabled for BookShort<'a> { ] } } + +use rusqlite::Row; + +impl Book { + pub fn from_row(row: &Row) -> rusqlite::Result<Self> { + Ok(Self { + id: row.get("id")?, + title: row.get("title")?, + author: row.get("author")?, + editor: row.get("editor")?, + year: row.get("year")?, + isbn: row.get("isbn")?, + language: row.get("language")?, + pages: row.get("pages")?, + genre: row.get("genre")?, + summary: row.get("summary")?, + room: row.get("room")?, + shelf: row.get("shelf")?, + row: row.get("row")?, + position: row.get("position")?, + added_at: row.get("added_at")?, + }) + } +} diff --git a/tools_private b/tools_private index e388cb0..7f8e961 160000 --- a/tools_private +++ b/tools_private @@ -1 +1 @@ -Subproject commit e388cb058f9baac622e2255433ee7984eea734d8 +Subproject commit 7f8e9613c5fd15fb70290274731f58d7228197bc