mirror of
https://github.com/davidgiven/fluxengine.git
synced 2025-10-31 11:17:01 -07:00
Non-functioning archival checkin.
This commit is contained in:
@@ -387,6 +387,12 @@ sources.)
|
||||
|
||||
Shows all the components inside a flux file.
|
||||
|
||||
- `fluxfile rm <fluxfile>:<path>...`
|
||||
|
||||
Removes flux from a flux file. You may specify the path to an individual read
|
||||
(e.g. `track.h0_t5.flux9`) or the track itself (`track.h0_t5`); the latter
|
||||
will remove all reads from the track.
|
||||
|
||||
### High density disks
|
||||
|
||||
High density disks use a different magnetic medium to low and double density
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "lib/core/globals.h"
|
||||
#include "lib/config/proto.h"
|
||||
#include "lib/config/common.pb.h"
|
||||
#include "google/protobuf/reflection.h"
|
||||
#include <regex>
|
||||
|
||||
static ConfigProto config = []()
|
||||
@@ -16,7 +17,7 @@ ConfigProto& globalConfigProto()
|
||||
return config;
|
||||
}
|
||||
|
||||
static double toFloat(const std::string& value)
|
||||
static float toFloat(const std::string& value)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -87,6 +88,21 @@ void setRange(RangeProto* range, const std::string& data)
|
||||
range->set_step(std::stoi(dmatch[4]));
|
||||
}
|
||||
|
||||
static int splitIndexedField(std::string& item)
|
||||
{
|
||||
static const std::regex INDEX_REGEX("(\\w+)\\[([0-9]+)\\]");
|
||||
int index = -1;
|
||||
|
||||
std::smatch dmatch;
|
||||
if (std::regex_match(item, dmatch, INDEX_REGEX))
|
||||
{
|
||||
item = dmatch[1];
|
||||
index = std::stoi(dmatch[2]);
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
static ProtoField resolveProtoPath(
|
||||
google::protobuf::Message* message, const std::string& path, bool create)
|
||||
{
|
||||
@@ -109,13 +125,7 @@ static ProtoField resolveProtoPath(
|
||||
{
|
||||
static const std::regex INDEX_REGEX("(\\w+)\\[([0-9]+)\\]");
|
||||
|
||||
int index = -1;
|
||||
std::smatch dmatch;
|
||||
if (std::regex_match(item, dmatch, INDEX_REGEX))
|
||||
{
|
||||
item = dmatch[1];
|
||||
index = std::stoi(dmatch[2]);
|
||||
}
|
||||
int index = splitIndexedField(item);
|
||||
|
||||
const auto* field = descriptor->FindFieldByName(item);
|
||||
if (!field)
|
||||
@@ -163,11 +173,12 @@ static ProtoField resolveProtoPath(
|
||||
descriptor = message->GetDescriptor();
|
||||
}
|
||||
|
||||
int index = splitIndexedField(trailing);
|
||||
const auto* field = descriptor->FindFieldByName(trailing);
|
||||
if (!field)
|
||||
fail();
|
||||
|
||||
return std::make_pair(message, field);
|
||||
return std::make_tuple(message, field, -1);
|
||||
}
|
||||
|
||||
ProtoField makeProtoPath(
|
||||
@@ -182,98 +193,208 @@ ProtoField findProtoPath(
|
||||
return resolveProtoPath(message, path, /* create= */ false);
|
||||
}
|
||||
|
||||
static bool parseBoolean(const std::string& value)
|
||||
{
|
||||
static const std::map<std::string, bool> boolvalues = {
|
||||
{"false", false},
|
||||
{"f", false},
|
||||
{"no", false},
|
||||
{"n", false},
|
||||
{"0", false},
|
||||
{"true", true },
|
||||
{"t", true },
|
||||
{"yes", true },
|
||||
{"y", true },
|
||||
{"1", true },
|
||||
};
|
||||
|
||||
const auto& it = boolvalues.find(value);
|
||||
if (it == boolvalues.end())
|
||||
error("invalid boolean value");
|
||||
return it->second;
|
||||
}
|
||||
|
||||
static int32_t parseEnum(
|
||||
const google::protobuf::FieldDescriptor* field, const std::string& value)
|
||||
{
|
||||
const auto* enumfield = field->enum_type();
|
||||
const auto* enumvalue = enumfield->FindValueByName(value);
|
||||
if (!enumvalue)
|
||||
error("unrecognised enum value '{}'", value);
|
||||
return enumvalue->index();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void updateRepeatedField(
|
||||
google::protobuf::MutableRepeatedFieldRef<T> mrfr, int index, T value)
|
||||
{
|
||||
mrfr.Set(index, value);
|
||||
}
|
||||
|
||||
void setProtoFieldFromString(ProtoField& protoField, const std::string& value)
|
||||
{
|
||||
google::protobuf::Message* message = protoField.first;
|
||||
const google::protobuf::FieldDescriptor* field = protoField.second;
|
||||
auto& [message, field, index] = protoField;
|
||||
|
||||
const auto* reflection = message->GetReflection();
|
||||
switch (field->type())
|
||||
if (field->label() == google::protobuf::FieldDescriptor::LABEL_REPEATED)
|
||||
{
|
||||
case google::protobuf::FieldDescriptor::TYPE_FLOAT:
|
||||
reflection->SetFloat(message, field, toFloat(value));
|
||||
break;
|
||||
if (index == -1)
|
||||
error("field '{}' is repeated but no index is provided");
|
||||
|
||||
case google::protobuf::FieldDescriptor::TYPE_DOUBLE:
|
||||
reflection->SetDouble(message, field, toDouble(value));
|
||||
break;
|
||||
|
||||
case google::protobuf::FieldDescriptor::TYPE_INT32:
|
||||
reflection->SetInt32(message, field, toInt64(value));
|
||||
break;
|
||||
|
||||
case google::protobuf::FieldDescriptor::TYPE_INT64:
|
||||
reflection->SetInt64(message, field, toInt64(value));
|
||||
break;
|
||||
|
||||
case google::protobuf::FieldDescriptor::TYPE_UINT32:
|
||||
reflection->SetUInt32(message, field, toUint64(value));
|
||||
break;
|
||||
|
||||
case google::protobuf::FieldDescriptor::TYPE_UINT64:
|
||||
reflection->SetUInt64(message, field, toUint64(value));
|
||||
break;
|
||||
|
||||
case google::protobuf::FieldDescriptor::TYPE_STRING:
|
||||
reflection->SetString(message, field, value);
|
||||
break;
|
||||
|
||||
case google::protobuf::FieldDescriptor::TYPE_BOOL:
|
||||
switch (field->type())
|
||||
{
|
||||
static const std::map<std::string, bool> boolvalues = {
|
||||
{"false", false},
|
||||
{"f", false},
|
||||
{"no", false},
|
||||
{"n", false},
|
||||
{"0", false},
|
||||
{"true", true },
|
||||
{"t", true },
|
||||
{"yes", true },
|
||||
{"y", true },
|
||||
{"1", true },
|
||||
};
|
||||
case google::protobuf::FieldDescriptor::TYPE_FLOAT:
|
||||
updateRepeatedField(
|
||||
reflection->GetMutableRepeatedFieldRef<float>(
|
||||
message, field),
|
||||
index,
|
||||
toFloat(value));
|
||||
break;
|
||||
|
||||
const auto& it = boolvalues.find(value);
|
||||
if (it == boolvalues.end())
|
||||
error("invalid boolean value");
|
||||
reflection->SetBool(message, field, it->second);
|
||||
break;
|
||||
}
|
||||
case google::protobuf::FieldDescriptor::TYPE_DOUBLE:
|
||||
updateRepeatedField(
|
||||
reflection->GetMutableRepeatedFieldRef<double>(
|
||||
message, field),
|
||||
index,
|
||||
toDouble(value));
|
||||
break;
|
||||
|
||||
case google::protobuf::FieldDescriptor::TYPE_ENUM:
|
||||
{
|
||||
const auto* enumfield = field->enum_type();
|
||||
const auto* enumvalue = enumfield->FindValueByName(value);
|
||||
if (!enumvalue)
|
||||
error("unrecognised enum value '{}'", value);
|
||||
case google::protobuf::FieldDescriptor::TYPE_INT32:
|
||||
updateRepeatedField(
|
||||
reflection->GetMutableRepeatedFieldRef<int32_t>(
|
||||
message, field),
|
||||
index,
|
||||
(int32_t)toInt64(value));
|
||||
break;
|
||||
|
||||
reflection->SetEnum(message, field, enumvalue);
|
||||
break;
|
||||
}
|
||||
case google::protobuf::FieldDescriptor::TYPE_INT64:
|
||||
updateRepeatedField(
|
||||
reflection->GetMutableRepeatedFieldRef<int64_t>(
|
||||
message, field),
|
||||
index,
|
||||
toInt64(value));
|
||||
break;
|
||||
|
||||
case google::protobuf::FieldDescriptor::TYPE_MESSAGE:
|
||||
if (field->message_type() == RangeProto::descriptor())
|
||||
{
|
||||
setRange(
|
||||
(RangeProto*)reflection->MutableMessage(message, field),
|
||||
case google::protobuf::FieldDescriptor::TYPE_UINT32:
|
||||
updateRepeatedField(
|
||||
reflection->GetMutableRepeatedFieldRef<uint32_t>(
|
||||
message, field),
|
||||
index,
|
||||
(uint32_t)toUInt64(value));
|
||||
break;
|
||||
|
||||
case google::protobuf::FieldDescriptor::TYPE_UINT64:
|
||||
updateRepeatedField(
|
||||
reflection->GetMutableRepeatedFieldRef<uint64_t>(
|
||||
message, field),
|
||||
index,
|
||||
toUInt64(value));
|
||||
break;
|
||||
|
||||
case google::protobuf::FieldDescriptor::TYPE_STRING:
|
||||
updateRepeatedField(
|
||||
reflection->GetMutableRepeatedFieldRef<std::string>(
|
||||
message, field),
|
||||
index,
|
||||
value);
|
||||
break;
|
||||
}
|
||||
if (field->containing_oneof() && value.empty())
|
||||
{
|
||||
reflection->MutableMessage(message, field);
|
||||
|
||||
case google::protobuf::FieldDescriptor::TYPE_BOOL:
|
||||
updateRepeatedField(
|
||||
reflection->GetMutableRepeatedFieldRef<bool>(
|
||||
message, field),
|
||||
index,
|
||||
parseBoolean(value));
|
||||
break;
|
||||
}
|
||||
/* fall through */
|
||||
default:
|
||||
error("can't set this config value type");
|
||||
|
||||
case google::protobuf::FieldDescriptor::TYPE_ENUM:
|
||||
reflection->SetRepeatedEnum(
|
||||
message, field, index, parseEnum(field, value));
|
||||
break;
|
||||
|
||||
case google::protobuf::FieldDescriptor::TYPE_MESSAGE:
|
||||
if (field->message_type() == RangeProto::descriptor())
|
||||
{
|
||||
setRange((RangeProto*)reflection->MutableRepeatedMessage(
|
||||
message, field, index),
|
||||
value);
|
||||
break;
|
||||
}
|
||||
if (field->containing_oneof() && value.empty())
|
||||
{
|
||||
reflection->MutableRepeatedMessage(message, field, index);
|
||||
break;
|
||||
}
|
||||
/* fall through */
|
||||
default:
|
||||
error("can't set this config value type");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (index != -1)
|
||||
error("field '{}' is not repeated but an index is provided");
|
||||
switch (field->type())
|
||||
{
|
||||
case google::protobuf::FieldDescriptor::TYPE_FLOAT:
|
||||
reflection->SetFloat(message, field, toFloat(value));
|
||||
break;
|
||||
|
||||
case google::protobuf::FieldDescriptor::TYPE_DOUBLE:
|
||||
reflection->SetDouble(message, field, toDouble(value));
|
||||
break;
|
||||
|
||||
case google::protobuf::FieldDescriptor::TYPE_INT32:
|
||||
reflection->SetInt32(message, field, toInt64(value));
|
||||
break;
|
||||
|
||||
case google::protobuf::FieldDescriptor::TYPE_INT64:
|
||||
reflection->SetInt64(message, field, toInt64(value));
|
||||
break;
|
||||
|
||||
case google::protobuf::FieldDescriptor::TYPE_UINT32:
|
||||
reflection->SetUInt32(message, field, toUint64(value));
|
||||
break;
|
||||
|
||||
case google::protobuf::FieldDescriptor::TYPE_UINT64:
|
||||
reflection->SetUInt64(message, field, toUint64(value));
|
||||
break;
|
||||
|
||||
case google::protobuf::FieldDescriptor::TYPE_STRING:
|
||||
reflection->SetString(message, field, value);
|
||||
break;
|
||||
|
||||
case google::protobuf::FieldDescriptor::TYPE_BOOL:
|
||||
reflection->SetBool(message, field, parseBoolean(value));
|
||||
break;
|
||||
|
||||
case google::protobuf::FieldDescriptor::TYPE_ENUM:
|
||||
reflection->SetEnum(message, field, parseEnum(field, value));
|
||||
break;
|
||||
|
||||
case google::protobuf::FieldDescriptor::TYPE_MESSAGE:
|
||||
if (field->message_type() == RangeProto::descriptor())
|
||||
{
|
||||
setRange(
|
||||
(RangeProto*)reflection->MutableMessage(message, field),
|
||||
value);
|
||||
break;
|
||||
}
|
||||
if (field->containing_oneof() && value.empty())
|
||||
{
|
||||
reflection->MutableMessage(message, field);
|
||||
break;
|
||||
}
|
||||
/* fall through */
|
||||
default:
|
||||
error("can't set this config value type");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string getProtoFieldValue(ProtoField& protoField)
|
||||
{
|
||||
google::protobuf::Message* message = protoField.first;
|
||||
const google::protobuf::FieldDescriptor* field = protoField.second;
|
||||
auto& [message, field, index] = protoField;
|
||||
|
||||
const auto* reflection = message->GetReflection();
|
||||
switch (field->type())
|
||||
@@ -363,8 +484,8 @@ std::set<unsigned> iterate(unsigned start, unsigned count)
|
||||
static bool shouldRecurse(const google::protobuf::FieldDescriptor* f)
|
||||
{
|
||||
if (f->type() != google::protobuf::FieldDescriptor::TYPE_MESSAGE)
|
||||
return false;
|
||||
return f->message_type()->options().GetExtension(::recurse);
|
||||
return false;
|
||||
return f->message_type()->options().GetExtension(::recurse);
|
||||
}
|
||||
|
||||
std::map<std::string, const google::protobuf::FieldDescriptor*>
|
||||
|
||||
@@ -16,8 +16,8 @@ public:
|
||||
|
||||
extern void setRange(RangeProto* range, const std::string& data);
|
||||
|
||||
typedef std::pair<google::protobuf::Message*,
|
||||
const google::protobuf::FieldDescriptor*>
|
||||
typedef std::tuple<google::protobuf::Message*,
|
||||
const google::protobuf::FieldDescriptor*, int>
|
||||
ProtoField;
|
||||
|
||||
extern ProtoField makeProtoPath(
|
||||
|
||||
9
lib/external/fl2.proto
vendored
9
lib/external/fl2.proto
vendored
@@ -1,5 +1,12 @@
|
||||
syntax = "proto2";
|
||||
|
||||
import "google/protobuf/descriptor.proto";
|
||||
|
||||
extend google.protobuf.FieldOptions
|
||||
{
|
||||
optional bool isflux = 60000 [default = false];
|
||||
}
|
||||
|
||||
enum FluxMagic {
|
||||
MAGIC = 0x466c7578;
|
||||
}
|
||||
@@ -12,7 +19,7 @@ enum FluxFileVersion {
|
||||
message TrackFluxProto {
|
||||
optional int32 track = 1;
|
||||
optional int32 head = 2;
|
||||
repeated bytes flux = 3;
|
||||
repeated bytes flux = 3 [(isflux) = true];
|
||||
}
|
||||
|
||||
enum DriveType {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
syntax = "proto2";
|
||||
|
||||
import "lib/config/common.proto";
|
||||
import "lib/config/layout.proto";
|
||||
|
||||
message AcornDfsProto
|
||||
{
|
||||
|
||||
@@ -5,9 +5,12 @@ cxxprogram(
|
||||
srcs=[
|
||||
"./fluxengine.cc",
|
||||
"./fluxengine.h",
|
||||
"./fluxfile.cc",
|
||||
"./fluxfile.h",
|
||||
"./fe-analysedriveresponse.cc",
|
||||
"./fe-analyselayout.cc",
|
||||
"./fe-fluxfilels.cc",
|
||||
"./fe-fluxfilerm.cc",
|
||||
"./fe-format.cc",
|
||||
"./fe-getdiskinfo.cc",
|
||||
"./fe-getfile.cc",
|
||||
@@ -39,6 +42,7 @@ cxxprogram(
|
||||
"+z_lib",
|
||||
"dep/adflib",
|
||||
"dep/agg",
|
||||
"dep/alphanum",
|
||||
"dep/fatfs",
|
||||
"dep/hfsutils",
|
||||
"dep/libusbp",
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "lib/data/flux.h"
|
||||
#include "lib/external/fl2.h"
|
||||
#include "lib/external/fl2.pb.h"
|
||||
#include "dep/alphanum/alphanum.h"
|
||||
#include "src/fluxengine.h"
|
||||
#include <fstream>
|
||||
|
||||
@@ -18,28 +19,19 @@ int mainFluxfileLs(int argc, const char* argv[])
|
||||
if (filenames.size() != 1)
|
||||
error("you must specify exactly one filename");
|
||||
|
||||
const auto& filename = *filenames.begin();
|
||||
fmt::print("Contents of {}:\n", filename);
|
||||
FluxFileProto f = loadFl2File(filename);
|
||||
|
||||
fmt::print("version: {}\n", getProtoByString(&f, "version"));
|
||||
fmt::print("rotational_period_ms: {}\n",
|
||||
getProtoByString(&f, "rotational_period_ms"));
|
||||
fmt::print("drive_type: {}\n", getProtoByString(&f, "drive_type"));
|
||||
fmt::print("format_type: {}\n", getProtoByString(&f, "format_type"));
|
||||
for (const auto& track : f.track())
|
||||
for (const auto& filename : filenames)
|
||||
{
|
||||
for (int i = 0; i < track.flux().size(); i++)
|
||||
{
|
||||
const auto& flux = track.flux().at(i);
|
||||
Fluxmap fluxmap(flux);
|
||||
fmt::print("Contents of {}:\n", filename);
|
||||
FluxFileProto f = loadFl2File(filename);
|
||||
|
||||
fmt::print("track.t{}_h{}.flux{}: {:.3f} ms, {} bytes\n",
|
||||
track.track(),
|
||||
track.head(),
|
||||
i,
|
||||
fluxmap.duration() / 1000000,
|
||||
fluxmap.bytes());
|
||||
auto fields = findAllProtoFields(f);
|
||||
std::set<std::string, doj::alphanum_less<std::string>> fieldsSorted;
|
||||
for (const auto& e : fields)
|
||||
fieldsSorted.insert(e.first);
|
||||
|
||||
for (const auto& e : fieldsSorted)
|
||||
{
|
||||
fmt::print("{}: {}\n", e, getProtoByString(&f, e));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
47
src/fe-fluxfilerm.cc
Normal file
47
src/fe-fluxfilerm.cc
Normal file
@@ -0,0 +1,47 @@
|
||||
#include "lib/core/globals.h"
|
||||
#include "lib/config/flags.h"
|
||||
#include "lib/data/fluxmap.h"
|
||||
#include "lib/data/sector.h"
|
||||
#include "lib/config/proto.h"
|
||||
#include "lib/data/flux.h"
|
||||
#include "lib/external/fl2.h"
|
||||
#include "lib/external/fl2.pb.h"
|
||||
#include "src/fluxengine.h"
|
||||
#include <fstream>
|
||||
|
||||
static FlagGroup flags;
|
||||
static std::string filename;
|
||||
|
||||
int mainFluxfileRm(int argc, const char* argv[])
|
||||
{
|
||||
const auto filenames = flags.parseFlagsWithFilenames(argc, argv);
|
||||
if (filenames.size() != 1)
|
||||
error("you must specify exactly one filename");
|
||||
|
||||
const auto& filename = *filenames.begin();
|
||||
fmt::print("Contents of {}:\n", filename);
|
||||
FluxFileProto f = loadFl2File(filename);
|
||||
|
||||
fmt::print("version: {}\n", getProtoByString(&f, "version"));
|
||||
fmt::print("rotational_period_ms: {}\n",
|
||||
getProtoByString(&f, "rotational_period_ms"));
|
||||
fmt::print("drive_type: {}\n", getProtoByString(&f, "drive_type"));
|
||||
fmt::print("format_type: {}\n", getProtoByString(&f, "format_type"));
|
||||
for (const auto& track : f.track())
|
||||
{
|
||||
for (int i = 0; i < track.flux().size(); i++)
|
||||
{
|
||||
const auto& flux = track.flux().at(i);
|
||||
Fluxmap fluxmap(flux);
|
||||
|
||||
fmt::print("track.t{}_h{}.flux{}: {:.3f} ms, {} bytes\n",
|
||||
track.track(),
|
||||
track.head(),
|
||||
i,
|
||||
fluxmap.duration() / 1000000,
|
||||
fluxmap.bytes());
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -7,6 +7,7 @@ typedef int command_cb(int agrc, const char* argv[]);
|
||||
extern command_cb mainAnalyseDriveResponse;
|
||||
extern command_cb mainAnalyseLayout;
|
||||
extern command_cb mainFluxfileLs;
|
||||
extern command_cb mainFluxfileRm;
|
||||
extern command_cb mainFormat;
|
||||
extern command_cb mainGetDiskInfo;
|
||||
extern command_cb mainGetFile;
|
||||
@@ -80,6 +81,7 @@ static std::vector<Command> testables =
|
||||
static std::vector<Command> fluxfileables =
|
||||
{
|
||||
{ "ls", mainFluxfileLs, "Lists the contents of a flux file.", },
|
||||
{ "rm", mainFluxfileRm, "Removes flux from a flux file.", },
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
|
||||
0
src/fluxfile.cc
Normal file
0
src/fluxfile.cc
Normal file
4
src/fluxfile.h
Normal file
4
src/fluxfile.h
Normal file
@@ -0,0 +1,4 @@
|
||||
#pragma once
|
||||
|
||||
extern void parseFluxfilePath(
|
||||
const std::string& combined, std::string& filename, std::string& path);
|
||||
@@ -57,6 +57,7 @@ protoencode(
|
||||
name="formats_cc",
|
||||
srcs={name: f"./{name}.textpb" for name in formats},
|
||||
proto="ConfigProto",
|
||||
include="lib/config/config.pb.h",
|
||||
symbol="formats",
|
||||
)
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ protoencode(
|
||||
name="drivetypes_cc",
|
||||
srcs={name: f"./{name}.textpb" for name in drivetypes},
|
||||
proto="ConfigProto",
|
||||
include="lib/config/config.pb.h",
|
||||
symbol="drivetypes",
|
||||
)
|
||||
|
||||
|
||||
@@ -85,6 +85,9 @@ static void test_getting(void)
|
||||
}
|
||||
secondoption {
|
||||
s: "2"
|
||||
r: 0
|
||||
r: 1
|
||||
r: 2
|
||||
}
|
||||
range {
|
||||
start: 1
|
||||
@@ -108,6 +111,7 @@ static void test_getting(void)
|
||||
AssertThat(getProtoByString(&tp, "r[1].s"), Equals("val3"));
|
||||
AssertThrows(
|
||||
ProtoPathNotFoundException, getProtoByString(&tp, "firstoption.s"));
|
||||
AssertThat(getProtoByString(&tp, "secondoption.r[2]"), Equals("2"));
|
||||
AssertThat(getProtoByString(&tp, "secondoption.s"), Equals("2"));
|
||||
AssertThat(getProtoByString(&tp, "range"), Equals("1-3x2"));
|
||||
}
|
||||
@@ -223,15 +227,19 @@ static void test_fields(void)
|
||||
Equals(std::vector<std::string>{"d",
|
||||
"f",
|
||||
"firstoption",
|
||||
"firstoption.r[]",
|
||||
"firstoption.s",
|
||||
"i32",
|
||||
"i64",
|
||||
"m",
|
||||
"m.r[]",
|
||||
"m.s",
|
||||
"r[]",
|
||||
"r[].r[]",
|
||||
"r[].s",
|
||||
"range",
|
||||
"secondoption",
|
||||
"secondoption.r[]",
|
||||
"secondoption.s",
|
||||
"u32",
|
||||
"u64"}));
|
||||
|
||||
@@ -7,6 +7,7 @@ message TestProto
|
||||
message SubMessageProto
|
||||
{
|
||||
optional string s = 1;
|
||||
repeated int32 r = 2;
|
||||
}
|
||||
|
||||
optional int64 i64 = 1 [(help) = "i64"];
|
||||
|
||||
Reference in New Issue
Block a user