[wpiutil] Add remove_prefix() and remove_suffix() (#6118)

This commit is contained in:
Joseph Eng
2024-06-04 21:01:52 -07:00
committed by GitHub
parent 8def7b2222
commit d6b66bfa55
12 changed files with 131 additions and 68 deletions

View File

@@ -96,25 +96,23 @@ void DataLogReaderThread::ReadMain() {
if (data.empty()) {
continue;
}
if (wpi::starts_with(name, "NT:")) {
name = wpi::drop_front(name, 3);
if (auto strippedName = wpi::remove_prefix(name, "NT:")) {
name = *strippedName;
}
if (wpi::starts_with(name, "/.schema/struct:")) {
auto typeStr = wpi::drop_front(name, 16);
if (auto typeStr = wpi::remove_prefix(name, "/.schema/struct:")) {
std::string_view schema{reinterpret_cast<const char*>(data.data()),
data.size()};
std::string err;
auto desc = m_structDb.Add(typeStr, schema, &err);
auto desc = m_structDb.Add(*typeStr, schema, &err);
if (!desc) {
wpi::print("could not decode struct '{}' schema '{}': {}\n", name,
schema, err);
}
} else if (wpi::starts_with(name, "/.schema/proto:")) {
} else if (auto filename = wpi::remove_prefix(name, "/.schema/proto:")) {
// protobuf descriptor handling
auto filename = wpi::drop_front(name, 15);
if (!m_protoDb.Add(filename, data)) {
if (!m_protoDb.Add(*filename, data)) {
wpi::print("could not decode protobuf '{}' filename '{}'\n", name,
filename);
*filename);
}
}
}

View File

@@ -126,7 +126,7 @@ void NTField2DModel::Update() {
for (auto&& event : m_poller.ReadQueue()) {
if (auto info = event.GetTopicInfo()) {
// handle publish/unpublish
auto name = wpi::drop_front(info->name, m_path.size());
auto name = wpi::remove_prefix(info->name, m_path).value_or("");
if (name.empty() || name[0] == '.') {
continue;
}
@@ -198,7 +198,9 @@ void NTField2DModel::ForEachFieldObject(
func) {
for (auto&& obj : m_objects) {
if (obj->Exists()) {
func(*obj, wpi::drop_front(obj->GetName(), m_path.size()));
if (auto name = wpi::remove_prefix(obj->GetName(), m_path)) {
func(*obj, *name);
}
}
}
}

View File

@@ -256,7 +256,7 @@ NTMechanism2DModel::~NTMechanism2DModel() = default;
void NTMechanism2DModel::Update() {
for (auto&& event : m_poller.ReadQueue()) {
if (auto info = event.GetTopicInfo()) {
auto name = wpi::drop_front(info->name, m_path.size());
auto name = wpi::remove_prefix(info->name, m_path).value_or("");
if (name.empty() || name[0] == '.') {
continue;
}
@@ -307,7 +307,7 @@ void NTMechanism2DModel::Update() {
}
} else {
auto fullName = nt::Topic{valueData->topic}.GetName();
auto name = wpi::drop_front(fullName, m_path.size());
auto name = wpi::remove_prefix(fullName, m_path).value_or("");
if (name.empty() || name[0] == '.') {
continue;
}

View File

@@ -727,11 +727,12 @@ void NetworkTablesModel::ValueSource::UpdateFromValue(
mpack_reader_init_data(&r, value.GetRaw());
UpdateMsgpackValueSource(model, this, r, name, value.last_change());
mpack_reader_destroy(&r);
} else if (wpi::starts_with(typeStr, "struct:")) {
auto structName = wpi::drop_front(typeStr, 7);
bool isArray = structName.ends_with("[]");
} else if (auto structNameOpt = wpi::remove_prefix(typeStr, "struct:")) {
auto structName = *structNameOpt;
auto withoutArray = wpi::remove_suffix(structName, "[]");
bool isArray = withoutArray.has_value();
if (isArray) {
structName = wpi::drop_back(structName, 2);
structName = *withoutArray;
}
auto desc = model.m_structDb.Find(structName);
if (desc && desc->IsValid()) {
@@ -762,8 +763,8 @@ void NetworkTablesModel::ValueSource::UpdateFromValue(
} else {
valueChildren.clear();
}
} else if (wpi::starts_with(typeStr, "proto:")) {
auto msg = model.m_protoDb.Find(wpi::drop_front(typeStr, 6));
} else if (auto filename = wpi::remove_prefix(typeStr, "proto:")) {
auto msg = model.m_protoDb.Find(*filename);
if (msg) {
msg->Clear();
auto raw = value.GetRaw();
@@ -808,13 +809,15 @@ void NetworkTablesModel::Update() {
m_server.publishers.clear();
} else if (info->name == "$serversub") {
m_server.subscribers.clear();
} else if (wpi::starts_with(info->name, "$clientpub$")) {
auto it = m_clients.find(wpi::drop_front(info->name, 11));
} else if (auto client =
wpi::remove_prefix(info->name, "$clientpub$")) {
auto it = m_clients.find(*client);
if (it != m_clients.end()) {
it->second.publishers.clear();
}
} else if (wpi::starts_with(info->name, "$clientsub$")) {
auto it = m_clients.find(wpi::drop_front(info->name, 11));
} else if (auto client =
wpi::remove_prefix(info->name, "$clientsub$")) {
auto it = m_clients.find(*client);
if (it != m_clients.end()) {
it->second.subscribers.clear();
}
@@ -854,27 +857,29 @@ void NetworkTablesModel::Update() {
m_server.UpdatePublishers(entry->value.GetRaw());
} else if (entry->info.name == "$serversub") {
m_server.UpdateSubscribers(entry->value.GetRaw());
} else if (wpi::starts_with(entry->info.name, "$clientpub$")) {
auto it = m_clients.find(wpi::drop_front(entry->info.name, 11));
} else if (auto client =
wpi::remove_prefix(entry->info.name, "$clientpub$")) {
auto it = m_clients.find(*client);
if (it != m_clients.end()) {
it->second.UpdatePublishers(entry->value.GetRaw());
}
} else if (wpi::starts_with(entry->info.name, "$clientsub$")) {
auto it = m_clients.find(wpi::drop_front(entry->info.name, 11));
} else if (auto client =
wpi::remove_prefix(entry->info.name, "$clientsub$")) {
auto it = m_clients.find(*client);
if (it != m_clients.end()) {
it->second.UpdateSubscribers(entry->value.GetRaw());
}
}
} else if (entry->value.IsRaw() &&
wpi::starts_with(entry->info.name, "/.schema/struct:") &&
} else if (auto typeStr =
wpi::remove_prefix(entry->info.name, "/.schema/struct:");
entry->value.IsRaw() && typeStr &&
entry->info.type_str == "structschema") {
// struct schema handling
auto typeStr = wpi::drop_front(entry->info.name, 16);
std::string_view schema{
reinterpret_cast<const char*>(entry->value.GetRaw().data()),
entry->value.GetRaw().size()};
std::string err;
auto desc = m_structDb.Add(typeStr, schema, &err);
auto desc = m_structDb.Add(*typeStr, schema, &err);
if (!desc) {
wpi::print("could not decode struct '{}' schema '{}': {}\n",
entry->info.name, schema, err);
@@ -884,25 +889,23 @@ void NetworkTablesModel::Update() {
if (!entryPair.second) {
continue;
}
std::string_view ts = entryPair.second->info.type_str;
if (!wpi::starts_with(ts, "struct:")) {
continue;
}
ts = wpi::drop_front(ts, 7);
if (ts == typeStr || (wpi::ends_with(ts, "[]") &&
wpi::drop_back(ts, 2) == typeStr)) {
entryPair.second->UpdateFromValue(*this);
if (auto ts = wpi::remove_prefix(entryPair.second->info.type_str,
"struct:")) {
if (*ts == *typeStr ||
wpi::remove_suffix(*ts, "[]").value_or(*ts) == *typeStr) {
entryPair.second->UpdateFromValue(*this);
}
}
}
}
} else if (entry->value.IsRaw() &&
wpi::starts_with(entry->info.name, "/.schema/proto:") &&
} else if (auto filename =
wpi::remove_prefix(entry->info.name, "/.schema/proto:");
entry->value.IsRaw() && filename &&
entry->info.type_str == "proto:FileDescriptorProto") {
// protobuf descriptor handling
auto filename = wpi::drop_front(entry->info.name, 15);
if (!m_protoDb.Add(filename, entry->value.GetRaw())) {
if (!m_protoDb.Add(*filename, entry->value.GetRaw())) {
wpi::print("could not decode protobuf '{}' filename '{}'\n",
entry->info.name, filename);
entry->info.name, *filename);
} else {
// loop over all protobuf entries and update (conservatively)
for (auto&& entryPair : m_entries) {
@@ -1080,18 +1083,18 @@ void NetworkTablesModel::UpdateClients(std::span<const uint8_t> data) {
}
static bool GetHeadingTypeString(std::string_view* ts) {
if (wpi::starts_with(*ts, "proto:")) {
*ts = wpi::drop_front(*ts, 6);
if (auto withoutProto = wpi::remove_prefix(*ts, "proto:")) {
*ts = *withoutProto;
auto lastdot = ts->rfind('.');
if (lastdot != std::string_view::npos) {
*ts = wpi::substr(*ts, lastdot + 1);
}
if (wpi::starts_with(*ts, "Protobuf")) {
*ts = wpi::drop_front(*ts, 8);
if (auto withoutProtobuf = wpi::remove_prefix(*ts, "Protobuf")) {
*ts = *withoutProtobuf;
}
return true;
} else if (wpi::starts_with(*ts, "struct:")) {
*ts = wpi::drop_front(*ts, 7);
} else if (auto withoutStruct = wpi::remove_prefix(*ts, "struct:")) {
*ts = *withoutStruct;
return true;
}
return false;

View File

@@ -157,7 +157,8 @@ void NetworkTablesProvider::Update() {
}
auto topicName = nt::GetTopicName(valueData->topic);
auto tableName = wpi::drop_back(topicName, 6);
auto tableName =
wpi::remove_suffix(topicName, "/.type").value_or(topicName);
GetOrCreateView(builderIt->second, nt::Topic{valueData->topic},
tableName);
@@ -196,9 +197,8 @@ void NetworkTablesProvider::Show(ViewEntry* entry, Window* window) {
if (!window) {
return;
}
if (wpi::starts_with(entry->name, "/SmartDashboard/")) {
window->SetDefaultName(
fmt::format("{} (SmartDashboard)", wpi::drop_front(entry->name, 16)));
if (auto name = wpi::remove_prefix(entry->name, "/SmartDashboard/")) {
window->SetDefaultName(fmt::format("{} (SmartDashboard)", *name));
}
entry->window = window;

View File

@@ -53,10 +53,11 @@ int LocalStorage::DataLoggerData::Start(TopicData* topic, int64_t time) {
} else if (typeStr == "int[]") {
typeStr = "int64[]";
}
return log.Start(fmt::format("{}{}", logPrefix,
wpi::drop_front(topic->name, prefix.size())),
typeStr, DataLoggerEntry::MakeMetadata(topic->propertiesStr),
time);
return log.Start(
fmt::format(
"{}{}", logPrefix,
wpi::remove_prefix(topic->name, prefix).value_or(topic->name)),
typeStr, DataLoggerEntry::MakeMetadata(topic->propertiesStr), time);
}
void LocalStorage::DataLoggerEntry::Append(const Value& v) {

View File

@@ -224,8 +224,8 @@ void NetworkServer::ServerConnection4::ProcessWsUpgrade() {
wpi::SmallString<128> nameBuf;
std::string_view name;
bool err = false;
if (wpi::starts_with(path, "/nt/")) {
name = wpi::UnescapeURI(wpi::drop_front(path, 4), nameBuf, &err);
if (auto uri = wpi::remove_prefix(path, "/nt/")) {
name = wpi::UnescapeURI(*uri, nameBuf, &err);
}
if (err || name.empty()) {
INFO("invalid path '{}' (from {}), must match /nt/[clientId], closing",

View File

@@ -567,7 +567,6 @@ bool nt::net::WireDecodeBinary(std::span<const uint8_t>* in, int64_t* outId,
outValue->SetServerTime(time);
outValue->SetTime(time == 0 ? 0 : time + localTimeOffset);
// update input range
*in = wpi::drop_front(*in,
in->size() - mpack_reader_remaining(&reader, nullptr));
*in = wpi::take_back(*in, mpack_reader_remaining(&reader, nullptr));
return true;
}

View File

@@ -169,14 +169,13 @@ void HALSimHttpConnection::ProcessRequest() {
!wpi::contains(path, "..") && !wpi::contains(path, "//")) {
// convert to fs native representation
fs::path nativePath;
if (wpi::starts_with(path, "/user/")) {
nativePath =
fs::path{m_server->GetWebrootSys()} /
fs::path{wpi::drop_front(path, 6), fs::path::format::generic_format};
if (auto userPath = wpi::remove_prefix(path, "/user/")) {
nativePath = fs::path{m_server->GetWebrootSys()} /
fs::path{*userPath, fs::path::format::generic_format};
} else {
nativePath =
fs::path{m_server->GetWebrootSys()} /
fs::path{wpi::drop_front(path, 1), fs::path::format::generic_format};
fs::path{wpi::drop_front(path), fs::path::format::generic_format};
}
if (fs::is_directory(nativePath)) {

View File

@@ -278,7 +278,7 @@ bool wpi::detail::ConsumeSignedInteger(
}
// Get the positive part of the value.
std::string_view str2 = wpi::drop_front(str, 1);
std::string_view str2 = wpi::drop_front(str);
if (wpi::detail::ConsumeUnsignedInteger(str2, radix, ullVal) ||
// Reject values so large they'd overflow as negative signed, but allow
// "-0". This negates the unsigned so that the negative isn't undefined

View File

@@ -357,6 +357,32 @@ inline bool contains_lower(std::string_view str, const char* other) noexcept {
return find_lower(str, other) != std::string_view::npos;
}
/**
* Return an optional containing @p str but with @p prefix removed if the string
* starts with the prefix. If the string does not start with the prefix, return
* an empty optional.
*/
constexpr std::optional<std::string_view> remove_prefix(std::string_view str, std::string_view prefix) noexcept {
if (str.starts_with(prefix)) {
str.remove_prefix(prefix.size());
return str;
}
return std::nullopt;
}
/**
* Return an optional containing @p str but with @p suffix removed if the
* string ends with the suffix. If the string does not end with the suffix,
* return an empty optional.
*/
constexpr std::optional<std::string_view> remove_suffix(std::string_view str, std::string_view suffix) noexcept {
if (str.ends_with(suffix)) {
str.remove_suffix(suffix.size());
return str;
}
return std::nullopt;
}
/**
* Return a string_view equal to @p str but with the first @p n elements
* dropped.

View File

@@ -0,0 +1,35 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#include <gtest/gtest.h>
#include "wpi/StringExtras.h"
TEST(StringExtrasTest, RemovePrefix) {
std::string_view original = "wpilib";
auto modified = wpi::remove_prefix(original, "wpi");
EXPECT_EQ(original, "wpilib");
EXPECT_EQ(modified, std::optional{"lib"});
}
TEST(StringExtrasTest, RemoveSuffix) {
std::string_view original = "wpilib";
auto modified = wpi::remove_suffix(original, "lib");
EXPECT_EQ(original, "wpilib");
EXPECT_EQ(modified, std::optional{"wpi"});
}
TEST(StringExtrasTest, RemovePrefixNoMatch) {
std::string_view original = "wpilib";
auto modified = wpi::remove_prefix(original, "foo");
EXPECT_EQ(original, "wpilib");
EXPECT_EQ(modified, std::nullopt);
}
TEST(StringExtrasTest, RemoveSuffixNoMatch) {
std::string_view original = "wpilib";
auto modified = wpi::remove_suffix(original, "foo");
EXPECT_EQ(original, "wpilib");
EXPECT_EQ(modified, std::nullopt);
}