Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Changelog

- #129 [feature] `uenv image find` and `uenv image ls` do partial match on uenv names
- #127 [feature] find views, mount point, etc. information using `uenv image inspect` without opening a uenv.

## 9.1.2
Expand Down
4 changes: 2 additions & 2 deletions src/cli/add_remove.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -369,7 +369,7 @@ int image_rm([[maybe_unused]] const image_rm_args& args,
} else if (r->size() > 1) {
term::error("the pattern {} matches more than one "
"uenv:\n{}use a more specific version",
U, format_record_set(*r));
U, format_record_set_list(*r));
return 1;
} else {
// check whether there are more than one tag attached to sha
Expand Down Expand Up @@ -411,7 +411,7 @@ int image_rm([[maybe_unused]] const image_rm_args& args,
} else {
term::msg("the following uenv {} removed:",
(removed.size() > 1 ? "were" : "was"));
print_record_set(removed, true);
print_record_set(removed, record_set_format::list);
}

return 0;
Expand Down
2 changes: 1 addition & 1 deletion src/cli/copy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ int image_copy([[maybe_unused]] const image_copy_args& args,
std::string errmsg =
fmt::format("more than one uenv found that matches '{}':\n",
args.src_uenv_description);
errmsg += format_record_set(*src_matches);
errmsg += format_record_set_table(*src_matches);
term::error("{}", errmsg);
return 1;
}
Expand Down
2 changes: 1 addition & 1 deletion src/cli/delete.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ int image_delete([[maybe_unused]] const image_delete_args& args,
std::string errmsg =
fmt::format("more than one sha found that matches '{}':\n",
args.uenv_description);
errmsg += format_record_set(*matches);
errmsg += format_record_set_table(*matches);
term::error("{}", errmsg);
return 1;
}
Expand Down
36 changes: 30 additions & 6 deletions src/cli/find.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,13 @@ void image_find_args::add_cli(CLI::App& cli,
find_cli->add_option("uenv", uenv_description, "search term");
find_cli->add_flag("--no-header", no_header,
"print only the matching records, with no header.");
find_cli->add_flag("--json", json, "format output as JSON.");
find_cli->add_flag("--build", build,
"invalid: replaced with 'build::' prefix on uenv label");
find_cli->add_flag("--json", json,
"format output as JSON (incompatible with --list).");
find_cli->add_flag("--list", list,
"list the full specs of matching records with no header "
"(incompatible with --json).");
find_cli->add_flag("--no-partials", no_partials,
"do not match partial names when searching.");
find_cli->callback(
[&settings]() { settings.mode = uenv::cli_mode::image_find; });

Expand All @@ -50,6 +54,11 @@ int image_find([[maybe_unused]] const image_find_args& args,
return 1;
}

auto format = get_record_set_format(args.no_header, args.json, args.list);
if (!format) {
term::error("{}", format.error());
}

// find the search term that was provided by the user
uenv_label label{};
std::string nspace{site::default_namespace()};
Expand All @@ -75,14 +84,13 @@ int image_find([[maybe_unused]] const image_find_args& args,
}

// search db for matching records
const auto result = store->query(label);
const auto result = store->query(label, !args.no_partials);
if (!result) {
term::error("invalid search term: {}", store.error());
return 1;
}

// pass results to print
print_record_set(*result, args.no_header, args.json);
print_record_set(*result, *format);

return 0;
}
Expand Down Expand Up @@ -133,4 +141,20 @@ std::string image_find_footer() {
return fmt::format("{}", fmt::join(items, "\n"));
}

util::expected<record_set_format, std::string>
get_record_set_format(bool no_header, bool json, bool list) {
if (json && list) {
return util::unexpected(
"the --json and --list options are incompatible and can not be "
"used at the same time");
}

if (!json && !list) {
return no_header ? record_set_format::table_no_header
: record_set_format::table;
}

return json ? record_set_format::json : record_set_format::list;
}

} // namespace uenv
2 changes: 2 additions & 0 deletions src/cli/find.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ struct image_find_args {
std::optional<std::string> uenv_description;
bool no_header = false;
bool json = false;
bool list = false;
bool no_partials = false;
bool build = false;
void add_cli(CLI::App&, global_settings& settings);
};
Expand Down
17 changes: 14 additions & 3 deletions src/cli/ls.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,13 @@ void image_ls_args::add_cli(CLI::App& cli,
ls_cli->add_option("uenv", uenv_description, "search term");
ls_cli->add_flag("--no-header", no_header,
"print only the matching records, with no header.");
ls_cli->add_flag("--json", json, "format output as JSON.");
ls_cli->add_flag("--json", json,
"format output as JSON (incompatible with --list).");
ls_cli->add_flag("--list", list,
"list the full specs of matching records with no header "
"(incompatible with --json).");
ls_cli->add_flag("--no-partials", no_partials,
"do not match partial names when searching.");
ls_cli->callback(
[&settings]() { settings.mode = uenv::cli_mode::image_ls; });

Expand All @@ -43,6 +49,11 @@ int image_ls(const image_ls_args& args, const global_settings& settings) {
return 1;
}

auto format = get_record_set_format(args.no_header, args.json, args.list);
if (!format) {
term::error("{}", format.error());
}

// open the repo
auto store = uenv::open_repository(settings.config.repo.value());
if (!store) {
Expand All @@ -67,13 +78,13 @@ int image_ls(const image_ls_args& args, const global_settings& settings) {
site::get_system_name(label.system, settings.calling_environment);

// query the repo
const auto result = store->query(label);
const auto result = store->query(label, !args.no_partials);
if (!result) {
term::error("invalid search term: {}", store.error());
return 1;
}

print_record_set(*result, args.no_header, args.json);
print_record_set(*result, *format);

return 0;
}
Expand Down
8 changes: 6 additions & 2 deletions src/cli/ls.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ struct image_ls_args {
std::optional<std::string> uenv_description;
bool no_header = false;
bool json = false;
bool list = false;
bool no_partials = false;
void add_cli(CLI::App&, global_settings& settings);
};

Expand All @@ -32,7 +34,9 @@ template <> class fmt::formatter<uenv::image_ls_args> {
template <typename FmtContext>
constexpr auto format(uenv::image_ls_args const& opts,
FmtContext& ctx) const {
return fmt::format_to(ctx.out(), "{{uenv: '{}'}}",
opts.uenv_description);
return fmt::format_to(
ctx.out(),
"{{uenv: '{}', json: {}, no_partials: {}, no_header: {}}}",
opts.uenv_description, opts.json, opts.no_partials, opts.no_header);
}
};
2 changes: 1 addition & 1 deletion src/cli/pull.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ int image_pull([[maybe_unused]] const image_pull_args& args,
std::string errmsg =
fmt::format("more than one uenv found that matches '{}':\n",
args.uenv_description);
errmsg += format_record_set(*remote_matches);
errmsg += format_record_set_table(*remote_matches);
term::error("{}", errmsg);
return 1;
}
Expand Down
2 changes: 1 addition & 1 deletion src/uenv/env.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ resolve_uenv(const uenv_description& desc,
fmt::format("more than one uenv matches the uenv description "
"'{}':\n",
desc.label().value());
errmsg += format_record_set(results);
errmsg += format_record_set_table(results);
return unexpected(errmsg);
}

Expand Down
31 changes: 27 additions & 4 deletions src/uenv/print.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@
#include <fmt/std.h>
#include <nlohmann/json.hpp>

#include <uenv/print.h>
#include <uenv/repository.h>
#include <uenv/uenv.h>
#include <util/color.h>

namespace uenv {

std::string format_record_set(const record_set& records, bool no_header) {
std::string format_record_set_table(const record_set& records, bool no_header) {
if (!no_header && records.empty()) {
if (!no_header) {
return "no matching uenv\n";
Expand Down Expand Up @@ -62,6 +63,16 @@ std::string format_record_set(const record_set& records, bool no_header) {
return result;
}

std::string format_record_set_list(const record_set& records) {
std::string result;
for (auto& r : records) {
result += fmt::format("{}/{}:{}@{}%{}\n", r.name, r.version, r.tag,
r.system, r.uarch);
}

return result;
}

std::string format_record_set_json(const record_set& records) {
using nlohmann::json;
std::vector<json> jrecords;
Expand All @@ -79,9 +90,21 @@ std::string format_record_set_json(const record_set& records) {
return json{{"records", jrecords}}.dump();
}

void print_record_set(const record_set& records, bool no_header, bool json) {
fmt::print("{}", json ? format_record_set_json(records)
: format_record_set(records, no_header));
void print_record_set(const record_set& records, record_set_format f) {
switch (f) {
case record_set_format::json:
fmt::print("{}", format_record_set_json(records));
return;
case record_set_format::list:
fmt::print("{}", format_record_set_list(records));
return;
case record_set_format::table:
fmt::print("{}", format_record_set_table(records, false));
return;
case record_set_format::table_no_header:
fmt::print("{}", format_record_set_table(records, true));
return;
}
}

} // namespace uenv
18 changes: 15 additions & 3 deletions src/uenv/print.h
Original file line number Diff line number Diff line change
@@ -1,10 +1,22 @@
#pragma once

#include <uenv/repository.h>
#include <util/expected.h>

namespace uenv {
void print_record_set(const record_set& result, bool no_header = true,
bool json = false);
std::string format_record_set(const record_set& records, bool no_header = true);

enum class record_set_format { table, table_no_header, json, list };

void print_record_set(const record_set& result, record_set_format format);

std::string format_record_set_table(const record_set& records,
bool no_header = true);
std::string format_record_set_list(const record_set& records);
std::string format_record_set_json(const record_set& records);

// a helper for determining the output format based on CLI flags:
// --no-header, --json and --list
util::expected<record_set_format, std::string>
get_record_set_format(bool no_header, bool json, bool list);

} // namespace uenv
16 changes: 10 additions & 6 deletions src/uenv/repository.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -396,7 +396,7 @@ struct repository_impl {
std::optional<fs::path> path;

util::expected<std::vector<uenv_record>, std::string>
query(const uenv_label&) const;
query(const uenv_label&, bool partial_name = false) const;
repository::pathset uenv_paths(sha256) const;

bool contains(const uenv_record&) const;
Expand Down Expand Up @@ -675,7 +675,7 @@ repository_impl::add(const uenv::uenv_record& r) {

util::expected<record_set, std::string>
repository_impl::remove(const sha256& sha) {
auto matches = query({.name = sha.string()});
auto matches = query(uenv_label{.name = sha.string()});
std::vector<std::string> statements{
// begin the transaction
"PRAGMA foreign_keys = ON", "BEGIN",
Expand Down Expand Up @@ -740,12 +740,16 @@ WHERE sha256='{}' AND tag='{}' AND version_id IN (
}

util::expected<std::vector<uenv_record>, std::string>
repository_impl::query(const uenv_label& label) const {
repository_impl::query(const uenv_label& label, bool partial_name) const {
std::vector<uenv_record> results;

std::vector<std::string> query_terms;
if (label.name) {
query_terms.push_back(fmt::format("name = '{}'", *label.name));
if (partial_name) {
query_terms.push_back(fmt::format("name LIKE '{}%'", *label.name));
} else {
query_terms.push_back(fmt::format("name = '{}'", *label.name));
}
}
if (label.tag) {
query_terms.push_back(fmt::format("tag = '{}'", *label.tag));
Expand Down Expand Up @@ -953,8 +957,8 @@ std::optional<fs::path> repository::path() const {
}

util::expected<record_set, std::string>
repository::query(const uenv_label& label) const {
return impl_->query(label);
repository::query(const uenv_label& label, bool partial_name) const {
return impl_->query(label, partial_name);
}

util::expected<void, std::string> repository::add(const uenv_record& r) {
Expand Down
5 changes: 4 additions & 1 deletion src/uenv/repository.h
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,10 @@ struct repository {
std::optional<std::filesystem::path> path() const;

util::expected<record_set, std::string>
query(const uenv_label& label) const;
// search for all records that match a label.
// the partial_name parameter toggles on partial matches on the
// label.name field, which is useful for `image ls` `image find` searches.
query(const uenv_label& label, bool partial_name = false) const;

bool contains(const uenv_record&) const;

Expand Down