Skip to content

Commit

Permalink
Add TOML support
Browse files Browse the repository at this point in the history
  • Loading branch information
IanAWatson committed Oct 8, 2024
1 parent e13ee26 commit 36334ac
Show file tree
Hide file tree
Showing 6 changed files with 235 additions and 3 deletions.
43 changes: 43 additions & 0 deletions src/Foundational/iwmisc/BUILD
Original file line number Diff line number Diff line change
@@ -1,3 +1,20 @@
load("@rules_proto//proto:defs.bzl", "proto_library")

proto_library(
name = "for_testing_proto",
srcs = [
"for_testing.proto",
],
)

cc_proto_library(
name = "for_testing_cc_proto",
deps = [
"for_testing_proto",
]
)


cc_library(
name = "activity_from_file",
srcs = [
Expand Down Expand Up @@ -86,13 +103,25 @@ cc_library(
deps = [
":internal",
":iwconfig",
":ltoml",
"//Foundational/cmdline:iwcmdline",
"//Foundational/data_source:iwstring_data_source",
"//Foundational/iwqsort",
"@com_google_protobuf//:protobuf",
],
)

cc_library(
name = "ltoml",
srcs = [
"toml_support.h",
],
deps = [
"@tomlplusplus//:toml++",
"@com_google_absl//absl/status:status",
],
)

cc_library(
name = "internal",
hdrs = [
Expand Down Expand Up @@ -280,3 +309,17 @@ cc_test(
"@benchmark//:benchmark_main",
],
)

cc_test(
name = "toml_support_test",
srcs = [
"toml_support_test.cc",
],
deps = [
"for_testing_cc_proto",
":iwmisc",
":ltoml",
"@googletest//:gtest_main",
"@tomlplusplus//:toml++",
]
)
24 changes: 24 additions & 0 deletions src/Foundational/iwmisc/for_testing.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
syntax = "proto3";

package for_testing;

message SubMessage {
optional int32 i1 = 1;
optional string str1 = 2;
}

message TestMessage {
optional string str1 = 1;
optional string str2 = 2;

optional int32 i1 = 3;
optional uint32 ui1 = 4;
optional float x = 5;

repeated int32 int_array = 6;
repeated float float_array = 7;

repeated string repeated_string = 8;

optional SubMessage sub_message = 9;
}
5 changes: 5 additions & 0 deletions src/Foundational/iwmisc/proto_support.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
#include "Foundational/iwmisc/proto_support.h"
#include "Foundational/iwstring/iwstring.h"

#include "toml_support.h"

namespace iwmisc {

using std::cerr;
Expand Down Expand Up @@ -144,6 +146,9 @@ ReadTextProto(IWString& fname) {
if (fname.ends_with(".json")) {
return ReadTextProtoJson<Proto>(fname);
}
if (fname.ends_with(".toml")) {
return ReadTomlProto<Proto>(fname);
}

AFile input(fname, O_RDONLY);
if (! input.good()) {
Expand Down
74 changes: 74 additions & 0 deletions src/Foundational/iwmisc/toml_support.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
#ifndef FOUNDATIONAL_IWMISC_TOML_SUPPORT_H_
#define FOUNDATIONAL_IWMISC_TOML_SUPPORT_H_

#include <iostream>
#include <optional>
#include <string>
#include <sstream>

#define TOML_EXCEPTIONS 0 // only necessary if you've left them enabled in your compiler
#define TOML_ENABLE_FORMATTERS 1

#include "absl/status/status.h"

#include "toml.hpp"
#include "google/protobuf/util/json_util.h"

#include "Foundational/iwstring/iwstring.h"

namespace iwmisc {


template <typename Proto>
std::optional<Proto>
TomlToProto(toml::table& tbl) {
std::stringstream ss;
ss << toml::json_formatter{tbl};
const std::string as_json = ss.str();

Proto proto;

google::protobuf::util::JsonParseOptions options;
auto status = google::protobuf::util::JsonStringToMessage(as_json, &proto, options);

// cannot figure out how to do error checking properly. I never really understood Status
// while I was at Google, still don't.
if (status != absl::OkStatus()) {
std::cerr << "TomlToProto:cannot parse JSON\n";
std::cerr << as_json << '\n';
return std::nullopt;
}

return proto;
}

template <typename Proto>
std::optional<Proto>
ReadTomlProto(const IWString& fname) {
const std::string string_fname(fname.data(), fname.length());

auto as_toml = toml::parse_file(string_fname);
if (! as_toml) {
std::cerr << "ReadTomlProto:parse_file failed " << string_fname << '\n';
return std::nullopt;
}

return TomlToProto<Proto>(std::move(as_toml));
}

template <typename Proto>
std::optional<Proto>
ParseFromToml(const std::string& toml_string) {
auto as_toml = toml::parse(toml_string);
if (! as_toml) {
std::cerr << "ParseFromToml:Cannot parse as TOML\n";
std::cerr << toml_string << '\n';
return std::nullopt;
}

return TomlToProto<Proto>(std::move(as_toml));
}

}

#endif // FOUNDATIONAL_IWMISC_TOML_SUPPORT_H_
88 changes: 88 additions & 0 deletions src/Foundational/iwmisc/toml_support_test.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// Unit tests

#include <optional>

#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "google/protobuf/text_format.h"

#include "Foundational/iwmisc/toml_support.h"

#include "Foundational/iwmisc/for_testing.pb.h"

namespace {

using testing::ElementsAre;
using testing::Eq;

using for_testing::TestMessage;

TEST(Test, TestStrings) {
const std::string toml = R"(
str1 = "hello"
str2 = "world"
)";
std::optional<TestMessage> msg = iwmisc::ParseFromToml<TestMessage>(toml);
EXPECT_NE(msg, std::nullopt);

EXPECT_EQ(msg->str1(), "hello");
EXPECT_EQ(msg->str2(), "world");
}


TEST(Test, TestInt) {
const std::string toml = R"(
i1 = -12
)";
std::optional<TestMessage> msg = iwmisc::ParseFromToml<TestMessage>(toml);
EXPECT_NE(msg, std::nullopt);

EXPECT_EQ(msg->i1(), -12);
}

TEST(Test, TestUInt) {
const std::string toml = R"(
ui1 = 12
)";
std::optional<TestMessage> msg = iwmisc::ParseFromToml<TestMessage>(toml);
EXPECT_NE(msg, std::nullopt);

EXPECT_EQ(msg->ui1(), 12);
}

TEST(Test, TestFloat) {
const std::string toml = R"(
x = 12.0
)";
std::optional<TestMessage> msg = iwmisc::ParseFromToml<TestMessage>(toml);
EXPECT_NE(msg, std::nullopt);

EXPECT_FLOAT_EQ(msg->x(), static_cast<float>(12.0));
}

TEST(Test, TestIntArray) {
const std::string toml = R"(
int_array = [1,2,3,4,5]
)";
std::optional<TestMessage> msg = iwmisc::ParseFromToml<TestMessage>(toml);
EXPECT_NE(msg, std::nullopt);

EXPECT_THAT(msg->int_array(), ElementsAre(1,2,3,4,5));
}

TEST(Test, TestSubMessage) {
const std::string toml = R"(
repeated_string = ["hello", "world"]
[sub_message]
i1 = 3
str1 = "vr46"
)";
std::optional<TestMessage> msg = iwmisc::ParseFromToml<TestMessage>(toml);
EXPECT_NE(msg, std::nullopt);

EXPECT_THAT(msg->repeated_string(), ElementsAre("hello", "world"));
EXPECT_THAT(msg->sub_message().i1(), Eq(3));
EXPECT_THAT(msg->sub_message().str1(), Eq("vr46"));
}

} // namespace
4 changes: 1 addition & 3 deletions src/MODULE.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,5 @@ bazel_dep(name = "rules_pkg", version = "0.10.1")
bazel_dep(name = "rules_proto", version = "6.0.2")
bazel_dep(name = "rules_proto_grpc", version = "5.0.0")
bazel_dep(name = "rules_ruby", version = "0.12.0")
# Seem to be two toml handlers. Figure out which to use...
# bazel_dep(name = "tomlplusplus", version = "3.4.0")
# bazel_dep(name = "cpptoml", version = "0.1.1")
bazel_dep(name = "tomlplusplus", version = "3.4.0")
bazel_dep(name = "zlib", version = "1.3.1")

0 comments on commit 36334ac

Please sign in to comment.