From 0c732309da100eb84158abb9784476935a48a7f9 Mon Sep 17 00:00:00 2001 From: Grzegorz Rychlik Date: Wed, 13 Nov 2019 15:39:29 +0100 Subject: [PATCH] Refactor Form and FormElement --- Src/C3.sln | 8 +- Src/ChannelLinter/ChannelLinter.cpp | 13 +- Src/ChannelLinter/ChannelLinter.h | 2 + Src/ChannelLinter/ChannelLinter.vcxproj | 5 +- .../ChannelLinter.vcxproj.filters | 5 +- Src/ChannelLinter/Form.cpp | 79 ++++----- Src/ChannelLinter/Form.h | 9 +- .../{FormElement.hpp => FormElement.cpp} | 162 ++++++++---------- Src/ChannelLinter/FormElement.h | 37 ++++ Src/ChannelLinter/InputVector.cpp | 41 ----- Src/ChannelLinter/InputVector.h | 30 ---- Src/ChannelLinter/StdAfx.h | 4 +- 12 files changed, 175 insertions(+), 220 deletions(-) rename Src/ChannelLinter/{FormElement.hpp => FormElement.cpp} (60%) create mode 100644 Src/ChannelLinter/FormElement.h delete mode 100644 Src/ChannelLinter/InputVector.cpp delete mode 100644 Src/ChannelLinter/InputVector.h diff --git a/Src/C3.sln b/Src/C3.sln index d7c1ede..edd810a 100644 --- a/Src/C3.sln +++ b/Src/C3.sln @@ -113,10 +113,10 @@ Global {F2EC73D1-D533-4EE4-955A-A62E306472CC}.Release|x64.Build.0 = Release|x64 {F2EC73D1-D533-4EE4-955A-A62E306472CC}.Release|x86.ActiveCfg = Release|Win32 {F2EC73D1-D533-4EE4-955A-A62E306472CC}.Release|x86.Build.0 = Release|Win32 - {F2EC73D1-D533-4EE4-955A-A62E306472CC}.ReleaseWithDebInfo|x64.ActiveCfg = Release|x64 - {F2EC73D1-D533-4EE4-955A-A62E306472CC}.ReleaseWithDebInfo|x64.Build.0 = Release|x64 - {F2EC73D1-D533-4EE4-955A-A62E306472CC}.ReleaseWithDebInfo|x86.ActiveCfg = Release|Win32 - {F2EC73D1-D533-4EE4-955A-A62E306472CC}.ReleaseWithDebInfo|x86.Build.0 = Release|Win32 + {F2EC73D1-D533-4EE4-955A-A62E306472CC}.ReleaseWithDebInfo|x64.ActiveCfg = ReleaseWithDebInfo|x64 + {F2EC73D1-D533-4EE4-955A-A62E306472CC}.ReleaseWithDebInfo|x64.Build.0 = ReleaseWithDebInfo|x64 + {F2EC73D1-D533-4EE4-955A-A62E306472CC}.ReleaseWithDebInfo|x86.ActiveCfg = ReleaseWithDebInfo|Win32 + {F2EC73D1-D533-4EE4-955A-A62E306472CC}.ReleaseWithDebInfo|x86.Build.0 = ReleaseWithDebInfo|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Src/ChannelLinter/ChannelLinter.cpp b/Src/ChannelLinter/ChannelLinter.cpp index 999ef54..1eeca83 100644 --- a/Src/ChannelLinter/ChannelLinter.cpp +++ b/Src/ChannelLinter/ChannelLinter.cpp @@ -22,7 +22,7 @@ namespace MWR::C3::Linter C3::Core::Profiler::Gateway::AddBuildInCommands(ret, true); return ret; } - catch (json::parse_error & e) + catch (json::parse_error& e) { throw std::runtime_error("Failed to parse channel's capability json. "s + e.what()); } @@ -39,8 +39,11 @@ namespace MWR::C3::Linter ChannelLinter::ChannelLinter(AppConfig config) : m_Config(std::move(config)), m_ChannelData(GetChannelInfo(m_Config.m_ChannelName)), - m_ChannelCapability(GetChannelCapability(m_ChannelData)) + m_ChannelCapability(GetChannelCapability(m_ChannelData)), + m_CreateForm(m_ChannelCapability.at("/create/arguments"_json_pointer)) { + for (auto&& command : m_ChannelCapability.at("commands")) + m_CommandForms.emplace_back(command.at("arguments")); } void ChannelLinter::Process() @@ -68,7 +71,9 @@ namespace MWR::C3::Linter { std::cout << "Create channel " << std::endl; Form form(m_ChannelCapability.at("/create/arguments"_json_pointer)); - auto createParams = form.FillForm(channnelArguments); + Form form2(m_ChannelCapability.at("/create/arguments"_json_pointer)); + form = form2; + auto createParams = form.Fill(channnelArguments); auto blob = MWR::C3::Core::Profiler::TranslateArguments(createParams); return MakeChannel(blob); } @@ -110,7 +115,7 @@ namespace MWR::C3::Linter json command = *commandIt; Form commandForm(command.at("arguments")); - command["arguments"] = commandForm.FillForm({begin(commandParams) + 1, end(commandParams)}); // + 1 to omit command id + command["arguments"] = commandForm.Fill({begin(commandParams) + 1, end(commandParams)}); // + 1 to omit command id return C3::Core::Profiler::TranslateCommand(command); } diff --git a/Src/ChannelLinter/ChannelLinter.h b/Src/ChannelLinter/ChannelLinter.h index 3d17226..4ec7e40 100644 --- a/Src/ChannelLinter/ChannelLinter.h +++ b/Src/ChannelLinter/ChannelLinter.h @@ -25,6 +25,8 @@ namespace MWR::C3::Linter AppConfig m_Config; InterfaceFactory::InterfaceData const& m_ChannelData; json m_ChannelCapability; + const Form m_CreateForm; + std::vector
m_CommandForms; }; } diff --git a/Src/ChannelLinter/ChannelLinter.vcxproj b/Src/ChannelLinter/ChannelLinter.vcxproj index a619cc7..4c92e02 100644 --- a/Src/ChannelLinter/ChannelLinter.vcxproj +++ b/Src/ChannelLinter/ChannelLinter.vcxproj @@ -299,9 +299,8 @@ - + - @@ -310,7 +309,7 @@ - + Create diff --git a/Src/ChannelLinter/ChannelLinter.vcxproj.filters b/Src/ChannelLinter/ChannelLinter.vcxproj.filters index 3808691..50fbbf9 100644 --- a/Src/ChannelLinter/ChannelLinter.vcxproj.filters +++ b/Src/ChannelLinter/ChannelLinter.vcxproj.filters @@ -5,21 +5,20 @@ - + - - + \ No newline at end of file diff --git a/Src/ChannelLinter/Form.cpp b/Src/ChannelLinter/Form.cpp index 2002f59..e864a0b 100644 --- a/Src/ChannelLinter/Form.cpp +++ b/Src/ChannelLinter/Form.cpp @@ -3,52 +3,49 @@ namespace MWR::C3::Linter { + Form::Form(json argumentForm) : + m_ArgumentsForm(std::move(argumentForm)) + { + for (auto& arg : m_ArgumentsForm) + { + if (arg.is_array()) + { + for (auto& a : arg) + { + m_Elements.emplace_back(MakeFormElement(a)); + } + } + else + { + m_Elements.emplace_back(MakeFormElement(arg)); + } + } + } - Form::Form(json argumentForm) : m_ArgumentsForm(std::move(argumentForm)) + Form::Form(Form const& other) noexcept : + Form(other.m_ArgumentsForm) { } - json Form::FillForm(InputVector input) + Form& Form::operator=(Form const& other) noexcept { - try - { - auto createParams = m_ArgumentsForm; - auto fillArg = [&input](json& arg) - { - ValidateAndSet(arg, input.GetNext()); - }; - for (size_t i = 0; i < createParams.size(); ++i) - { - auto& arg = createParams[i]; - if (arg.is_array()) - for (size_t j = 0; j < arg.size(); ++j) - fillArg(arg[j]); - else - fillArg(arg); - } - return createParams; - } - catch (std::out_of_range&) - { - input.Reset(); - std::string message; - auto AddParameterMessage = [&message, &input](json& arg) - { - auto& name = arg.at("name").get_ref(); - auto& type = arg.at("type").get_ref(); - message += name + " (" + type + ") > " + input.GetOptionalNext().value_or("") + '\n'; - }; - for (size_t i = 0; i < m_ArgumentsForm.size(); ++i) - { - auto& arg = m_ArgumentsForm[i]; - if (arg.is_array()) - for (size_t j = 0; j < arg.size(); ++j) - AddParameterMessage(arg[j]); - else - AddParameterMessage(arg); - } - throw std::runtime_error("Failed to fill from arguments: not enough arguments given.\nRequired parameter > received argument\n" + message); - } + Form tmp(other); + std::swap(*this, tmp); + return *this; + } + + json Form::Fill(StringVector input) + { + if (input.size() < m_Elements.size()) + throw std::runtime_error("Not enough arguments given to fill out form [required = " + std::to_string(m_Elements.size()) + ", given = " + std::to_string(input.size()) + "]"); + + // TODO log "Too many arguments, ignoring some of them" + // if (input.size() > m_Elemets.size()) + + auto inputIt = begin(input); + std::for_each(begin(m_Elements), end(m_Elements), [&inputIt](auto& element) {element->ValidateAndSet(*inputIt++); }); + + return m_ArgumentsForm; } StringVector Form::GetComplementaryArgs(StringVector input) diff --git a/Src/ChannelLinter/Form.h b/Src/ChannelLinter/Form.h index bd0b60d..2d17233 100644 --- a/Src/ChannelLinter/Form.h +++ b/Src/ChannelLinter/Form.h @@ -7,13 +7,18 @@ namespace MWR::C3::Linter public: Form(json argumentForm); - json FillForm(InputVector input); + Form(Form const& other) noexcept; + Form& operator=(Form const& other) noexcept; + Form(Form&& other) noexcept = default; + Form& operator=(Form&& other) noexcept = default; + + json Fill(StringVector input); StringVector GetComplementaryArgs(StringVector input); private: - // TODO Form should contain a list of form elements (validated), not just a json json m_ArgumentsForm; + std::vector> m_Elements; }; } diff --git a/Src/ChannelLinter/FormElement.hpp b/Src/ChannelLinter/FormElement.cpp similarity index 60% rename from Src/ChannelLinter/FormElement.hpp rename to Src/ChannelLinter/FormElement.cpp index 17c3ffc..6c9f301 100644 --- a/Src/ChannelLinter/FormElement.hpp +++ b/Src/ChannelLinter/FormElement.cpp @@ -1,40 +1,10 @@ -#pragma once +#include "StdAfx.h" +#include "FormElement.h" namespace MWR::C3::Linter { namespace { - class FormElement - { - public: - enum class Type - { - Unknown = 0, - Uint8, - Uint16, - Uint32, - Uint64, - Int8, - Int16, - Int32, - Int64, - Float, - Boolean, - String, - Ip, - Binary, - }; - - virtual void ValidateAndSet(std::string_view input) = 0; - - protected: - FormElement(json& definition) : m_Definition(definition) - { - } - - json& m_Definition; - }; - inline void CheckLengthConstraint(json const& definition, std::string_view input) { if (definition.contains("min") && input.length() < definition["min"].get()) @@ -146,68 +116,80 @@ namespace MWR::C3::Linter m_Definition["value"] = value; } }; - - inline std::unique_ptr MakeFormElement(json& element) - { - switch (element.at("type").get()) - { - case FormElement::Type::Uint8: - return std::make_unique>(element); - case FormElement::Type::Uint16: - return std::make_unique>(element); - case FormElement::Type::Uint32: - return std::make_unique>(element); - case FormElement::Type::Uint64: - return std::make_unique>(element); - case FormElement::Type::Int8: - return std::make_unique>(element); - case FormElement::Type::Int16: - return std::make_unique>(element); - case FormElement::Type::Int32: - return std::make_unique>(element); - case FormElement::Type::Int64: - return std::make_unique>(element); - case FormElement::Type::Float: - return std::make_unique>(element); - case FormElement::Type::Boolean: - return std::make_unique(element); - case FormElement::Type::String: - return std::make_unique(element); - case FormElement::Type::Ip: - return std::make_unique(element); - case FormElement::Type::Binary: - return std::make_unique(element); - default: - throw std::runtime_error("Unknown form argument type: \"" + element["type"].get() + '"'); - } - } - - NLOHMANN_JSON_SERIALIZE_ENUM - ( - FormElement::Type, - { - {FormElement::Type::Unknown, nullptr}, - {FormElement::Type::Uint8, "uint8"}, - {FormElement::Type::Uint16, "uint16"}, - {FormElement::Type::Uint32, "uint32"}, - {FormElement::Type::Uint64, "uint64"}, - {FormElement::Type::Int8, "int8"}, - {FormElement::Type::Int16, "int16"}, - {FormElement::Type::Int32, "int32"}, - {FormElement::Type::Int64, "int64"}, - {FormElement::Type::Float, "float"}, - {FormElement::Type::Boolean, "boolean"}, - {FormElement::Type::String, "string"}, - {FormElement::Type::Ip, "ip"}, - {FormElement::Type::Binary, "binary"}, - } - ); } - inline void ValidateAndSet(json& element, std::string_view input) + FormElement::FormElement(json& definition) : + m_Definition(definition) + { + if (!m_Definition.contains("name")) + throw std::invalid_argument{ "Form element must contain 'name' property." }; + } + + NLOHMANN_JSON_SERIALIZE_ENUM + ( + FormElement::Type, + { + {FormElement::Type::Unknown, nullptr}, + {FormElement::Type::Uint8, "uint8"}, + {FormElement::Type::Uint16, "uint16"}, + {FormElement::Type::Uint32, "uint32"}, + {FormElement::Type::Uint64, "uint64"}, + {FormElement::Type::Int8, "int8"}, + {FormElement::Type::Int16, "int16"}, + {FormElement::Type::Int32, "int32"}, + {FormElement::Type::Int64, "int64"}, + {FormElement::Type::Float, "float"}, + {FormElement::Type::Boolean, "boolean"}, + {FormElement::Type::String, "string"}, + {FormElement::Type::Ip, "ip"}, + {FormElement::Type::Binary, "binary"}, + } + ); + + std::unique_ptr MakeFormElement(json& element) + { + if (!element.is_object()) + throw std::invalid_argument { "Form element must be a json object." }; + if (!element.contains("type")) + throw std::invalid_argument{ "Form element must contain 'type' property." }; + + switch (element["type"].get()) + { + case FormElement::Type::Uint8: + return std::make_unique>(element); + case FormElement::Type::Uint16: + return std::make_unique>(element); + case FormElement::Type::Uint32: + return std::make_unique>(element); + case FormElement::Type::Uint64: + return std::make_unique>(element); + case FormElement::Type::Int8: + return std::make_unique>(element); + case FormElement::Type::Int16: + return std::make_unique>(element); + case FormElement::Type::Int32: + return std::make_unique>(element); + case FormElement::Type::Int64: + return std::make_unique>(element); + case FormElement::Type::Float: + return std::make_unique>(element); + case FormElement::Type::Boolean: + return std::make_unique(element); + case FormElement::Type::String: + return std::make_unique(element); + case FormElement::Type::Ip: + return std::make_unique(element); + case FormElement::Type::Binary: + return std::make_unique(element); + default: + throw std::runtime_error("Unknown form argument type: \"" + element["type"].get() + '"'); + } + } + + void ValidateAndSet(json& element, std::string_view input) { auto formElement = MakeFormElement(element); formElement->ValidateAndSet(input); } -} +} diff --git a/Src/ChannelLinter/FormElement.h b/Src/ChannelLinter/FormElement.h new file mode 100644 index 0000000..4b65786 --- /dev/null +++ b/Src/ChannelLinter/FormElement.h @@ -0,0 +1,37 @@ +#pragma once + +namespace MWR::C3::Linter +{ + class FormElement + { + public: + enum class Type + { + Unknown = 0, + Uint8, + Uint16, + Uint32, + Uint64, + Int8, + Int16, + Int32, + Int64, + Float, + Boolean, + String, + Ip, + Binary, + }; + + virtual void ValidateAndSet(std::string_view input) = 0; + + protected: + FormElement(json& definition); + + json& m_Definition; + }; + + std::unique_ptr MakeFormElement(json& element); + + void ValidateAndSet(json& element, std::string_view input); +} diff --git a/Src/ChannelLinter/InputVector.cpp b/Src/ChannelLinter/InputVector.cpp deleted file mode 100644 index 472bb21..0000000 --- a/Src/ChannelLinter/InputVector.cpp +++ /dev/null @@ -1,41 +0,0 @@ -#include "stdafx.h" - -namespace MWR::C3::Linter -{ - InputVector::InputVector(StringVector input) : m_Input{ std::move(input) }, m_Current{ m_Input.cbegin() } - { - m_Current = m_Input.cbegin(); - } - - InputVector::InputVector(InputVector const& other) : - m_Input{ other.m_Input }, - m_Current{ m_Input.cbegin() + std::distance(other.m_Input.begin(), other.m_Current) } - { - } - - InputVector& InputVector::operator=(InputVector const& other) - { - InputVector tmp(other); - std::swap(tmp, *this); - return *this; - } - - std::string InputVector::GetNext() - { - if (m_Current == m_Input.cend()) - throw std::out_of_range("Input vector has no more elements"); - return *m_Current++; - } - - void InputVector::Reset() - { - m_Current = m_Input.cbegin(); - } - - std::optional InputVector::GetOptionalNext() - { - if (m_Current == m_Input.cend()) - return {}; - return *m_Current++; - } -} diff --git a/Src/ChannelLinter/InputVector.h b/Src/ChannelLinter/InputVector.h deleted file mode 100644 index bdff726..0000000 --- a/Src/ChannelLinter/InputVector.h +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once - -namespace MWR::C3::Linter -{ - class InputVector - { - public: - InputVector(StringVector input); - - template - InputVector(It first, It last) :m_Input{ first, last }, m_Current{ begin(m_Input) } - { - } - - InputVector(InputVector const& other); - InputVector& operator = (InputVector const& other); - - std::string GetNext(); - void Reset(); - size_t Size() { return m_Input.size(); } - std::string& operator[](size_t index) { return m_Input.operator [](index); } - std::string const& operator[](size_t index) const { return m_Input.operator [](index); } - std::string at(size_t index) { return m_Input.at(index); } - std::string const& at(size_t index) const { return m_Input.at(index); } - std::optional GetOptionalNext(); - private: - StringVector m_Input; - StringVector::const_iterator m_Current; - }; -} diff --git a/Src/ChannelLinter/StdAfx.h b/Src/ChannelLinter/StdAfx.h index af57363..5acdf1d 100644 --- a/Src/ChannelLinter/StdAfx.h +++ b/Src/ChannelLinter/StdAfx.h @@ -8,6 +8,7 @@ #include #include #include +#include // C3 inclusion. #include "Common/MWR/C3/Sdk.hpp" @@ -20,8 +21,7 @@ using json = nlohmann::json; #include "argparse.hpp" #include "AppConfig.hpp" #include "ArgumentParser.h" -#include "InputVector.h" -#include "FormElement.hpp" +#include "FormElement.h" #include "Form.h" #include "MockDeviceBridge.h" #include "ChannelLinter.h"