Browse Source

Refactors of did-you-mean to simplify

default_compile_flags
vector-of-bool 3 years ago
parent
commit
3d3eb26460
14 changed files with 121 additions and 121 deletions
  1. +9
    -8
      src/dds/cli/cmd/pkg_get.cpp
  2. +5
    -9
      src/dds/cli/cmd/pkg_repo_err_handle.cpp
  3. +0
    -8
      src/dds/dym.cpp
  4. +0
    -48
      src/dds/dym.hpp
  5. +12
    -0
      src/dds/error/nonesuch.cpp
  6. +19
    -0
      src/dds/error/nonesuch.hpp
  7. +17
    -0
      src/dds/error/on_error.hpp
  8. +12
    -0
      src/dds/error/result.hpp
  9. +14
    -0
      src/dds/error/result_fwd.hpp
  10. +4
    -4
      src/dds/pkg/db.cpp
  11. +3
    -2
      src/dds/pkg/db.hpp
  12. +9
    -11
      src/dds/pkg/get/get.cpp
  13. +14
    -14
      src/dds/pkg/remote.cpp
  14. +3
    -17
      src/dds/util/result.hpp

+ 9
- 8
src/dds/cli/cmd/pkg_get.cpp View File



#include <dds/dym.hpp> #include <dds/dym.hpp>
#include <dds/error/errors.hpp> #include <dds/error/errors.hpp>
#include <dds/error/nonesuch.hpp>
#include <dds/pkg/db.hpp> #include <dds/pkg/db.hpp>
#include <dds/pkg/get/get.hpp> #include <dds/pkg/get/get.hpp>
#include <dds/util/http/pool.hpp> #include <dds/util/http/pool.hpp>
static int _pkg_get(const options& opts) { static int _pkg_get(const options& opts) {
auto cat = opts.open_pkg_db(); auto cat = opts.open_pkg_db();
for (const auto& item : opts.pkg.get.pkgs) { for (const auto& item : opts.pkg.get.pkgs) {
auto id = pkg_id::parse(item);
dds::dym_target dym;
auto info = cat.get(id);
if (!info) {
dds::throw_user_error<dds::errc::no_such_catalog_package>(
"No package in the database matched the ID '{}'.{}", item, dym.sentence_suffix());
}
auto tsd = get_package_sdist(*info);
auto id = pkg_id::parse(item);
auto info = *cat.get(id);
auto tsd = get_package_sdist(info);
auto dest = opts.out_path.value_or(fs::current_path()) / id.to_string(); auto dest = opts.out_path.value_or(fs::current_path()) / id.to_string();
dds_log(info, "Create sdist at {}", dest.string()); dds_log(info, "Create sdist at {}", dest.string());
fs::remove_all(dest); fs::remove_all(dest);
dds_log(error, "Error accessing the package database: {}", e.message); dds_log(error, "Error accessing the package database: {}", e.message);
return 1; return 1;
}, },
[](e_nonesuch nonesuch) -> int {
nonesuch.log_error("There is no entry in the package database for '{}'.");
write_error_marker("pkg-get-no-pkg-id-listing");
return 1;
},
[&](dds::e_system_error_exc e, dds::network_origin conn) { [&](dds::e_system_error_exc e, dds::network_origin conn) {
dds_log(error, dds_log(error,
"Error opening connection to [{}:{}]: {}", "Error opening connection to [{}:{}]: {}",

+ 5
- 9
src/dds/cli/cmd/pkg_repo_err_handle.cpp View File



#include <dds/dym.hpp> #include <dds/dym.hpp>
#include <dds/error/errors.hpp> #include <dds/error/errors.hpp>
#include <dds/error/nonesuch.hpp>
#include <dds/pkg/remote.hpp> #include <dds/pkg/remote.hpp>
#include <dds/util/http/pool.hpp> #include <dds/util/http/pool.hpp>
#include <dds/util/log.hpp> #include <dds/util/log.hpp>
e.message); e.message);
return 1; return 1;
}, },
[](matchv<pkg_repo_subcommand::remove>,
user_error<errc::no_catalog_remote_info>,
e_remote_name reponame,
dds::e_did_you_mean dym) {
dds_log(error,
"Cannot delete remote '{}', as no such remote repository is locally "
"registered by that name.",
reponame.value);
dym.log_as_error();
[](matchv<pkg_repo_subcommand::remove>, e_nonesuch missing) {
missing.log_error(
"Cannot delete remote '{}', as no such remote repository is locally registered by "
"that name.");
write_error_marker("repo-rm-no-such-repo"); write_error_marker("repo-rm-no-such-repo");
return 1; return 1;
}); });

+ 0
- 8
src/dds/dym.cpp View File



using namespace dds; using namespace dds;


thread_local dym_target* dym_target::_tls_current = nullptr;

std::size_t dds::lev_edit_distance(std::string_view a, std::string_view b) noexcept { std::size_t dds::lev_edit_distance(std::string_view a, std::string_view b) noexcept {
const auto n_rows = b.size() + 1; const auto n_rows = b.size() + 1;
const auto n_columns = a.size() + 1; const auto n_columns = a.size() + 1;


return matrix.back().back(); return matrix.back().back();
} }

void dds::e_did_you_mean::log_as_error() const noexcept {
if (value) {
dds_log(error, " (Did you mean \"{}\"?)", *value);
}
}

+ 0
- 48
src/dds/dym.hpp View File



std::size_t lev_edit_distance(std::string_view a, std::string_view b) noexcept; std::size_t lev_edit_distance(std::string_view a, std::string_view b) noexcept;


struct e_did_you_mean {
std::optional<std::string> value;

void log_as_error() const noexcept;
};

class dym_target {
std::optional<std::string> _candidate;
dym_target* _tls_prev = nullptr;
static thread_local dym_target* _tls_current;

public:
dym_target()
: _tls_prev(_tls_current) {
_tls_current = this;
}
dym_target(const dym_target&) = delete;
~dym_target() { _tls_current = _tls_prev; }

template <typename Func>
static void fill(Func&& fn) noexcept {
if (_tls_current) {
_tls_current->_candidate = fn();
}
}

auto& candidate() const noexcept { return _candidate; }

auto e_value() const noexcept { return e_did_you_mean{_candidate}; }

std::string sentence_suffix() const noexcept {
if (_candidate) {
return " (Did you mean '" + *_candidate + "'?)";
}
return "";
}
};

template <typename Range> template <typename Range>
std::optional<std::string> did_you_mean(std::string_view given, Range&& strings) noexcept { std::optional<std::string> did_you_mean(std::string_view given, Range&& strings) noexcept {
auto cand = ranges::min_element(strings, ranges::less{}, [&](std::string_view candidate) { auto cand = ranges::min_element(strings, ranges::less{}, [&](std::string_view candidate) {
return did_you_mean(given, ranges::views::all(strings)); return did_you_mean(given, ranges::views::all(strings));
} }


template <typename Range>
e_did_you_mean calc_e_did_you_mean(std::string_view given, Range&& strings) noexcept {
return {did_you_mean(given, strings)};
}

inline e_did_you_mean calc_e_did_you_mean(std::string_view given,
std::initializer_list<std::string_view> il) noexcept {
return calc_e_did_you_mean(given, ranges::views::all(il));
}

} // namespace dds } // namespace dds

+ 12
- 0
src/dds/error/nonesuch.cpp View File

#include "./nonesuch.hpp"

#include <dds/util/log.hpp>

using namespace dds;

void e_nonesuch::log_error(std::string_view fmt) const noexcept {
dds_log(error, fmt, given);
if (nearest) {
dds_log(error, " (Did you mean '{}'?)", *nearest);
}
}

+ 19
- 0
src/dds/error/nonesuch.hpp View File

#pragma once

#include <optional>
#include <string>

namespace dds {

struct e_nonesuch {
std::string given;
std::optional<std::string> nearest;

e_nonesuch(std::string_view gn, std::optional<std::string> nr) noexcept
: given{gn}
, nearest{nr} {}

void log_error(std::string_view fmt) const noexcept;
};

} // namespace dds

+ 17
- 0
src/dds/error/on_error.hpp View File

#pragma once

#include <boost/leaf/on_error.hpp>

/**
* @brief Generate a callable object that returns the given expression.
*
* Use this as a parameter to leaf's error-loading APIs.
*/
#define DDS_E_ARG(...) ([&] { return __VA_ARGS__; })

/**
* @brief Generate a leaf::on_error object that loads the given expression into the currently
* in-flight error if the current scope is exitted via exception or a bad result<>
*/
#define DDS_E_SCOPE(...) \
auto NEO_CONCAT(_err_info_, __LINE__) = boost::leaf::on_error(DDS_E_ARG(__VA_ARGS__))

+ 12
- 0
src/dds/error/result.hpp View File

#pragma once

#include "./result_fwd.hpp"

#include <boost/leaf/error.hpp>
#include <boost/leaf/result.hpp>

namespace dds {

using boost::leaf::new_error;

} // namespace dds

+ 14
- 0
src/dds/error/result_fwd.hpp View File

#pragma once

namespace boost::leaf {

template <typename T>
class result;

} // namespace boost::leaf

namespace dds {

using boost::leaf::result;

} // namespace dds

+ 4
- 4
src/dds/pkg/db.cpp View File



#include <dds/dym.hpp> #include <dds/dym.hpp>
#include <dds/error/errors.hpp> #include <dds/error/errors.hpp>
#include <dds/error/nonesuch.hpp>
#include <dds/solve/solve.hpp> #include <dds/solve/solve.hpp>
#include <dds/util/log.hpp> #include <dds/util/log.hpp>
#include <dds/util/paths.hpp> #include <dds/util/paths.hpp>
do_store_pkg(_db, _stmt_cache, pkg); do_store_pkg(_db, _stmt_cache, pkg);
} }


std::optional<pkg_listing> pkg_db::get(const pkg_id& pk_id) const noexcept {
result<pkg_listing> pkg_db::get(const pkg_id& pk_id) const noexcept {
auto ver_str = pk_id.version.to_string(); auto ver_str = pk_id.version.to_string();
dds_log(trace, "Lookup package {}@{}", pk_id.name, ver_str); dds_log(trace, "Lookup package {}@{}", pk_id.name, ver_str);
auto& st = _stmt_cache(R"( auto& st = _stmt_cache(R"(
st.bindings() = std::forward_as_tuple(pk_id.name, ver_str); st.bindings() = std::forward_as_tuple(pk_id.name, ver_str);
auto ec = st.step(std::nothrow); auto ec = st.step(std::nothrow);
if (ec == nsql::errc::done) { if (ec == nsql::errc::done) {
dym_target::fill([&] {
return new_error([&] {
auto all_ids = this->all(); auto all_ids = this->all();
auto id_strings auto id_strings
= ranges::views::transform(all_ids, [&](auto id) { return id.to_string(); }); = ranges::views::transform(all_ids, [&](auto id) { return id.to_string(); });
return did_you_mean(pk_id.to_string(), id_strings);
return e_nonesuch{pk_id.to_string(), did_you_mean(pk_id.to_string(), id_strings)};
}); });
return std::nullopt;
} }
neo_assert_always(invariant, neo_assert_always(invariant,
ec == nsql::errc::row, ec == nsql::errc::row,

+ 3
- 2
src/dds/pkg/db.hpp View File



#include "./listing.hpp" #include "./listing.hpp"


#include <dds/error/result.hpp>
#include <dds/util/fs.hpp> #include <dds/util/fs.hpp>


#include <neo/sqlite3/database.hpp> #include <neo/sqlite3/database.hpp>


static fs::path default_path() noexcept; static fs::path default_path() noexcept;


void store(const pkg_listing& info);
std::optional<pkg_listing> get(const pkg_id& id) const noexcept;
void store(const pkg_listing& info);
result<pkg_listing> get(const pkg_id& id) const noexcept;


std::vector<pkg_id> all() const noexcept; std::vector<pkg_id> all() const noexcept;
std::vector<pkg_id> by_name(std::string_view sv) const noexcept; std::vector<pkg_id> by_name(std::string_view sv) const noexcept;

+ 9
- 11
src/dds/pkg/get/get.cpp View File

void dds::get_all(const std::vector<pkg_id>& pkgs, pkg_cache& repo, const pkg_db& cat) { void dds::get_all(const std::vector<pkg_id>& pkgs, pkg_cache& repo, const pkg_db& cat) {
std::mutex repo_mut; std::mutex repo_mut;


auto absent_pkg_infos = pkgs //
auto absent_pkg_infos
= pkgs //
| ranges::views::filter([&](auto pk) { | ranges::views::filter([&](auto pk) {
std::scoped_lock lk{repo_mut};
return !repo.find(pk);
})
std::scoped_lock lk{repo_mut};
return !repo.find(pk);
})
| ranges::views::transform([&](auto id) { | ranges::views::transform([&](auto id) {
auto info = cat.get(id);
neo_assert(invariant,
info.has_value(),
"No database entry for package id?",
id.to_string());
return *info;
});
auto info = cat.get(id);
neo_assert(invariant, !!info, "No database entry for package id?", id.to_string());
return *info;
});


auto okay = parallel_run(absent_pkg_infos, 8, [&](pkg_listing inf) { auto okay = parallel_run(absent_pkg_infos, 8, [&](pkg_listing inf) {
dds_log(info, "Download package: {}", inf.ident.to_string()); dds_log(info, "Download package: {}", inf.ident.to_string());

+ 14
- 14
src/dds/pkg/remote.cpp View File



#include <dds/dym.hpp> #include <dds/dym.hpp>
#include <dds/error/errors.hpp> #include <dds/error/errors.hpp>
#include <dds/error/nonesuch.hpp>
#include <dds/pkg/db.hpp> #include <dds/pkg/db.hpp>
#include <dds/temp.hpp> #include <dds/temp.hpp>
#include <dds/util/http/pool.hpp> #include <dds/util/http/pool.hpp>
get_rowid_st.bindings()[1] = name; get_rowid_st.bindings()[1] = name;
auto row = neo::sqlite3::unpack_single_opt<std::int64_t>(get_rowid_st); auto row = neo::sqlite3::unpack_single_opt<std::int64_t>(get_rowid_st);
if (!row) { if (!row) {
auto calc_dym = [&] {
auto all_st = db.prepare("SELECT name FROM dds_pkg_remotes");
auto tups = neo::sqlite3::iter_tuples<std::string>(all_st);
auto names = tups | ranges::views::transform([](auto&& tup) {
auto&& [n] = tup;
return n;
})
| ranges::to_vector;
return calc_e_did_you_mean(name, names);
};
BOOST_LEAF_THROW_EXCEPTION(make_user_error<errc::no_catalog_remote_info>(
"There is no remote with name '{}'", name),
DDS_E_ARG(e_remote_name{std::string(name)}),
calc_dym);
BOOST_LEAF_THROW_EXCEPTION( //
make_user_error<errc::no_catalog_remote_info>("There is no remote with name '{}'",
name),
[&] {
auto all_st = db.prepare("SELECT name FROM dds_pkg_remotes");
auto tups = neo::sqlite3::iter_tuples<std::string>(all_st);
auto names = tups | ranges::views::transform([](auto&& tup) {
auto&& [n] = tup;
return n;
})
| ranges::to_vector;
return e_nonesuch{name, did_you_mean(name, names)};
});
} }
auto [rowid] = *row; auto [rowid] = *row;
neo::sqlite3::exec(db.prepare("DELETE FROM dds_pkg_remotes WHERE remote_id = ?"), rowid); neo::sqlite3::exec(db.prepare("DELETE FROM dds_pkg_remotes WHERE remote_id = ?"), rowid);

+ 3
- 17
src/dds/util/result.hpp View File

#pragma once #pragma once


#include <dds/error/on_error.hpp>
#include <dds/error/result.hpp>

#include <boost/leaf/handle_error.hpp> #include <boost/leaf/handle_error.hpp>
#include <boost/leaf/on_error.hpp> #include <boost/leaf/on_error.hpp>
#include <boost/leaf/result.hpp> #include <boost/leaf/result.hpp>
std::filesystem::path path; std::filesystem::path path;
}; };


struct e_error_marker {
std::string_view value;
};

struct e_parse_error {
std::string value;
};

/** /**
* @brief Capture currently in-flight special exceptions as new error object. Works around a bug in * @brief Capture currently in-flight special exceptions as new error object. Works around a bug in
* Boost.LEAF when catching std::system error. * Boost.LEAF when catching std::system error.
*/ */
[[noreturn]] void capture_exception(); [[noreturn]] void capture_exception();


#define DDS_E_ARG(...) ([&] { return __VA_ARGS__; })

void write_error_marker(std::string_view error) noexcept; void write_error_marker(std::string_view error) noexcept;


/**
* @brief Generate a leaf::on_error object that loads the given expression into the currently
* in-flight error if the current scope is exitted via exception or a bad result<>
*/
#define DDS_E_SCOPE(...) \
auto NEO_CONCAT(_err_info_, __LINE__) = boost::leaf::on_error(DDS_E_ARG(__VA_ARGS__))

} // namespace dds } // namespace dds

Loading…
Cancel
Save