matjson status 1/?

This commit is contained in:
altalk23 2024-11-09 09:05:16 +03:00
parent 8aa2c2283a
commit f2ec5fa3f8
29 changed files with 173 additions and 265 deletions

View file

@ -236,8 +236,8 @@ if (ANDROID)
endif()
set(MAT_JSON_AS_INTERFACE ON)
CPMAddPackage("gh:geode-sdk/json#cda9807")
CPMAddPackage("gh:geode-sdk/result@1.0.1")
CPMAddPackage("gh:geode-sdk/json#1b182dd")
CPMAddPackage("gh:geode-sdk/result@1.1.0")
CPMAddPackage("gh:fmtlib/fmt#10.2.1")
target_compile_definitions(${PROJECT_NAME} INTERFACE MAT_JSON_DYNAMIC=1)

View file

@ -41,7 +41,7 @@
#include <Geode/utils/casts.hpp>
#ifndef GEODE_IS_MEMBER_TEST
#include <matjson.hpp>
#include <matjson3.hpp>
#endif
namespace geode {

View file

@ -2,7 +2,7 @@
#include "../DefaultInclude.hpp"
#include "../utils/general.hpp"
#include <matjson.hpp>
#include <matjson3.hpp>
#include "Tulip.hpp"
#include <cinttypes>
#include <string_view>

View file

@ -3,7 +3,7 @@
#include "Event.hpp"
#include "Loader.hpp"
#include "Mod.hpp"
#include <matjson.hpp>
#include <matjson3.hpp>
namespace geode::ipc {
#ifdef GEODE_IS_WINDOWS

View file

@ -8,7 +8,7 @@
#include "Types.hpp"
#include <atomic>
#include <matjson.hpp>
#include <matjson3.hpp>
#include <mutex>
#include <optional>
#include <string_view>
@ -121,7 +121,7 @@ namespace geode {
* @param name The argument name
*/
template <class T>
std::optional<T> parseLaunchArgument(std::string_view const name) const {
Result<T> parseLaunchArgument(std::string_view const name) const {
auto str = this->getLaunchArgument(name);
if (!str.has_value()) {
return std::nullopt;
@ -133,10 +133,7 @@ namespace geode {
return std::nullopt;
}
auto value = jsonOpt.value();
if (!value.is<T>()) {
return std::nullopt;
}
return value.as<T>();
return value.template as<T>();
}
void queueInMainThread(ScheduledFunction&& func);

View file

@ -7,7 +7,7 @@
#include <ccTypes.h>
#include <chrono>
#include <filesystem>
#include <matjson.hpp>
#include <matjson3.hpp>
#include <type_traits>
#include <fmt/core.h>
// for formatting std::vector and such
@ -65,12 +65,6 @@ namespace std::filesystem {
}
}
namespace matjson {
GEODE_INLINE GEODE_HIDDEN std::string format_as(matjson::Value const& value) {
return value.dump(matjson::NO_INDENTATION);
}
}
namespace geode {
class Mod;

View file

@ -13,7 +13,7 @@
#include "Types.hpp"
#include "Loader.hpp"
#include <matjson.hpp>
#include <matjson3.hpp>
#include <matjson/stl_serialize.hpp>
#include <optional>
#include <string_view>
@ -51,22 +51,6 @@ namespace geode {
return action == ModRequestedAction::Uninstall || action == ModRequestedAction::UninstallWithSaveData;
}
template <class T>
static consteval bool typeImplementsIsJSON() {
using namespace matjson;
if constexpr (requires(const Value& json) { Serialize<std::decay_t<T>>::is_json(json); })
return true;
if constexpr (std::is_same_v<T, Value>) return true;
if constexpr (std::is_same_v<T, Array>) return true;
if constexpr (std::is_same_v<T, Object>) return true;
if constexpr (std::is_constructible_v<std::string, T>) return true;
if constexpr (std::is_integral_v<T> || std::is_floating_point_v<T>) return true;
if constexpr (std::is_same_v<T, bool>) return true;
if constexpr (std::is_same_v<T, std::nullptr_t>) return true;
return false;
}
GEODE_HIDDEN Mod* takeNextLoaderMod();
class ModImpl;
@ -264,24 +248,22 @@ namespace geode {
template <class T>
T getSavedValue(std::string_view const key) {
static_assert(geode::typeImplementsIsJSON<T>(), "T must implement is_json in matjson::Serialize<T>, otherwise this always returns default value.");
auto& saved = this->getSaveContainer();
if (saved.contains(key)) {
if (auto value = saved.try_get<T>(key)) {
return *value;
}
if (auto res = saved.get(key).andThen([](auto&& v) {
return v.template as<T>();
}); res.isOk()) {
return res.unwrap();
}
return T();
}
template <class T>
T getSavedValue(std::string_view const key, T const& defaultValue) {
static_assert(geode::typeImplementsIsJSON<T>(), "T must implement is_json in matjson::Serialize<T>, otherwise this always returns default value.");
auto& saved = this->getSaveContainer();
if (saved.contains(key)) {
if (auto value = saved.try_get<T>(key)) {
return *value;
}
if (auto res = saved.get(key).andThen([](auto&& v) {
return v.template as<T>();
}); res.isOk()) {
return res.unwrap();
}
saved[key] = defaultValue;
return defaultValue;

View file

@ -4,7 +4,7 @@
#include "../utils/VersionInfo.hpp"
#include "Types.hpp"
#include <matjson.hpp>
#include <matjson3.hpp>
#include <memory>
namespace geode {
@ -290,7 +290,8 @@ namespace geode {
template <>
struct matjson::Serialize<geode::ModMetadata> {
static matjson::Value to_json(geode::ModMetadata const& info) {
return info.toJSON();
static Value toJson(geode::ModMetadata const& value)
{
return Value(value.toJSON());
}
};

View file

@ -306,14 +306,15 @@ namespace geode {
}
bool load(matjson::Value const& json) override {
if (json.is<T>()) {
m_impl->value = json.as<T>();
return true;
}
auto res = json.as<T>();
if (res.isErr()) {
return false;
}
m_impl->value = res.unwrap();
return true;
}
bool save(matjson::Value& json) const override {
json = m_impl->value;
json = matjson::Value(m_impl->value);
return true;
}
};

View file

@ -2,7 +2,7 @@
#include "../DefaultInclude.hpp"
#include "../platform/cplatform.h"
#include <matjson.hpp>
#include <matjson3.hpp>
#include <string>

View file

@ -1,6 +1,6 @@
#pragma once
#include <matjson.hpp>
#include <matjson3.hpp>
#include "../loader/Log.hpp"
#include <set>
#include <variant>
@ -104,22 +104,15 @@ namespace geode {
return this->getJSONRef();
}
else {
try {
if (this->getJSONRef().is<T>()) {
return this->getJSONRef().as<T>();
auto res = this->getJSONRef().as<T>();
if (res) {
return res.unwrap();
}
else {
this->setError(
"unexpected type {}",
this->matJsonTypeToString(this->getJSONRef().type())
);
}
}
// matjson can throw variant exceptions too so you need to do this
catch(std::exception const& e) {
this->setError("unable to parse json: {}", e);
}
}
return std::nullopt;
}

View file

@ -2,7 +2,7 @@
#include "../DefaultInclude.hpp"
#include <string_view>
#include <matjson.hpp>
#include <matjson3.hpp>
#include <tuple>
#include <Geode/Result.hpp>
@ -256,25 +256,17 @@ namespace geode {
template <class V>
requires std::is_same_v<V, geode::VersionInfo> || std::is_same_v<V, geode::ComparableVersionInfo>
struct matjson::Serialize<V> {
static matjson::Value to_json(V const& info) {
return info.toString();
static geode::Result<V, std::string> fromJson(Value const& value)
{
auto str = GEODE_UNWRAP(value.asString());
auto version = GEODE_UNWRAP(V::parse(str).mapErr([](auto&& err) {
return geode::Err("Invalid version format: {}", err);
}));
return geode::Ok(version);
}
static bool is_json(matjson::Value const& json) {
if (json.is_string()) {
auto ver = V::parse(json.as_string());
return !ver.isErr();
}
return false;
}
static V from_json(matjson::Value const& json) {
auto ver = V::parse(json.as_string());
if (!ver) {
throw matjson::JsonException(
"Invalid version format: " + ver.unwrapErr()
);
}
return ver.unwrap();
static Value toJson(V const& value)
{
return Value(value.toString());
}
};

View file

@ -1,6 +1,6 @@
#pragma once
#include <matjson.hpp>
#include <matjson3.hpp>
#include "casts.hpp"
#include "general.hpp"
#include "../DefaultInclude.hpp"
@ -15,16 +15,14 @@
template <>
struct matjson::Serialize<cocos2d::ccColor3B> {
static matjson::Value GEODE_DLL to_json(cocos2d::ccColor3B const& color);
static cocos2d::ccColor3B GEODE_DLL from_json(matjson::Value const& color);
static bool GEODE_DLL is_json(matjson::Value const& json);
static geode::Result<cocos2d::ccColor3B, std::string> fromJson(Value const& value);
static Value toJson(cocos2d::ccColor3B const& value);
};
template <>
struct matjson::Serialize<cocos2d::ccColor4B> {
static matjson::Value GEODE_DLL to_json(cocos2d::ccColor4B const& color);
static cocos2d::ccColor4B GEODE_DLL from_json(matjson::Value const& color);
static bool GEODE_DLL is_json(matjson::Value const& json);
static geode::Result<cocos2d::ccColor4B, std::string> fromJson(Value const& value);
static Value toJson(cocos2d::ccColor4B const& value);
};
// operators for CC geometry

View file

@ -5,7 +5,7 @@
#include "../loader/Event.hpp"
#include "Task.hpp"
#include <matjson.hpp>
#include <matjson3.hpp>
#include <Geode/DefaultInclude.hpp>
#include <filesystem>
#include <string>
@ -13,14 +13,15 @@
template <>
struct matjson::Serialize<std::filesystem::path> {
static matjson::Value to_json(std::filesystem::path const& path) {
return path.string();
static geode::Result<std::filesystem::path, std::string> fromJson(Value const& value)
{
auto str = GEODE_UNWRAP(value.asString());
return geode::Ok(std::filesystem::path(str).make_preferred());
}
static std::filesystem::path from_json(matjson::Value const& value) {
return std::filesystem::path(value.as_string()).make_preferred();
}
static bool is_json(matjson::Value const& value) {
return value.is_string();
static Value toJson(std::filesystem::path const& value)
{
return Value(value.string());
}
};
@ -32,10 +33,7 @@ namespace geode::utils::file {
template <class T>
Result<T> readFromJson(std::filesystem::path const& file) {
GEODE_UNWRAP_INTO(auto json, readJson(file));
if (!json.is<T>()) {
return Err("JSON is not of type {}", typeid(T).name());
}
return Ok(json.as<T>());
return json.as<T>();
}
GEODE_DLL Result<> writeString(std::filesystem::path const& path, std::string const& data);

View file

@ -9,7 +9,7 @@
#include <string>
#include <vector>
#include <filesystem>
#include <matjson.hpp>
#include <matjson3.hpp>
#include <charconv>
#include <clocale>
#include <type_traits>
@ -170,7 +170,8 @@ namespace geode {
template<>
struct matjson::Serialize<geode::ByteVector> {
static matjson::Value to_json(geode::ByteVector const& bytes) {
static Value toJson(geode::ByteVector const& bytes)
{
return matjson::Array(bytes.begin(), bytes.end());
}
};

View file

@ -1,7 +1,7 @@
#pragma once
#include <Geode/loader/Loader.hpp> // another great circular dependency fix
#include <matjson.hpp>
#include <matjson3.hpp>
#include <Geode/Result.hpp>
#include "Task.hpp"
#include <chrono>

View file

@ -1,6 +1,6 @@
#include <Geode/loader/IPC.hpp>
#include "IPC.hpp"
#include <matjson.hpp>
#include <matjson3.hpp>
#include <Geode/loader/Mod.hpp>
using namespace geode::prelude;

View file

@ -1,7 +1,7 @@
#pragma once
#include <string>
#include <matjson.hpp>
#include <matjson3.hpp>
namespace geode::ipc {
void setup();

View file

@ -2,7 +2,7 @@
#include "FileWatcher.hpp"
#include <matjson.hpp>
#include <matjson3.hpp>
#include <Geode/loader/Dirs.hpp>
#include <Geode/loader/Loader.hpp>
#include <Geode/loader/Log.hpp>

View file

@ -1,6 +1,6 @@
#pragma once
#include <matjson.hpp>
#include <matjson3.hpp>
#include "ModPatch.hpp"
#include <Geode/loader/Loader.hpp>
#include <string_view>
@ -47,7 +47,7 @@ namespace geode {
/**
* Saved values
*/
matjson::Value m_saved = matjson::Object();
matjson::Value m_saved = matjson::Value();
/**
* Setting values. This is behind unique_ptr for interior mutability
*/

View file

@ -5,7 +5,7 @@
#include <Geode/utils/string.hpp>
#include <Geode/utils/general.hpp>
#include <about.hpp>
#include <matjson.hpp>
#include <matjson3.hpp>
#include <utility>
#include <clocale>

View file

@ -74,52 +74,44 @@ namespace geode {
template <>
struct matjson::Serialize<geode::ModMetadata::Dependency::Importance> {
static matjson::Value GEODE_DLL to_json(geode::ModMetadata::Dependency::Importance const& importance) {
switch (importance) {
case geode::ModMetadata::Dependency::Importance::Required: return {"required"};
case geode::ModMetadata::Dependency::Importance::Recommended: return {"recommended"};
case geode::ModMetadata::Dependency::Importance::Suggested: return {"suggested"};
default: return {"unknown"};
}
}
static geode::ModMetadata::Dependency::Importance GEODE_DLL from_json(matjson::Value const& importance) {
auto impStr = importance.as_string();
if (impStr == "required")
return geode::ModMetadata::Dependency::Importance::Required;
if (impStr == "recommended")
return geode::ModMetadata::Dependency::Importance::Recommended;
if (impStr == "suggested")
return geode::ModMetadata::Dependency::Importance::Suggested;
throw matjson::JsonException(R"(Expected importance to be "required", "recommended" or "suggested")");
static geode::Result<geode::ModMetadata::Dependency::Importance, std::string> fromJson(Value const& value)
{
auto str = GEODE_UNWRAP(value.asString());
if (str == "required") return geode::Ok(geode::ModMetadata::Dependency::Importance::Required);
if (str == "recommended") return geode::Ok(geode::ModMetadata::Dependency::Importance::Recommended);
if (str == "suggested") return geode::Ok(geode::ModMetadata::Dependency::Importance::Suggested);
return geode::Err("Invalid importance");
}
static bool is_json(matjson::Value const& value) {
return value.is_string();
static Value toJson(geode::ModMetadata::Dependency::Importance const& value)
{
switch (value) {
case geode::ModMetadata::Dependency::Importance::Required: return "required";
case geode::ModMetadata::Dependency::Importance::Recommended: return "recommended";
case geode::ModMetadata::Dependency::Importance::Suggested: return "suggested";
}
return "unknown";
}
};
template <>
struct matjson::Serialize<geode::ModMetadata::Incompatibility::Importance> {
static matjson::Value GEODE_DLL to_json(geode::ModMetadata::Incompatibility::Importance const& importance) {
switch (importance) {
case geode::ModMetadata::Incompatibility::Importance::Breaking: return {"breaking"};
case geode::ModMetadata::Incompatibility::Importance::Conflicting: return {"conflicting"};
case geode::ModMetadata::Incompatibility::Importance::Superseded: return {"superseded"};
default: return {"unknown"};
}
}
static geode::ModMetadata::Incompatibility::Importance GEODE_DLL from_json(matjson::Value const& importance) {
auto impStr = importance.as_string();
if (impStr == "breaking")
return geode::ModMetadata::Incompatibility::Importance::Breaking;
if (impStr == "conflicting")
return geode::ModMetadata::Incompatibility::Importance::Conflicting;
if (impStr == "superseded")
return geode::ModMetadata::Incompatibility::Importance::Superseded;
throw matjson::JsonException(R"(Expected importance to be "breaking", "conflicting", or "superseded")");
static geode::Result<geode::ModMetadata::Incompatibility::Importance, std::string> fromJson(Value const& value)
{
auto str = GEODE_UNWRAP(value.asString());
if (str == "breaking") return geode::Ok(geode::ModMetadata::Incompatibility::Importance::Breaking);
if (str == "conflicting") return geode::Ok(geode::ModMetadata::Incompatibility::Importance::Conflicting);
if (str == "superseded") return geode::Ok(geode::ModMetadata::Incompatibility::Importance::Superseded);
return geode::Err("Invalid importance");
}
static bool is_json(matjson::Value const& value) {
return value.is_string();
static Value toJson(geode::ModMetadata::Incompatibility::Importance const& value)
{
switch (value) {
case geode::ModMetadata::Incompatibility::Importance::Breaking: return "breaking";
case geode::ModMetadata::Incompatibility::Importance::Conflicting: return "conflicting";
case geode::ModMetadata::Incompatibility::Importance::Superseded: return "superseded";
}
return "unknown";
}
};

View file

@ -1,7 +1,7 @@
#pragma once
#include <string>
#include <matjson.hpp>
#include <matjson3.hpp>
#include <Geode/loader/Event.hpp>
namespace geode::updater {

View file

@ -4,7 +4,7 @@
#include <Geode/DefaultInclude.hpp>
#include <Geode/utils/web.hpp>
#include <chrono>
#include <matjson.hpp>
#include <matjson3.hpp>
#include <vector>
using namespace geode::prelude;

View file

@ -4,7 +4,7 @@
#include <Geode/utils/VersionInfo.hpp>
#include <Geode/utils/general.hpp>
#include <matjson.hpp>
#include <matjson3.hpp>
using namespace geode::prelude;

View file

@ -1,123 +1,82 @@
#include <Geode/modify/LoadingLayer.hpp>
#include <Geode/utils/cocos.hpp>
#include <matjson.hpp>
#include <matjson3.hpp>
#include <charconv>
#include <Geode/binding/CCTextInputNode.hpp>
#include <Geode/binding/GameManager.hpp>
using namespace geode::prelude;
bool matjson::Serialize<ccColor3B>::is_json(matjson::Value const& json) {
if (json.is_array()) {
return json.as_array().size() == 3;
Result<cocos2d::ccColor3B, std::string> matjson::Serialize<ccColor3B>::fromJson(matjson::Value const& value) {
if (value.isArray()) {
auto arr = GEODE_UNWRAP(value.asArray());
if (arr.size() == 3) {
auto r = GEODE_UNWRAP(arr[0].asInt());
auto g = GEODE_UNWRAP(arr[1].asInt());
auto b = GEODE_UNWRAP(arr[2].asInt());
return Ok(cocos2d::ccc3(r, g, b));
}
if (json.is_object()) {
return json.contains("r") && json.contains("g") && json.contains("b");
return Err("Expected color array to have 3 items");
}
if (json.is_string()) {
return !cc3bFromHexString(json.as_string()).isErr();
if (value.isObject()) {
auto r = GEODE_UNWRAP(GEODE_UNWRAP(value.get("r")).asInt());
auto g = GEODE_UNWRAP(GEODE_UNWRAP(value.get("g")).asInt());
auto b = GEODE_UNWRAP(GEODE_UNWRAP(value.get("b")).asInt());
return Ok(cocos2d::ccc3(r, g, b));
}
return false;
if (value.isString()) {
auto hex = GEODE_UNWRAP(value.asString());
auto res = cc3bFromHexString(hex);
if (!res) {
return Err("Invalid hex color string: {}", res.unwrapErr());
}
return Ok(res.unwrap());
}
}
matjson::Value matjson::Serialize<ccColor3B>::toJson(cocos2d::ccColor3B const& value) {
return matjson::makeObject({
{ "r", value.r },
{ "g", value.g },
{ "b", value.b }
});
}
matjson::Value matjson::Serialize<ccColor3B>::to_json(ccColor3B const& color) {
return matjson::Object {
{ "r", color.r },
{ "g", color.g },
{ "b", color.b }
};
Result<cocos2d::ccColor4B, std::string> matjson::Serialize<ccColor4B>::fromJson(matjson::Value const& value) {
if (value.isArray()) {
auto arr = GEODE_UNWRAP(value.asArray());
if (arr.size() == 4) {
auto r = GEODE_UNWRAP(arr[0].asInt());
auto g = GEODE_UNWRAP(arr[1].asInt());
auto b = GEODE_UNWRAP(arr[2].asInt());
auto a = GEODE_UNWRAP(arr[3].asInt());
return Ok(cocos2d::ccc4(r, g, b, a));
}
return Err("Expected color array to have 4 items");
}
if (value.isObject()) {
auto r = GEODE_UNWRAP(GEODE_UNWRAP(value.get("r")).asInt());
auto g = GEODE_UNWRAP(GEODE_UNWRAP(value.get("g")).asInt());
auto b = GEODE_UNWRAP(GEODE_UNWRAP(value.get("b")).asInt());
auto a = GEODE_UNWRAP(GEODE_UNWRAP(value.get("a")).asInt());
return Ok(cocos2d::ccc4(r, g, b, a));
}
if (value.isString()) {
auto hex = GEODE_UNWRAP(value.asString());
auto res = cc4bFromHexString(hex);
if (!res) {
return Err("Invalid hex color string: {}", res.unwrapErr());
}
return Ok(res.unwrap());
}
}
ccColor3B matjson::Serialize<ccColor3B>::from_json(matjson::Value const& json) {
ccColor3B color;
// array
if (json.is_array()) {
if (json.as_array().size() == 3) {
color.r = json[0].as_int();
color.g = json[1].as_int();
color.b = json[2].as_int();
}
else {
throw matjson::JsonException("Expected color array to have 3 items");
}
}
// object
else if (json.is_object()) {
color.r = json["r"].as_int();
color.g = json["g"].as_int();
color.b = json["b"].as_int();
}
// hex string
else if (json.is_string()) {
auto c = cc3bFromHexString(json.as_string());
if (!c) {
throw matjson::JsonException("Invalid color hex string");
}
color = c.unwrap();
}
// bad
else {
throw matjson::JsonException("Expected color to be array, object or hex string");
}
return color;
}
bool matjson::Serialize<ccColor4B>::is_json(matjson::Value const& json) {
if (json.is_array()) {
return json.as_array().size() == 4;
}
if (json.is_object()) {
return json.contains("r") && json.contains("g") && json.contains("b") && json.contains("a");
}
if (json.is_string()) {
return !cc4bFromHexString(json.as_string()).isErr();
}
return false;
}
matjson::Value matjson::Serialize<ccColor4B>::to_json(ccColor4B const& color) {
return matjson::Object {
{ "r", color.r },
{ "g", color.g },
{ "b", color.b },
{ "a", color.a }
};
}
ccColor4B matjson::Serialize<ccColor4B>::from_json(matjson::Value const& json) {
ccColor4B color;
// array
if (json.is_array()) {
if (json.as_array().size() == 4) {
color.r = json[0].as_int();
color.g = json[1].as_int();
color.b = json[2].as_int();
color.a = json[3].as_int();
}
else {
throw matjson::JsonException("Expected color array to have 4 items");
}
}
// object
else if (json.is_object()) {
color.r = json["r"].as_int();
color.g = json["g"].as_int();
color.b = json["b"].as_int();
color.a = json["a"].as_int();
}
// hex string
else if (json.is_string()) {
auto c = cc4bFromHexString(json.as_string());
if (!c) {
throw matjson::JsonException("Invalid color hex string: " + c.unwrapErr());
}
color = c.unwrap();
}
// bad
else {
throw matjson::JsonException("Expected color to be array, object or hex string");
}
return color;
matjson::Value matjson::Serialize<ccColor4B>::toJson(cocos2d::ccColor4B const& value) {
return matjson::makeObject({
{ "r", value.r },
{ "g", value.g },
{ "b", value.b },
{ "a", value.a }
});
}
Result<ccColor3B> geode::cocos::cc3bFromHexString(std::string const& rawHexValue, bool permissive) {

View file

@ -3,7 +3,7 @@
#include <Geode/utils/file.hpp>
#include <Geode/utils/map.hpp>
#include <Geode/utils/string.hpp>
#include <matjson.hpp>
#include <matjson3.hpp>
#include <fstream>
#include <mz.h>
#include <mz_os.h>

View file

@ -3,7 +3,7 @@
#include <filesystem>
#include <fmt/core.h>
#include <fstream>
#include <matjson.hpp>
#include <matjson3.hpp>
#include <system_error>
#define CURL_STATICLIB
#include <curl/curl.h>

View file

@ -54,7 +54,7 @@ struct $modify(MenuLayer) {
log::popNest();
log::info("Mod has launch arg 'mod-arg': {}", Mod::get()->hasLaunchArgument("mod-arg"));
log::info("Loader flag 'bool-arg': {}", Loader::get()->getLaunchFlag("bool-arg"));
log::info("Loader int 'int-arg': {}", Loader::get()->parseLaunchArgument<int>("int-arg"));
log::info("Loader int 'int-arg': {}", Loader::get()->parseLaunchArgument<int>("int-arg").unwrapOr(0));
log::popNest();
return true;