Initial release
This commit is contained in:
@@ -0,0 +1,43 @@
|
||||
|
||||
# ModuleInfoLib
|
||||
|
||||
This is a c++17 library to parse and create the Steinberg moduleinfo.json files.
|
||||
|
||||
## Parsing
|
||||
|
||||
To parse a moduleinfo.json file you need to include the following files to your project:
|
||||
|
||||
* moduleinfoparser.cpp
|
||||
* moduleinfoparser.h
|
||||
* moduleinfo.h
|
||||
* json.h
|
||||
* jsoncxx.h
|
||||
|
||||
And add a header search path to the root folder of the VST SDK.
|
||||
|
||||
Now to parse a moduleinfo.json file in code you need to read the moduleinfo.json into a memory buffer and call
|
||||
|
||||
``` c++
|
||||
auto moduleInfo = ModuleInfoLib::parseCompatibilityJson (std::string_view (buffer, bufferSize), &std::cerr);
|
||||
```
|
||||
|
||||
Afterwards if parsing succeeded the moduleInfo optional has a value containing the ModuleInfo.
|
||||
|
||||
## Creating
|
||||
|
||||
The VST3 SDK contains the moduleinfotool utility that can create moduleinfo.json files from VST3 modules.
|
||||
|
||||
To add this capability to your own project you need to link to the sdk_hosting library from the SDK and include the following files to your project:
|
||||
|
||||
* moduleinfocreator.cpp
|
||||
* moduleinfocreator.h
|
||||
* moduleinfo.h
|
||||
|
||||
Additionally you need to add the module platform implementation from the hosting directory (module_win32.cpp, module_mac.mm or module_linux.cpp).
|
||||
|
||||
Now you can use the two methods in moduleinfocreator.h to create a moduleinfo.json file:
|
||||
|
||||
``` c++
|
||||
auto moduleInfo = ModuleInfoLib::createModuleInfo (module, false);
|
||||
ModuleInfoLib::outputJson (moduleInfo, std::cout);
|
||||
```
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,407 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Project : VST SDK
|
||||
// Flags : clang-format SMTGSequencer
|
||||
//
|
||||
// Category :
|
||||
// Filename : public.sdk/source/vst/moduleinfo/jsoncxx.h
|
||||
// Created by : Steinberg, 12/2021
|
||||
// Description :
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
// This file is part of a Steinberg SDK. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this distribution
|
||||
// and at www.steinberg.net/sdklicenses.
|
||||
// No part of the SDK, including this file, may be copied, modified, propagated,
|
||||
// or distributed except according to the terms contained in the LICENSE file.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "json.h"
|
||||
#include <cassert>
|
||||
#include <cstdlib>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <variant>
|
||||
|
||||
#if defined(_MSC_VER) || __has_include(<charconv>)
|
||||
#include <charconv>
|
||||
#define SMTG_HAS_CHARCONV
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
namespace JSON {
|
||||
namespace Detail {
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
template <typename JsonT>
|
||||
struct Base
|
||||
{
|
||||
explicit Base (JsonT* o) : object_ (o) {}
|
||||
explicit Base (const Base& o) : object_ (o.object_) {}
|
||||
|
||||
Base& operator= (const Base& o) = default;
|
||||
|
||||
operator JsonT* () const { return object_; }
|
||||
JsonT* jsonValue () const { return object_; }
|
||||
|
||||
protected:
|
||||
Base () : object_ (nullptr) {}
|
||||
|
||||
JsonT* object_;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
template <typename JsonElement>
|
||||
struct Iterator
|
||||
{
|
||||
explicit Iterator (JsonElement el) : el (el) {}
|
||||
|
||||
bool operator== (const Iterator& other) const { return other.el == el; }
|
||||
bool operator!= (const Iterator& other) const { return other.el != el; }
|
||||
|
||||
const JsonElement& operator* () const { return el; }
|
||||
const JsonElement& operator-> () const { return el; }
|
||||
|
||||
Iterator& operator++ ()
|
||||
{
|
||||
if (el)
|
||||
el = el.next ();
|
||||
return *this;
|
||||
}
|
||||
|
||||
Iterator operator++ (int)
|
||||
{
|
||||
auto it = Iterator (el);
|
||||
operator++ ();
|
||||
return it;
|
||||
}
|
||||
|
||||
private:
|
||||
JsonElement el;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
} // Detail
|
||||
|
||||
struct Object;
|
||||
struct Array;
|
||||
struct String;
|
||||
struct Number;
|
||||
struct Boolean;
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
enum class Type
|
||||
{
|
||||
Object,
|
||||
Array,
|
||||
String,
|
||||
Number,
|
||||
True,
|
||||
False,
|
||||
Null,
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
struct SourceLocation
|
||||
{
|
||||
size_t offset;
|
||||
size_t line;
|
||||
size_t row;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
struct Value : Detail::Base<json_value_s>
|
||||
{
|
||||
using Detail::Base<json_value_s>::Base;
|
||||
using VariantT = std::variant<Object, Array, String, Number, Boolean, std::nullptr_t>;
|
||||
|
||||
std::optional<Object> asObject () const;
|
||||
std::optional<Array> asArray () const;
|
||||
std::optional<String> asString () const;
|
||||
std::optional<Number> asNumber () const;
|
||||
std::optional<Boolean> asBoolean () const;
|
||||
std::optional<std::nullptr_t> asNull () const;
|
||||
|
||||
VariantT asVariant () const;
|
||||
Type type () const;
|
||||
|
||||
SourceLocation getSourceLocation () const;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
struct Boolean
|
||||
{
|
||||
Boolean (size_t type) : value (type == json_type_true) {}
|
||||
|
||||
operator bool () const { return value; }
|
||||
|
||||
private:
|
||||
bool value;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
struct String : Detail::Base<json_string_s>
|
||||
{
|
||||
using Detail::Base<json_string_s>::Base;
|
||||
|
||||
std::string_view text () const { return {jsonValue ()->string, jsonValue ()->string_size}; }
|
||||
|
||||
SourceLocation getSourceLocation () const;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
struct Number : Detail::Base<json_number_s>
|
||||
{
|
||||
using Detail::Base<json_number_s>::Base;
|
||||
|
||||
std::string_view text () const { return {jsonValue ()->number, jsonValue ()->number_size}; }
|
||||
|
||||
std::optional<int64_t> getInteger () const;
|
||||
std::optional<double> getDouble () const;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
struct ObjectElement : Detail::Base<json_object_element_s>
|
||||
{
|
||||
using Detail::Base<json_object_element_s>::Base;
|
||||
|
||||
String name () const { return String (jsonValue ()->name); }
|
||||
Value value () const { return Value (jsonValue ()->value); }
|
||||
|
||||
ObjectElement next () const { return ObjectElement (jsonValue ()->next); }
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
struct Object : Detail::Base<json_object_s>
|
||||
{
|
||||
using Detail::Base<json_object_s>::Base;
|
||||
using Iterator = Detail::Iterator<ObjectElement>;
|
||||
|
||||
size_t size () const { return jsonValue ()->length; }
|
||||
|
||||
Iterator begin () const { return Iterator (ObjectElement (jsonValue ()->start)); }
|
||||
Iterator end () const { return Iterator (ObjectElement (nullptr)); }
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
struct ArrayElement : Detail::Base<json_array_element_s>
|
||||
{
|
||||
using Detail::Base<json_array_element_s>::Base;
|
||||
|
||||
Value value () const { return Value (jsonValue ()->value); }
|
||||
|
||||
ArrayElement next () const { return ArrayElement (jsonValue ()->next); }
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
struct Array : Detail::Base<json_array_s>
|
||||
{
|
||||
using Detail::Base<json_array_s>::Base;
|
||||
using Iterator = Detail::Iterator<ArrayElement>;
|
||||
|
||||
size_t size () const { return jsonValue ()->length; }
|
||||
|
||||
Iterator begin () const { return Iterator (ArrayElement (jsonValue ()->start)); }
|
||||
Iterator end () const { return Iterator (ArrayElement (nullptr)); }
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
struct Document : Value
|
||||
{
|
||||
static std::variant<Document, json_parse_result_s> parse (std::string_view data)
|
||||
{
|
||||
auto allocate = [] (void*, size_t allocSize) { return std::malloc (allocSize); };
|
||||
json_parse_result_s parse_result {};
|
||||
auto value = json_parse_ex (data.data (), data.size (),
|
||||
json_parse_flags_allow_json5 |
|
||||
json_parse_flags_allow_location_information,
|
||||
allocate, nullptr, &parse_result);
|
||||
if (value)
|
||||
return Document (value);
|
||||
return parse_result;
|
||||
}
|
||||
~Document () noexcept
|
||||
{
|
||||
if (object_)
|
||||
std::free (object_);
|
||||
}
|
||||
|
||||
Document (Document&& doc) noexcept { *this = std::move (doc); }
|
||||
Document& operator= (Document&& doc) noexcept
|
||||
{
|
||||
std::swap (object_, doc.object_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
using Value::Value;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
inline std::optional<Object> Value::asObject () const
|
||||
{
|
||||
if (type () != Type::Object)
|
||||
return {};
|
||||
return Object (json_value_as_object (jsonValue ()));
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
inline std::optional<Array> Value::asArray () const
|
||||
{
|
||||
if (type () != Type::Array)
|
||||
return {};
|
||||
return Array (json_value_as_array (jsonValue ()));
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
inline std::optional<String> Value::asString () const
|
||||
{
|
||||
if (type () != Type::String)
|
||||
return {};
|
||||
return String (json_value_as_string (jsonValue ()));
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
inline std::optional<Number> Value::asNumber () const
|
||||
{
|
||||
if (type () != Type::Number)
|
||||
return {};
|
||||
return Number (json_value_as_number (jsonValue ()));
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
inline std::optional<Boolean> Value::asBoolean () const
|
||||
{
|
||||
if (type () == Type::True || type () == Type::False)
|
||||
return Boolean (jsonValue ()->type);
|
||||
return {};
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
inline std::optional<std::nullptr_t> Value::asNull () const
|
||||
{
|
||||
if (type () != Type::Null)
|
||||
return {};
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
inline Type Value::type () const
|
||||
{
|
||||
switch (jsonValue ()->type)
|
||||
{
|
||||
case json_type_string: return Type::String;
|
||||
case json_type_number: return Type::Number;
|
||||
case json_type_object: return Type::Object;
|
||||
case json_type_array: return Type::Array;
|
||||
case json_type_true: return Type::True;
|
||||
case json_type_false: return Type::False;
|
||||
case json_type_null: return Type::Null;
|
||||
}
|
||||
assert (false);
|
||||
return Type::Null;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
inline Value::VariantT Value::asVariant () const
|
||||
{
|
||||
switch (type ())
|
||||
{
|
||||
case Type::String: return *asString ();
|
||||
case Type::Number: return *asNumber ();
|
||||
case Type::Object: return *asObject ();
|
||||
case Type::Array: return *asArray ();
|
||||
case Type::True: return *asBoolean ();
|
||||
case Type::False: return *asBoolean ();
|
||||
case Type::Null: return *asNull ();
|
||||
}
|
||||
assert (false);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
inline SourceLocation Value::getSourceLocation () const
|
||||
{
|
||||
auto exValue = reinterpret_cast<json_value_ex_s*> (jsonValue ());
|
||||
return {exValue->offset, exValue->line_no, exValue->row_no};
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
inline SourceLocation String::getSourceLocation () const
|
||||
{
|
||||
auto exValue = reinterpret_cast<json_string_ex_s*> (jsonValue ());
|
||||
return {exValue->offset, exValue->line_no, exValue->row_no};
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
inline std::optional<int64_t> Number::getInteger () const
|
||||
{
|
||||
#if defined(SMTG_HAS_CHARCONV)
|
||||
int64_t result {0};
|
||||
auto res = std::from_chars (jsonValue ()->number,
|
||||
jsonValue ()->number + jsonValue ()->number_size, result);
|
||||
if (res.ec == std::errc ())
|
||||
return result;
|
||||
return {};
|
||||
#else
|
||||
int64_t result {0};
|
||||
std::string str (jsonValue ()->number, jsonValue ()->number + jsonValue ()->number_size);
|
||||
if (std::sscanf (str.data (), "%lld", &result) != 1)
|
||||
return {};
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
inline std::optional<double> Number::getDouble () const
|
||||
{
|
||||
#if 1 // clang still has no floting point from_chars version
|
||||
size_t ctrl {0};
|
||||
auto result = std::stod (std::string (jsonValue ()->number, jsonValue ()->number_size), &ctrl);
|
||||
if (ctrl > 0)
|
||||
return result;
|
||||
#else
|
||||
double result {0.};
|
||||
auto res = std::from_chars (jsonValue ()->number,
|
||||
jsonValue ()->number + jsonValue ()->number_size, result);
|
||||
if (res.ec == std::errc ())
|
||||
return result;
|
||||
#endif
|
||||
return {};
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
inline std::string_view errorToString (json_parse_error_e error)
|
||||
{
|
||||
switch (error)
|
||||
{
|
||||
case json_parse_error_e::json_parse_error_none: return {};
|
||||
case json_parse_error_e::json_parse_error_expected_comma_or_closing_bracket:
|
||||
return "json_parse_error_expected_comma_or_closing_bracket";
|
||||
case json_parse_error_e::json_parse_error_expected_colon:
|
||||
return "json_parse_error_expected_colon";
|
||||
case json_parse_error_e::json_parse_error_expected_opening_quote:
|
||||
return "json_parse_error_expected_opening_quote";
|
||||
case json_parse_error_e::json_parse_error_invalid_string_escape_sequence:
|
||||
return "json_parse_error_invalid_string_escape_sequence";
|
||||
case json_parse_error_e::json_parse_error_invalid_number_format:
|
||||
return "json_parse_error_invalid_number_format";
|
||||
case json_parse_error_e::json_parse_error_invalid_value:
|
||||
return "json_parse_error_invalid_value";
|
||||
case json_parse_error_e::json_parse_error_premature_end_of_buffer:
|
||||
return "json_parse_error_premature_end_of_buffer";
|
||||
case json_parse_error_e::json_parse_error_invalid_string:
|
||||
return "json_parse_error_invalid_string";
|
||||
case json_parse_error_e::json_parse_error_allocator_failed:
|
||||
return "json_parse_error_allocator_failed";
|
||||
case json_parse_error_e::json_parse_error_unexpected_trailing_characters:
|
||||
return "json_parse_error_unexpected_trailing_characters";
|
||||
case json_parse_error_e::json_parse_error_unknown: return "json_parse_error_unknown";
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
} // JSON
|
||||
@@ -0,0 +1,80 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Project : VST SDK
|
||||
// Flags : clang-format SMTGSequencer
|
||||
//
|
||||
// Category : moduleinfo
|
||||
// Filename : public.sdk/source/vst/moduleinfo/moduleinfo.h
|
||||
// Created by : Steinberg, 12/2021
|
||||
// Description :
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
// This file is part of a Steinberg SDK. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this distribution
|
||||
// and at www.steinberg.net/sdklicenses.
|
||||
// No part of the SDK, including this file, may be copied, modified, propagated,
|
||||
// or distributed except according to the terms contained in the LICENSE file.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
namespace Steinberg {
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
struct ModuleInfo
|
||||
{
|
||||
//------------------------------------------------------------------------
|
||||
struct FactoryInfo
|
||||
{
|
||||
std::string vendor;
|
||||
std::string url;
|
||||
std::string email;
|
||||
int32_t flags {0};
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
struct Snapshot
|
||||
{
|
||||
double scaleFactor {1.};
|
||||
std::string path;
|
||||
};
|
||||
using SnapshotList = std::vector<Snapshot>;
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
struct ClassInfo
|
||||
{
|
||||
std::string cid;
|
||||
std::string category;
|
||||
std::string name;
|
||||
std::string vendor;
|
||||
std::string version;
|
||||
std::string sdkVersion;
|
||||
std::vector<std::string> subCategories;
|
||||
SnapshotList snapshots;
|
||||
int32_t cardinality {0x7FFFFFFF};
|
||||
uint32_t flags {0};
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
struct Compatibility
|
||||
{
|
||||
std::string newCID;
|
||||
std::vector<std::string> oldCID;
|
||||
};
|
||||
|
||||
using ClassList = std::vector<ClassInfo>;
|
||||
using CompatibilityList = std::vector<Compatibility>;
|
||||
|
||||
std::string name;
|
||||
std::string version;
|
||||
FactoryInfo factoryInfo;
|
||||
ClassList classes;
|
||||
CompatibilityList compatibility;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
} // Steinberg
|
||||
+289
@@ -0,0 +1,289 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Project : VST SDK
|
||||
// Flags : clang-format SMTGSequencer
|
||||
//
|
||||
// Category : moduleinfo
|
||||
// Filename : public.sdk/source/vst/moduleinfo/moduleinfocreator.cpp
|
||||
// Created by : Steinberg, 12/2021
|
||||
// Description : utility functions to create moduleinfo json files
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
// This file is part of a Steinberg SDK. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this distribution
|
||||
// and at www.steinberg.net/sdklicenses.
|
||||
// No part of the SDK, including this file, may be copied, modified, propagated,
|
||||
// or distributed except according to the terms contained in the LICENSE file.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "moduleinfocreator.h"
|
||||
#include "jsoncxx.h"
|
||||
#include <algorithm>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
namespace Steinberg::ModuleInfoLib {
|
||||
using namespace VST3;
|
||||
namespace {
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
struct JSON5Writer
|
||||
{
|
||||
private:
|
||||
std::ostream& stream;
|
||||
bool beautify;
|
||||
bool lastIsComma {false};
|
||||
int32_t intend {0};
|
||||
|
||||
void doBeautify ()
|
||||
{
|
||||
if (beautify)
|
||||
{
|
||||
stream << '\n';
|
||||
for (int i = 0; i < intend; ++i)
|
||||
stream << " ";
|
||||
}
|
||||
}
|
||||
|
||||
void writeComma ()
|
||||
{
|
||||
if (lastIsComma)
|
||||
return;
|
||||
stream << ",";
|
||||
lastIsComma = true;
|
||||
}
|
||||
void startObject ()
|
||||
{
|
||||
stream << "{";
|
||||
++intend;
|
||||
lastIsComma = false;
|
||||
}
|
||||
void endObject ()
|
||||
{
|
||||
--intend;
|
||||
doBeautify ();
|
||||
stream << "}";
|
||||
lastIsComma = false;
|
||||
}
|
||||
void startArray ()
|
||||
{
|
||||
stream << "[";
|
||||
++intend;
|
||||
lastIsComma = false;
|
||||
}
|
||||
void endArray ()
|
||||
{
|
||||
--intend;
|
||||
doBeautify ();
|
||||
stream << "]";
|
||||
lastIsComma = false;
|
||||
}
|
||||
|
||||
public:
|
||||
JSON5Writer (std::ostream& stream, bool beautify = true) : stream (stream), beautify (beautify)
|
||||
{
|
||||
}
|
||||
|
||||
void string (std::string_view str)
|
||||
{
|
||||
stream << "\"" << str << "\"";
|
||||
lastIsComma = false;
|
||||
}
|
||||
|
||||
void boolean (bool val)
|
||||
{
|
||||
stream << (val ? "true" : "false");
|
||||
lastIsComma = false;
|
||||
}
|
||||
|
||||
template <typename ValueT>
|
||||
void value (ValueT val)
|
||||
{
|
||||
stream << val;
|
||||
lastIsComma = false;
|
||||
}
|
||||
|
||||
template <typename Proc>
|
||||
void object (Proc proc)
|
||||
{
|
||||
startObject ();
|
||||
proc ();
|
||||
endObject ();
|
||||
}
|
||||
|
||||
template <typename Iterator, typename Proc>
|
||||
void array (Iterator begin, Iterator end, Proc proc)
|
||||
{
|
||||
startArray ();
|
||||
while (begin != end)
|
||||
{
|
||||
doBeautify ();
|
||||
proc (begin);
|
||||
++begin;
|
||||
writeComma ();
|
||||
}
|
||||
endArray ();
|
||||
}
|
||||
|
||||
template <typename Proc>
|
||||
void keyValue (std::string_view key, Proc proc)
|
||||
{
|
||||
doBeautify ();
|
||||
string (key);
|
||||
stream << ": ";
|
||||
proc ();
|
||||
writeComma ();
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void writeSnapshots (const ModuleInfo::SnapshotList& snapshots, JSON5Writer& w)
|
||||
{
|
||||
w.keyValue ("Snapshots", [&] () {
|
||||
w.array (snapshots.begin (), snapshots.end (), [&] (const auto& el) {
|
||||
w.object ([&] () {
|
||||
w.keyValue ("Scale Factor", [&] () { w.value (el->scaleFactor); });
|
||||
w.keyValue ("Path", [&] () { w.string (el->path); });
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void writeClassInfo (const ModuleInfo::ClassInfo& cls, JSON5Writer& w)
|
||||
{
|
||||
w.keyValue ("CID", [&] () { w.string (cls.cid); });
|
||||
w.keyValue ("Category", [&] () { w.string (cls.category); });
|
||||
w.keyValue ("Name", [&] () { w.string (cls.name); });
|
||||
w.keyValue ("Vendor", [&] () { w.string (cls.vendor); });
|
||||
w.keyValue ("Version", [&] () { w.string (cls.version); });
|
||||
w.keyValue ("SDKVersion", [&] () { w.string (cls.sdkVersion); });
|
||||
const auto& sc = cls.subCategories;
|
||||
if (!sc.empty ())
|
||||
{
|
||||
w.keyValue ("Sub Categories", [&] () {
|
||||
w.array (sc.begin (), sc.end (), [&] (const auto& cat) { w.string (*cat); });
|
||||
});
|
||||
}
|
||||
w.keyValue ("Class Flags", [&] () { w.value (cls.flags); });
|
||||
w.keyValue ("Cardinality", [&] () { w.value (cls.cardinality); });
|
||||
writeSnapshots (cls.snapshots, w);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void writePluginCompatibility (const ModuleInfo::CompatibilityList& compat, JSON5Writer& w)
|
||||
{
|
||||
if (compat.empty ())
|
||||
return;
|
||||
w.keyValue ("Compatibility", [&] () {
|
||||
w.array (compat.begin (), compat.end (), [&] (auto& el) {
|
||||
w.object ([&] () {
|
||||
w.keyValue ("New", [&] () { w.string (el->newCID); });
|
||||
w.keyValue ("Old", [&] () {
|
||||
w.array (el->oldCID.begin (), el->oldCID.end (),
|
||||
[&] (auto& oldEl) { w.string (*oldEl); });
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void writeFactoryInfo (const ModuleInfo::FactoryInfo& fi, JSON5Writer& w)
|
||||
{
|
||||
w.keyValue ("Factory Info", [&] () {
|
||||
w.object ([&] () {
|
||||
w.keyValue ("Vendor", [&] () { w.string (fi.vendor); });
|
||||
w.keyValue ("URL", [&] () { w.string (fi.url); });
|
||||
w.keyValue ("E-Mail", [&] () { w.string (fi.email); });
|
||||
w.keyValue ("Flags", [&] () {
|
||||
w.object ([&] () {
|
||||
w.keyValue ("Unicode",
|
||||
[&] () { w.boolean (fi.flags & PFactoryInfo::kUnicode); });
|
||||
w.keyValue ("Classes Discardable", [&] () {
|
||||
w.boolean (fi.flags & PFactoryInfo::kClassesDiscardable);
|
||||
});
|
||||
w.keyValue ("Component Non Discardable", [&] () {
|
||||
w.boolean (fi.flags & PFactoryInfo::kComponentNonDiscardable);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
} // anonymous
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
ModuleInfo createModuleInfo (const VST3::Hosting::Module& module, bool includeDiscardableClasses)
|
||||
{
|
||||
ModuleInfo info;
|
||||
|
||||
const auto& factory = module.getFactory ();
|
||||
auto factoryInfo = factory.info ();
|
||||
|
||||
info.name = module.getName ();
|
||||
auto pos = info.name.find_last_of ('.');
|
||||
if (pos != std::string::npos)
|
||||
info.name.erase (pos);
|
||||
|
||||
info.factoryInfo.vendor = factoryInfo.vendor ();
|
||||
info.factoryInfo.url = factoryInfo.url ();
|
||||
info.factoryInfo.email = factoryInfo.email ();
|
||||
info.factoryInfo.flags = factoryInfo.flags ();
|
||||
|
||||
if (factoryInfo.classesDiscardable () == false ||
|
||||
(factoryInfo.classesDiscardable () && includeDiscardableClasses))
|
||||
{
|
||||
auto snapshots = VST3::Hosting::Module::getSnapshots (module.getPath ());
|
||||
for (const auto& ci : factory.classInfos ())
|
||||
{
|
||||
ModuleInfo::ClassInfo classInfo;
|
||||
classInfo.cid = ci.ID ().toString ();
|
||||
classInfo.category = ci.category ();
|
||||
classInfo.name = ci.name ();
|
||||
classInfo.vendor = ci.vendor ();
|
||||
classInfo.version = ci.version ();
|
||||
classInfo.sdkVersion = ci.sdkVersion ();
|
||||
classInfo.subCategories = ci.subCategories ();
|
||||
classInfo.cardinality = ci.cardinality ();
|
||||
classInfo.flags = ci.classFlags ();
|
||||
auto snapshotIt = std::find_if (snapshots.begin (), snapshots.end (),
|
||||
[&] (const auto& el) { return el.uid == ci.ID (); });
|
||||
if (snapshotIt != snapshots.end ())
|
||||
{
|
||||
for (auto& s : snapshotIt->images)
|
||||
{
|
||||
std::string_view path (s.path);
|
||||
if (path.find (module.getPath ()) == 0)
|
||||
path.remove_prefix (module.getPath ().size () + 1);
|
||||
classInfo.snapshots.emplace_back (
|
||||
ModuleInfo::Snapshot {s.scaleFactor, {path.data (), path.size ()}});
|
||||
}
|
||||
snapshots.erase (snapshotIt);
|
||||
}
|
||||
info.classes.emplace_back (std::move (classInfo));
|
||||
}
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void outputJson (const ModuleInfo& info, std::ostream& output)
|
||||
{
|
||||
JSON5Writer w (output);
|
||||
w.object ([&] () {
|
||||
w.keyValue ("Name", [&] () { w.string (info.name); });
|
||||
w.keyValue ("Version", [&] () { w.string (info.version); });
|
||||
writeFactoryInfo (info.factoryInfo, w);
|
||||
writePluginCompatibility (info.compatibility, w);
|
||||
w.keyValue ("Classes", [&] () {
|
||||
w.array (info.classes.begin (), info.classes.end (),
|
||||
[&] (const auto& cls) { w.object ([&] () { writeClassInfo (*cls, w); }); });
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
} // Steinberg::ModuleInfoLib
|
||||
@@ -0,0 +1,47 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Project : VST SDK
|
||||
// Flags : clang-format SMTGSequencer
|
||||
//
|
||||
// Category : moduleinfo
|
||||
// Filename : public.sdk/source/vst/moduleinfo/moduleinfocreator.h
|
||||
// Created by : Steinberg, 12/2021
|
||||
// Description : utility functions to create moduleinfo json files
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
// This file is part of a Steinberg SDK. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this distribution
|
||||
// and at www.steinberg.net/sdklicenses.
|
||||
// No part of the SDK, including this file, may be copied, modified, propagated,
|
||||
// or distributed except according to the terms contained in the LICENSE file.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "moduleinfo.h"
|
||||
#include "public.sdk/source/vst/hosting/module.h"
|
||||
#include <iostream>
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
namespace Steinberg::ModuleInfoLib {
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
/** create a ModuleInfo from a module
|
||||
*
|
||||
* @param module module to create the module info from
|
||||
* @param includeDiscardableClasses if true adds the current available classes to the module info
|
||||
* @return a ModuleInfo struct with the classes and factory info of the module
|
||||
*/
|
||||
ModuleInfo createModuleInfo (const VST3::Hosting::Module& module, bool includeDiscardableClasses);
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
/** output the ModuleInfo as json to the stream
|
||||
*
|
||||
* @param info module info
|
||||
* @param output output stream
|
||||
*/
|
||||
void outputJson (const ModuleInfo& info, std::ostream& output);
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
} // Steinberg::ModuelInfoLib
|
||||
@@ -0,0 +1,517 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Project : VST SDK
|
||||
// Flags : clang-format SMTGSequencer
|
||||
//
|
||||
// Category : moduleinfo
|
||||
// Filename : public.sdk/source/vst/moduleinfo/moduleinfoparser.cpp
|
||||
// Created by : Steinberg, 01/2022
|
||||
// Description : utility functions to parse moduleinfo json files
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
// This file is part of a Steinberg SDK. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this distribution
|
||||
// and at www.steinberg.net/sdklicenses.
|
||||
// No part of the SDK, including this file, may be copied, modified, propagated,
|
||||
// or distributed except according to the terms contained in the LICENSE file.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "moduleinfoparser.h"
|
||||
#include "jsoncxx.h"
|
||||
#include "pluginterfaces/base/ipluginbase.h"
|
||||
#include <limits>
|
||||
#include <stdexcept>
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
namespace Steinberg::ModuleInfoLib {
|
||||
namespace {
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void printJsonParseError (json_parse_result_s& parseResult, std::ostream& errorOut)
|
||||
{
|
||||
errorOut << "error : "
|
||||
<< JSON::errorToString (static_cast<json_parse_error_e> (parseResult.error)) << '\n';
|
||||
errorOut << "offset : " << parseResult.error_offset << '\n';
|
||||
errorOut << "line no: " << parseResult.error_line_no << '\n';
|
||||
errorOut << "row no : " << parseResult.error_row_no << '\n';
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
struct parse_error : std::exception
|
||||
{
|
||||
parse_error (const std::string& str, const JSON::Value& value)
|
||||
: str (str), location (value.getSourceLocation ())
|
||||
{
|
||||
addLocation (location);
|
||||
}
|
||||
parse_error (const std::string& str, const JSON::String& value)
|
||||
: str (str), location (value.getSourceLocation ())
|
||||
{
|
||||
addLocation (location);
|
||||
}
|
||||
const char* what () const noexcept override { return str.data (); }
|
||||
|
||||
private:
|
||||
void addLocation (const JSON::SourceLocation& loc)
|
||||
{
|
||||
str += '\n';
|
||||
str += "offset:";
|
||||
str += std::to_string (loc.offset);
|
||||
str += '\n';
|
||||
str += "line:";
|
||||
str += std::to_string (loc.line);
|
||||
str += '\n';
|
||||
str += "row:";
|
||||
str += std::to_string (loc.row);
|
||||
str += '\n';
|
||||
}
|
||||
|
||||
std::string str;
|
||||
JSON::SourceLocation location;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
struct ModuleInfoJsonParser
|
||||
{
|
||||
ModuleInfoJsonParser () = default;
|
||||
|
||||
std::string_view getText (const JSON::Value& value) const
|
||||
{
|
||||
if (auto str = value.asString ())
|
||||
return str->text ();
|
||||
throw parse_error ("Expect a String here", value);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T getInteger (const JSON::Value& value) const
|
||||
{
|
||||
if (auto number = value.asNumber ())
|
||||
{
|
||||
if (auto result = number->getInteger ())
|
||||
{
|
||||
if (result > static_cast<int64_t> (std::numeric_limits<T>::max ()) ||
|
||||
result < static_cast<int64_t> (std::numeric_limits<T>::min ()))
|
||||
throw parse_error ("Value is out of range here", value);
|
||||
return static_cast<T> (*result);
|
||||
}
|
||||
throw parse_error ("Expect an Integer here", value);
|
||||
}
|
||||
throw parse_error ("Expect a Number here", value);
|
||||
}
|
||||
|
||||
double getDouble (const JSON::Value& value) const
|
||||
{
|
||||
if (auto number = value.asNumber ())
|
||||
{
|
||||
if (auto result = number->getDouble ())
|
||||
return *result;
|
||||
throw parse_error ("Expect a Double here", value);
|
||||
}
|
||||
throw parse_error ("Expect a Number here", value);
|
||||
}
|
||||
|
||||
void parseFactoryInfo (const JSON::Value& value)
|
||||
{
|
||||
enum ParsedBits
|
||||
{
|
||||
Vendor = 1 << 0,
|
||||
URL = 1 << 1,
|
||||
EMail = 1 << 2,
|
||||
Flags = 1 << 3,
|
||||
};
|
||||
uint32_t parsed {0};
|
||||
if (auto obj = value.asObject ())
|
||||
{
|
||||
for (const auto& el : *obj)
|
||||
{
|
||||
auto elementName = el.name ().text ();
|
||||
if (elementName == "Vendor")
|
||||
{
|
||||
if (parsed & ParsedBits::Vendor)
|
||||
throw parse_error ("Only one 'Vendor' key allowed", el.name ());
|
||||
parsed |= ParsedBits::Vendor;
|
||||
info.factoryInfo.vendor = getText (el.value ());
|
||||
}
|
||||
else if (elementName == "URL")
|
||||
{
|
||||
if (parsed & ParsedBits::URL)
|
||||
throw parse_error ("Only one 'URL' key allowed", el.name ());
|
||||
parsed |= ParsedBits::URL;
|
||||
info.factoryInfo.url = getText (el.value ());
|
||||
}
|
||||
else if (elementName == "E-Mail")
|
||||
{
|
||||
if (parsed & ParsedBits::EMail)
|
||||
throw parse_error ("Only one 'E-Mail' key allowed", el.name ());
|
||||
parsed |= ParsedBits::EMail;
|
||||
info.factoryInfo.email = getText (el.value ());
|
||||
}
|
||||
else if (elementName == "Flags")
|
||||
{
|
||||
if (parsed & ParsedBits::Flags)
|
||||
throw parse_error ("Only one 'Flags' key allowed", el.name ());
|
||||
auto flags = el.value ().asObject ();
|
||||
if (!flags)
|
||||
throw parse_error ("Expect 'Flags' to be a JSON Object", el.name ());
|
||||
for (const auto& flag : *flags)
|
||||
{
|
||||
auto flagName = flag.name ().text ();
|
||||
auto flagValue = flag.value ().asBoolean ();
|
||||
if (!flagValue)
|
||||
throw parse_error ("Flag must be a boolean", flag.value ());
|
||||
if (flagName == "Classes Discardable")
|
||||
{
|
||||
if (*flagValue)
|
||||
info.factoryInfo.flags |= PFactoryInfo::kClassesDiscardable;
|
||||
}
|
||||
else if (flagName == "Component Non Discardable")
|
||||
{
|
||||
if (*flagValue)
|
||||
info.factoryInfo.flags |= PFactoryInfo::kComponentNonDiscardable;
|
||||
}
|
||||
else if (flagName == "Unicode")
|
||||
{
|
||||
if (*flagValue)
|
||||
info.factoryInfo.flags |= PFactoryInfo::kUnicode;
|
||||
}
|
||||
else
|
||||
throw parse_error ("Unknown flag", flag.name ());
|
||||
}
|
||||
parsed |= ParsedBits::Flags;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!(parsed & ParsedBits::Vendor))
|
||||
throw std::logic_error ("Missing 'Vendor' in Factory Info");
|
||||
if (!(parsed & ParsedBits::URL))
|
||||
throw std::logic_error ("Missing 'URL' in Factory Info");
|
||||
if (!(parsed & ParsedBits::EMail))
|
||||
throw std::logic_error ("Missing 'EMail' in Factory Info");
|
||||
if (!(parsed & ParsedBits::Flags))
|
||||
throw std::logic_error ("Missing 'Flags' in Factory Info");
|
||||
}
|
||||
|
||||
void parseClasses (const JSON::Value& value)
|
||||
{
|
||||
enum ParsedBits
|
||||
{
|
||||
CID = 1 << 0,
|
||||
Category = 1 << 1,
|
||||
Name = 1 << 2,
|
||||
Vendor = 1 << 3,
|
||||
Version = 1 << 4,
|
||||
SDKVersion = 1 << 5,
|
||||
SubCategories = 1 << 6,
|
||||
ClassFlags = 1 << 7,
|
||||
Snapshots = 1 << 8,
|
||||
Cardinality = 1 << 9,
|
||||
};
|
||||
|
||||
auto array = value.asArray ();
|
||||
if (!array)
|
||||
throw parse_error ("Expect Classes Array", value);
|
||||
for (const auto& classInfoEl : *array)
|
||||
{
|
||||
auto classInfo = classInfoEl.value ().asObject ();
|
||||
if (!classInfo)
|
||||
throw parse_error ("Expect Class Object", classInfoEl.value ());
|
||||
|
||||
ModuleInfo::ClassInfo ci {};
|
||||
|
||||
uint32_t parsed {0};
|
||||
|
||||
for (const auto& el : *classInfo)
|
||||
{
|
||||
auto elementName = el.name ().text ();
|
||||
if (elementName == "CID")
|
||||
{
|
||||
if (parsed & ParsedBits::CID)
|
||||
throw parse_error ("Only one 'CID' key allowed", el.name ());
|
||||
ci.cid = getText (el.value ());
|
||||
parsed |= ParsedBits::CID;
|
||||
}
|
||||
else if (elementName == "Category")
|
||||
{
|
||||
if (parsed & ParsedBits::Category)
|
||||
throw parse_error ("Only one 'Category' key allowed", el.name ());
|
||||
ci.category = getText (el.value ());
|
||||
parsed |= ParsedBits::Category;
|
||||
}
|
||||
else if (elementName == "Name")
|
||||
{
|
||||
if (parsed & ParsedBits::Name)
|
||||
throw parse_error ("Only one 'Name' key allowed", el.name ());
|
||||
ci.name = getText (el.value ());
|
||||
parsed |= ParsedBits::Name;
|
||||
}
|
||||
else if (elementName == "Vendor")
|
||||
{
|
||||
if (parsed & ParsedBits::Vendor)
|
||||
throw parse_error ("Only one 'Vendor' key allowed", el.name ());
|
||||
ci.vendor = getText (el.value ());
|
||||
parsed |= ParsedBits::Vendor;
|
||||
}
|
||||
else if (elementName == "Version")
|
||||
{
|
||||
if (parsed & ParsedBits::Version)
|
||||
throw parse_error ("Only one 'Version' key allowed", el.name ());
|
||||
ci.version = getText (el.value ());
|
||||
parsed |= ParsedBits::Version;
|
||||
}
|
||||
else if (elementName == "SDKVersion")
|
||||
{
|
||||
if (parsed & ParsedBits::SDKVersion)
|
||||
throw parse_error ("Only one 'SDKVersion' key allowed", el.name ());
|
||||
ci.sdkVersion = getText (el.value ());
|
||||
parsed |= ParsedBits::SDKVersion;
|
||||
}
|
||||
else if (elementName == "Sub Categories")
|
||||
{
|
||||
if (parsed & ParsedBits::SubCategories)
|
||||
throw parse_error ("Only one 'Sub Categories' key allowed", el.name ());
|
||||
auto subCatArr = el.value ().asArray ();
|
||||
if (!subCatArr)
|
||||
throw parse_error ("Expect Array here", el.value ());
|
||||
for (const auto& catEl : *subCatArr)
|
||||
{
|
||||
auto cat = getText (catEl.value ());
|
||||
ci.subCategories.emplace_back (cat);
|
||||
}
|
||||
parsed |= ParsedBits::SubCategories;
|
||||
}
|
||||
else if (elementName == "Class Flags")
|
||||
{
|
||||
if (parsed & ParsedBits::ClassFlags)
|
||||
throw parse_error ("Only one 'Class Flags' key allowed", el.name ());
|
||||
ci.flags = getInteger<uint32_t> (el.value ());
|
||||
parsed |= ParsedBits::ClassFlags;
|
||||
}
|
||||
else if (elementName == "Cardinality")
|
||||
{
|
||||
if (parsed & ParsedBits::Cardinality)
|
||||
throw parse_error ("Only one 'Cardinality' key allowed", el.name ());
|
||||
ci.cardinality = getInteger<int32_t> (el.value ());
|
||||
parsed |= ParsedBits::Cardinality;
|
||||
}
|
||||
else if (elementName == "Snapshots")
|
||||
{
|
||||
if (parsed & ParsedBits::Snapshots)
|
||||
throw parse_error ("Only one 'Snapshots' key allowed", el.name ());
|
||||
auto snapArr = el.value ().asArray ();
|
||||
if (!snapArr)
|
||||
throw parse_error ("Expect Array here", el.value ());
|
||||
for (const auto& snapEl : *snapArr)
|
||||
{
|
||||
auto snap = snapEl.value ().asObject ();
|
||||
if (!snap)
|
||||
throw parse_error ("Expect Object here", snapEl.value ());
|
||||
ModuleInfo::Snapshot snapshot;
|
||||
for (const auto& spEl : *snap)
|
||||
{
|
||||
auto spElName = spEl.name ().text ();
|
||||
if (spElName == "Path")
|
||||
snapshot.path = getText (spEl.value ());
|
||||
else if (spElName == "Scale Factor")
|
||||
snapshot.scaleFactor = getDouble (spEl.value ());
|
||||
else
|
||||
throw parse_error ("Unexpected key", spEl.name ());
|
||||
}
|
||||
if (snapshot.scaleFactor == 0. || snapshot.path.empty ())
|
||||
throw parse_error ("Missing Snapshot keys", snapEl.value ());
|
||||
ci.snapshots.emplace_back (std::move (snapshot));
|
||||
}
|
||||
parsed |= ParsedBits::Snapshots;
|
||||
}
|
||||
else
|
||||
throw parse_error ("Unexpected key", el.name ());
|
||||
}
|
||||
if (!(parsed & ParsedBits::CID))
|
||||
throw parse_error ("'CID' key missing", classInfoEl.value ());
|
||||
if (!(parsed & ParsedBits::Category))
|
||||
throw parse_error ("'Category' key missing", classInfoEl.value ());
|
||||
if (!(parsed & ParsedBits::Name))
|
||||
throw parse_error ("'Name' key missing", classInfoEl.value ());
|
||||
if (!(parsed & ParsedBits::Vendor))
|
||||
throw parse_error ("'Vendor' key missing", classInfoEl.value ());
|
||||
if (!(parsed & ParsedBits::Version))
|
||||
throw parse_error ("'Version' key missing", classInfoEl.value ());
|
||||
if (!(parsed & ParsedBits::SDKVersion))
|
||||
throw parse_error ("'SDK Version' key missing", classInfoEl.value ());
|
||||
if (!(parsed & ParsedBits::ClassFlags))
|
||||
throw parse_error ("'Class Flags' key missing", classInfoEl.value ());
|
||||
if (!(parsed & ParsedBits::Cardinality))
|
||||
throw parse_error ("'Cardinality' key missing", classInfoEl.value ());
|
||||
info.classes.emplace_back (std::move (ci));
|
||||
}
|
||||
}
|
||||
|
||||
void parseCompatibility (const JSON::Value& value)
|
||||
{
|
||||
auto arr = value.asArray ();
|
||||
if (!arr)
|
||||
throw parse_error ("Expect Array here", value);
|
||||
for (const auto& el : *arr)
|
||||
{
|
||||
auto obj = el.value ().asObject ();
|
||||
if (!obj)
|
||||
throw parse_error ("Expect Object here", el.value ());
|
||||
|
||||
ModuleInfo::Compatibility compat;
|
||||
for (const auto& objEl : *obj)
|
||||
{
|
||||
auto elementName = objEl.name ().text ();
|
||||
if (elementName == "New")
|
||||
compat.newCID = getText (objEl.value ());
|
||||
else if (elementName == "Old")
|
||||
{
|
||||
auto oldElArr = objEl.value ().asArray ();
|
||||
if (!oldElArr)
|
||||
throw parse_error ("Expect Array here", objEl.value ());
|
||||
for (const auto& old : *oldElArr)
|
||||
{
|
||||
compat.oldCID.emplace_back (getText (old.value ()));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (compat.newCID.empty ())
|
||||
throw parse_error ("Expect New CID here", el.value ());
|
||||
if (compat.oldCID.empty ())
|
||||
throw parse_error ("Expect Old CID here", el.value ());
|
||||
info.compatibility.emplace_back (std::move (compat));
|
||||
}
|
||||
}
|
||||
|
||||
void parse (const JSON::Document& doc)
|
||||
{
|
||||
auto docObj = doc.asObject ();
|
||||
if (!docObj)
|
||||
throw parse_error ("Unexpected", doc);
|
||||
|
||||
enum ParsedBits
|
||||
{
|
||||
Name = 1 << 0,
|
||||
Version = 1 << 1,
|
||||
FactoryInfo = 1 << 2,
|
||||
Compatibility = 1 << 3,
|
||||
Classes = 1 << 4,
|
||||
};
|
||||
|
||||
uint32_t parsed {0};
|
||||
for (const auto& el : *docObj)
|
||||
{
|
||||
auto elementName = el.name ().text ();
|
||||
if (elementName == "Name")
|
||||
{
|
||||
if (parsed & ParsedBits::Name)
|
||||
throw parse_error ("Only one 'Name' key allowed", el.name ());
|
||||
parsed |= ParsedBits::Name;
|
||||
info.name = getText (el.value ());
|
||||
}
|
||||
else if (elementName == "Version")
|
||||
{
|
||||
if (parsed & ParsedBits::Version)
|
||||
throw parse_error ("Only one 'Version' key allowed", el.name ());
|
||||
parsed |= ParsedBits::Version;
|
||||
info.version = getText (el.value ());
|
||||
}
|
||||
else if (elementName == "Factory Info")
|
||||
{
|
||||
if (parsed & ParsedBits::FactoryInfo)
|
||||
throw parse_error ("Only one 'Factory Info' key allowed", el.name ());
|
||||
parseFactoryInfo (el.value ());
|
||||
parsed |= ParsedBits::FactoryInfo;
|
||||
}
|
||||
else if (elementName == "Compatibility")
|
||||
{
|
||||
if (parsed & ParsedBits::Compatibility)
|
||||
throw parse_error ("Only one 'Compatibility' key allowed", el.name ());
|
||||
parseCompatibility (el.value ());
|
||||
parsed |= ParsedBits::Compatibility;
|
||||
}
|
||||
else if (elementName == "Classes")
|
||||
{
|
||||
if (parsed & ParsedBits::Classes)
|
||||
throw parse_error ("Only one 'Classes' key allowed", el.name ());
|
||||
parseClasses (el.value ());
|
||||
parsed |= ParsedBits::Classes;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw parse_error ("Unexpected JSON Token", el.name ());
|
||||
}
|
||||
}
|
||||
if (!(parsed & ParsedBits::Name))
|
||||
throw std::logic_error ("'Name' key missing");
|
||||
if (!(parsed & ParsedBits::Version))
|
||||
throw std::logic_error ("'Version' key missing");
|
||||
if (!(parsed & ParsedBits::FactoryInfo))
|
||||
throw std::logic_error ("'Factory Info' key missing");
|
||||
if (!(parsed & ParsedBits::Classes))
|
||||
throw std::logic_error ("'Classes' key missing");
|
||||
}
|
||||
|
||||
ModuleInfo&& takeInfo () { return std::move (info); }
|
||||
|
||||
private:
|
||||
ModuleInfo info;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
} // anonymous
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
std::optional<ModuleInfo> parseJson (std::string_view jsonData, std::ostream* optErrorOutput)
|
||||
{
|
||||
auto docVar = JSON::Document::parse (jsonData);
|
||||
if (auto res = std::get_if<json_parse_result_s> (&docVar))
|
||||
{
|
||||
if (optErrorOutput)
|
||||
printJsonParseError (*res, *optErrorOutput);
|
||||
return {};
|
||||
}
|
||||
auto doc = std::get_if<JSON::Document> (&docVar);
|
||||
assert (doc);
|
||||
try
|
||||
{
|
||||
ModuleInfoJsonParser parser;
|
||||
parser.parse (*doc);
|
||||
return parser.takeInfo ();
|
||||
}
|
||||
catch (std::exception& error)
|
||||
{
|
||||
if (optErrorOutput)
|
||||
*optErrorOutput << error.what () << '\n';
|
||||
return {};
|
||||
}
|
||||
// unreachable
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
std::optional<ModuleInfo::CompatibilityList> parseCompatibilityJson (std::string_view jsonData,
|
||||
std::ostream* optErrorOutput)
|
||||
{
|
||||
auto docVar = JSON::Document::parse (jsonData);
|
||||
if (auto res = std::get_if<json_parse_result_s> (&docVar))
|
||||
{
|
||||
if (optErrorOutput)
|
||||
printJsonParseError (*res, *optErrorOutput);
|
||||
return {};
|
||||
}
|
||||
auto doc = std::get_if<JSON::Document> (&docVar);
|
||||
assert (doc);
|
||||
try
|
||||
{
|
||||
ModuleInfoJsonParser parser;
|
||||
parser.parseCompatibility (*doc);
|
||||
return parser.takeInfo ().compatibility;
|
||||
}
|
||||
catch (std::exception& error)
|
||||
{
|
||||
if (optErrorOutput)
|
||||
*optErrorOutput << error.what () << '\n';
|
||||
return {};
|
||||
}
|
||||
// unreachable
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
} // Steinberg::ModuelInfoLib
|
||||
@@ -0,0 +1,48 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Project : VST SDK
|
||||
// Flags : clang-format SMTGSequencer
|
||||
//
|
||||
// Category : moduleinfo
|
||||
// Filename : public.sdk/source/vst/moduleinfo/moduleinfoparser.h
|
||||
// Created by : Steinberg, 01/2022
|
||||
// Description : utility functions to parse moduleinfo json files
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
// This file is part of a Steinberg SDK. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this distribution
|
||||
// and at www.steinberg.net/sdklicenses.
|
||||
// No part of the SDK, including this file, may be copied, modified, propagated,
|
||||
// or distributed except according to the terms contained in the LICENSE file.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "moduleinfo.h"
|
||||
#include <iostream>
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
namespace Steinberg::ModuleInfoLib {
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
/** parse a json formatted string to a ModuleInfo struct
|
||||
*
|
||||
* @param jsonData a string view to a json formatted string
|
||||
* @param optErrorOutput optional error output stream where to print parse error
|
||||
* @return ModuleInfo if parsing succeeded
|
||||
*/
|
||||
std::optional<ModuleInfo> parseJson (std::string_view jsonData, std::ostream* optErrorOutput);
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
/** parse a json formatted string to a ModuleInfo::CompatibilityList
|
||||
*
|
||||
* @param jsonData a string view to a json formatted string
|
||||
* @param optErrorOutput optional error output stream where to print parse error
|
||||
* @return ModuleInfo::CompatibilityList if parsing succeeded
|
||||
*/
|
||||
std::optional<ModuleInfo::CompatibilityList> parseCompatibilityJson (std::string_view jsonData,
|
||||
std::ostream* optErrorOutput);
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
} // Steinberg::ModuelInfoLib
|
||||
Reference in New Issue
Block a user