From 93b2c5c9fc87f65d3d738a3eae4870109cf15e62 Mon Sep 17 00:00:00 2001 From: Andrew Potter Date: Thu, 18 May 2023 13:01:02 -0700 Subject: [PATCH 1/5] Bookmark proof of concept Adds JSON library --- debian/control | 3 ++- dregarnuhr.spec | 1 + meson.build | 10 +++++++++- src/meson.build | 3 ++- src/zip.cpp | 6 +++--- 5 files changed, 17 insertions(+), 6 deletions(-) diff --git a/debian/control b/debian/control index 0ebd4d3..2460a89 100644 --- a/debian/control +++ b/debian/control @@ -17,7 +17,8 @@ Build-Depends: debhelper-compat (= 13), libctre-dev, libmagicenum-dev, libminiz3-dev, - libxml++5.0-dev + libxml++5.0-dev, + libjsoncpp-dev Standards-Version: 4.6.0 Section: libs Homepage: https://github.com/talisein/dregarnuhr diff --git a/dregarnuhr.spec b/dregarnuhr.spec index f3827b6..e86372c 100644 --- a/dregarnuhr.spec +++ b/dregarnuhr.spec @@ -20,6 +20,7 @@ BuildRequires: libpng-devel BuildRequires: libjpeg-turbo-devel BuildRequires: miniz-devel BuildRequires: outcome-devel +BuildRequires: jsoncpp-devel BuildRequires: libxml++50-devel >= 5.0.2 BuildRequires: boost-ext-ut-static BuildRequires: ctre-static diff --git a/meson.build b/meson.build index a97072d..7c314ba 100644 --- a/meson.build +++ b/meson.build @@ -29,7 +29,7 @@ else cpp23_args += '-fexperimental-library' add_global_link_arguments('-stdlib=libc++', language: 'cpp') - if get_option('buildtype') == 'debug' + if get_option('buildtype') == 'debug' and cpp_compiler.get_linker_id() == 'ld.bfd' cpp23_args += '-gdwarf-4' add_global_arguments('-gdwarf-4', language: 'c') endif @@ -50,8 +50,13 @@ cpp_compiler.has_header_symbol('version', '__cpp_lib_integer_comparison_function cpp_compiler.has_header_symbol('version', '__cpp_lib_expected', required: true, args: cpp23_args) cpp_compiler.has_header_symbol('version', '__cpp_size_t_suffix', args: cpp23_args) cpp_compiler.has_header_symbol('version', '__cpp_lib_ranges_zip', args: cpp23_args) +cpp_compiler.has_header_symbol('version', '__cpp_lib_ranges_enumerate', args: cpp23_args) +cpp_compiler.has_header_symbol('version', '__cpp_lib_ranges_to_container', args: cpp23_args) +cpp_compiler.has_header_symbol('version', '__cpp_lib_ranges_to', args: cpp23_args) cpp_compiler.has_header_symbol('version', '__cpp_lib_stacktrace', args: cpp23_args) cpp_compiler.has_header_symbol('version', '__cpp_lib_spanstream', args: cpp23_args) +cpp_compiler.has_header_symbol('version', '__cpp_lib_print', args: cpp23_args) + has_chrono = 1 == cpp_compiler.compute_int('__cpp_lib_chrono >= 201907L', prefix: '#include', args: cpp23_args) @@ -136,5 +141,8 @@ ctre_dep = dependency('ctre', magic_enum_dep = dependency('magic_enum', fallback: ['magic_enum']) +json_dep = dependency('jsoncpp', + fallback: ['jsoncpp']) + subdir('src') subdir('test') diff --git a/src/meson.build b/src/meson.build index 932cce6..e0914a4 100644 --- a/src/meson.build +++ b/src/meson.build @@ -4,7 +4,8 @@ conf_data = configuration_data() conf_data.set('IS_WINDOWS', host_machine.system() == 'windows' ? 'true' : 'false') conf_data.set10('HAVE_CHRONO', has_chrono) -if cpp_compiler.has_header_symbol('ios', 'std::ios_base::noreplace', args: cpp23_args) + +if cpp_compiler.has_header_symbol('version', '__cpp_lib_ios_noreplace', args: cpp23_args) or cpp_compiler.has_header_symbol('ios', 'std::ios_base::noreplace', args: cpp23_args) conf_data.set('NOREPLACE', 'std::ios_base::noreplace') elif cpp_compiler.has_header_symbol('ios', 'std::ios_base::__noreplace', args: cpp23_args) conf_data.set('NOREPLACE', 'std::ios_base::__noreplace') diff --git a/src/zip.cpp b/src/zip.cpp index 9c2a5c0..1dfe062 100644 --- a/src/zip.cpp +++ b/src/zip.cpp @@ -20,7 +20,7 @@ namespace { static constexpr time_t ZIP_TIME_UNSET = 312796800; extern "C" { - size_t + static size_t _extract_to_std_string(void *opaque, mz_uint64, const void *buf, size_t n) { auto str = static_cast(opaque); @@ -32,7 +32,7 @@ namespace { } } - size_t + static size_t _extract_to_std_basic_string_uchar(void *opaque, mz_uint64, const void *buf, size_t n) { auto str = static_cast*>(opaque); @@ -44,7 +44,7 @@ namespace { } } - size_t + static size_t _mz_file_read_func(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n) { std::istream *in = static_cast(pOpaque); From 67336d0b28fed753dfdd446d83cd1c49bcc262b7 Mon Sep 17 00:00:00 2001 From: Andrew Potter Date: Thu, 18 May 2023 13:09:06 -0700 Subject: [PATCH 2/5] Add initial json stubs for retainers and dieties --- .github/workflows/main.yml | 5 +++++ res/dieties.json | 15 +++++++++++++++ res/retainers.json | 18 ++++++++++++++++++ test/meson.build | 7 +++++++ 4 files changed, 45 insertions(+) create mode 100644 res/dieties.json create mode 100644 res/retainers.json diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 970631b..576c6dc 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -178,6 +178,11 @@ jobs: meson introspect --targets build | jq '.[] | select(.type == "executable" and .subproject == null) | .filename[]' | xargs ldd continue-on-error: true + - if: ${{ matrix.flavor == 'ubuntu' }} + name: Verify valid json + run: | + jq . res/*.json + - if: ${{ matrix.flavor == 'mac' }} name: ldd macos run: otool -L build/src/dregarnuhr diff --git a/res/dieties.json b/res/dieties.json new file mode 100644 index 0000000..e712cb8 --- /dev/null +++ b/res/dieties.json @@ -0,0 +1,15 @@ +[ + { + "name": "Angriff", + "text": "God of War, subordinate to Liedenshaft", + "subordinateTo": "Liedenshaft", + "spoilerUntil": "P3V3", + "pattern": "Angriff", + "antiPattern": "God of War Angriff" +}, { + "name": "Vantole", + "text": "God of Alcohol", + "spoilerUntil": "P1V3", + "pattern": "Vantole" +} +] diff --git a/res/retainers.json b/res/retainers.json new file mode 100644 index 0000000..9bb660f --- /dev/null +++ b/res/retainers.json @@ -0,0 +1,18 @@ +[ + { + "name": "Leberecht", + "text": "Scholar retainer for Florencia, Hartmut's father, and husband to Ottilie.", + "lord": "Florencia", + "spoilerUntil": "P4V7", + "profession": "Scholar", + "pattern": "Leberecht" +}, { + "name": "Alexis", + "text": "Knight retainer for Wilfried. Son of Geibe Kirnberger.", + "lord": "Wilfried", + "spoilerUntil": "P4V2", + "profession": "Knight", + "pattern": "Alexis", + "divineProtections": ["Angriff", "Steifebrise"] +} +] diff --git a/test/meson.build b/test/meson.build index bf2efc5..f6dc24e 100644 --- a/test/meson.build +++ b/test/meson.build @@ -12,3 +12,10 @@ test('utils', utils_test) args_test = executable('args_test', 'args.cpp', dependencies: [boostut_dep, dep]) test('args', args_test) + +jq = find_program('jq', required: false) + +if jq.found() + test('dieties', jq, args: ['.', '../res/dieties.json']) + test('retainers', jq, args: ['.', '../res/retainers.json']) +endif From 8fbc8b45c55cdc4178a86cd5b3e28d70a7c03c73 Mon Sep 17 00:00:00 2001 From: Andrew Potter Date: Thu, 18 May 2023 13:38:00 -0700 Subject: [PATCH 3/5] Add --search option --- src/args.cpp | 24 ++++++++++++++++++++++++ src/args.h | 3 +++ test/args.cpp | 14 ++++++++++++++ 3 files changed, 41 insertions(+) diff --git a/src/args.cpp b/src/args.cpp index 927220a..387f5ec 100644 --- a/src/args.cpp +++ b/src/args.cpp @@ -392,6 +392,16 @@ parse(int argc, char** argv) log_info("This executable works."); options.command = args::command_e::TEST; } + + options.search_pattern = utils::find_if_optarg(args_options, "--search="sv).and_then([](std::string&& pattern) -> std::optional { + return std::make_optional(std::move(pattern), std::regex_constants::icase); + }); + + if (options.search_pattern) { + options.command = args::command_e::SEARCH; + } + + /* End of option checking, now set defaults */ if (options.omnibus_type) { options.do_nested = true; if (find (args_options, "--no-nested"sv) != args_options.end()) { @@ -451,6 +461,20 @@ parse(int argc, char** argv) } log_verbose("Verified output dir: ", options.output_dir.string()); } + + if (args::command_e::SEARCH == options.command) { + if (std::distance(args_files.begin(), args_files.end()) != 1) { + usage(argv[0]); + return std::unexpected(std::make_error_code(std::errc::invalid_argument)); + } + + auto it = args_files.begin(); + options.input_dir = *it; + if (auto res = verify_input_directory(options.input_dir); !res.has_value()) { + return res; + } + log_verbose("Verified input dir: ", options.input_dir.string()); + } return {}; } diff --git a/src/args.h b/src/args.h index 9f15af6..0a3023f 100644 --- a/src/args.h +++ b/src/args.h @@ -18,6 +18,7 @@ struct args { NORMAL, DUMP, + SEARCH, TEST } command; @@ -40,6 +41,8 @@ struct args std::optional jpg_scale; std::optional omnibus_type; std::optional compression_level; + std::optional search_pattern; + std::optional search_antipattern; }; std::ostream& operator<<(std::ostream& os, args::command_e c); diff --git a/test/args.cpp b/test/args.cpp index 08d51a1..a278c24 100644 --- a/test/args.cpp +++ b/test/args.cpp @@ -149,4 +149,18 @@ int main() { } | std::vector>{ {"--base=", "ascendence-of-a-bookworm"s}, {"--base=cutey", "cutey"s}, }; + + "search"_test = [] { + const char *argv[] = {"dregarnuhr", "--search=vantole", ".."}; + auto res = parse(sizeof(argv)/sizeof(argv[0]), const_cast(argv)); + expect(res.has_value()); + expect(get_options()->search_pattern.has_value()); + }; + + "search no input"_test = [] { + const char *argv[] = {"dregarnuhr", "--search=vantole"}; + auto res = parse(sizeof(argv)/sizeof(argv[0]), const_cast(argv)); + expect(not res.has_value()); + }; + } From 28f4e3180be2d4ea95834977ec52fbfa552b9dc0 Mon Sep 17 00:00:00 2001 From: Andrew Potter Date: Mon, 22 May 2023 18:48:49 -0700 Subject: [PATCH 4/5] Add search option --- src/args.cpp | 71 ++++++++++++++++++------------------ src/args.h | 3 +- src/book_searcher.cpp | 84 +++++++++++++++++++++++++++++++++++++++++++ src/book_searcher.h | 20 +++++++++++ src/bookmaker.h | 2 -- src/epub.h | 2 ++ src/log.h | 8 +++++ src/main.cpp | 69 +++++++++++++++++++---------------- src/meson.build | 1 + 9 files changed, 189 insertions(+), 71 deletions(-) create mode 100644 src/book_searcher.cpp create mode 100644 src/book_searcher.h diff --git a/src/args.cpp b/src/args.cpp index 387f5ec..1f9d53c 100644 --- a/src/args.cpp +++ b/src/args.cpp @@ -1,14 +1,18 @@ -#include -#include -#include -#include #include +#include +#include #include +#include +#include +#include #include -#include +#include + +#include + +#include "args.h" #include "ctre.hpp" #include "config.h" -#include "args.h" #include "log.h" #include "miniz.h" #include "updates.h" @@ -160,7 +164,7 @@ namespace { } // anonymous namespace -std::expected +outcome::result parse(int argc, char** argv) { using namespace std::string_view_literals; @@ -171,7 +175,7 @@ parse(int argc, char** argv) options.command = args::command_e::NORMAL; if (argc < 2) { usage(argv[0]); - return std::unexpected(std::make_error_code(std::errc::invalid_argument)); + return std::errc::invalid_argument; } auto args_options { views::filter(args_sv, [](const auto &sv) { return sv.starts_with("--"sv); }) }; auto args_files { views::filter(args_sv, [](const auto &sv) { return !sv.starts_with("--"sv); }) }; @@ -208,7 +212,7 @@ parse(int argc, char** argv) log_info("Filename prefix set to ", std::quoted(*options.prefix)); if (options.prefix->empty() && (!options.suffix || options.suffix->empty())) { log_error("No suffix and empty prefix defined. This would create files with the same name as the input; that will make things too confusing!"); - return std::unexpected(std::make_error_code(std::errc::invalid_argument)); + return std::errc::invalid_argument; } } @@ -239,10 +243,10 @@ parse(int argc, char** argv) }); } catch (std::system_error &e) { log_error("Failed to prepare regex name filter: ", e.what()); - return std::unexpected(e.code()); + return e.code(); } catch (std::exception &e) { log_error("Failed to prepare regex name filter: ", e.what()); - return std::unexpected(std::make_error_code(std::errc::invalid_argument)); + return std::errc::invalid_argument; } } @@ -252,7 +256,7 @@ parse(int argc, char** argv) if (q.has_value()) { options.jpg_quality = std::clamp(q.value(), 1, 100); } else { - return std::unexpected(q.error()); + return q.error(); } } @@ -261,7 +265,7 @@ parse(int argc, char** argv) if (s.has_value()) { options.jpg_scale = std::clamp(s.value(), 1, 16); } else { - return std::unexpected(s.error()); + return s.error(); } } @@ -269,7 +273,7 @@ parse(int argc, char** argv) if (options.title) { if (options.title.value().size() == 0) { log_error("Can not set a blank title"); - return std::unexpected(std::make_error_code(std::errc::invalid_argument)); + return std::errc::invalid_argument; } log_info("Title set to ", std::quoted(options.title.value())); } @@ -294,7 +298,7 @@ parse(int argc, char** argv) options.omnibus_type = omnibus::ALL; } else { log_error("Unrecognized omnibus value: ", type); - return std::unexpected(std::make_error_code(std::errc::invalid_argument)); + return std::errc::invalid_argument; } log_info("Omnibus ", *options.omnibus_type, " selected."); } @@ -331,22 +335,22 @@ parse(int argc, char** argv) auto status = fs::status(path, ec); if (ec) { log_error("Cover ", path, " can't be read: ", ec); - return std::unexpected(ec); + return ec; } if (!fs::exists(status)) { log_error("Cover ", path, " doesn't exist."); - return std::unexpected(std::make_error_code(std::errc::no_such_file_or_directory)); + return std::errc::no_such_file_or_directory; } if (!fs::is_regular_file(status)) { log_error("Cover ", path, " isn't a regular file."); - return std::unexpected(std::make_error_code(std::errc::invalid_argument)); + return std::errc::invalid_argument; } if (!ctre::match<"[.]jpg", ctre::case_insensitive>(path.extension().string())) { log_error("Cover ", path, " isn't a .jpg"); - return std::unexpected(std::make_error_code(std::errc::invalid_argument)); + return std::errc::invalid_argument; } options.cover = path; } @@ -362,13 +366,13 @@ parse(int argc, char** argv) if (ec == std::errc()) { // success if (ptr != std::to_address(std::end(*level))) { log_error("Compression level ", std::quoted(*level), " is invalid."); - return std::unexpected(std::make_error_code(std::errc::invalid_argument)); + return std::errc::invalid_argument; } options.compression_level = std::clamp(l, MZ_NO_COMPRESSION, MZ_UBER_COMPRESSION); log_info("Compression level set to ", *options.compression_level); } else { log_error("Compression level ", std::quoted(*level), " is invalid: ", std::make_error_code(ec)); - return std::unexpected(std::make_error_code(ec)); + return std::make_error_code(ec); } } } @@ -426,7 +430,7 @@ parse(int argc, char** argv) const auto num_files = std::distance(args_files.begin(), args_files.end()); if (num_files == 0) { usage(argv[0]); - return std::unexpected(std::make_error_code(std::errc::invalid_argument)); + return std::errc::invalid_argument; } auto it = args_files.begin(); @@ -438,14 +442,14 @@ parse(int argc, char** argv) } if (auto res = verify_input_file(options.input_file); !res.has_value()) { - return res; + return res.error(); } } if (args::command_e::NORMAL == options.command) { if (std::distance(args_files.begin(), args_files.end()) != 2) { usage(argv[0]); - return std::unexpected(std::make_error_code(std::errc::invalid_argument)); + return std::errc::invalid_argument; } auto it = args_files.begin(); @@ -453,11 +457,11 @@ parse(int argc, char** argv) options.output_dir = *it; if (auto res = verify_input_directory(options.input_dir); !res.has_value()) { - return res; + return res.error(); } log_verbose("Verified input dir: ", options.input_dir.string()); if (auto res = verify_output_directory(options.input_dir, options.output_dir); !res) { - return std::unexpected(res.error()); + return res.error(); } log_verbose("Verified output dir: ", options.output_dir.string()); } @@ -465,28 +469,21 @@ parse(int argc, char** argv) if (args::command_e::SEARCH == options.command) { if (std::distance(args_files.begin(), args_files.end()) != 1) { usage(argv[0]); - return std::unexpected(std::make_error_code(std::errc::invalid_argument)); + return std::errc::invalid_argument; } auto it = args_files.begin(); options.input_dir = *it; if (auto res = verify_input_directory(options.input_dir); !res.has_value()) { - return res; + return res.error(); } log_verbose("Verified input dir: ", options.input_dir.string()); } - return {}; + return outcome::success(); } std::ostream& operator<<(std::ostream& os, args::command_e c) { - switch (c) { - case args::command_e::DUMP: os << "Command::DUMP"; break; - case args::command_e::NORMAL: os << "Command::NORMAL"; break; - case args::command_e::TEST: os << "Command::TEST"; break; - default: - os << "Command:UNIMPLMENTED?"; break; - } - return os; + return magic_enum::ostream_operators::operator<<(os, c); } diff --git a/src/args.h b/src/args.h index 0a3023f..dbc78c6 100644 --- a/src/args.h +++ b/src/args.h @@ -4,7 +4,6 @@ #include #include #include -#include #include "outcome/result.hpp" #include "volumes.h" @@ -49,4 +48,4 @@ std::ostream& operator<<(std::ostream& os, args::command_e c); const args* get_options(); -std::expected parse(int argc, char **argv); +outcome::result parse(int argc, char **argv); diff --git a/src/book_searcher.cpp b/src/book_searcher.cpp new file mode 100644 index 0000000..29cff84 --- /dev/null +++ b/src/book_searcher.cpp @@ -0,0 +1,84 @@ +#include + +#include "outcome/try.hpp" +#include "libxml++/libxml++.h" + +#include "book_searcher.h" +#include "log.h" +#include "libxml/tree.h" + +namespace +{ + struct text_node_wrapper_deleter + { + void operator()(xmlpp::Node* node) const + { + /* Stolen xmlpp::Node::free_wrappers, since its broken for TextNodes */ + auto textnode = dynamic_cast(node); + if (!textnode) return; + auto cobj = textnode->cobj(); + for (auto child = cobj->children; child; child = child->next) { + xmlpp::Node::free_wrappers(child); + } + delete textnode; + cobj->_private = nullptr; + } + }; + + using text_node_p = std::unique_ptr; +} + +book_searcher::book_searcher(epub::books_t&& books, + epub::readers_t&& book_readers) : + whitespace("[[:space:]]*"), + src_books(std::move(books)), src_readers(std::move(book_readers)) +{ +} + +OUTCOME_V2_NAMESPACE::result> +book_searcher::search() const +{ + if (!get_options()->search_pattern) { + return std::errc::invalid_argument; + } + + std::vector matched_xpaths; + + for (const auto& [vol, reader_p] : src_readers) { + OUTCOME_TRY(auto filenames, reader_p->zip.get_files()); + int matched = 0; + for (auto filename : filenames | std::views::filter([] (const auto &f) { return f.ends_with(".xhtml"); })) { + OUTCOME_TRY(auto idx, reader_p->zip.locate_file(filename.c_str())); + OUTCOME_TRY(auto raw_xhtml, reader_p->zip.extract_raw(idx)); + xmlpp::TextReader text_reader(raw_xhtml.data(), raw_xhtml.size()); + try { + auto&& pattern = get_options()->search_pattern.value(); + std::string previous_value; + while (text_reader.read()) { + if (text_reader.has_value()) { + auto node_text = text_reader.get_value(); + if(std::regex_search(node_text, pattern)) { + log_info("-----------"); + if (!previous_value.empty()) log_info(vol, ' ', filename, ": ", previous_value); + log_info(vol, ' ', filename, ": ", node_text); + const text_node_p node(text_reader.get_current_node()); + matched_xpaths.push_back(node->get_path()); + matched = 1; + } else if (matched > 0) { + if (!std::regex_match(node_text, whitespace)) { + log_info(vol, ' ', filename, ": ", node_text); + --matched; + } + } + + if (!std::regex_match(node_text, whitespace)) { + previous_value = node_text; + } + } + } + } catch (...) { + } + } + } + return matched_xpaths; +} diff --git a/src/book_searcher.h b/src/book_searcher.h new file mode 100644 index 0000000..a648f65 --- /dev/null +++ b/src/book_searcher.h @@ -0,0 +1,20 @@ +#pragma once + +#include + +#include "epub.h" +#include "outcome/result.hpp" + +class book_searcher +{ +public: + book_searcher(epub::books_t&& books, + epub::readers_t&& book_readers); + + OUTCOME_V2_NAMESPACE::result> search() const; + +private: + const std::regex whitespace; + epub::books_t src_books; + epub::readers_t src_readers; +}; diff --git a/src/bookmaker.h b/src/bookmaker.h index 1b48ecf..564b7b3 100644 --- a/src/bookmaker.h +++ b/src/bookmaker.h @@ -11,8 +11,6 @@ namespace epub namespace fs = std::filesystem; using outcome::result; using definition_t = std::span; - using books_t = std::map; - using readers_t = std::map>; class bookmaker { diff --git a/src/epub.h b/src/epub.h index 0eee89f..1b312ff 100644 --- a/src/epub.h +++ b/src/epub.h @@ -108,5 +108,7 @@ namespace epub }; + using books_t = std::map; + using readers_t = std::map>; } diff --git a/src/log.h b/src/log.h index c8df7e8..63b2f09 100644 --- a/src/log.h +++ b/src/log.h @@ -36,6 +36,14 @@ void _log_helper(std::ostream& out, bool autonl, T t) out << sv << '\n'; return; } + } else if constexpr (std::is_same_v, std::string>) { + if ('\n' == t.back()) { + out << t; + } else { + out << t; + if (autonl) + out << '\n'; + } } else { out << t; if (autonl) diff --git a/src/main.cpp b/src/main.cpp index 601ac1b..6139a13 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,3 +1,4 @@ +#include #include #include #include @@ -18,6 +19,7 @@ #include "config.h" #include "bookmaker.h" #include "updates.h" +#include "book_searcher.h" #if HAVE_CHRONO #include @@ -29,7 +31,8 @@ namespace fs = std::filesystem; -result print_books(const fs::path& input_dir) +std::expected, std::error_code> +search_input_directory(const fs::path& input_dir) { const fs::path epub_ext{".epub"}; epub::readers_t book_readers; @@ -64,6 +67,34 @@ result print_books(const fs::path& input_dir) books.emplace(vol.value(), std::move(book.value())); } } + if (!books.empty()) { + return std::make_pair(std::move(book_readers), std::move(books)); + } else { + return std::unexpected(std::make_error_code(std::errc::no_such_file_or_directory)); + } +} + +result search_books(const fs::path& input_dir) +{ + auto search_res = search_input_directory(input_dir); + if (!search_res.has_value()) { + return search_res.error(); + } + auto&& [book_readers, books] = search_res.value(); + + book_searcher searcher {std::move(books), std::move(book_readers)}; + OUTCOME_TRY(searcher.search()); + + return outcome::success(); +} + +result print_books(const fs::path& input_dir) +{ + auto search_res = search_input_directory(input_dir); + if (!search_res.has_value()) { + return search_res.error(); + } + auto&& [book_readers, books] = search_res.value(); if (books.end() == books.find(volume::FB1)) { log_info("Couldn't find Fanbook 1, so those chapters will be skipped in the new epubs."); @@ -163,33 +194,6 @@ void do_dump() } } -namespace { - extern "C" { - void _xmlStructuredErrorFunc (void *, xmlErrorPtr error) - { - switch (error->level) { - case XML_ERR_NONE: - log_info("libxml2: ", xmlpp::format_xml_error(error)); - break; - case XML_ERR_WARNING: - log_info("Warning: libxml2: ", xmlpp::format_xml_error(error)); - break; - case XML_ERR_ERROR: - case XML_ERR_FATAL: - log_error("libxml2: ", xmlpp::format_xml_error(error)); - break; - default: - std::unreachable(); - } - } - } -} - -void init() -{ - xmlSetStructuredErrorFunc(nullptr, _xmlStructuredErrorFunc); -} - int main(int argc, char* argv[]) { try { @@ -209,8 +213,6 @@ int main(int argc, char* argv[]) return EXIT_SUCCESS; } - init(); - if (args::command_e::DUMP == get_options()->command) { do_dump(); } else if (args::command_e::NORMAL == get_options()->command) { @@ -229,6 +231,13 @@ int main(int argc, char* argv[]) } return EXIT_SUCCESS; } + } else if (args::command_e::SEARCH == get_options()->command) { + auto res = search_books(get_options()->input_dir); + + if (res.has_error()) { + log_error("Failed to search books: ", res.error()); + return EXIT_FAILURE; + } } return EXIT_SUCCESS; diff --git a/src/meson.build b/src/meson.build index e0914a4..f5017f6 100644 --- a/src/meson.build +++ b/src/meson.build @@ -33,6 +33,7 @@ lib_srcs = ['zip.cpp', 'chapter_type.cpp', 'volume_definition.cpp', 'book_writer.cpp', + 'book_searcher.cpp', 'bookmaker.cpp', 'jpeg.cpp', 'png_reader.cpp', From cd2fec34b324540af2a18befec0b2a790eadd21e Mon Sep 17 00:00:00 2001 From: Andrew Potter Date: Mon, 22 May 2023 18:55:52 -0700 Subject: [PATCH 5/5] include wrap file --- subprojects/jsoncpp.wrap | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 subprojects/jsoncpp.wrap diff --git a/subprojects/jsoncpp.wrap b/subprojects/jsoncpp.wrap new file mode 100644 index 0000000..ee3eb2e --- /dev/null +++ b/subprojects/jsoncpp.wrap @@ -0,0 +1,9 @@ +[wrap-file] +directory = jsoncpp-1.9.5 +source_url = https://github.com/open-source-parsers/jsoncpp/archive/1.9.5.tar.gz +source_filename = jsoncpp-1.9.5.tar.gz +source_hash = f409856e5920c18d0c2fb85276e24ee607d2a09b5e7d5f0a371368903c275da2 + +[provide] +jsoncpp = jsoncpp_dep +