Ended up writing an ArgsParser class.
This commit is contained in:
@@ -28,6 +28,6 @@ if (WIN32)
|
|||||||
add_library(json STATIC ${SOURCES})
|
add_library(json STATIC ${SOURCES})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_executable(json_demo main.cpp)
|
add_executable(json_cli main.cpp)
|
||||||
|
|
||||||
target_link_libraries(json_demo PUBLIC json)
|
target_link_libraries(json_cli PUBLIC json)
|
24
cmake/CPM.cmake
Normal file
24
cmake/CPM.cmake
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
#
|
||||||
|
# SPDX-FileCopyrightText: Copyright (c) 2019-2023 Lars Melchior and contributors
|
||||||
|
|
||||||
|
set(CPM_DOWNLOAD_VERSION 0.38.7)
|
||||||
|
set(CPM_HASH_SUM "83e5eb71b2bbb8b1f2ad38f1950287a057624e385c238f6087f94cdfc44af9c5")
|
||||||
|
|
||||||
|
if(CPM_SOURCE_CACHE)
|
||||||
|
set(CPM_DOWNLOAD_LOCATION "${CPM_SOURCE_CACHE}/cpm/CPM_${CPM_DOWNLOAD_VERSION}.cmake")
|
||||||
|
elseif(DEFINED ENV{CPM_SOURCE_CACHE})
|
||||||
|
set(CPM_DOWNLOAD_LOCATION "$ENV{CPM_SOURCE_CACHE}/cpm/CPM_${CPM_DOWNLOAD_VERSION}.cmake")
|
||||||
|
else()
|
||||||
|
set(CPM_DOWNLOAD_LOCATION "${CMAKE_BINARY_DIR}/cmake/CPM_${CPM_DOWNLOAD_VERSION}.cmake")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Expand relative path. This is important if the provided path contains a tilde (~)
|
||||||
|
get_filename_component(CPM_DOWNLOAD_LOCATION ${CPM_DOWNLOAD_LOCATION} ABSOLUTE)
|
||||||
|
|
||||||
|
file(DOWNLOAD
|
||||||
|
https://github.com/cpm-cmake/CPM.cmake/releases/download/v${CPM_DOWNLOAD_VERSION}/CPM.cmake
|
||||||
|
${CPM_DOWNLOAD_LOCATION} EXPECTED_HASH SHA256=${CPM_HASH_SUM}
|
||||||
|
)
|
||||||
|
|
||||||
|
include(${CPM_DOWNLOAD_LOCATION})
|
353
main.cpp
353
main.cpp
@@ -1,6 +1,11 @@
|
|||||||
|
/// A command-line tool for JSON parsing and file manipulation.
|
||||||
|
|
||||||
#include <json.hpp>
|
#include <json.hpp>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
|
||||||
/// Open a text file and return the contents.
|
/// Open a text file and return the contents.
|
||||||
@@ -33,167 +38,205 @@ void parse_json_file(const std::string& file_path) {
|
|||||||
std::cout << json::deparse(text) << std::endl;
|
std::cout << json::deparse(text) << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Vector3
|
namespace argue {
|
||||||
|
class ArgsParser {
|
||||||
|
public:
|
||||||
|
ArgsParser(int argc, char** argv) {
|
||||||
|
args = std::vector<std::string>(argv, argv+argc);
|
||||||
|
}
|
||||||
|
explicit ArgsParser(const std::vector<std::string>& value): args(value) {}
|
||||||
|
|
||||||
|
bool has_flag(const std::string& option) {
|
||||||
|
return std::ranges::find(args, option) != args.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool has_flag(const std::initializer_list<std::string>& flags) {
|
||||||
|
for (auto& flag : flags)
|
||||||
|
if (has_flag(flag))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Searches the arguments for all given flag tokens, and removes all occurrences of it.
|
||||||
|
/// @return true if the flag was found in the arguments.
|
||||||
|
bool consume_flag(const std::string& option) {
|
||||||
|
if (has_flag(option)) {
|
||||||
|
args.erase(std::remove(args.begin(), args.end(), option), args.end());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Searches the arguments for all given flag tokens, and removes them.
|
||||||
|
/// @return True if the flag was found in the arguments.
|
||||||
|
bool consume_flag(const std::initializer_list<std::string>& flags) {
|
||||||
|
bool had_any_flag = false;
|
||||||
|
for (auto& flag : flags) {
|
||||||
|
if (has_flag(flags)) {
|
||||||
|
had_any_flag = true;
|
||||||
|
args.erase(std::remove(args.begin(), args.end(), flag), args.end());
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return had_any_flag;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool has_flag_arg(const std::string& option) {
|
||||||
|
if (has_flag(option)) {
|
||||||
|
auto it = std::ranges::find(args, option);
|
||||||
|
return it!=args.end() && ++it!=args.end();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string get_flag_arg(const std::string& option) {
|
||||||
|
auto it = std::ranges::find(args, option);
|
||||||
|
++it;
|
||||||
|
|
||||||
|
return *it;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::tuple<bool, std::string> consume_flag_arg(const std::string& flag) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
std::tuple<bool, std::string> consume_flag_arg(const std::initializer_list<std::string>& flags) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::vector<std::string> args;
|
||||||
|
protected:
|
||||||
|
private:
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void show_help() {
|
||||||
|
std::cout << "Usage:" << std::endl;
|
||||||
|
std::cout << ">json <--options> content" << std::endl;
|
||||||
|
std::cout << "Options: " << std::endl;
|
||||||
|
std::cout << "--help --about --license " << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void show_license() {
|
||||||
|
std::cout << "" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void show_version()
|
||||||
{
|
{
|
||||||
float x, y, z;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/// Creates json array out of Vector3 type.
|
|
||||||
json::value vector3_to_json(const Vector3& v) {
|
|
||||||
//auto x = json::number(input.x);
|
|
||||||
//auto y = json::number(input.y);
|
|
||||||
//auto z = json::number(input.z);
|
|
||||||
return json::array({json::number(v.x), json::number(v.y), json::number(v.z)});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates Vector3 type out of json array object.
|
|
||||||
Vector3 json_to_vector3(const json::value& val) {
|
|
||||||
Vector3 value;
|
|
||||||
auto arr = val.as_array_value();
|
|
||||||
|
|
||||||
value.x = arr[0].as_float_or(0);
|
|
||||||
value.y = arr.at(1).as_float_or(0);
|
|
||||||
value.z = arr.at(2).as_float_or(0);
|
|
||||||
|
|
||||||
return {value};
|
|
||||||
}
|
|
||||||
|
|
||||||
struct product_info
|
|
||||||
{
|
|
||||||
[[nodiscard]] json::value serialize() const;
|
|
||||||
product_info() = default;
|
|
||||||
/// @schema object {string category, string description, boolean is_available, string name, number price, array[@review] reviews}
|
|
||||||
/// @note This class design assumes all entries in `reviews` are serializable to @struct review,
|
|
||||||
///
|
|
||||||
explicit product_info(const json::value& jv);
|
|
||||||
|
|
||||||
struct rating_metrics {
|
|
||||||
double average;
|
|
||||||
int total;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// @schema { rating : number, review_text : string, user : string }
|
|
||||||
struct review {
|
|
||||||
[[nodiscard]] json::value serialize() const;
|
|
||||||
explicit review(const json::value& jv);
|
|
||||||
|
|
||||||
int rating;
|
|
||||||
std::string review_text;
|
|
||||||
std::string user;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::string category;
|
|
||||||
std::string description;
|
|
||||||
bool is_available;
|
|
||||||
std::string manufacturer;
|
|
||||||
std::string name;
|
|
||||||
double price;
|
|
||||||
std::string product_id;
|
|
||||||
rating_metrics ratings;
|
|
||||||
std::string release_date;
|
|
||||||
std::vector<review> reviews;
|
|
||||||
};
|
|
||||||
|
|
||||||
json::value product_info::serialize() const {
|
|
||||||
json::object root;
|
|
||||||
|
|
||||||
root.insert("category", this->category);
|
|
||||||
root += {"category", json::string( this->category)};
|
|
||||||
root += {"manufacturer", json::string( this->manufacturer)};
|
|
||||||
root += {"is_available", json::boolean(this->is_available)};
|
|
||||||
root += {"description", json::string( this->description)};
|
|
||||||
root += {"name", json::string( this->name)};
|
|
||||||
|
|
||||||
auto review_list = json::array();
|
|
||||||
for (auto& rev : this->reviews)
|
|
||||||
review_list += rev.serialize();
|
|
||||||
|
|
||||||
root["ratings"] = json::object({
|
|
||||||
{"average", json::number(this->ratings.average)},
|
|
||||||
{"total", json::number(this->ratings.total)}
|
|
||||||
});
|
|
||||||
|
|
||||||
root += {"reviews", review_list};
|
|
||||||
root += {"taters", json::number(5)};
|
|
||||||
|
|
||||||
return root;
|
|
||||||
}
|
|
||||||
|
|
||||||
product_info::product_info(const json::value &jv) {
|
|
||||||
if (jv.type != json::value_type::object)
|
|
||||||
throw std::runtime_error("Malformed JSON for product info!");
|
|
||||||
|
|
||||||
auto jvo = jv.as_object();
|
|
||||||
|
|
||||||
this->category = jvo["category"].as_string();
|
|
||||||
this->description = jvo["description"].as_string();
|
|
||||||
this->is_available = jvo["is_available"].as_bool();
|
|
||||||
this->manufacturer = jvo["manufacturer"].as_string();
|
|
||||||
this->name = jvo["name"].string.value();
|
|
||||||
this->price = double(jvo["price"]);
|
|
||||||
|
|
||||||
for (auto& review_json : jvo["reviews"].as_array()) {
|
|
||||||
this->reviews.emplace_back(review_json);
|
|
||||||
}
|
|
||||||
|
|
||||||
this->ratings.average = jvo["ratings"]["average"].number.value();
|
|
||||||
this->ratings.total = jvo["ratings"]["total"].number.value();
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
json::value product_info::review::serialize() const {
|
/// Parses a string of JSON and outputs any syntax errors, if present.
|
||||||
json::object obj = json::object();
|
void validate_json_string(const std::string& input) {
|
||||||
obj["rating"] = json::number(rating);
|
|
||||||
obj["review_text"] = json::string(review_text);
|
|
||||||
obj["user"] = json::string(user);
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
product_info::review::review(const json::value &jv) {
|
|
||||||
if (jv.type != json::value_type::object)
|
|
||||||
throw std::runtime_error("Malformed JSON for review!");
|
|
||||||
|
|
||||||
rating = jv["rating"].as_int();
|
|
||||||
review_text = jv["review_text"].as_string();
|
|
||||||
user = jv["user"].as_string();
|
|
||||||
}
|
|
||||||
|
|
||||||
void test_product_info()
|
|
||||||
{
|
|
||||||
std::cout << "Testing parsing of product_info file." << std::endl;
|
|
||||||
//parse_json_file("../samples/product_info.json");
|
|
||||||
auto file_contents = read_file("../samples/product_info.json");
|
|
||||||
|
|
||||||
auto [text, err] = json::parse(file_contents);
|
|
||||||
|
|
||||||
if (!err.empty()) {
|
|
||||||
std::cerr << err << std::endl;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::cout << "Testing deserialization of product_info json." << std::endl;
|
|
||||||
// Construct from json object.
|
|
||||||
product_info test(text);
|
|
||||||
|
|
||||||
std::cout << "Testing serialization of product_info struct." << std::endl;
|
|
||||||
|
|
||||||
json::value result = test.serialize();
|
|
||||||
|
|
||||||
result.convert_descendants();
|
|
||||||
std::cout << json::deparse(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
void test_tile_data() {
|
|
||||||
//parse_json_file("../samples/tiles.json");
|
|
||||||
}
|
|
||||||
|
|
||||||
void test_widgets() {
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Some progress has been made on small scale.
|
void echo_json_formatted(const std::string& input) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void show_readme() {
|
||||||
|
std::cout << "A cli tool for parsing json files." << std::endl;
|
||||||
|
std::cout << "Developed & maintained by josh@redacted.cc" << std::endl;
|
||||||
|
std::cout << "This program is included with the Redacted Software JSON Library for C++." << std::endl;
|
||||||
|
std::cout << "https://git.redacted.cc/josh/json" << std::endl;
|
||||||
|
std::cout << "Run `json --help` for help information." << std::endl;
|
||||||
|
std::cout << "Run `json --license` for license information." << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read options and their args, remove from master args list.
|
||||||
|
|
||||||
int main(int argc, char* argv[]) {
|
int main(int argc, char* argv[]) {
|
||||||
test_product_info();
|
struct flag {
|
||||||
test_tile_data();
|
std::string name;
|
||||||
|
std::vector<std::string> aliases;
|
||||||
|
|
||||||
|
std::vector<std::string> Matches() {
|
||||||
|
auto copy = aliases;
|
||||||
|
copy.push_back(name);
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
bool color_output = true;
|
||||||
|
std::string input_content;
|
||||||
|
bool read_from_stdin = true;
|
||||||
|
|
||||||
|
argue::ArgsParser args(argc,argv);
|
||||||
|
|
||||||
|
//std::vector<std::string> args(argv, argv+argc);
|
||||||
|
|
||||||
|
// Terminal Options - Parsed first, close the program w/o further action.
|
||||||
|
|
||||||
|
if (args.has_flag({"--about", "--info", "--readme"})) {
|
||||||
|
|
||||||
|
show_readme(); return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args.has_flag({"--help", "-h", "--h"})) {
|
||||||
|
show_help(); return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args.has_flag({"--license", "-L"})) {
|
||||||
|
show_license(); return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args.has_flag("--raw"))
|
||||||
|
color_output = false;
|
||||||
|
|
||||||
|
if (args.has_flag_arg("--file"))
|
||||||
|
{
|
||||||
|
|
||||||
|
auto [success, file_name] = args.consume_flag_arg("--file");
|
||||||
|
|
||||||
|
input_content = read_file(file_name);
|
||||||
|
read_from_stdin = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (read_from_stdin)
|
||||||
|
{
|
||||||
|
std::string line;
|
||||||
|
while(std::getline(std::cin, line) && !line.empty()) {
|
||||||
|
input_content += line;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args.has_flag("--validate"))
|
||||||
|
{
|
||||||
|
validate_json_string(input_content); return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> remaining = args.args;
|
||||||
|
|
||||||
|
for (auto& e : remaining) {
|
||||||
|
std::cout << e << " ";
|
||||||
|
}
|
||||||
|
std::cout << std::endl;
|
||||||
|
|
||||||
|
auto[val, err] = json::parse(input_content);
|
||||||
|
|
||||||
|
if (!err.empty())
|
||||||
|
{
|
||||||
|
std::cerr << "Error reading JSON: " << err << std::endl;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
std::string roundtripped = json::deparse(val);
|
||||||
|
|
||||||
|
std::cout << roundtripped << std::endl;
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user