blob: 511caed5748eefac9946bb847f4aecf9b3a1379a [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "garnet/bin/zxdb/console/format_settings.h"
#include "garnet/bin/zxdb/client/setting_schema.h"
#include "garnet/bin/zxdb/client/setting_store.h"
#include "garnet/bin/zxdb/client/session.h"
#include "garnet/bin/zxdb/client/system.h"
#include "garnet/bin/zxdb/client/target.h"
#include "garnet/bin/zxdb/client/thread.h"
#include "garnet/bin/zxdb/common/err.h"
#include "garnet/bin/zxdb/console/command.h"
#include "garnet/bin/zxdb/console/format_table.h"
#include "garnet/bin/zxdb/console/output_buffer.h"
#include "garnet/bin/zxdb/console/string_util.h"
#include "lib/fxl/strings/string_printf.h"
#include "lib/fxl/strings/join_strings.h"
namespace zxdb {
namespace {
std::string SettingValueToString(const SettingValue& value) {
switch (value.type()) {
case SettingType::kBoolean:
return value.GetBool() ? "true" : "false";
case SettingType::kInteger:
return fxl::StringPrintf("%d", value.GetInt());
case SettingType::kString: {
auto string = value.GetString();
return string.empty() ? "<empty>" : string;
}
case SettingType::kList:
// Lists are formatted as a colon separated string.
// Example
// list = {"first", "second", "third"} -> "first:second:third"
return fxl::JoinStrings(value.GetList(), ":");
case SettingType::kNull:
return "<null>";
}
}
std::vector<std::string> ListToBullet(const std::vector<std::string>& list) {
std::vector<std::string> output;
output.reserve(list.size());
auto bullet = GetBullet();
for (const std::string& item : list)
output.emplace_back(fxl::StringPrintf("%s %s", bullet.data(), item.data()));
return output;
}
// |add_heading| refers whether it should show the setting name or just list the
// values.
void AddSettingToTable(const StoredSetting& setting,
std::vector<std::vector<OutputBuffer>>* rows,
bool add_heading = true) {
// TODO(donosoc): We need to check what level the setting comes from so we can
// highlight it in the listing.
if (!setting.value.is_list()) {
// Normal values as just entered as key-value pairs.
auto& row = rows->emplace_back();
if (add_heading)
row.emplace_back(setting.schema_item.name());
row.emplace_back(SettingValueToString(setting.value));
} else {
// List get special treatment so that we can show them as bullet lists.
// This make reading them much easier when the elements of the lists
// are long (eg. paths).
auto bullet_list = ListToBullet(setting.value.GetList());
// Special case for empty list.
if (bullet_list.empty()) {
auto& row = rows->emplace_back();
if (add_heading)
row.emplace_back(setting.schema_item.name());
row.emplace_back("<empty>");
} else {
for (size_t i = 0; i < bullet_list.size(); i++) {
auto& row = rows->emplace_back();
if (add_heading) {
// The first entry has the setting name.
auto title = i == 0 ? OutputBuffer(setting.schema_item.name())
: OutputBuffer();
auto it = row.emplace_back(std::move(title));
}
row.emplace_back(std::move(bullet_list[i]));
}
}
}
}
OutputBuffer FormatSettingStore(const SettingStore& store) {
std::vector<std::vector<OutputBuffer>> rows;
for (auto [key, item] : store.schema()->items()) {
// Overriden settings are meant to be listen in another schema.
if (item.overriden())
continue;
auto setting = store.GetSetting(key);
FXL_DCHECK(!setting.value.is_null());
AddSettingToTable(setting, &rows);
}
OutputBuffer table;
FormatTable(std::vector<ColSpec>(3), rows, &table);
return table;
}
OutputBuffer FormatSchema(const SettingSchema& schema) {
std::vector<std::vector<OutputBuffer>> rows;
for (auto [key, item] : schema.items()) {
// Overriden settings are meant to be listen in another schema.
if (item.overriden())
continue;
StoredSetting setting;
setting.schema_item = schema.GetItem(key);
setting.value = setting.schema_item.value();
FXL_DCHECK(!setting.value.is_null());
AddSettingToTable(setting, &rows);
}
OutputBuffer table;
FormatTable(std::vector<ColSpec>(3), rows, &table);
return table;
}
} // namespace
void FormatSettings(const Command& cmd, OutputBuffer* out) {
// We print each setting
out->Append({Syntax::kHeading, "\nSystem\n"});
out->Append(FormatSettingStore(cmd.target()->session()->system().settings()));
OutputBuffer current;
JobContext* job = cmd.job_context();
current = job ? FormatSettingStore(job->settings())
: FormatSchema(*JobContext::GetSchema());
if (!current.empty()) {
out->Append({Syntax::kHeading, "\nJob\n"});
out->Append(current);
}
Target* target = cmd.target();
current = target ? FormatSettingStore(target->settings())
: FormatSchema(*Target::GetSchema());
if (!current.empty()) {
out->Append({Syntax::kHeading, "\nTarget\n"});
out->Append(current);
}
Thread* thread = cmd.thread();
current = thread ? FormatSettingStore(thread->settings())
: FormatSchema(*Thread::GetSchema());
if (!current.empty()) {
out->Append({Syntax::kHeading, "\nThread\n"});
out->Append(current);
}
}
Err FormatSetting(const Command& cmd, const std::string& setting_name,
OutputBuffer* out) {
// We look from more specific to less specific for the setting.
Thread* thread = cmd.thread();
if (thread && thread->settings().HasSetting(setting_name)) {
OutputBuffer tmp;
Err err = FormatSetting(thread->settings(), setting_name, &tmp);
out->Append(std::move(tmp));
return err;
}
Target* target = cmd.target();
if (target && target->settings().HasSetting(setting_name)) {
OutputBuffer tmp;
Err err = FormatSetting(target->settings(), setting_name, &tmp);
out->Append(std::move(tmp));
return err;
}
JobContext* job = cmd.job_context();
if (job && job->settings().HasSetting(setting_name)) {
OutputBuffer tmp;
Err err = FormatSetting(job->settings(), setting_name, &tmp);
out->Append(std::move(tmp));
return err;
}
System& system = cmd.target()->session()->system();
if (system.settings().HasSetting(setting_name)) {
OutputBuffer tmp;
Err err = FormatSetting(system.settings(), setting_name, &tmp);
out->Append(std::move(tmp));
return err;
}
return Err("Could not find setting \"%s\"", setting_name.data());
}
Err FormatSetting(const SettingStore& store, const std::string& setting_name,
OutputBuffer* out) {
if (!store.HasSetting(setting_name))
return Err("Could not find setting \"%s\"", setting_name.data());
auto setting = store.GetSetting(setting_name);
out->Append({Syntax::kHeading, setting.schema_item.name()});
out->Append(OutputBuffer("\n"));
out->Append(setting.schema_item.description());
out->Append(OutputBuffer("\n\n"));
out->Append({Syntax::kHeading, "Type: "});
out->Append(SettingTypeToString(setting.schema_item.type()));
out->Append("\n\n");
out->Append({Syntax::kHeading, "Value(s):\n"});
out->Append(FormatSettingValue(setting));
// List have a copy-paste value for setting the value.
if (setting.value.is_list()) {
out->Append("\n");
out->Append({Syntax::kComment,
"See \"help set\" about using the set value for lists.\n"});
out->Append(fxl::StringPrintf("Set value: %s",
SettingValueToString(setting.value).data()));
out->Append("\n");
}
return Err();
}
OutputBuffer FormatSettingValue(const StoredSetting& setting) {
FXL_DCHECK(!setting.value.is_null());
OutputBuffer out;
std::vector<std::vector<OutputBuffer>> rows;
AddSettingToTable(setting, &rows, false);
FormatTable(std::vector<ColSpec>{1}, std::move(rows), &out);
return out;
}
const char* SettingSchemaLevelToString(SettingSchema::Level level) {
switch (level) {
case SettingSchema::Level::kDefault:
return "default";
case SettingSchema::Level::kSystem:
return "system";
case SettingSchema::Level::kJob:
return "job";
case SettingSchema::Level::kTarget:
return "target";
case SettingSchema::Level::kThread:
return "thread";
}
// Just in case.
return "<invalid>";
}
} // namespace zxdb